Newer
Older
// SPDX-License-Identifier: BSD-3-Clause
* defines structs and functions for TLVs and CMDU manipulation.
* Copyright (C) 2021-2024 IOPSYS Software Solutions AB. All rights reserved.
* Copyright (C) 2025 Genexis AB.
*
* Author: anjan.chanda@iopsys.eu
*/
#ifndef CMDU_H
#define CMDU_H
#include <stdint.h>
uint8_t rsvd; /**< reserved */
uint16_t mid; /**< message id */
uint8_t fid; /**< fragment id */
uint8_t flag;
} __attribute__ ((packed));
#define IS_CMDU_LAST_FRAGMENT(c) !!((c)->hdr.flag & 0x80)
#define IS_CMDU_RELAY_MCAST(c) !!((c)->hdr.flag & 0x40)
#define CMDU_SET_LAST_FRAGMENT(c) (c)->hdr.flag |= 0x80
#define CMDU_SET_RELAY_MCAST(c) (c)->hdr.flag |= 0x40
#define CMDU_DEFAULT_TIMEOUT 1000
/** struct cmdu_linear - represents a CMDU frame with header and TLVs */
struct cmdu_linear {
struct cmdu_header hdr;
uint8_t data[];
} __attribute__ ((packed));
#define TLV_HLEN 3
struct cmdu_frag {
uint8_t *data;
uint16_t len;
struct list_head list;
};
enum cmdu_frag_scheme {
CMDU_FRAG_SCHEME_BOUNDARY_TLV,
CMDU_FRAG_SCHEME_BOUNDARY_OCTET,
};
/**
* @struct cmdu_buff
* This structure abstracts out a CMDU frame. In addition to holding pointer
* to an actual CMDU frame, it holds meta information about the CMDU,
* such as, for a Rx CMDU which interface it arrived on, sender's macaddress
* APIs are provided for working with a cmdu_buff. There are functions to
* parse a cmdu_buff and get the list of TLVs within it, ititerate through TLVs
* in a CMDU, adding a TLV, peeking a TLV, getting size of a CMDU frame etc.
*
* On the transmit and receive path, it is possible to achieve zero-memcpy for
* CMDU manipulation including inserting/extracting the ethernet frame header.
*/
struct cmdu_buff {
uint8_t *head;
uint8_t *data;
uint8_t *tail;
uint8_t *end;
uint8_t dev_macaddr[6];
uint8_t origin[6]; /* source address of sender */
uint8_t aladdr[6]; /* AL address of sender (when available) */
uint32_t flags;
uint16_t datalen;
uint16_t len;
struct cmdu_linear *cdata;
uint32_t num_frags;
struct list_head fraglist;
struct list_head list;
};
#define CMDU_PROCESSED 0x1
#define IS_CMDU_PROCESSED(c) !!((c)->flags & CMDU_PROCESSED)
#define CMDU_SET_PROCESSED(c) ((c)->flags |= CMDU_PROCESSED)
/**
* @enum tlv_presence
* @brief defines the policy for occurrences of a TLV in a CMDU frame.
*/
enum tlv_presence {
TLV_PRESENT_UNDEFINED,
TLV_PRESENT_ONE, /**< only one tlv of this type present */
TLV_PRESENT_MORE, /**< one or more tlvs of this tlv present */
TLV_PRESENT_ONE_OR_MORE = TLV_PRESENT_MORE,
TLV_PRESENT_OPTIONAL_ONE, /**< zero or one of this tlv present */
TLV_PRESENT_ZERO_OR_ONE = TLV_PRESENT_OPTIONAL_ONE,
TLV_PRESENT_OPTIONAL_MORE, /**< zero or more of this tlv present */
TLV_PRESENT_ZERO_OR_MORE = TLV_PRESENT_OPTIONAL_MORE,
TLV_PRESENT_NUM,
};
enum tlv_presence present;
/**
* @struct tlv
struct tlv {
uint8_t type;
uint16_t len;
uint8_t data[];
} __attribute__ ((packed));
/**
* Allocates a TLV
* @param[in] datalen length of tlv data in bytes
*
* @return newly allocated tlv on success, or NULL if failed.
*/
/** Free an allocated TLV */
void tlv_free_linear(struct tlv *t);
/** Zeros out a TLV */
/* following functions for internal use only */
int tlv_ok(struct tlv *t, int rem);
struct tlv *tlv_next(struct tlv *t, int *rem);
/** Get total length of a TLV including the header */
uint16_t tlv_total_length(struct tlv *t);
/** Helper function to stringify TLV type */
const char *tlv_type2str(uint8_t type);
/* Allocates cmdu_buff to hold 'size' length cmdu payload */
struct cmdu_buff *cmdu_alloc(int size); // XXX: internal use
/** Allocates cmdu_buff to hold 'size' length cmdu payload */
struct cmdu_buff *cmdu_alloc_frame(int size);
/**
* Allocates cmdu_buff to hold full-size ethernet frame of 1500 bytes
*
* @return newly allocated cmdu_buff on success, or NULL if failed.
*
* This function is useful to allocate CMDUs for transmit when size of the
* payload i.e. TLV data is not known a-priori.
*/
/**
* Allocates full-sized cmdu_buff without CMDU header info
*
* @return newly allocated cmdu_buff on success, or NULL if failed.
*
* This function is useful to allocate cmdu_buff for tx/rx of LLDP frames.
*/
struct cmdu_buff *cmdu_alloc_nohdr(void);
/**
* Convenient function to allocate cmdu_buff that can hold a full-size CMDU
* @param[in] type CMDU type
* @param[in|out] mid CMDU mid if nonzero, or valid mid returned
*
* @return newly allocated cmdu_buff on success, or NULL if failed.
*
* If mid passed is 0, then the next valid mid gets assigned to the newly
* allocated cmdu.
*/
struct cmdu_buff *cmdu_alloc_simple(uint16_t type, uint16_t *mid);
/**
* Prepare cmdu_buff from a received CMDU whose fields are known
* @param[in] type CMDU type
* @param[in|out] mid CMDU mid if nonzero, or valid mid returned
* @param[in] ifname interface name through which the CMDU is received
* @param[in] origin macaddress of the source that sent the CMDU
* @param[in] tlvs flattened TLV data bytes
* @param[in] tlvslen length of the TLV data bytes
*
* @return newly allocated cmdu_buff on success, or NULL if failed.
*/
struct cmdu_buff *cmdu_alloc_custom(uint16_t type, uint16_t *mid,
char *ifname, uint8_t *origin,
uint8_t *tlvs, uint32_t tlvslen);
struct cmdu_buff *cmdu_realloc(struct cmdu_buff *c, size_t size);
/** Free CMDU allocated through cmdu_alloc*() functions */
void cmdu_set_type(struct cmdu_buff *c, uint16_t type);
uint16_t cmdu_get_type(struct cmdu_buff *c);
void cmdu_set_mid(struct cmdu_buff *c, uint16_t mid);
uint16_t cmdu_get_mid(struct cmdu_buff *c);
void cmdu_set_fid(struct cmdu_buff *c, uint8_t fid);
uint8_t cmdu_get_fid(struct cmdu_buff *c);
uint8_t *cmdu_get_origin(struct cmdu_buff *c);
/** Full size of a CMDU frame including the CMDU header */
/** Get a valid CMDU 'mid' that can be used next */
int cmdu_midgen_init(void);
void cmdu_midgen_exit(void);
/** Helper function to get expected response CMDU for a request CMDU */
uint16_t cmdu_expect_response(uint16_t req_type);
/** Helper function to check if a CMDU is of relay multicast type */
int cmdu_should_relay(uint16_t type);
/** Function to check if a CMDU type is valid */
int is_cmdu_type_valid(uint16_t type);
/** Function to check if a CMDU is of response type */
int is_cmdu_type_response(uint16_t type);
/** Function to check if a CMDU type must include atleast one TLV */
int is_cmdu_tlv_required(uint16_t type);
/** Parsing status of received CMDU */
enum CMDU_STATUS {
CMDU_STATUS_OK,
CMDU_STATUS_ERR_TLV_MALFORMED,
CMDU_STATUS_ERR_TLV_NUM_LESS, /* mandatory tlv(s) absent */
CMDU_STATUS_ERR_TLV_NUM_MORE,
CMDU_STATUS_ERR_TLV_NO_EOM,
CMDU_STATUS_ERR_TLV_RESIDUE_DATA,
CMDU_STATUS_ERR_TLV_LEN_INSUFFICIENT,
CMDU_STATUS_ERR_TLV_LEN_OVERFLOW,
CMDU_STATUS_ERR_CMDU_MALFORMED,
CMDU_STATUS_ERR_MISC,
IEEE1905_ERROR_MAXNUM,
IEEE1905_ERROR_LAST = IEEE1905_ERROR_MAXNUM - 1,
};
extern int *ieee1905_get_errval(void);
#define ieee1905_error (*ieee1905_get_errval())
const char *ieee1905_strerror(int err);
/* Maximum number of tlvs of a single type allowed per cmdu */
/** Parse a CMDU to get list of the TLVs present in it
*
* @param[in] c cmdu_buff for parsing
* @param[in|out] tv array of TLVs to hold the returned TLVs
* @param[in] policy policy for TLV parsing
* @param[in] policy_len length of the policy
*
* @return 0 on success, else non-zero.
*
* The TLVs are returned in the passed 'tv' array.
*/
int cmdu_parse_tlvs(struct cmdu_buff *c, struct tlv *tv[][TLV_MAXNUM],
/** Parse a CMDU to get list of the TLVs of a single type
*
* @param[in] c cmdu_buff for parsing
* @param[in|out] tv array of TLVs to hold the returned TLVs
* @param[in] policy policy for TLV parsing
* @param[in|out] *num number of TLVs
*
* @return 0 on success, else non-zero.
*
* This function can be used when number of TLVs of the same type is present
* more then 16 times; f.e. CMDUs containing TLVs for scanresults.
* The TLVs are returned in the passed 'tv' array. And, *num is updated with
* the actual number of TLVs present.
*/
int cmdu_parse_tlv_single(struct cmdu_buff *c, struct tlv *tv[],
struct tlv_policy *policy, int *num);
/** Copy append flattended TLVs data buffer into a CMDU */
int cmdu_copy_tlvs_linear(struct cmdu_buff *c, uint8_t *tlvs, uint32_t tlvslen);
/** Copy append un-flattened TLVs into a CMDU */
int cmdu_copy_tlvs(struct cmdu_buff *c, struct tlv *tv[], int tv_arrsize);
struct cmdu_buff *cmdu_clone(struct cmdu_buff *frm);
/** Function to reserve space at the tail of a CMDU buffer */
struct tlv *cmdu_reserve_tlv(struct cmdu_buff *c, uint16_t tlv_datalen);
/** Function puts a TLV within the CMDU buffer at the reserved area */
int cmdu_put(struct cmdu_buff *c, uint8_t *bytes, int len);
int cmdu_put_eom(struct cmdu_buff *c);
/** Remove End-Of-Message TLV from a CMDU */
int cmdu_pull_eom(struct cmdu_buff *c);
struct tlv *cmdu_extract_tlv(struct cmdu_buff *c, uint8_t tlv_type);
struct tlv *cmdu_peek_tlv(struct cmdu_buff *c, uint8_t tlv_type);
int cmdu_reserve(struct cmdu_buff *c, size_t s);
int cmdu_expand(struct cmdu_buff *c, size_t newsize);
const char *cmdu_type2str(uint16_t type);
#define cmdu_for_each_tlv(pos, tlvs, rem) \
for (pos = (struct tlv *) tlvs; \
tlv_ok(pos, rem); \
pos = tlv_next(pos, &rem))
#endif /* CMDU_H */