Interface used for: - creation, building and storing new SyncE PDU's for later transmission - obtain Quality Level TLV and Extended Quality Level TLV from existing SyncE PDU's
synce_msg_private.h has constants and structs as defined by Synchronous Ethernet standard - Recommendation ITU-T G.8264//Y.1364. Co-developed-by: Anatolii Gerasymenko <anatolii.gerasyme...@intel.com> Signed-off-by: Anatolii Gerasymenko <anatolii.gerasyme...@intel.com> Co-developed-by: Michal Michalik <michal.micha...@intel.com> Signed-off-by: Michal Michalik <michal.micha...@intel.com> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalew...@intel.com> --- v2: updated license headers v3: rebase patch series synce_msg.c | 259 ++++++++++++++++++++++++++++++++++++++++++++ synce_msg.h | 174 +++++++++++++++++++++++++++++ synce_msg_private.h | 87 +++++++++++++++ 3 files changed, 520 insertions(+) create mode 100644 synce_msg.c create mode 100644 synce_msg.h create mode 100644 synce_msg_private.h diff --git a/synce_msg.c b/synce_msg.c new file mode 100644 index 0000000..749a27d --- /dev/null +++ b/synce_msg.c @@ -0,0 +1,259 @@ +/** + * @file synce_msg.c + * @brief Implements the ESMC message type. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <stdlib.h> +#include <stdbool.h> +#include <sys/queue.h> + +#include "sk.h" +#include "print.h" +#include "address.h" +#include "synce_msg.h" +#include "synce_msg_private.h" + +static void init_tlvs(struct synce_pdu *pdu) +{ + TAILQ_INIT(&pdu->tlv_list); +} + +struct synce_pdu *synce_msg_create(const char *iface) +{ + const unsigned char dstMac[] = SYNCE_DEST_MACADDR; + const unsigned char ituOui[] = SYNCE_ITUOUI; + struct synce_pdu *pdu; + struct address srcMac; + + if (sk_interface_macaddr(iface, &srcMac)) { + pr_err("mac get failed"); + return NULL; + } + + pdu = (struct synce_pdu *) malloc(sizeof(struct synce_pdu)); + if (!pdu) { + pr_err("memory allocation for Sync-E PDU failed"); + return NULL; + } + memset(pdu, 0, sizeof(struct synce_pdu)); + memcpy(&pdu->header.dstAddr, &dstMac, sizeof(pdu->header.dstAddr)); + pdu->header.ethertype = htons(ETH_P_SLOW); + pdu->header.ethersubtype = SYNCE_ETHERSUBTYPE; + memcpy(&pdu->header.ituOui, &ituOui, sizeof(pdu->header.ituOui)); + pdu->header.ituSubtype = htons(SYNCE_ITUSUBTYPE); + pdu->header.verEvtflag = SYNCE_VERSION << SYNCE_VERSION_SHIFT; + memset(&pdu->header.reserved, 0, sizeof(pdu->header.reserved)); + memcpy(&pdu->header.srcAddr, &srcMac.sll.sll_addr, + sizeof(pdu->header.srcAddr)); + + init_tlvs(pdu); + + return pdu; +} + +void synce_msg_delete(struct synce_pdu *pdu) +{ + synce_msg_reset_tlvs(pdu); + free(pdu); +} + +static void attach_esmc_tlv(struct synce_pdu *pdu, struct esmc_tlv *tlv) +{ + TAILQ_INSERT_TAIL(&pdu->tlv_list, tlv, list); +} + +int synce_msg_get_esmc_tlvs_size(struct synce_pdu *pdu) +{ + struct esmc_tlv *tlv; + int size = 0; + + TAILQ_FOREACH(tlv, &pdu->tlv_list, list) { + size += ntohs(tlv->ql_tlv.length); + } + + return size; +} + +static int generate_tlvs(struct synce_pdu *pdu) +{ + struct esmc_tlv *tlv; + void *cur; + int size; + + size = synce_msg_get_esmc_tlvs_size(pdu); + if (ETH_DATA_LEN - ETH_FCS_LEN - size < 0) { + pr_err("too many tlvs"); + return -EMSGSIZE; + } + + cur = (void *)pdu + sizeof(struct esmc_header); + TAILQ_FOREACH(tlv, &pdu->tlv_list, list) { + size = ntohs(tlv->ql_tlv.length); + memcpy(cur, tlv, size); + cur += size; + } + + return 0; +} + +int synce_msg_attach_ql_tlv(struct synce_pdu *pdu, uint8_t ssmCode) +{ + struct esmc_tlv *tlv; + + if (~QL_TLV_SSM_MASK & ssmCode) { + pr_err("4 upper bits of QL TLV ssmCode should not be used"); + return -EINVAL; + } + + tlv = malloc(sizeof(struct esmc_tlv)); + if (!tlv) { + pr_err("malloc failed for TLV: %m"); + return -EINVAL; + } + + tlv->ql_tlv.type = QL_TLV_TYPE; + tlv->ql_tlv.length = htons(QL_TLV_LEN); + tlv->ql_tlv.ssmCode = ssmCode; + + attach_esmc_tlv(pdu, tlv); + + generate_tlvs(pdu); + + return 0; +} + +int synce_msg_get_ql_tlv(struct synce_pdu *pdu, uint8_t *ssmCode) +{ + struct esmc_tlv *tlv; + + if (!pdu || !ssmCode) { + return -ENXIO; + } + + TAILQ_FOREACH(tlv, &pdu->tlv_list, list) { + if (tlv->ql_tlv.type == QL_TLV_TYPE) { + break; + } + } + + if (!tlv) { + return -EAGAIN; + } + + *ssmCode = tlv->ql_tlv.ssmCode; + + return 0; +} + +int synce_msg_attach_extended_ql_tlv(struct synce_pdu *pdu, + struct synce_msg_ext_ql *ext_ql) +{ + struct esmc_tlv *tlv; + + if (!pdu || !ext_ql) { + pr_err("either pdu or ext_ql not provided"); + return -ENXIO; + } + + tlv = malloc(sizeof(struct esmc_tlv)); + if (!tlv) { + pr_err("malloc failed for TLV: %m"); + return -ENOMEM; + } + + tlv->extended_ql_tlv.type = EXT_QL_TLV_TYPE; + tlv->extended_ql_tlv.length = htons(EXT_QL_TLV_LEN); + tlv->extended_ql_tlv.enhancedSsmCode = ext_ql->enhancedSsmCode; + memcpy(&tlv->extended_ql_tlv.clockIdentity, &ext_ql->clockIdentity, + sizeof(tlv->extended_ql_tlv.clockIdentity)); + tlv->extended_ql_tlv.flag = ext_ql->flag; + tlv->extended_ql_tlv.cascaded_eEEcs = ext_ql->cascaded_eEEcs; + tlv->extended_ql_tlv.cascaded_EEcs = ext_ql->cascaded_EEcs; + + attach_esmc_tlv(pdu, tlv); + + generate_tlvs(pdu); + + return 0; +} + +int synce_msg_get_extended_ql_tlv(struct synce_pdu *pdu, + struct synce_msg_ext_ql *ext_ql) +{ + struct esmc_tlv *tlv; + + if (!pdu || !ext_ql) { + return -ENXIO; + } + + TAILQ_FOREACH(tlv, &pdu->tlv_list, list) { + if (tlv->ql_tlv.type == EXT_QL_TLV_TYPE) { + break; + } + } + + if (!tlv) { + return -EAGAIN; + } + + ext_ql->enhancedSsmCode = tlv->extended_ql_tlv.enhancedSsmCode; + memcpy(&ext_ql->clockIdentity, &tlv->extended_ql_tlv.clockIdentity, + sizeof(ext_ql->clockIdentity)); + ext_ql->flag = tlv->extended_ql_tlv.flag; + ext_ql->cascaded_eEEcs = tlv->extended_ql_tlv.cascaded_eEEcs; + ext_ql->cascaded_EEcs = tlv->extended_ql_tlv.cascaded_EEcs; + + return 0; +} + +void synce_msg_reset_tlvs(struct synce_pdu *pdu) +{ + struct esmc_tlv *tlv, *tlv_temp; + + tlv = TAILQ_FIRST(&pdu->tlv_list); + while (tlv != NULL) { + tlv_temp = TAILQ_NEXT(tlv, list); + free(tlv); + tlv = tlv_temp; + } + init_tlvs(pdu); +} + +static bool is_valid_synce_tlv(struct esmc_tlv *tlv) +{ + if (tlv->ql_tlv.type == QL_TLV_TYPE && + ntohs(tlv->ql_tlv.length) == QL_TLV_LEN) + return true; + if (tlv->ql_tlv.type == EXT_QL_TLV_TYPE && + ntohs(tlv->ql_tlv.length) == EXT_QL_TLV_LEN) + return true; + return false; +} + +void synce_msg_recover_tlvs(struct synce_pdu *pdu) +{ + struct esmc_tlv *tlv, *new_tlv; + + if (TAILQ_EMPTY(&pdu->tlv_list)) { + init_tlvs(pdu); + } + + synce_msg_reset_tlvs(pdu); + + tlv = (struct esmc_tlv *)((void *)pdu + sizeof(struct esmc_header)); + while (is_valid_synce_tlv(tlv)) { + new_tlv = malloc(sizeof(struct esmc_tlv)); + if (!new_tlv) { + pr_err("allocate new_tlv failed"); + return; + } + memset(new_tlv, 0, sizeof(*new_tlv)); + /* We copy data from receiving buffer into larger structure. The + * TLV size is validated in is_valid_synce_tlv before copying. + */ + memcpy(new_tlv, tlv, ntohs(tlv->ql_tlv.length)); + TAILQ_INSERT_TAIL(&pdu->tlv_list, new_tlv, list); + tlv = (struct esmc_tlv *)((void *)tlv + ntohs(tlv->ql_tlv.length)); + } +} diff --git a/synce_msg.h b/synce_msg.h new file mode 100644 index 0000000..3ab6d9b --- /dev/null +++ b/synce_msg.h @@ -0,0 +1,174 @@ +/** + * @file synce_msg.h + * @brief Implements the ESMC message type. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_MSG_H +#define HAVE_SYNCE_MSG_H + +#include "ddt.h" + +#define SYNCE_NETWORK_OPT_1 1 +#define SYNCE_NETWORK_OPT_2 2 + +/* Enhanced SSM codes for SyncE */ +#define QL_EEC1_ENHSSM 0xFF +#define QL_EEC2_ENHSSM 0xFF +#define QL_OTHER_CLOCK_TYPES_ENHSSM 0xFF +#define QL_PRTC_ENHSSM 0x20 +#define QL_ePRTC_ENHSSM 0x21 +#define QL_eEEC_ENHSSM 0x22 +#define QL_ePRC_ENHSSM 0x23 + +/* SSM codes and enhanced SSM codes for SyncE in option 1 networks */ +#define O1N_QL_PRC_SSM 0x2 +#define O1N_QL_PRC_ENHSSM 0xFF +#define O1N_QL_SSU_A_SSM 0x4 +#define O1N_QL_SSU_A_ENHSSM 0xFF +#define O1N_QL_SSU_B_SSM 0x8 +#define O1N_QL_SSU_B_ENHSSM 0xFF +#define O1N_QL_EEC1_SSM 0xB +#define O1N_QL_EEC1_ENHSSM 0xFF +#define O1N_QL_DNU_SSM 0xF +#define O1N_QL_DNU_ENHSSM 0xFF +#define O1N_QL_PRTC_SSM 0x2 +#define O1N_QL_PRTC_ENHSSM 0x20 +#define O1N_QL_EPRTC_SSM 0x2 +#define O1N_QL_EPRTC_ENHSSM 0x21 +#define O1N_QL_EEEC_SSM 0xB +#define O1N_QL_EEEC_ENHSSM 0x22 +#define O1N_QL_EPRC_SSM 0x2 +#define O1N_QL_EPRC_ENHSSM 0x23 + +/* SSM codes and enhanced SSM codes for SyncE in option 2 networks */ +#define O2N_QL_PRS_SSM 0x1 +#define O2N_QL_PRS_ENHSSM 0xFF +#define O2N_QL_STU_SSM 0x0 +#define O2N_QL_STU_ENHSSM 0xFF +#define O2N_QL_ST2_SSM 0x7 +#define O2N_QL_ST2_ENHSSM 0xFF +#define O2N_QL_TNC_SSM 0x4 +#define O2N_QL_TNC_ENHSSM 0xFF +#define O2N_QL_ST3E_SSM 0xD +#define O2N_QL_ST3E_ENHSSM 0xFF +#define O2N_QL_ST3_SSM 0xA +#define O2N_QL_ST3_ENHSSM 0xFF +#define O2N_QL_EEC2_SSM 0xA +#define O2N_QL_EEC2_ENHSSM 0xFF +#define O2N_QL_PROV_SSM 0xE +#define O2N_QL_PROV_ENHSSM 0xFF +#define O2N_QL_DUS_SSM 0xF +#define O2N_QL_DUS_ENHSSM 0xFF +#define O2N_QL_PRTC_SSM 0x1 +#define O2N_QL_PRTC_ENHSSM 0x20 +#define O2N_QL_EPRTC_SSM 0x1 +#define O2N_QL_EPRTC_ENHSSM 0x21 +#define O2N_QL_EEEC_SSM 0xA +#define O2N_QL_EEEC_ENHSSM 0x22 +#define O2N_QL_EPRC_SSM 0x1 +#define O2N_QL_EPRC_ENHSSM 0x23 + +/* Flags as defined in SyncE specification */ +#define MIXED_EEC_CHAIN_FLAG (1 << 0) +#define PARTIAL_EEC_CHAIN_FLAG (1 << 1) + +/* 5 seconds is a period defined by standard for QL-failed state */ +#define QL_FAILED_PERIOD_SEC 5 + +struct synce_msg_ext_ql { + uint8_t enhancedSsmCode; + struct ClockIdentity clockIdentity; + uint8_t flag; + uint8_t cascaded_eEEcs; + uint8_t cascaded_EEcs; +}; + +/** + * Create a ESMC Protocol Data Unit (PDU) template. + * + * This high level API creates structure for storing ESMC PDU and + * initializes TLV vector for conveniently storing and processing + * TLV structures (esmc_tlv). All the fields are stored in network + * byte ordering. + * + * @param iface A name of interface to create prepopulated template for + * @return A pointer to a ESMC Sync-E PDU prepopulated template + */ +struct synce_pdu *synce_msg_create(const char *iface); + +/** + * Delete a ESMC Protocol Data Unit (PDU) from memory. + * + * This high level API frees all the memory stored in TLV vector and + * then free ESMC PDU itself. + * + * @param pdu A pointer to a ESMC Sync-E PDU + */ +void synce_msg_delete(struct synce_pdu *pdu); + +/** + * Attach QL TLV to Sync-E PDU. + * + * @param pdu A pointer to a ESMC Sync-E PDU + * @param ssmCode SSM Code of newly created QL TLV + * @return Zero on success, non-zero if failure + */ +int synce_msg_attach_ql_tlv(struct synce_pdu *pdu, uint8_t ssmCode); + +/** + * Get QL TLVs SSM Code from Sync-E PDU. + * + * @param pdu A pointer to a ESMC Sync-E PDU + * @param ssmCode SSM Code variable to store + * @return Zero on success, non-zero if failure + */ +int synce_msg_get_ql_tlv(struct synce_pdu *pdu, uint8_t *ssmCode); + +/** + * Attach Extended QL TLV to Sync-E PDU. + * + * @param pdu A pointer to a ESMC Sync-E PDU + * @param enhancedSsmCode Enhanced SSM Code of newly created QL TLV + * @param clockIdentity SyncE clockIdentity of the originator of the extended QL TLV + * @param flag Flag (see ITU-T G.8264) + * @param cascaded_eEEcs Number of cascaded eEECs from the nearest SSU/PRC/ePRC + * @param cascaded_EEcs Number of cascaded EECs from the nearest SSU/PRC/ePRC + * @return Zero on success, non-zero if failure + */ +int synce_msg_attach_extended_ql_tlv(struct synce_pdu *pdu, + struct synce_msg_ext_ql *ext_ql); + +/** + * Get QL Extended TLVs from Sync-E PDU in. + * + * @param pdu A pointer to a ESMC Sync-E PDU + * @param ext_ql A pointer to Extended Quality level + * @return Zero on success, non-zero if failure + */ +int synce_msg_get_extended_ql_tlv(struct synce_pdu *pdu, + struct synce_msg_ext_ql *ext_ql); + +/** + * Reset (clear and reinitialize) all the TLV vector entries. + * + * @param pdu A pointer to a ESMC Sync-E PDU + */ +void synce_msg_reset_tlvs(struct synce_pdu *pdu); + +/** + * Recover TLVs from ESMC frame into TLV vector entries in PDU structure. + * + * @param pdu A pointer to a ESMC Sync-E PDU + */ +void synce_msg_recover_tlvs(struct synce_pdu *pdu); + +/** + * Returns the size of TLV vector entries in PDU. + * + * @param pdu A pointer to a ESMC Sync-E PDU + * @return Number of bytes of all TLV entries attached to PDU + */ +int synce_msg_get_esmc_tlvs_size(struct synce_pdu *pdu); + +#endif diff --git a/synce_msg_private.h b/synce_msg_private.h new file mode 100644 index 0000000..aa770f3 --- /dev/null +++ b/synce_msg_private.h @@ -0,0 +1,87 @@ +/** + * @file synce_msg_private.h + * @brief Implements the ESMC message private structures. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_MSG_PRIVATE_H +#define HAVE_SYNCE_MSG_PRIVATE_H + +/* Sync-E Frame */ +#define SYNCE_DEST_MACADDR { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x02 } +#define SYNCE_ETHERTYPE ETH_P_SLOW +#define SYNCE_ETHERSUBTYPE 0x0A +#define SYNCE_ITUOUI { 0x00, 0x19, 0xA7 } +#define SYNCE_ITUOUI_SIZE 3 +#define SYNCE_ITUSUBTYPE 0x0001 +#define SYNCE_VERSION 1 +#define SYNCE_VERSION_SHIFT 4 +#define SYNCE_EVENT_SHIFT 4 + +/* QL TLV */ +#define QL_TLV_TYPE 1 +#define QL_TLV_LEN 0x4 +#define QL_TLV_SSM_MASK 0x0f +#define EXT_QL_TLV_TYPE 2 +#define EXT_QL_TLV_LEN 0x14 + +#include <errno.h> +#include <sys/queue.h> +#include <linux/if_ether.h> + +#include "ddt.h" +#include "ether.h" + +struct macaddr { + uint8_t addr[MAC_LEN]; +} PACKED; + +struct ql_tlv { + uint8_t type; + uint16_t length; + uint8_t ssmCode; /* unused:4 | SSM code:4 */ +} PACKED; + +struct extended_ql_tlv { + uint8_t type; + uint16_t length; + uint8_t enhancedSsmCode; + struct ClockIdentity clockIdentity; + uint8_t flag; + uint8_t cascaded_eEEcs; + uint8_t cascaded_EEcs; + uint8_t reserved[5]; +} PACKED; + +struct esmc_tlv { + union { + struct ql_tlv ql_tlv; + struct extended_ql_tlv extended_ql_tlv; + }; + TAILQ_ENTRY(esmc_tlv) list; +} PACKED; + +struct esmc_header { + struct macaddr dstAddr; + struct macaddr srcAddr; + uint16_t ethertype; + uint8_t ethersubtype; + uint8_t ituOui[SYNCE_ITUOUI_SIZE]; /* Organizationally Unique Identifier */ + uint16_t ituSubtype; + uint8_t verEvtflag; /* version:4 | event flag:1 | reserved:3 */ + uint8_t reserved[3]; +} PACKED; + +struct esmc_data { + uint8_t buffer[ETH_DATA_LEN]; +}; + +struct synce_pdu { + union { + struct esmc_header header; + struct esmc_data data; + } PACKED; + TAILQ_HEAD(ql_tlv_list, esmc_tlv) tlv_list; +}; + +#endif -- 2.26.0 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel