osmo-v5/src/v5x_protocol.c

625 lines
26 KiB
C

/* (C) 2021-2022 by Harald Welte <laforge@gnumonks.org>
* (C) 2022-2023 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.
*
*/
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <arpa/inet.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/lapd_core.h>
#include "logging.h"
#include "v5x_internal.h"
#include "v5x_protocol.h"
#include "lapv5.h"
#include "v5x_le_ctrl_fsm.h"
#include "v5x_le_port_fsm.h"
#include "v5x_le_pstn_fsm.h"
#include "v52_le_bcc_fsm.h"
#include "v52_le_pp_fsm.h"
#include "v5x_le_management.h"
const struct tlv_definition v5x_ctrl_tlv_def = {
.def = {
/* single byte: PSTN / G.964 Table 17 */
[V5X_CTRL_IEI_PULSE_NOTIFICATION] = { TLV_TYPE_SINGLE_TV, 0 },
[V5X_CTRL_IEI_LINE_INFORMATION] = { TLV_TYPE_SINGLE_TV, 0 },
[V5X_CTRL_IEI_STATE] = { TLV_TYPE_SINGLE_TV, 0 },
[V5X_CTRL_IEI_AUTONOMOUS_SIG_SEQ] = { TLV_TYPE_SINGLE_TV, 0 },
[V5X_CTRL_IEI_SEQUENCE_RESPONSE] = { TLV_TYPE_SINGLE_TV, 0 },
/* single byte: ISDN / G.964 Table 53 */
[V5X_CTRL_IEI_PERFORMANCE_GRADING] = { TLV_TYPE_SINGLE_TV, 0 },
[V5X_CTRL_IEI_REJECTION_CAUSE] = { TLV_TYPE_SINGLE_TV, 0 },
/* variable length: PSTN / G.964 Table 17 */
[V5X_CTRL_IEI_SEQUENCE_NR] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_CADENCED_RINGING] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_PULSED_SIGNAL] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_STEADY_SIGNAL] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_DIGIT_SIGNAL] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_RECOGNITION_TIME] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_ENABLE_AUTONOMOUS_ACK] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_DISABLE_AUTONOMOUS_ACK] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_CAUSE] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_RESOURCE_UNAVAILABLE] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_ENABLE_METERING] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_METERING_REPORT] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_ATTENUATION] = { TLV_TYPE_TLV, 0 },
/* variable length: ISDN / G.964 Table 53 */
[V5X_CTRL_IEI_CTRL_F_ELEMENT] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_CTRL_F_ID] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_VARIANT] = { TLV_TYPE_TLV, 0 },
[V5X_CTRL_IEI_INTERFACE_ID] = { TLV_TYPE_TLV, 0 },
/* variable length: LCP / G.965 Table FIXME */
[V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION] = { TLV_TYPE_TLV, 0 },
/* variable length: BCC */
[V52_CTRL_IEI_BCC_USER_PORT_ID] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_BCC_V5_TS_ID] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_BCC_MULTI_TS_MAP] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_BCC_REJECT_CAUSE] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY] = { TLV_TYPE_TLV, 0 },
/* variable-length: Protection */
[V52_CTRL_IEI_PP_SEQUENCE_NR] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_PP_REJECTION_CAUSE] = { TLV_TYPE_TLV, 0 },
[V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE] = { TLV_TYPE_TLV, 0 },
},
};
const struct value_string v5x_ctrl_msg_typ_str[] = {
{ V5X_CTRL_MSGT_ESTABLISH, "ESTABLISH" },
{ V5X_CTRL_MSGT_ESTABLISH_ACK, "ESTABLISH_ACK" },
{ V5X_CTRL_MSGT_SIGNAL, "SIGNAL" },
{ V5X_CTRL_MSGT_SIGNAL_ACK, "SIGNAL_ACK" },
{ V5X_CTRL_MSGT_DISCONNECT, "DISCONNECT" },
{ V5X_CTRL_MSGT_DISCONNECT_COMPLETE, "DISCONNECT_COMPLETE" },
{ V5X_CTRL_MSGT_STATUS_ENQUIRY, "STATUS_ENQUIRY" },
{ V5X_CTRL_MSGT_STATUS, "STATUS" },
{ V5X_CTRL_MSGT_PROTOCOL_PARAMETER, "PROTOCOL_PARAMETER" },
{ V5X_CTRL_MSGT_PORT_CTRL, "PORT_CTRL" },
{ V5X_CTRL_MSGT_PORT_CTRL_ACK, "PORT_CTRL_ACK" },
{ V5X_CTRL_MSGT_COMMON_CTRL, "COMMON_CTRL" },
{ V5X_CTRL_MSGT_COMMON_CTRL_ACK, "COMMON_CTRL_ACK" },
{ V52_CTRL_MSGT_PP_SWITCH_OVER_REQ, "SWITCH_OVER_REQ" },
{ V52_CTRL_MSGT_PP_SWITCH_OVER_COM, "SWITCH_OVER_COM" },
{ V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM, "OS_SWITCH_OVER_COM" },
{ V52_CTRL_MSGT_PP_SWITCH_OVER_ACK, "SWITCH_OVER_ACK" },
{ V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT, "SWITCH_OVER_REJECT" },
{ V52_CTRL_MSGT_PP_PROTOCOL_ERROR, "PROTOCOL_ERROR" },
{ V52_CTRL_MSGT_PP_RESET_SN_COM, "RESET_SN_COM" },
{ V52_CTRL_MSGT_PP_RESET_SN_ACK, "RESET_SN_ACK" },
{ V52_CTRL_MSGT_ALLOCATION, "ALLOCATION" },
{ V52_CTRL_MSGT_ALLOCATION_COMPLETE, "ALLOCATION_COMPLETE" },
{ V52_CTRL_MSGT_ALLOCATION_REJECT, "ALLOCATION_REJECT" },
{ V52_CTRL_MSGT_DE_ALLOCATION, "DE_ALLOCATION" },
{ V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE, "DE_ALLOCATION_COMPLETE" },
{ V52_CTRL_MSGT_DE_ALLOCATION_REJECT, "DE_ALLOCATION_REJECT" },
{ V52_CTRL_MSGT_AUDIT, "AUDIT" },
{ V52_CTRL_MSGT_AUDIT_COMPLETE, "AUDIT_COMPLETE" },
{ V52_CTRL_MSGT_AN_FAULT, "AN_FAULT" },
{ V52_CTRL_MSGT_AN_FAULT_ACK, "AN_FAULT_ACKNOWLEDGE" },
{ V52_CTRL_MSGT_PROTOCOL_ERROR, "PROTOCOL_ERROR" },
{ V52_CTRL_MSGT_LCP_LINK_CTRL, "LINK_CTRL" },
{ V52_CTRL_MSGT_LCP_LINK_CTRL_ACK, "LINK_CTRL_ACK" },
{ 0, NULL }
};
const struct value_string v5x_ctrl_iei_str[] = {
{ V5X_CTRL_IEI_PULSE_NOTIFICATION, "PULSE_NOTIFICATION" },
{ V5X_CTRL_IEI_LINE_INFORMATION, "LINE_INFORMATION" },
{ V5X_CTRL_IEI_STATE, "STATE" },
{ V5X_CTRL_IEI_AUTONOMOUS_SIG_SEQ, "AUTONOMOUS_SIG_SEQ" },
{ V5X_CTRL_IEI_SEQUENCE_RESPONSE, "SEQUENCE_RESPONSE" },
{ V5X_CTRL_IEI_PERFORMANCE_GRADING, "PERFORMANCE_GRADING" },
{ V5X_CTRL_IEI_REJECTION_CAUSE, "REJECTION_CAUSE" },
{ V5X_CTRL_IEI_SEQUENCE_NR, "SEQUENCE_NR" },
{ V5X_CTRL_IEI_CADENCED_RINGING, "CADENCED_RINGING" },
{ V5X_CTRL_IEI_PULSED_SIGNAL, "PULSED_SIGNAL" },
{ V5X_CTRL_IEI_STEADY_SIGNAL, "STEADY_SIGNAL" },
{ V5X_CTRL_IEI_DIGIT_SIGNAL, "DIGIT_SIGNAL" },
{ V5X_CTRL_IEI_RECOGNITION_TIME, "RECOGNITION_TIME" },
{ V5X_CTRL_IEI_ENABLE_AUTONOMOUS_ACK, "ENABLE_AUTONOMOUS_ACK" },
{ V5X_CTRL_IEI_DISABLE_AUTONOMOUS_ACK, "DISABLE_AUTONOMOUS_ACK" },
{ V5X_CTRL_IEI_CAUSE, "CAUSE" },
{ V5X_CTRL_IEI_RESOURCE_UNAVAILABLE, "RESOURCE_UNAVAILABLE" },
{ V5X_CTRL_IEI_ENABLE_METERING, "ENABLE_METERING" },
{ V5X_CTRL_IEI_METERING_REPORT, "METERING_REPORT" },
{ V5X_CTRL_IEI_ATTENUATION, "ATTENUATION" },
{ V5X_CTRL_IEI_CTRL_F_ELEMENT, "CTRL_F_ELEMENT" },
{ V5X_CTRL_IEI_CTRL_F_ID, "CTRL_F_ID" },
{ V5X_CTRL_IEI_VARIANT, "VARIANT" },
{ V5X_CTRL_IEI_INTERFACE_ID, "INTERFACE_ID" },
{ V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, "LCP_LINK_CTRL_FUNCTION" },
{ V52_CTRL_IEI_BCC_USER_PORT_ID, "BCC_USER_PORT_ID" },
{ V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID, "BCC_ISDN_PORT_TS_ID" },
{ V52_CTRL_IEI_BCC_V5_TS_ID, "BCC_V5_TS_ID" },
{ V52_CTRL_IEI_BCC_MULTI_TS_MAP, "BCC_MULTI_TS_MAP" },
{ V52_CTRL_IEI_BCC_REJECT_CAUSE, "BCC_REJECT_CAUSE" },
{ V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE, "BCC_PROTOCOL_ERROR_CAUSE" },
{ V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE, "BCC_CONNECTION_INCOMPLETE" },
{ V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY, "BCC_INFO_TRANSFER_CAPABILITY" },
{ V52_CTRL_IEI_PP_SEQUENCE_NR, "PP_SEQUENCE_NR" },
{ V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID, "PP_PHYSICAL_C_CHAN_ID" },
{ V52_CTRL_IEI_PP_REJECTION_CAUSE, "PP_REJECTION_CAUSE" },
{ V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE, "PP_PROTOCOL_ERROR_CAUSE" },
{ 0, NULL }
};
const struct value_string v5x_ctrl_func_el_str[] = {
{ V5X_CTRL_FE101_ACTIVATE_ACCESS, "FE101_ACTIVATE_ACCESS" },
{ V5X_CTRL_FE102_ACT_INIT_BY_USER, "FE102_ACT_INIT_BY_USER" },
{ V5X_CTRL_FE103_DS_ACTIVATED, "FE103_DS_ACTIVATED" },
{ V5X_CTRL_FE104_ACCESS_ACTIVATED, "FE104_ACCESS_ACTIVATED" },
{ V5X_CTRL_FE105_DEACTIVATE_ACCESS, "FE105_DEACTIVATE_ACCESS" },
{ V5X_CTRL_FE106_ACCESS_DEACTIVATED, "FE106_ACCESS_DEACTIVATED" },
{ V5X_CTRL_FE201_UNBLOCK, "FE201_UNBLOCK" },
{ V5X_CTRL_FE203_BLOCK, "FE203_BLOCK" },
{ V5X_CTRL_FE205_BLOCK_REQ, "FE205_BLOCK_REQ" },
{ V5X_CTRL_FE206_PERFORMANCE_GRADING, "FE206_PERFORMANCE_GRADING" },
{ V5X_CTRL_FE207_D_CHANNEL_BLOCK, "FE207_D_CHANNEL_BLOCK" },
{ V5X_CTRL_FE208_D_CHANNEL_UNBLOCK, "FE208_D_CHANNEL_UNBLOCK" },
{ 0, NULL }
};
const struct value_string v5x_ctrl_func_id_str[] = {
{ V5X_CTRL_ID_VERIFY_RE_PROVISIONING, "VERIFY_RE_PROVISIONING" },
{ V5X_CTRL_ID_READY_FOR_RE_PROVISIONING, "READY_FOR_RE_PROVISIONING" },
{ V5X_CTRL_ID_NOT_READY_FOR_RE_PROVISIONING, "NOT_READY_FOR_RE_PROVISIONING" },
{ V5X_CTRL_ID_SWITCH_OVER_TO_NEW_VARIANT, "SWITCH_OVER_TO_NEW_VARIANT" },
{ V5X_CTRL_ID_RE_PROVISIONING_STARTED, "RE_PROVISIONING_STARTED" },
{ V5X_CTRL_ID_CANNOT_RE_PROVISION, "CANNOT_RE_PROVISION" },
{ V5X_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID, "REQUEST_VARIANT_AND_INTERFACE_ID" },
{ V5X_CTRL_ID_VARIANT_AND_INTERFACE_ID, "VARIANT_AND_INTERFACE_ID" },
{ V5X_CTRL_ID_BLOCKING_STARTED, "BLOCKING_STARTED" },
{ V5X_CTRL_ID_RESTART_REQUEST, "RESTART_REQUEST" },
{ V5X_CTRL_ID_RESTART_COMPLETE, "RESTART_COMPLETE" },
{ 0, NULL }
};
const struct value_string v5x_cause_type_str[] = {
{ V5X_CAUSE_T_RESP_TO_STATUS_ENQ, "Response to status enquiry" },
{ V5X_CAUSE_T_PROTOCOL_DISC_ERROR, "Protocol discriminator error" },
{ V5X_CAUSE_T_MSG_TYPE_UNRECOGNIZED, "Message type unrecognized" },
{ V5X_CAUSE_T_OUT_OF_SEQUENCE_IE, "Out of sequence information element" },
{ V5X_CAUSE_T_REPEATED_OPT_IE, "Repeated optional information element" },
{ V5X_CAUSE_T_MAND_IE_MISSING, "Mandatory information element missing" },
{ V5X_CAUSE_T_UNRECOGNIZED_IE, "Unrecognized information element" },
{ V5X_CAUSE_T_MAND_IE_CONTENT_ERROR, "Mandatory information element content error" },
{ V5X_CAUSE_T_OPT_IE_CONTENT_ERROR, "Optional information element content error" },
{ V5X_CAUSE_T_MSG_INCOMP_STATE, "Message not compatible with protocol state" },
{ V5X_CAUSE_T_REPEATED_MAND_IE, "Repeated mandatory information element" },
{ V5X_CAUSE_T_TOO_MANY_IE, "Too many information elements" },
{ V5X_CAUSE_T_REF_NR_CODING_ERROR, "Reference Number coding error" },
{ 0, NULL }
};
const struct value_string v52_bcc_reject_cause_type_str[] = {
{ V52_BCC_REJECT_CAUSE_T_UNSPECIFIED, "Unspecified" },
{ V52_BCC_REJECT_CAUSE_T_ACCESS_NET_FAUL, "Access network fault" },
{ V52_BCC_REJECT_CAUSE_T_ACCESS_NET_BLOCKED, "Access network blocked (internally)" },
{ V52_BCC_REJECT_CAUSE_T_CONN_PRESENT_PSTN,
"Connection already present at the PSTN user port to a different V5-time slot" },
{ V52_BCC_REJECT_CAUSE_T_CONN_PRESENT_V5_TS,
"Connection already present at the V5-time slot(s) to a different port or ISDN user port time slot" },
{ V52_BCC_REJECT_CAUSE_T_CONN_PRESENT_ISDN,
"Connection already present at the ISDN user port time slot(s) to a different V5-time slot(s)" },
{ V52_BCC_REJECT_CAUSE_T_USER_PORT_UNAVAILABLE, "User port unavailable (blocked)" },
{ V52_BCC_REJECT_CAUSE_T_DEALLOC_INCOMPAT_DATA,
"De-allocation cannot be completed due to incompatible data content" },
{ V52_BCC_REJECT_CAUSE_T_DEALLOC_V5_TS_DATA,
"De-allocation cannot be completed due to V5-time slot(s) data incompatibility" },
{ V52_BCC_REJECT_CAUSE_T_DEALLOC_PORT_DATA,
"De-allocation cannot be completed due to port data incompatibility" },
{ V52_BCC_REJECT_CAUSE_T_DEALLOC_USER_TS_DATA,
"De-allocation cannot be completed due to user port time slot(s) data incompatibility" },
{ V52_BCC_REJECT_CAUSE_T_USER_NOT_PROVISIONED, "User port not provisioned" },
{ V52_BCC_REJECT_CAUSE_T_INVALID_V5_TS_IDENT, "Invalid V5-time slot(s) identification(s)" },
{ V52_BCC_REJECT_CAUSE_T_INVALID_V5_LINK_IDENT, "Invalid V5 2048 kbit/s link identification" },
{ V52_BCC_REJECT_CAUSE_T_INVALID_USER_TS_IDENT, "Invalid user port time slot(s) identification(s)" },
{ V52_BCC_REJECT_CAUSE_T_V5_TS_USED_CCHANNEL, "V5-time slot(s) being used as physical C-channel(s)" },
{ V52_BCC_REJECT_CAUSE_T_V5_LINK_UNAVAILABLE, "V5-link unavailable (blocked)" },
{ 0, NULL }
};
const struct value_string v52_pp_reject_cause_type_str[] = {
{ V52_PP_REJECT_CAUSE_T_NO_STANDBY_CC_AVAILABLE,"No standby C-channel available" },
{ V52_PP_REJECT_CAUSE_T_TARGET_CC_NOT_OPER, "Target physical C-channel not operational" },
{ V52_PP_REJECT_CAUSE_T_TARGET_CC_NOT_PROV, "Target physical C-channel not provisioned" },
{ V52_PP_REJECT_CAUSE_T_PROT_SWITCHING_IMPOSS, "Protection switching impossible (AN/LE failure)" },
{ V52_PP_REJECT_CAUSE_T_PROT_GROUP_MISMATCH, "Protection group mismatch" },
{ V52_PP_REJECT_CAUSE_T_ALLOC_EXISTS_ALREADY, "Requested allocation exists already" },
{ V52_PP_REJECT_CAUSE_T_TARGET_CC_ALREADY_CC, "Target physical C-channel already has logical C-channel" },
{ 0, NULL }
};
static const uint8_t signal_mand_ies[] = { V5X_CTRL_IEI_SEQUENCE_NR };
static const uint8_t signal_ack_mand_ies[] = { V5X_CTRL_IEI_SEQUENCE_NR };
static const uint8_t status_mand_ies[] = { V5X_CTRL_IEI_STATE, V5X_CTRL_IEI_CAUSE };
static const uint8_t prot_par_mand_ies[] = { V5X_CTRL_IEI_SEQUENCE_NR };
static const uint8_t port_ctrl_mand_ies[] = { V5X_CTRL_IEI_CTRL_F_ELEMENT };
static const uint8_t common_ctrl_mand_ies[] = { V5X_CTRL_IEI_CTRL_F_ID };
static const uint8_t lcp_mand_ies[] = { V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION };
static const uint8_t alloc_mand_ies[] = { V52_CTRL_IEI_BCC_USER_PORT_ID };
static const uint8_t alloc_rej_mand_ies[] = { V52_CTRL_IEI_BCC_REJECT_CAUSE };
static const uint8_t prot_err_mand_ies[] = { V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE };
static const uint8_t pp_swo_mand_ies[] = {
V52_CTRL_IEI_PP_SEQUENCE_NR,
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID
};
static const uint8_t pp_swo_rej_mand_ies[] = {
V52_CTRL_IEI_PP_SEQUENCE_NR,
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID,
V52_CTRL_IEI_PP_REJECTION_CAUSE,
};
static const uint8_t pp_perr_mand_ies[] = {
V52_CTRL_IEI_PP_SEQUENCE_NR,
V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE,
};
const struct osmo_tlv_prot_def v5x_ctrl_msg_tlv = {
.name = "V5X_CTRL",
.tlv_def = &v5x_ctrl_tlv_def,
.msg_def = {
/* G.964 Section 13.3 */
[V5X_CTRL_MSGT_ESTABLISH] = MSG_DEF("ESTABLISH", NULL, 0),
[V5X_CTRL_MSGT_ESTABLISH_ACK] = MSG_DEF("ESTABLISH", NULL, 0),
[V5X_CTRL_MSGT_SIGNAL] = MSG_DEF("SIGNAL", signal_mand_ies, 0),
[V5X_CTRL_MSGT_SIGNAL_ACK] = MSG_DEF("SIGNAL_ACK", signal_ack_mand_ies, 0),
[V5X_CTRL_MSGT_STATUS] = MSG_DEF("STATUS", status_mand_ies, 0),
[V5X_CTRL_MSGT_STATUS_ENQUIRY] = MSG_DEF("STATUS_ENQUIRY", NULL, 0),
[V5X_CTRL_MSGT_DISCONNECT] = MSG_DEF("DISCONNECT", NULL, 0),
[V5X_CTRL_MSGT_DISCONNECT_COMPLETE] = MSG_DEF("DISCONNECT_COMPLETE", NULL, 0),
[V5X_CTRL_MSGT_PROTOCOL_PARAMETER] = MSG_DEF("PROTOCOL_PARAMETER", prot_par_mand_ies, 0),
/* G.964 Section 14.4 */
[V5X_CTRL_MSGT_PORT_CTRL] = MSG_DEF("PORT_CTRL", port_ctrl_mand_ies, 0),
[V5X_CTRL_MSGT_PORT_CTRL_ACK] = MSG_DEF("PORT_CTRL_ACK", port_ctrl_mand_ies, 0),
[V5X_CTRL_MSGT_COMMON_CTRL] = MSG_DEF("COMMON_CTRL", common_ctrl_mand_ies, 0),
[V5X_CTRL_MSGT_COMMON_CTRL_ACK] = MSG_DEF("COMMON_CTRL_ACK", common_ctrl_mand_ies, 0),
/* G.965 Section 16.3 LCP */
[V52_CTRL_MSGT_LCP_LINK_CTRL] = MSG_DEF("LINK_CONTROL", lcp_mand_ies, 0),
[V52_CTRL_MSGT_LCP_LINK_CTRL_ACK] = MSG_DEF("LINK_CONTROL_ACK", lcp_mand_ies, 0),
/* G.965 Section 17.3 BCC */
[V52_CTRL_MSGT_ALLOCATION] = MSG_DEF("ALLOCATION", alloc_mand_ies, 0),
[V52_CTRL_MSGT_ALLOCATION_COMPLETE] = MSG_DEF("ALLOCATION_COMPLETE", NULL, 0),
[V52_CTRL_MSGT_ALLOCATION_REJECT] = MSG_DEF("ALLOCATION_REJECT", alloc_rej_mand_ies, 0),
[V52_CTRL_MSGT_DE_ALLOCATION] = MSG_DEF("DE_ALLOCATION", alloc_mand_ies, 0),
[V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE] = MSG_DEF("DE_ALLOCATION_COMPLETE", NULL, 0),
[V52_CTRL_MSGT_DE_ALLOCATION_REJECT] = MSG_DEF("DE_ALLOCATION_REJECT", alloc_rej_mand_ies, 0),
[V52_CTRL_MSGT_AUDIT] = MSG_DEF("AUDIT", NULL, 0),
[V52_CTRL_MSGT_AUDIT_COMPLETE] = MSG_DEF("AUDIT_COMPLETE", NULL, 0),
[V52_CTRL_MSGT_AN_FAULT] = MSG_DEF("AN_FAULT", NULL, 0),
[V52_CTRL_MSGT_AN_FAULT_ACK] = MSG_DEF("AN_FAULT_ACK", NULL, 0),
[V52_CTRL_MSGT_PROTOCOL_ERROR] = MSG_DEF("PROTOCOL_ERROR", prot_err_mand_ies, 0),
/* G.965 Section 18.3 PP */
[V52_CTRL_MSGT_PP_SWITCH_OVER_REQ] = MSG_DEF("SWITCH_OVER_REQ", pp_swo_mand_ies, 0),
[V52_CTRL_MSGT_PP_SWITCH_OVER_COM] = MSG_DEF("SWITCH_OVER_COM", pp_swo_mand_ies, 0),
[V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM] = MSG_DEF("OS_SWITCH_OVER_COM", pp_swo_mand_ies, 0),
[V52_CTRL_MSGT_PP_SWITCH_OVER_ACK] = MSG_DEF("SWITCH_OVER_ACK", pp_swo_mand_ies, 0),
[V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT] = MSG_DEF("SWITCH_OVER_REJECT", pp_swo_rej_mand_ies, 0),
[V52_CTRL_MSGT_PP_PROTOCOL_ERROR] = MSG_DEF("PROTOCOL_ERROR", pp_perr_mand_ies, 0),
[V52_CTRL_MSGT_PP_RESET_SN_COM] = MSG_DEF("RESET_SN_COM", NULL, 0),
[V52_CTRL_MSGT_PP_RESET_SN_ACK] = MSG_DEF("RESET_SN_ACK", NULL, 0),
},
.ie_def = {
/* single byte, only upper nibble matches IEI */
[V5X_CTRL_IEI_PULSE_NOTIFICATION] = { 0, "PULSE_NOTIFICATION" },
[V5X_CTRL_IEI_LINE_INFORMATION] = { 0, "LINE_INFORMATION" },
[V5X_CTRL_IEI_STATE] = { 0, "STATE" },
[V5X_CTRL_IEI_AUTONOMOUS_SIG_SEQ] = { 0, "AUTONOMOUS_SIG_SEQ" },
[V5X_CTRL_IEI_SEQUENCE_RESPONSE] = { 0, "SEQUENCE_RESPONSE" },
/* single byte: ISDN */
[V5X_CTRL_IEI_PERFORMANCE_GRADING] = { 0, "PERFORMANCE_GRADING" },
[V5X_CTRL_IEI_REJECTION_CAUSE] = { 0, "REJECTION_CAUSE" },
/* variable length: PSTN */
[V5X_CTRL_IEI_SEQUENCE_NR] = { 1, "SEQUENCE_NR" },
[V5X_CTRL_IEI_CADENCED_RINGING] = { 1, "CADENCED_RINGING" },
[V5X_CTRL_IEI_PULSED_SIGNAL] = { 1, "PULSED_SIGNAL" },
[V5X_CTRL_IEI_STEADY_SIGNAL] = { 1, "STEADY_SIGNAL" },
[V5X_CTRL_IEI_DIGIT_SIGNAL] = { 1, "DIGIT_SIGNAL" },
[V5X_CTRL_IEI_RECOGNITION_TIME] = { 2, "RECOGNITION_TIME" },
[V5X_CTRL_IEI_ENABLE_AUTONOMOUS_ACK] = { 2, "ENABLE_AUTONOMOUS_ACK" },
[V5X_CTRL_IEI_DISABLE_AUTONOMOUS_ACK] = { 1, "DISABLE_AUTONOMOUS_ACK" },
[V5X_CTRL_IEI_CAUSE] = { 1, "CAUSE" },
[V5X_CTRL_IEI_RESOURCE_UNAVAILABLE] = { 1, "RESOURCE_UNAVAILABLE" },
[V5X_CTRL_IEI_ENABLE_METERING] = { 1, "ENABLE_METERING" },
[V5X_CTRL_IEI_METERING_REPORT] = { 2, "METERING_REPORT" },
[V5X_CTRL_IEI_ATTENUATION] = { 1, "ATTENUATION" },
/* variable length: ISDN */
[V5X_CTRL_IEI_CTRL_F_ELEMENT] = { 1, "CTRL_F_ELEMENT" },
[V5X_CTRL_IEI_CTRL_F_ID] = { 1, "CTRL_F_ID" },
[V5X_CTRL_IEI_VARIANT] = { 1, "VARIANT" },
[V5X_CTRL_IEI_INTERFACE_ID] = { 3, "INTERFACE_ID" },
/* variable length: LCP */
[V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION] = { 1, "LINK_CTRL_FUNCTION" },
/* variable length: BCC */
[V52_CTRL_IEI_BCC_USER_PORT_ID] = { 2, "USER_PORT_ID" },
[V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID] = { 1, "ISDN_PORT_TS_ID" },
[V52_CTRL_IEI_BCC_V5_TS_ID] = { 2, "V5_TS_ID" },
[V52_CTRL_IEI_BCC_MULTI_TS_MAP] = { 9, "MULTI_TS_MAP" },
[V52_CTRL_IEI_BCC_REJECT_CAUSE] = { 1, "REJECT_CAUSE" },
[V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE] = { 1, "PROTOCOL_ERR_CAUSE" },
[V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE]= { 1, "CONNECTION_INCOMPLETE" },
[V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY] = { 1, "INFO_TRANSFER_CAPABILITY" },
/* variable length: PP */
[V52_CTRL_IEI_PP_SEQUENCE_NR] = { 1, "SEQUENCE_NR" },
[V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID] = { 2, "PHYSICAL_C_CHAN_ID" },
[V52_CTRL_IEI_PP_REJECTION_CAUSE] = { 1, "REJECTION_CAUSE" },
[V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE] = { 1, "PROTOCOL_ERR_CAUSE" },
},
.msgt_names = v5x_ctrl_msg_typ_str,
};
/* main entry point for received V5 messages */
int v5x_dl_rcv(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata)
{
struct v5x_interface *v5if = rx_cbdata;
struct v5x_link *v5l = rx_cbdata;
struct msgb *msg = odp->oph.msg;
struct v5x_l3_hdr *l3h;
struct tlv_parsed tp;
uint16_t l3_addr;
bool is_isdn;
struct v5x_user_port *v5up;
int rc;
switch (odp->oph.primitive) {
case PRIM_DL_DATA:
LOGV5IF(v5if, DV5, LOGL_DEBUG, "DL-DATA indication received (ldaddr=%d)\n", dladdr);
break;
case PRIM_DL_EST:
LOGV5IF(v5if, DV5, LOGL_DEBUG, "DL-EST indication received (ldaddr=%d)\n", dladdr);
switch (dladdr) {
case V5X_DLADDR_CTRL:
case V5X_DLADDR_PSTN:
case V52_DLADDR_LCP:
case V52_DLADDR_BCC:
v5x_le_mdl_rcv(v5if, NULL, MDL_ESTABLISH_ind, dladdr);
break;
case V52_DLADDR_PROTECTION:
v5x_le_mdl_rcv(v5l->interface, v5l, MDL_ESTABLISH_ind, dladdr);
break;
}
goto out;
case PRIM_DL_REL:
LOGV5IF(v5if, DV5, LOGL_DEBUG, "DL-REL indication received (ldaddr=%d)\n", dladdr);
switch (dladdr) {
case V5X_DLADDR_CTRL:
case V5X_DLADDR_PSTN:
case V52_DLADDR_LCP:
case V52_DLADDR_BCC:
v5x_le_mdl_rcv(v5if, NULL, MDL_RELEASE_ind, dladdr);
break;
case V52_DLADDR_PROTECTION:
v5x_le_mdl_rcv(v5l->interface, v5l, MDL_RELEASE_ind, dladdr);
break;
}
goto out;
case PRIM_MDL_ERROR:
/* already generates a log message in lapd_core.c, suppress the below log line */
rc = 0;
goto out;
default:
LOGV5IF(v5if, DV5, LOGL_NOTICE, "Unhandled prim=%d (ldaddr=%d)\n", odp->oph.primitive, dladdr);
rc = -EINVAL;
goto out;
}
l3h = msgb_l3(msg);
if (msgb_l3len(msg) < sizeof(*l3h)) {
LOGV5IF(v5if, DV5, LOGL_ERROR, "Received short message (%u bytes < %lu)\n", msgb_l3len(msg), sizeof(*l3h));
rc = -EINVAL;
goto out;
}
if (l3h->pdisc != V5X_CTRL_PDISC) {
LOGV5IF(v5if, DV5, LOGL_ERROR, "Received unsupported protocol discriminator 0x%02x\n", l3h->pdisc);
rc = -EINVAL;
goto out;
}
rc = osmo_tlv_prot_parse(&v5x_ctrl_msg_tlv, &tp, 1, l3h->msg_type, msgb_l3(msg) + sizeof(*l3h),
msgb_l3len(msg) - sizeof(*l3h), 0, 0, DV5, __func__);
if (rc < 0)
goto out;
LOGV5IF(v5if, DV5, LOGL_DEBUG, "Received message from AN with msg_type %d\n", l3h->msg_type);
switch (dladdr) {
case V5X_DLADDR_PSTN:
/* PSTN signaling protocol */
switch (l3h->msg_type) {
case V5X_CTRL_MSGT_ESTABLISH:
case V5X_CTRL_MSGT_ESTABLISH_ACK:
case V5X_CTRL_MSGT_SIGNAL:
case V5X_CTRL_MSGT_SIGNAL_ACK:
case V5X_CTRL_MSGT_DISCONNECT:
case V5X_CTRL_MSGT_DISCONNECT_COMPLETE:
case V5X_CTRL_MSGT_STATUS_ENQUIRY:
case V5X_CTRL_MSGT_STATUS:
case V5X_CTRL_MSGT_PROTOCOL_PARAMETER:
/* look-up user port based on L3 addr */
l3_addr = v5x_l3_addr_dec(ntohs(l3h->l3_addr), &is_isdn);
v5up = v5x_user_port_find(v5if, l3_addr, is_isdn);
if (!v5up) {
rc = -ENODEV;
break;
}
rc = v5x_le_pstn_dl_rcv(v5up, l3h->msg_type, &tp);
break;
default:
rc = -ENOTSUP;
LOGV5IF(v5if, DV5, LOGL_ERROR, "Received unsupported PSTN message type %s\n",
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
}
break;
case V5X_DLADDR_CTRL:
/* control protocol (Section 14 G.964) */
switch (l3h->msg_type) {
case V5X_CTRL_MSGT_PORT_CTRL:
case V5X_CTRL_MSGT_PORT_CTRL_ACK:
case V5X_CTRL_MSGT_COMMON_CTRL:
case V5X_CTRL_MSGT_COMMON_CTRL_ACK:
/* look-up user port based on L3 addr */
l3_addr = v5x_l3_addr_dec(ntohs(l3h->l3_addr), &is_isdn);
rc = v5x_le_ctrl_dl_rcv(v5if, l3_addr, is_isdn, l3h->msg_type, &tp);
break;
default:
rc = -ENOTSUP;
LOGV5IF(v5if, DV5, LOGL_ERROR, "Received unsupported CTRL message type %s\n",
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
}
break;
case V52_DLADDR_BCC:
/* protection protocol (Section 18 G.965) */
switch (l3h->msg_type) {
case V52_CTRL_MSGT_ALLOCATION:
case V52_CTRL_MSGT_ALLOCATION_COMPLETE:
case V52_CTRL_MSGT_ALLOCATION_REJECT:
case V52_CTRL_MSGT_DE_ALLOCATION:
case V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE:
case V52_CTRL_MSGT_DE_ALLOCATION_REJECT:
case V52_CTRL_MSGT_AUDIT:
case V52_CTRL_MSGT_AUDIT_COMPLETE:
case V52_CTRL_MSGT_AN_FAULT:
case V52_CTRL_MSGT_AN_FAULT_ACK:
case V52_CTRL_MSGT_PROTOCOL_ERROR:
rc = v52_le_bcc_dl_rcv(v5if, ntohs(l3h->l3_addr), l3h->msg_type, &tp);
break;
default:
rc = -ENOTSUP;
LOGV5IF(v5if, DV5, LOGL_ERROR, "Received unsupported BCC message type %s\n",
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
}
break;
case V52_DLADDR_PROTECTION:
/* BCC protocol (Section 17 G.965) */
switch (l3h->msg_type) {
case V52_CTRL_MSGT_PP_SWITCH_OVER_REQ:
case V52_CTRL_MSGT_PP_SWITCH_OVER_COM:
case V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM:
case V52_CTRL_MSGT_PP_SWITCH_OVER_ACK:
case V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT:
case V52_CTRL_MSGT_PP_PROTOCOL_ERROR:
case V52_CTRL_MSGT_PP_RESET_SN_COM:
case V52_CTRL_MSGT_PP_RESET_SN_ACK:
rc = v52_le_pp_dl_rcv(v5l, ntohs(l3h->l3_addr), l3h->msg_type, &tp);
break;
default:
rc = -ENOTSUP;
LOGV5IF(v5if, DV5, LOGL_ERROR, "Received unsupported PROTECTION message type %s\n",
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
}
break;
case V52_DLADDR_LCP:
/* Link control protocol (Section 16 G.965) */
switch (l3h->msg_type) {
case V52_CTRL_MSGT_LCP_LINK_CTRL:
case V52_CTRL_MSGT_LCP_LINK_CTRL_ACK:
rc = v5x_le_ctrl_dl_rcv(v5if, ntohs(l3h->l3_addr), 0, l3h->msg_type, &tp);
break;
default:
rc = -ENOTSUP;
LOGV5IF(v5if, DV5, LOGL_ERROR, "Received unsupported LCP message type %s\n",
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
}
break;
default:
rc = -ENOTSUP;
LOGV5IF(v5if, DV5, LOGL_ERROR, "Received unsupported message type %s\n",
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
}
out:
if (msg)
msgb_free(msg);
return rc;
}
/* send V5 messages to DL instance, given by dladdr */
int v5x_dl_snd(struct v5x_interface *v5if, uint16_t dladdr, struct msgb *msg)
{
struct lapv5_instance *li = NULL, *li2 = NULL;
switch (dladdr) {
case V5X_DLADDR_PSTN:
li = v5if->pstn.li;
break;
case V5X_DLADDR_CTRL:
li = v5if->control.li;
break;
case V52_DLADDR_BCC:
li = v5if->bcc.li;
break;
case V52_DLADDR_PROTECTION:
li = v5if->protection.li[0];
li2 = v5if->protection.li[1];
break;
case V52_DLADDR_LCP:
li = v5if->lcp.li;
break;
}
if (!li) {
LOGV5IF(v5if, DV5, LOGL_ERROR, "No instance for dladdr %d.\n", dladdr);
msgb_free(msg);
return -EINVAL;
}
if (li2)
lapv5_dl_data_req(li2, dladdr, msgb_copy(msg, "V5 MSG clone"));
return lapv5_dl_data_req(li, dladdr, msg);
}
// FIXME
#define V5_MSGB_SIZE 128
#define V5_MSGB_HEADROOM 56
struct msgb *msgb_alloc_v5x(void)
{
struct msgb *msg;
msg = msgb_alloc_headroom(V5_MSGB_SIZE, V5_MSGB_HEADROOM, "V5 MSG");
if (!msg)
return NULL;
msg->l3h = msg->data;
return msg;
}