mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-upf.git
synced 2025-11-02 13:03:35 +00:00
move libosmo-gtlv to libosmo-pfcp.git
Related: SYS#5599 Change-Id: Id72cdf94da60d4b6d09d0044c74e672c4412c15d
This commit is contained in:
@@ -42,6 +42,7 @@ AC_SUBST(LIBRARY_DL)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGTLV, libosmo-gtlv >= 0.1.0)
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
@@ -197,18 +198,13 @@ AM_CONFIG_HEADER(config.h)
|
||||
AC_OUTPUT(
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/gtlv/Makefile
|
||||
include/osmocom/pfcp/Makefile
|
||||
include/osmocom/upf/Makefile
|
||||
src/Makefile
|
||||
src/libosmo-gtlv/Makefile
|
||||
src/libosmo-pfcp/Makefile
|
||||
src/osmo-upf/Makefile
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/libosmo-gtlv/Makefile
|
||||
tests/libosmo-gtlv/test_gtlv_gen/Makefile
|
||||
tests/libosmo-gtlv/test_tliv/Makefile
|
||||
tests/libosmo-pfcp/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
|
||||
@@ -30,6 +30,7 @@ export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||
osmo-build-dep.sh libosmo-pfcp
|
||||
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
SUBDIRS = \
|
||||
gtlv \
|
||||
pfcp \
|
||||
upf \
|
||||
$(NULL)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
tlv_HEADERS = \
|
||||
gtlv.h \
|
||||
gtlv_dec_enc.h \
|
||||
gtlv_gen.h \
|
||||
$(NULL)
|
||||
|
||||
tlvdir = $(includedir)/osmocom/gtlv
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct msgb;
|
||||
struct osmo_gtlv_load;
|
||||
struct osmo_gtlv_put;
|
||||
struct value_string;
|
||||
|
||||
struct osmo_gtlv_tag_inst {
|
||||
unsigned int tag;
|
||||
bool instance_present;
|
||||
unsigned int instance;
|
||||
};
|
||||
|
||||
int osmo_gtlv_tag_inst_cmp(const struct osmo_gtlv_tag_inst *a, const struct osmo_gtlv_tag_inst *b);
|
||||
|
||||
int osmo_gtlv_tag_inst_to_str_buf(char *buf, size_t buflen, const struct osmo_gtlv_tag_inst *ti,
|
||||
const struct value_string *tag_names);
|
||||
char *osmo_gtlv_tag_inst_to_str_c(void *ctx, const struct osmo_gtlv_tag_inst *ti,
|
||||
const struct value_string *tag_names);
|
||||
|
||||
/*! TL configuration for osmo_gtlv_load*() and osmo_gtlv_put*(). Depending on these implementations provided by the caller,
|
||||
* osmo_gtlv can load any sizes of tag and length fields (that don't surpass the value range of unsigned int and size_t,
|
||||
* respectively), as well as TV (fixed-length) or TvLV (variable-sized length).
|
||||
*
|
||||
* See osmo_t8l8v_cfg and osmo_t16l16v_cfg, ready implementations for plain 8bit and 16bit TLV protocols.
|
||||
*
|
||||
* libosmo-pfcp serves as example for using this entire TLV API, uncluding de/encoding to structs and generating parts
|
||||
* of the TLV parsing code based on message definitions. It uses osmo_t16l16v_cfg.
|
||||
*/
|
||||
struct osmo_gtlv_cfg {
|
||||
/*! The length in bytes of the shortest possible TL header (e.g. 4 for T16L16V, or 1 for 8bit tags where TV IEs
|
||||
* without a length exist). A src_data_len passed to store_tl() below is guaranteed to be >= this value. If at
|
||||
* any point there is remaining message data smaller than this value, a parsing error is returned.
|
||||
*/
|
||||
size_t tl_min_size;
|
||||
|
||||
/*! Read one TL from the start of src_data.
|
||||
* \param gtlv Return the T (tag) value read from src_data in gtlv->tag.
|
||||
* Return the L (length) value read from src_data in gtlv->len.
|
||||
* Return the I (instance) value read from src_data in gtlv->len; ignore if there is no I.
|
||||
* Return the position just after the TL in gtlv->*val. If there is V data, point at the start of the
|
||||
* V data in src_data. If there is no V data, point at the byte just after the TL part in src_data.
|
||||
* \param src_data Part of raw message being decoded.
|
||||
* \param src_data_len Remaining message data length at src_data.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
int (*load_tl)(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len);
|
||||
|
||||
/*! Write a TL to dst_data, and return the size of the TL written.
|
||||
* This is also invoked by osmo_gtlv_put_update_tl() to overwrite a previous TL header. If the TL part's size
|
||||
* can be different than the first time (e.g. due to a large L value in a TvLV protocol), an implementation can
|
||||
* use the 'gtlv' arg to figure out how to memmove the message data:
|
||||
* When invoked by osmo_gtlv_put_tl(), dst_data == gtlv->dst->tail and dst_data_avail == msgb_tailroom().
|
||||
* When invoked by osmo_gtlv_put_update_tl(), dst_data < gtlv->dst->tail, dst_data points at the start of the
|
||||
* TL section written earlier by osmo_gtlv_put_tl() and dst_data_avail == the size of the TL written earlier.
|
||||
*
|
||||
* \param dst_data Write TL data to the start of this buffer.
|
||||
* \param dst_data_avail Remaining available space in dst_data.
|
||||
* \param tag The T value to store in dst_data.
|
||||
* \param instance The I value to store in dst_data (if this tag is a TLIV); ignore when not a TLIV.
|
||||
* \param len The L value to store in dst_data.
|
||||
* \param gtlv Backpointer to the osmo_gtlv_put struct, including gtlv->dst, the underlying msgb.
|
||||
* \return the size of the TL part in bytes on success, -EINVAL if tag is invalid, -EMSGSIZE if len is too large
|
||||
* or dst_data_avail is too small for the TL.
|
||||
*/
|
||||
int (*store_tl)(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv);
|
||||
};
|
||||
|
||||
/*! Configuration that allows parsing an 8bit tag and 8bit length TLV. */
|
||||
extern const struct osmo_gtlv_cfg osmo_t8l8v_cfg;
|
||||
|
||||
/*! Configuration that allows parsing a 16bit tag and 16bit length TLV (see for example PFCP). */
|
||||
extern const struct osmo_gtlv_cfg osmo_t16l16v_cfg;
|
||||
|
||||
/*! State for loading a TLV structure from raw data. */
|
||||
struct osmo_gtlv_load {
|
||||
/*! Caller-defined context pointer available for use by load_tl() and store_tl() implementations. */
|
||||
void *priv;
|
||||
|
||||
/*! Definition of tag and length sizes (by function pointers). */
|
||||
const struct osmo_gtlv_cfg *cfg;
|
||||
|
||||
/*! Overall message buffer being parsed. */
|
||||
struct {
|
||||
const uint8_t *data;
|
||||
size_t len;
|
||||
} src;
|
||||
|
||||
/*! Return value from last invocation of osmo_gtlv_load_next*(): tag value of parsed IE. */
|
||||
struct osmo_gtlv_tag_inst ti;
|
||||
/*! Return value from last invocation of osmo_gtlv_load_next*(): Start of the IE's payload data (after tag and
|
||||
* length). If the end of the src buffer is reached, val == NULL. If a TLV contained no value part, len == 0,
|
||||
* but this still points just after the TL. */
|
||||
const uint8_t *val;
|
||||
/*! Return value from last invocation of osmo_gtlv_load_next*(): Length of the IE's payload data (without tag and
|
||||
* length) */
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* Start or restart the gtlv from the first IE in the overall TLV data. */
|
||||
static inline void osmo_gtlv_load_start(struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
gtlv->val = NULL;
|
||||
}
|
||||
|
||||
int osmo_gtlv_load_next(struct osmo_gtlv_load *gtlv);
|
||||
int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv, struct osmo_gtlv_tag_inst *ti);
|
||||
int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag);
|
||||
int osmo_gtlv_load_next_by_tag_inst(struct osmo_gtlv_load *gtlv, const struct osmo_gtlv_tag_inst *ti);
|
||||
|
||||
/* State for storing a TLV structure into a msgb. */
|
||||
struct osmo_gtlv_put {
|
||||
/*! Caller-defined context pointer available for use by load_tl() and store_tl() implementations. */
|
||||
void *priv;
|
||||
|
||||
/* Definition of tag and length sizes (by function pointers). */
|
||||
const struct osmo_gtlv_cfg *cfg;
|
||||
|
||||
/* msgb to append new TL to */
|
||||
struct msgb *dst;
|
||||
/* What was the last TL written and where are its TL and V */
|
||||
struct osmo_gtlv_tag_inst last_ti;
|
||||
uint8_t *last_tl;
|
||||
uint8_t *last_val;
|
||||
};
|
||||
|
||||
int osmo_gtlv_put_tl(struct osmo_gtlv_put *gtlv, unsigned int tag, size_t len);
|
||||
int osmo_gtlv_put_tli(struct osmo_gtlv_put *gtlv, const struct osmo_gtlv_tag_inst *ti, size_t len);
|
||||
int osmo_gtlv_put_update_tl(struct osmo_gtlv_put *gtlv);
|
||||
@@ -1,201 +0,0 @@
|
||||
/* Decode and encode the value parts of a TLV structure */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
struct value_string;
|
||||
|
||||
/* User defined function to decode a single TLV value part. See struct osmo_gtlv_coding.
|
||||
* \param decoded_struct Pointer to the root struct, as context information, e.g. for logging.
|
||||
* \param decode_to Pointer to the struct member, write the decoded value here.
|
||||
* \param gtlv TLV loader, pointing at a gtlv->val of gtlv->len bytes.
|
||||
* \return 0 on success, nonzero on error, e.g. -EINVAL if the gtlv->val is invalid.
|
||||
*/
|
||||
typedef int (*osmo_gtlv_dec_func)(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv);
|
||||
|
||||
/* User defined function to encode a single TLV value part. See struct osmo_gtlv_coding.
|
||||
* \param gtlv TLV writer, pointing at a gtlv->dst to msgb_put() data in.
|
||||
* \param decoded_struct Pointer to the root struct, as context information, e.g. for logging.
|
||||
* \param encode_from Pointer to the struct member, obtain the value to encode from here.
|
||||
* \return 0 on success, nonzero on error, e.g. -EINVAL if encode_from has an un-encodable value.
|
||||
*/
|
||||
typedef int (*osmo_gtlv_enc_func)(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from);
|
||||
|
||||
/* Optional user defined function to convert a decoded IE struct (the Value part stored as C struct) to string. See
|
||||
* struct osmo_gtlv_coding.
|
||||
* \param buf Return string in this buffer.
|
||||
* \param buflen Size of buf.
|
||||
* \param str_of Pointer to the struct member described by an osmo_gtlv_coding, obtain the value to encode from here.
|
||||
* \return number of characters that would be written if the buffer is large enough, like snprintf().
|
||||
*/
|
||||
typedef int (*osmo_gtlv_enc_to_str_func)(char *buf, size_t buflen, const void *str_of);
|
||||
|
||||
/* Whether TLV structures nested inside the value data of an outer IE should be parsed in the same order. */
|
||||
enum osmo_gtlv_coding_nested_ies_ordered {
|
||||
/*! When stepping into nested IEs, keep the same ordering requirement as the outer IE. */
|
||||
OSMO_GTLV_NESTED_IES_ORDERING_SAME = 0,
|
||||
/*! Require IEs in a PDU to appear exactly in the order defined by osmo_gtlv_coding arrays. Causes a parsing
|
||||
* failure if the TLVs appear in a different order. Does much less iterating looking for matching tags when
|
||||
* decoding (faster). */
|
||||
OSMO_GTLV_NESTED_IES_ORDERED,
|
||||
/*! Do not require IEs to be in the defined order in decoded PDUs. When encoding a TLV, IEs will always be
|
||||
* encoded in the order they are defined. This has an effect on decoding only. */
|
||||
OSMO_GTLV_NESTED_IES_UNORDERED,
|
||||
};
|
||||
|
||||
#define OSMO_ARRAY_PITCH(arr) ((char *)(&(arr)[1]) - (char *)(arr))
|
||||
#define OSMO_MEMB_ARRAY_PITCH(obj_type, arr_memb) OSMO_ARRAY_PITCH(((obj_type *)0)->arr_memb)
|
||||
|
||||
/*! Definition of how to decode/encode a IE to/from a struct.
|
||||
* Kept in lists describing TLV structures, and nestable.
|
||||
*
|
||||
* Instance lists of this can be composed manually, or auto-generated using gtlv_gen.c. Auto-generating has the benefit
|
||||
* that the decoded structs to match the IEs are also generated at the same time and thus always match the message
|
||||
* definitions. For an example, see tests/libosmo-gtlv/test_gtlv_gen/. */
|
||||
struct osmo_gtlv_coding {
|
||||
/*! the IEI discriminator, and optional instance number */
|
||||
struct osmo_gtlv_tag_inst ti;
|
||||
|
||||
/*! Decoding function callback. Invoked for each defined and present IE encountered in the message.
|
||||
* Return 0 on success, negative on failure. */
|
||||
osmo_gtlv_dec_func dec_func;
|
||||
/*! Encoding function callback. Invoked for each defined and present IE encountered in the message.
|
||||
* Return 0 on success, negative on failure. */
|
||||
osmo_gtlv_enc_func enc_func;
|
||||
|
||||
/*! Means to output the decoded value to a human readable string, optional. */
|
||||
osmo_gtlv_enc_to_str_func enc_to_str_func;
|
||||
|
||||
/*! offsetof(decoded_struct_type, member_var): how far into the base struct you find a specific field for decoded
|
||||
* value. For example, memb_ofs = offsetof(struct foo_msg, ies.bar_response.cause).
|
||||
* When decoding, the decoded value is written here, when encoding it is read from here. */
|
||||
unsigned int memb_ofs;
|
||||
/*! For repeated IEs (.has_count = true), the array pitch / the offset to add to get to the next array index. */
|
||||
unsigned int memb_array_pitch;
|
||||
|
||||
/*! True for optional/conditional IEs. */
|
||||
bool has_presence_flag;
|
||||
/* For optional/conditional IEs (has_presence_flag = true), the offset of the bool foo_present flag,
|
||||
* For example, if there are
|
||||
*
|
||||
* struct foo_msg {
|
||||
* struct baz baz;
|
||||
* bool baz_present;
|
||||
* };
|
||||
*
|
||||
* then set
|
||||
* memb_ofs = offsetof(struct foo_msg, baz);
|
||||
* has_presence_flag = true;
|
||||
* presence_flag_ofs = offsetof(struct foo_msg, baz_present);
|
||||
*/
|
||||
unsigned int presence_flag_ofs;
|
||||
|
||||
/*! True for repeated IEs, for array members:
|
||||
*
|
||||
* struct foo_msg {
|
||||
* struct moo moo[10];
|
||||
* unsigned int moo_count;
|
||||
* };
|
||||
*
|
||||
* memb_ofs = offsetof(struct foo_msg, moo);
|
||||
* has_count = true;
|
||||
* count_ofs = offsetof(struct foo_msg, moo_count);
|
||||
* count_max = 10;
|
||||
*/
|
||||
bool has_count;
|
||||
/*! For repeated IEs, the offset of the unsigned int foo_count indicator of how many array indexes are
|
||||
* in use. See has_count. */
|
||||
unsigned int count_ofs;
|
||||
/*! Maximum array size for member_var[]. See has_count. */
|
||||
unsigned int count_max;
|
||||
/*! If nonzero, it is an error when less than this amount of the repeated IE have been decoded. */
|
||||
unsigned int count_mandatory;
|
||||
|
||||
/*! For nested TLVs: if this IE's value part is itself a separate TLV structure, point this at the list of IE
|
||||
* coding definitions for the inner IEs.
|
||||
* In this example, the nested IEs decode/encode to different sub structs depending on the tag value.
|
||||
*
|
||||
* struct bar {
|
||||
* int aaa;
|
||||
* int bbb;
|
||||
* };
|
||||
*
|
||||
* struct foo_msg {
|
||||
* struct bar bar;
|
||||
* struct bar other_bar;
|
||||
* };
|
||||
*
|
||||
* struct osmo_gtlv_coding bar_nested_ies[] = {
|
||||
* { FOO_IEI_AAA, .memb_ofs = offsetof(struct bar, aaa), },
|
||||
* { FOO_IEI_BBB, .memb_ofs = offsetof(struct bar, bbb), },
|
||||
* {}
|
||||
* };
|
||||
*
|
||||
* struct osmo_gtlv_coding foo_msg_ies[] = {
|
||||
* { FOO_IEI_GOO, .memb_ofs = offsetof(struct foo_msg, bar), .nested_ies = bar_nested_ies, },
|
||||
* { FOO_IEI_OTHER_GOO, .memb_ofs = offsetof(struct foo_msg, other_bar), .nested_ies = bar_nested_ies, },
|
||||
* {}
|
||||
* };
|
||||
*/
|
||||
const struct osmo_gtlv_coding *nested_ies;
|
||||
|
||||
/*! If the nested TLV has a different tag/length size than the outer TLV structure, provide a different config
|
||||
* here. If they are the same, just keep this NULL. */
|
||||
const struct osmo_gtlv_cfg *nested_ies_cfg;
|
||||
|
||||
/*! When stepping into nested IEs, what is the ordering requirement for the nested TLV structure? */
|
||||
enum osmo_gtlv_coding_nested_ies_ordered nested_ies_ordered;
|
||||
};
|
||||
|
||||
|
||||
/*! User defined hook for error logging during TLV and value decoding.
|
||||
* \param decoded_struct Pointer to the base struct describing this message, for context.
|
||||
* \param file Source file of where the error occurred.
|
||||
* \param line Source file line of where the error occurred.
|
||||
* \param fmt Error message string format.
|
||||
* \param ... Error message string args.
|
||||
*/
|
||||
typedef void (*osmo_gtlv_err_cb)(void *data, void *decoded_struct, const char *file, int line, const char *fmt, ...);
|
||||
|
||||
int osmo_gtlvs_decode(void *decoded_struct, unsigned int obj_ofs, struct osmo_gtlv_load *gtlv, bool tlv_ordered,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);
|
||||
|
||||
int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);
|
||||
|
||||
int osmo_gtlvs_encode_to_str_buf(char *buf, size_t buflen, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding, const struct value_string *iei_strs);
|
||||
char *osmo_gtlvs_encode_to_str_c(void *ctx, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding, const struct value_string *iei_strs);
|
||||
|
||||
static inline bool osmo_gtlv_coding_end(const struct osmo_gtlv_coding *iec)
|
||||
{
|
||||
return iec->dec_func == NULL && iec->enc_func == NULL && iec->nested_ies == NULL;
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/* Write h and c source files for TLV protocol definitions, based on very sparse TLV definitions.
|
||||
* For a usage example see tests/libosmo-gtlv/test_gtlv_gen/. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct osmo_gtlv_gen_ie;
|
||||
|
||||
/* O means optional, M means mandatory.
|
||||
* If all of the IE struct, tag name and functions can be derived from the name, just pass osmo_gtlv_gen_ie_auto as
|
||||
* TLV_GEN_IE. */
|
||||
#define OSMO_GTLV_GEN_O(TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .optional = true, .ie = &(TLV_GEN_IE) }
|
||||
#define OSMO_GTLV_GEN_M(TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .ie = &(TLV_GEN_IE) }
|
||||
#define OSMO_GTLV_GEN_O_MULTI(MAX, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .multi = MAX, .ie = &(TLV_GEN_IE) }
|
||||
#define OSMO_GTLV_GEN_M_MULTI(MAX, MAND_COUNT, TLV_GEN_IE, MEMB_NAME) \
|
||||
{ MEMB_NAME, .multi = MAX, .multi_mandatory = MAND_COUNT, .ie = &(TLV_GEN_IE) }
|
||||
#define OSMO_GTLV_GEN_O_INST(INSTANCE, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .optional = true, .instance = INSTANCE, .ie = &TLV_GEN_IE }
|
||||
#define OSMO_GTLV_GEN_M_INST(INSTANCE, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .instance = INSTANCE, .ie = &(TLV_GEN_IE) }
|
||||
|
||||
#define OSMO_GTLV_GEN_NO_INSTANCE INT_MAX
|
||||
|
||||
/*! osmo_gtlv_gen_ie with all members == NULL, so that all are derived from the member name. */
|
||||
extern const struct osmo_gtlv_gen_ie osmo_gtlv_gen_ie_auto;
|
||||
|
||||
/*! Modifier for Mandatory/Optional/Multiple around an osmo_gtlv_gen_ie. */
|
||||
struct osmo_gtlv_gen_ie_o {
|
||||
/*! The C name of the member in a decoded struct, to be of the type defined by .ie.
|
||||
* All parts of .ie, if NULL, are derived from this name.
|
||||
*
|
||||
* For example, simply this
|
||||
*
|
||||
* struct osmo_gtlv_gen_ie_o foo[] = {
|
||||
* OSMO_GTLV_GEN_O("bar", NULL),
|
||||
* };
|
||||
*
|
||||
* Generates
|
||||
*
|
||||
* struct myproto_msg_foo {
|
||||
* struct myproto_ie_bar bar;
|
||||
* }
|
||||
*
|
||||
* and an osmo_gtlv_coding entry of
|
||||
*
|
||||
* { MYPROTO_IEI_BAR,
|
||||
* .memb_ofs = offsetof(struct myproto_msg_foo, bar),
|
||||
* .dec_func = myproto_dec_bar,
|
||||
* .enc_func = myproto_enc_bar,
|
||||
* .enc_to_str_func = myproto_enc_to_str_bar,
|
||||
* }
|
||||
*
|
||||
* See also osmo_gtlv_gen_cfg.add_enc_to_str.
|
||||
*/
|
||||
const char *name;
|
||||
|
||||
/*! Whether to add a bool foo_present, and to skip encoding/decoding if false.
|
||||
* Only useful for non-multi IEs (compare OSMO_GTLV_GEN_O_MULTI() vs OSMO_GTLV_GEN_M_MULTI()). */
|
||||
bool optional;
|
||||
|
||||
/*! If non-NULL, the member is an array: foo[123] with an unsigned int foo_count.
|
||||
* Set to the maximum number of array elements; for foo[123] set .multi = 123. */
|
||||
unsigned int multi;
|
||||
/*! Number of mandatory occurences of the IE, only has an effect if .multi > 0. */
|
||||
unsigned int multi_mandatory;
|
||||
|
||||
/* If any, the instance nr to match, in C that yields an unsigned int.
|
||||
* e.g. "1" or "MYPROTO_FOO_INST_ONE". */
|
||||
const char *instance;
|
||||
|
||||
/*! IE decoding / encoding instructions. If NULL, the entire IE definition is derived from .name.
|
||||
* 'MYPROTO_IEI_NAME', 'myproto_dec_name()', 'myproto_enc_name()', 'myproto_enc_to_str_name()'.
|
||||
* Your myproto_ies_custom.h needs to define an enum value MYPROTO_IEI_NAME and*/
|
||||
const struct osmo_gtlv_gen_ie *ie;
|
||||
};
|
||||
|
||||
/*! Define decoding and encoding of a single IE, i.e. one full TLV. */
|
||||
struct osmo_gtlv_gen_ie {
|
||||
/*! like "uint32_t" or "struct foo".
|
||||
* If NULL, use "struct myproto_ie_<name>" instead, where <name> comes from the osmo_gtlv_gen_ie_o.
|
||||
* When there are nested IEs, the struct definition is auto-generated, deriving the struct members from the
|
||||
* nested_ies list.
|
||||
* When there are no nested IEs, the type needs to be defined manually by a myproto_ies_custom.h. */
|
||||
const char *decoded_type;
|
||||
|
||||
/*! C name of this tag value, e.g. "foo" to use tag "MYPROTO_IEI_FOO".
|
||||
* If NULL, take "MYPROTO_IEI_"+upper(memb_name) instead, where memb_name comes from the osmo_gtlv_gen_ie_o.
|
||||
* decoded_type and/or dec_enc may be derived from this, if they are NULL. */
|
||||
const char *tag_name;
|
||||
|
||||
/*! Name suffix of the dec/enc functions. "foo" -> myproto_dec_foo(), myproto_enc_foo(),
|
||||
* myproto_enc_to_str_foo().
|
||||
* These functions need to be implemented manually in a myproto_ies_custom.c.
|
||||
* When osmo_gtlv_gen_cfg.add_enc_to_str is false, the myproto_enc_to_str_foo() is not required. */
|
||||
const char *dec_enc;
|
||||
|
||||
/*! List of inner IEs terminated by {}. If non-NULL, this is a "Grouped IE" with an inner TLV structure inside
|
||||
* this IE's V part. */
|
||||
const struct osmo_gtlv_gen_ie_o *nested_ies;
|
||||
|
||||
/*! To place a spec comment in the generated code. */
|
||||
const char *spec_ref;
|
||||
};
|
||||
|
||||
/*! General TLV decoding and encoding definitions applying to all IEs (and nested IEs). */
|
||||
struct osmo_gtlv_gen_cfg {
|
||||
/*! Name of the protocol for use in C type or function names, like "myproto". */
|
||||
const char *proto_name;
|
||||
|
||||
/*! When placing comments to spec references, prefix with this. For example, "3GPP TS 12.345 ". */
|
||||
const char *spec_ref_prefix;
|
||||
|
||||
/*! The type to pass a message discriminator as, like 'enum myproto_message_types' */
|
||||
const char *message_type_enum;
|
||||
/*! To reference a message type discriminator like MYPROTO_MSGT_FOO, this would be "MYPROTO_MSGT_". */
|
||||
const char *message_type_prefix;
|
||||
|
||||
/*! Type to use to represent tag IEI in decoded form.
|
||||
* For example "enum foo_msg_iei". */
|
||||
const char *tag_enum;
|
||||
/*! The tag IEI enum value is uppercase(tag_prefix + (iedef->tag_name or iedef->name)).
|
||||
* For example, with tag_prefix = "OSMO_FOO_IEI_", we would generate code like
|
||||
* enum osmo_foo_iei tag = OSMO_FOO_IEI_BAR; */
|
||||
const char *tag_prefix;
|
||||
|
||||
/*! When an osmo_gtlv_gen_ie provides no decoded_type string, it is derived from .name and this prefix is
|
||||
* added. For example, with decoded_type_prefix = "struct foo_ie_", the decoded_type defaults to
|
||||
* struct foo_ie_bar for an IE definition with name = "bar". */
|
||||
const char *decoded_type_prefix;
|
||||
|
||||
/*! To include user defined headers, set to something like "#include <osmocom/foo/foo_tlv_devs.h". This is put at
|
||||
* the head of the generated .h file. */
|
||||
const char *h_header;
|
||||
|
||||
/*! To include user defined headers, set to something like "#include <osmocom/foo/foo_msg.h". This is put at
|
||||
* the head of the generated .c file. */
|
||||
const char *c_header;
|
||||
|
||||
/*! Array of message IE definitions, indexed by message type. */
|
||||
const struct osmo_gtlv_gen_msg *msg_defs;
|
||||
|
||||
/*! Whether to add to_str functions. When true, every automatically derived IE (that has no nested IEs) needs to
|
||||
* have a myproto_enc_to_str_foo() defined by a myproto_ies_custom.c. When false, osmo_gtlvs_encode_to_str_buf()
|
||||
* will print '?' instead of the IE contents. */
|
||||
bool add_enc_to_str;
|
||||
};
|
||||
|
||||
/*! For generating the outer union that composes a protocol's PDU variants, an entry of the list of message names and
|
||||
* IEs in each message. */
|
||||
struct osmo_gtlv_gen_msg {
|
||||
const char *name;
|
||||
const struct osmo_gtlv_gen_ie_o *ies;
|
||||
};
|
||||
|
||||
int osmo_gtlv_gen_main(const struct osmo_gtlv_gen_cfg *cfg, int argc, const char **argv);
|
||||
@@ -17,9 +17,6 @@ CLEANFILES = \
|
||||
pfcp_ies_auto.h \
|
||||
$(NULL)
|
||||
|
||||
pfcp_ies_auto.h: $(top_srcdir)/src/libosmo-pfcp/gen__pfcp_ies_auto.c \
|
||||
$(top_srcdir)/src/libosmo-gtlv/gtlv_gen.c \
|
||||
$(top_srcdir)/include/osmocom/gtlv/gtlv_gen.h
|
||||
$(MAKE) -C $(top_builddir)/src/libosmo-gtlv
|
||||
pfcp_ies_auto.h: $(top_srcdir)/src/libosmo-pfcp/gen__pfcp_ies_auto.c
|
||||
$(MAKE) -C $(top_builddir)/src/libosmo-pfcp gen__pfcp_ies_auto
|
||||
$(top_builddir)/src/libosmo-pfcp/gen__pfcp_ies_auto h > $(builddir)/pfcp_ies_auto.h
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
SUBDIRS = \
|
||||
libosmo-gtlv \
|
||||
libosmo-pfcp \
|
||||
osmo-upf \
|
||||
$(NULL)
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
-I$(builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_LIBRARIES = \
|
||||
libosmo-gtlv.a \
|
||||
$(NULL)
|
||||
|
||||
libosmo_gtlv_a_SOURCES = \
|
||||
gtlv.c \
|
||||
gtlv_dec_enc.c \
|
||||
gtlv_gen.c \
|
||||
$(NULL)
|
||||
@@ -1,342 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
int osmo_gtlv_tag_inst_cmp(const struct osmo_gtlv_tag_inst *a, const struct osmo_gtlv_tag_inst *b)
|
||||
{
|
||||
int cmp;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
cmp = OSMO_CMP(a->tag, b->tag);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
cmp = OSMO_CMP(a->instance_present ? 1 : 0, b->instance_present ? 1 : 0);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
if (a->instance_present)
|
||||
return OSMO_CMP(a->instance, b->instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_gtlv_tag_inst_to_str_buf(char *buf, size_t buflen, const struct osmo_gtlv_tag_inst *ti,
|
||||
const struct value_string *tag_names)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
if (!tag_names)
|
||||
OSMO_STRBUF_PRINTF(sb, "%u", ti->tag);
|
||||
else
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", get_value_string(tag_names, ti->tag));
|
||||
if (ti->instance_present)
|
||||
OSMO_STRBUF_PRINTF(sb, "[%u]", ti->instance);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *osmo_gtlv_tag_inst_to_str_c(void *ctx, const struct osmo_gtlv_tag_inst *ti,
|
||||
const struct value_string *tag_names)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_gtlv_tag_inst_to_str_buf, ti, tag_names)
|
||||
}
|
||||
|
||||
static int next_tl_valid(const struct osmo_gtlv_load *gtlv, const uint8_t **ie_start_p, size_t *buflen_left_p)
|
||||
{
|
||||
const uint8_t *ie_start;
|
||||
size_t buflen_left;
|
||||
|
||||
/* Start of next IE, or first IE for first invocation. */
|
||||
if (!gtlv->val)
|
||||
ie_start = gtlv->src.data;
|
||||
else
|
||||
ie_start = gtlv->val + gtlv->len;
|
||||
|
||||
/* Sanity */
|
||||
if (ie_start < gtlv->src.data || ie_start > gtlv->src.data + gtlv->src.len)
|
||||
return -ENOSPC;
|
||||
|
||||
buflen_left = gtlv->src.len - (ie_start - gtlv->src.data);
|
||||
|
||||
/* Too short for parsing an IE? Check also against integer overflow. */
|
||||
if (buflen_left && ((buflen_left < gtlv->cfg->tl_min_size) || (buflen_left > gtlv->src.len)))
|
||||
return -EBADMSG;
|
||||
|
||||
*ie_start_p = ie_start;
|
||||
*buflen_left_p = buflen_left;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return a TLV IE from a message buffer.
|
||||
*
|
||||
* Return the first or next TLV data found in the data buffer, based on the state of the gtlv parameter.
|
||||
* When gtlv->val is NULL, return the first IE in the data buffer.
|
||||
* Otherwise assume that gtlv points at a valid IE in the data structure, and return the subsequent IE.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* struct osmo_gtlv gtlv = {
|
||||
* .cfg = osmo_t16l16v_cfg,
|
||||
* .src = { .data = msgb_l3(msg), .len = msgb_l3len(msg) },
|
||||
* };
|
||||
* for (;;) {
|
||||
* if (osmo_gtlv_next(>lv)) {
|
||||
* printf("Error\n");
|
||||
* break;
|
||||
* }
|
||||
* if (!gtlv.val) {
|
||||
* printf("End\n");
|
||||
* break;
|
||||
* }
|
||||
* printf("Tag %u: %zu octets: %s\n", gtlv.tag, gtlv.len, osmo_hexdump(gtlv.val, gtlv.len));
|
||||
* }
|
||||
*
|
||||
* \param[inout] gtlv Buffer to return the IE data, and state for TLV parsing position. gtlv->msg should indicate the
|
||||
* overall message buffer. The other gtlv members should be zero initialized before the first call, and
|
||||
* remain unchanged between invocations of this function.
|
||||
* \returns 0 on success, negative on TLV parsing error. The IE data is returned in gtlv->tag, gtlv->len and gtlv->val;
|
||||
* gtlv->val == NULL if no more IEs remain in the buffer.
|
||||
*/
|
||||
int osmo_gtlv_load_next(struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
const uint8_t *ie_start;
|
||||
const uint8_t *ie_end;
|
||||
size_t buflen_left;
|
||||
int rc;
|
||||
|
||||
rc = next_tl_valid(gtlv, &ie_start, &buflen_left);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* No more IEs? */
|
||||
if (!buflen_left) {
|
||||
gtlv->val = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Locate next IE */
|
||||
OSMO_ASSERT(gtlv->cfg->load_tl);
|
||||
gtlv->ti = (struct osmo_gtlv_tag_inst){};
|
||||
rc = gtlv->cfg->load_tl(gtlv, ie_start, buflen_left);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Sanity */
|
||||
ie_end = gtlv->val + gtlv->len;
|
||||
if (ie_end < gtlv->src.data || ie_end > gtlv->src.data + gtlv->src.len)
|
||||
return -EBADMSG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the tag of the IE that osmo_gtlv_next() would yield, do not change the gtlv state.
|
||||
*
|
||||
* \param[in] gtlv state for TLV parsing position; is not modified.
|
||||
* \param[out] tag the tag number on success, if NULL don't return the tag.
|
||||
* \param[out] instance the instance number or OSMO_GTLV_NO_INSTANCE if there is no instance value,
|
||||
* if NULL don't return the instance value.
|
||||
* \returns 0 on success, negative on TLV parsing error, -ENOENT when no more tags follow.
|
||||
*/
|
||||
int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv, struct osmo_gtlv_tag_inst *ti)
|
||||
{
|
||||
const uint8_t *ie_start;
|
||||
size_t buflen_left;
|
||||
int rc;
|
||||
/* Guard against modification by load_tl(). */
|
||||
struct osmo_gtlv_load mtlv = *gtlv;
|
||||
mtlv.ti = (struct osmo_gtlv_tag_inst){};
|
||||
|
||||
rc = next_tl_valid(&mtlv, &ie_start, &buflen_left);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!buflen_left)
|
||||
return -ENOENT;
|
||||
|
||||
/* Return next IE tag*/
|
||||
OSMO_ASSERT(mtlv.cfg->load_tl);
|
||||
rc = gtlv->cfg->load_tl(&mtlv, ie_start, buflen_left);
|
||||
if (rc)
|
||||
return -EBADMSG;
|
||||
if (ti)
|
||||
*ti = mtlv.ti;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Same as osmo_gtlv_load_next(), but skip any IEs until the given tag is reached. Change the gtlv state only when success
|
||||
* is returned.
|
||||
* \param[out] gtlv Return the next IE's TLV info.
|
||||
* \param[in] tag Tag value to match.
|
||||
* \param[in] instance Instance value to match; For IEs that have no instance value (no TLIV), pass
|
||||
* OSMO_GTLV_NO_INSTANCE.
|
||||
* \return 0 when the tag is found. Return -ENOENT when no such tag follows and keep the gtlv unchanged. */
|
||||
int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag)
|
||||
{
|
||||
struct osmo_gtlv_tag_inst ti = { .tag = tag };
|
||||
return osmo_gtlv_load_next_by_tag_inst(gtlv, &ti);
|
||||
}
|
||||
|
||||
int osmo_gtlv_load_next_by_tag_inst(struct osmo_gtlv_load *gtlv, const struct osmo_gtlv_tag_inst *ti)
|
||||
{
|
||||
struct osmo_gtlv_load work = *gtlv;
|
||||
for (;;) {
|
||||
int rc = osmo_gtlv_load_next(&work);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!work.val)
|
||||
return -ENOENT;
|
||||
if (!osmo_gtlv_tag_inst_cmp(&work.ti, ti)) {
|
||||
*gtlv = work;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Put tag header and length at the end of the msgb, according to gtlv->cfg->store_tl().
|
||||
* If the length is not known yet, it can be passed as 0 at first, and osmo_gtlv_put_update_tl() can determine the
|
||||
* resulting length after the value part was put into the msgb.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* struct msgb *msg = msgb_alloc(1024, "foo"),
|
||||
* struct osmo_gtlv_put gtlv = {
|
||||
* .cfg = osmo_t16l16v_cfg,
|
||||
* .dst = msg,
|
||||
* }
|
||||
*
|
||||
* osmo_gtlv_put_tl(gtlv, 23, 0); // tag 23, length 0 = not known yet
|
||||
*
|
||||
* msgb_put(msg, 42);
|
||||
* ...
|
||||
* msgb_put(msg, 42);
|
||||
* ...
|
||||
* msgb_put(msg, 42);
|
||||
*
|
||||
* osmo_gtlv_put_update_tl(gtlv);
|
||||
*
|
||||
* Return 0 on success, -EINVAL if the tag value is invalid, -EMSGSIZE if len is too large.
|
||||
*/
|
||||
int osmo_gtlv_put_tl(struct osmo_gtlv_put *gtlv, unsigned int tag, size_t len)
|
||||
{
|
||||
struct osmo_gtlv_tag_inst ti = { .tag = tag };
|
||||
return osmo_gtlv_put_tli(gtlv, &ti, len);
|
||||
}
|
||||
|
||||
/* Put tag header, instance value and length at the end of the msgb, according to gtlv->cfg->store_tl().
|
||||
* This is the same as osmo_gtlv_put_tl(), only osmo_gtlv_put_tl() passes instance = 0.
|
||||
*/
|
||||
int osmo_gtlv_put_tli(struct osmo_gtlv_put *gtlv, const struct osmo_gtlv_tag_inst *ti, size_t len)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *last_tl;
|
||||
OSMO_ASSERT(gtlv->cfg->store_tl);
|
||||
last_tl = gtlv->dst->tail;
|
||||
rc = gtlv->cfg->store_tl(gtlv->dst->tail, msgb_tailroom(gtlv->dst), ti, len, gtlv);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (rc > 0)
|
||||
msgb_put(gtlv->dst, rc);
|
||||
gtlv->last_ti = *ti;
|
||||
gtlv->last_tl = last_tl;
|
||||
gtlv->last_val = gtlv->dst->tail;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update the length of the last put IE header (last call to osmo_gtlv_put_tl()) to match with the current
|
||||
* gtlv->dst->tail.
|
||||
* Return 0 on success, -EMSGSIZE if the amount of data written since osmo_gtlv_put_tl() is too large.
|
||||
*/
|
||||
int osmo_gtlv_put_update_tl(struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
size_t len = gtlv->dst->tail - gtlv->last_val;
|
||||
int rc = gtlv->cfg->store_tl(gtlv->last_tl, gtlv->last_val - gtlv->last_tl, >lv->last_ti, len, gtlv);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
/* In case the TL has changed in size, hopefully the implementation has moved the msgb data. Make sure last_val
|
||||
* points at the right place now. */
|
||||
gtlv->last_val = gtlv->last_tl + rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int t8l8v_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
|
||||
{
|
||||
/* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */
|
||||
gtlv->ti.tag = src_data[0];
|
||||
gtlv->len = src_data[1];
|
||||
gtlv->val = src_data + 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int t8l8v_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
if (ti->tag > UINT8_MAX)
|
||||
return -EINVAL;
|
||||
if (len > UINT8_MAX)
|
||||
return -EMSGSIZE;
|
||||
if (dst_data_avail < 2)
|
||||
return -ENOSPC;
|
||||
dst_data[0] = ti->tag;
|
||||
dst_data[1] = len;
|
||||
return 2;
|
||||
}
|
||||
|
||||
const struct osmo_gtlv_cfg osmo_t8l8v_cfg = {
|
||||
.tl_min_size = 2,
|
||||
.load_tl = t8l8v_load_tl,
|
||||
.store_tl = t8l8v_store_tl,
|
||||
};
|
||||
|
||||
static int t16l16v_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
|
||||
{
|
||||
/* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 4. */
|
||||
gtlv->ti.tag = osmo_load16be(src_data);
|
||||
gtlv->len = osmo_load16be(src_data + 2);
|
||||
gtlv->val = src_data + 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int t16l16v_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
if (ti->tag > UINT16_MAX)
|
||||
return -EINVAL;
|
||||
if (len > UINT16_MAX)
|
||||
return -EMSGSIZE;
|
||||
if (dst_data_avail < 4)
|
||||
return -ENOSPC;
|
||||
osmo_store16be(ti->tag, dst_data);
|
||||
osmo_store16be(len, dst_data + 2);
|
||||
return 4;
|
||||
}
|
||||
|
||||
const struct osmo_gtlv_cfg osmo_t16l16v_cfg = {
|
||||
.tl_min_size = 4,
|
||||
.load_tl = t16l16v_load_tl,
|
||||
.store_tl = t16l16v_store_tl,
|
||||
};
|
||||
@@ -1,530 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv_dec_enc.h>
|
||||
|
||||
/* Reverse offsetof(): return the address of the struct member for a given osmo_gtlv_msg and member ofs_foo value. */
|
||||
#define MEMB(M, MEMB_OFS) ((void *)((char *)(M) + (MEMB_OFS)))
|
||||
|
||||
#define RETURN_ERROR(RC, TAG_INST, FMT, ARGS...) \
|
||||
do {\
|
||||
if (err_cb) { \
|
||||
if ((TAG_INST).instance_present) \
|
||||
err_cb(err_cb_data, (void *)decoded_struct, __FILE__, __LINE__, \
|
||||
"tag 0x%x = %s instance %u: " FMT " (%d: %s)\n", \
|
||||
(TAG_INST).tag, get_value_string(iei_strs, (TAG_INST).tag), \
|
||||
(TAG_INST).instance, ##ARGS, \
|
||||
RC, strerror((RC) > 0 ? (RC) : -(RC))); \
|
||||
else \
|
||||
err_cb(err_cb_data, (void *)decoded_struct, __FILE__, __LINE__, \
|
||||
"tag 0x%x = %s: " FMT " (%d: %s)\n", \
|
||||
(TAG_INST).tag, get_value_string(iei_strs, (TAG_INST).tag), ##ARGS, \
|
||||
RC, strerror((RC) > 0 ? (RC) : -(RC))); \
|
||||
} \
|
||||
return RC; \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*! Decode a TLV structure from raw data to a decoded struct, for unordered TLV IEs.
|
||||
* How to decode IE values and where to place them in the decoded struct, is defined by ie_coding, an array terminated
|
||||
* by a '{}' entry.
|
||||
* The IEs may appear in any ordering in the TLV data.
|
||||
* For unordered decoding, only IEs with has_presence_flag == true or has_count == true may repeat. Other IE definitions
|
||||
* cause the last read TLV to overwrite all previous decodings, all into the first occurrence in ie_coding.
|
||||
* \param[out] decoded_struct Pointer to the struct to write parsed IE data to.
|
||||
* \param[in] obj_ofs Pass as zero. Used for nested IEs: offset added to decoded_struct to get to a sub-struct.
|
||||
* \param[in] gtlv TLV data to parse, as given in gtlv->msg.*. Must be ready for osmo_gtlv_load_start().
|
||||
* \param[in] ie_coding A list of permitted/expected IEI tags and instructions for decoding.
|
||||
* \param[in] err_cb Function to call to report an error message, or NULL.
|
||||
* \param[in] err_cb_data Caller supplied context to pass to the err_cb as 'data' argument.
|
||||
* \param[in] iei_strs value_string array to give IEI names in error messages passed to err_cb(), or NULL.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_ofs, struct osmo_gtlv_load *gtlv,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)
|
||||
{
|
||||
void *obj = MEMB(decoded_struct, obj_ofs);
|
||||
const struct osmo_gtlv_coding *iec;
|
||||
unsigned int *multi_count_p;
|
||||
|
||||
/* To check for presence of mandatory IEs, need to keep a flag stack of seen ie_coding entries. This array has
|
||||
* to have at least the nr of entries that the ie_coding array has. Let's allow up to this many ie_coding
|
||||
* entries to avoid dynamic allocation. Seems like enough. */
|
||||
bool seen_ie_coding_entries[4096] = {};
|
||||
bool *seen_p;
|
||||
#define CHECK_SEEN(IEC) do { \
|
||||
unsigned int ie_coding_idx = (IEC) - ie_coding; \
|
||||
if (ie_coding_idx >= ARRAY_SIZE(seen_ie_coding_entries)) \
|
||||
RETURN_ERROR(-ENOTSUP, gtlv->ti, \
|
||||
"Too many IE definitions for decoding an unordered TLV structure"); \
|
||||
seen_p = &seen_ie_coding_entries[ie_coding_idx]; \
|
||||
} while (0)
|
||||
|
||||
|
||||
osmo_gtlv_load_start(gtlv);
|
||||
|
||||
/* IEs are allowed to come in any order. So traverse the TLV structure once, and find an IE parser for each (if
|
||||
* any). */
|
||||
for (;;) {
|
||||
int rc;
|
||||
bool *presence_flag_p;
|
||||
unsigned int memb_next_array_idx;
|
||||
unsigned int memb_ofs;
|
||||
unsigned int ie_max_allowed_count;
|
||||
|
||||
rc = osmo_gtlv_load_next(gtlv);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, gtlv->ti, "Decoding IEs failed on or after this tag");
|
||||
if (!gtlv->val) {
|
||||
/* End of the TLV structure */
|
||||
break;
|
||||
}
|
||||
|
||||
/* ie_max_allowed_count counts how often the same IEI may appear in a message until all struct members
|
||||
* that can store them are filled up. */
|
||||
ie_max_allowed_count = 0;
|
||||
|
||||
do {
|
||||
/* Find the IE coding for this tag */
|
||||
for (iec = ie_coding;
|
||||
!osmo_gtlv_coding_end(iec) && osmo_gtlv_tag_inst_cmp(&iec->ti, >lv->ti);
|
||||
iec++);
|
||||
/* No such IE coding found. */
|
||||
if (osmo_gtlv_coding_end(iec))
|
||||
break;
|
||||
|
||||
/* Keep track how often this tag can occur */
|
||||
ie_max_allowed_count += iec->has_count ? iec->count_max : 1;
|
||||
|
||||
/* Was this iec instance already decoded? Then skip to the next one, if any. */
|
||||
presence_flag_p = iec->has_presence_flag ? MEMB(obj, iec->presence_flag_ofs) : NULL;
|
||||
multi_count_p = iec->has_count ? MEMB(obj, iec->count_ofs) : NULL;
|
||||
if ((presence_flag_p && *presence_flag_p)
|
||||
|| (multi_count_p && *multi_count_p >= iec->count_max))
|
||||
continue;
|
||||
/* For IEs with a presence flag or a multi count, the decoded struct provides the information
|
||||
* whether the IE has already been decoded. Do the same for mandatory IEs, using local state in
|
||||
* seen_ie_coding_entries[]. */
|
||||
CHECK_SEEN(iec);
|
||||
if (*seen_p)
|
||||
continue;
|
||||
} while (0);
|
||||
if (osmo_gtlv_coding_end(iec)) {
|
||||
if (ie_max_allowed_count) {
|
||||
/* There have been IE definitions for this IEI, but all slots to decode it are already
|
||||
* filled. */
|
||||
RETURN_ERROR(-ENOTSUP, gtlv->ti, "Only %u instances of this IE are supported per message",
|
||||
ie_max_allowed_count);
|
||||
}
|
||||
/* No such IE defined in ie_coding, just skip the TLV. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If this is a repeated IE, decode into the correct array index memb[idx],
|
||||
* next idx == (*multi_count_p). We've already guaranteed above that *multi_count_p < count_max. */
|
||||
memb_next_array_idx = multi_count_p ? *multi_count_p : 0;
|
||||
memb_ofs = iec->memb_ofs + memb_next_array_idx * iec->memb_array_pitch;
|
||||
|
||||
/* Decode IE value part */
|
||||
if (iec->nested_ies) {
|
||||
/* A nested IE: the value part of this TLV is in turn a TLV structure. Decode the inner
|
||||
* IEs. */
|
||||
struct osmo_gtlv_load inner_tlv = {
|
||||
.cfg = iec->nested_ies_cfg ? : gtlv->cfg,
|
||||
.src = {
|
||||
.data = gtlv->val,
|
||||
.len = gtlv->len,
|
||||
}
|
||||
};
|
||||
bool ordered;
|
||||
switch (iec->nested_ies_ordered) {
|
||||
case OSMO_GTLV_NESTED_IES_ORDERED:
|
||||
ordered = true;
|
||||
break;
|
||||
case OSMO_GTLV_NESTED_IES_ORDERING_SAME:
|
||||
case OSMO_GTLV_NESTED_IES_UNORDERED:
|
||||
ordered = false;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
rc = osmo_gtlvs_decode(decoded_struct, obj_ofs + memb_ofs, &inner_tlv, ordered, iec->nested_ies,
|
||||
err_cb, err_cb_data, iei_strs);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, gtlv->ti, "Error while decoding TLV structure nested inside this IE");
|
||||
} else {
|
||||
/* Normal IE, decode the specific IE data. */
|
||||
if (!iec->dec_func)
|
||||
RETURN_ERROR(-EIO, gtlv->ti, "IE definition lacks a dec_func()");
|
||||
rc = iec->dec_func(decoded_struct, MEMB(obj, memb_ofs), gtlv);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, gtlv->ti, "Error while decoding this IE");
|
||||
}
|
||||
|
||||
if (multi_count_p) {
|
||||
/* A repeated IE, record that we've added one entry. This increments the foo_count value in the
|
||||
* decoded osmo_gtlv_msg.ies.*.
|
||||
* For example, multi_count_p points at osmo_gtlv_msg_session_est_req.create_pdr_count,
|
||||
* and memb_ofs points at osmo_gtlv_msg_session_est_req.create_pdr. */
|
||||
(*multi_count_p)++;
|
||||
}
|
||||
if (presence_flag_p) {
|
||||
*presence_flag_p = true;
|
||||
}
|
||||
CHECK_SEEN(iec);
|
||||
*seen_p = true;
|
||||
}
|
||||
|
||||
/* Check presence of mandatory IEs */
|
||||
for (iec = ie_coding; !osmo_gtlv_coding_end(iec); iec++) {
|
||||
if (iec->has_presence_flag)
|
||||
continue;
|
||||
multi_count_p = iec->has_count ? MEMB(obj, iec->count_ofs) : NULL;
|
||||
if (multi_count_p) {
|
||||
if (*multi_count_p < iec->count_mandatory)
|
||||
RETURN_ERROR(-EINVAL, iec->ti, "%u instances of this IE are mandatory, got %u",
|
||||
iec->count_mandatory, *multi_count_p);
|
||||
continue;
|
||||
}
|
||||
/* Neither an optional nor a multi member, hence it must be mandatory. */
|
||||
CHECK_SEEN(iec);
|
||||
if (!*seen_p)
|
||||
RETURN_ERROR(-EINVAL, iec->ti, "Missing mandatory IE");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Decode a TLV structure from raw data to a decoded struct, for ordered TLV IEs.
|
||||
* How to decode IE values and where to place them in the decoded struct, is defined by ie_coding, an array terminated
|
||||
* by a '{}' entry.
|
||||
* The IEs in the TLV structure must appear in the same order as they are defined in ie_coding.
|
||||
* cause the last read TLV to overwrite all previous decodings, all into the first occurrence in ie_coding.
|
||||
* \param[out] decoded_struct Pointer to the struct to write parsed IE data to.
|
||||
* \param[in] obj_ofs Pass as zero. Used for nested IEs: offset added to decoded_struct to get to a sub-struct.
|
||||
* \param[in] gtlv TLV data to parse, as given in gtlv->msg.*. Must be ready for osmo_gtlv_load_start().
|
||||
* \param[in] ie_coding A list of permitted/expected IEI tags and instructions for decoding.
|
||||
* \param[in] err_cb Function to call to report an error message, or NULL.
|
||||
* \param[in] err_cb_data Caller supplied context to pass to the err_cb as 'data' argument.
|
||||
* \param[in] iei_strs value_string array to give IEI names in error messages passed to err_cb(), or NULL.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs, struct osmo_gtlv_load *gtlv,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)
|
||||
{
|
||||
void *obj = MEMB(decoded_struct, obj_ofs);
|
||||
|
||||
osmo_gtlv_load_start(gtlv);
|
||||
|
||||
for (; !osmo_gtlv_coding_end(ie_coding); ie_coding++) {
|
||||
int rc;
|
||||
bool *presence_flag = ie_coding->has_presence_flag ? MEMB(obj, ie_coding->presence_flag_ofs) : NULL;
|
||||
unsigned int *multi_count = ie_coding->has_count ? MEMB(obj, ie_coding->count_ofs) : NULL;
|
||||
struct osmo_gtlv_tag_inst peek_ti;
|
||||
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(gtlv, &ie_coding->ti);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
break;
|
||||
case -ENOENT:
|
||||
if (!presence_flag && (!multi_count || *multi_count < ie_coding->count_mandatory))
|
||||
RETURN_ERROR(rc, ie_coding->ti, "Missing mandatory IE");
|
||||
if (presence_flag)
|
||||
*presence_flag = false;
|
||||
continue;
|
||||
default:
|
||||
RETURN_ERROR(rc, ie_coding->ti, "Error in TLV structure");
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* If this is a repeated IE, decode into the correct array index memb[idx],
|
||||
* next idx == (*multi_count) */
|
||||
unsigned int memb_next_array_idx = multi_count ? *multi_count : 0;
|
||||
unsigned int memb_ofs = ie_coding->memb_ofs + memb_next_array_idx * ie_coding->memb_array_pitch;
|
||||
|
||||
if (multi_count && memb_next_array_idx >= ie_coding->count_max)
|
||||
RETURN_ERROR(-ENOTSUP, ie_coding->ti, "Only %u instances of this IE are supported per message",
|
||||
ie_coding->count_max);
|
||||
|
||||
/* Decode IE value part */
|
||||
if (ie_coding->nested_ies) {
|
||||
/* A nested IE: the value part of this TLV is in turn a TLV structure. Decode the inner
|
||||
* IEs. */
|
||||
struct osmo_gtlv_load inner_tlv = {
|
||||
.cfg = ie_coding->nested_ies_cfg ? : gtlv->cfg,
|
||||
.src = {
|
||||
.data = gtlv->val,
|
||||
.len = gtlv->len,
|
||||
}
|
||||
};
|
||||
bool ordered;
|
||||
switch (ie_coding->nested_ies_ordered) {
|
||||
case OSMO_GTLV_NESTED_IES_ORDERING_SAME:
|
||||
case OSMO_GTLV_NESTED_IES_ORDERED:
|
||||
ordered = true;
|
||||
break;
|
||||
case OSMO_GTLV_NESTED_IES_UNORDERED:
|
||||
ordered = false;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
rc = osmo_gtlvs_decode(decoded_struct, obj_ofs + memb_ofs, &inner_tlv, ordered,
|
||||
ie_coding->nested_ies, err_cb, err_cb_data, iei_strs);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, ie_coding->ti,
|
||||
"Error while decoding TLV structure nested inside this IE");
|
||||
} else {
|
||||
/* Normal IE, decode the specific IE data. */
|
||||
if (!ie_coding->dec_func)
|
||||
RETURN_ERROR(-EIO, ie_coding->ti, "IE definition lacks a dec_func()");
|
||||
rc = ie_coding->dec_func(decoded_struct, MEMB(obj, memb_ofs), gtlv);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, ie_coding->ti, "Error while decoding this IE");
|
||||
}
|
||||
|
||||
if (presence_flag)
|
||||
*presence_flag = true;
|
||||
|
||||
if (!multi_count) {
|
||||
/* Not a repeated IE. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* A repeated IE, record that we've added one entry. This increments the foo_count value in the
|
||||
* decoded osmo_pfcp_msg.ies.*.
|
||||
* For example, multi_count points at osmo_pfcp_msg_session_est_req.create_pdr_count,
|
||||
* and memb_ofs points at osmo_pfcp_msg_session_est_req.create_pdr. */
|
||||
(*multi_count)++;
|
||||
|
||||
/* Does another one of these IEs follow? */
|
||||
if (osmo_gtlv_load_peek_tag(gtlv, &peek_ti)
|
||||
|| osmo_gtlv_tag_inst_cmp(&peek_ti, >lv->ti)) {
|
||||
/* Next tag is a different IE, end the repetition. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* continue, parsing the next repetition of this tag. */
|
||||
rc = osmo_gtlv_load_next(gtlv);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
/* continue parsing the next tag. */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Decode an entire TLV message from raw data to decoded struct.
|
||||
* How to decode IE values and where to put them in the decoded struct is defined by ie_coding, an array terminated by
|
||||
* a '{}' entry.
|
||||
* \param[out] decoded_struct Pointer to the struct to write parsed IE data to.
|
||||
* \param[in] obj_ofs Pass as zero. Used for nested IEs: offset added to decoded_struct to get to a sub-struct.
|
||||
* \param[in] gtlv TLV data to parse, as given in gtlv->msg.*. Must be ready for osmo_gtlv_load_start().
|
||||
* \param[in] ie_coding A list of permitted/expected IEI tags and instructions for decoding.
|
||||
* \param[in] err_cb Function to call to report an error message, or NULL.
|
||||
* \param[in] err_cb_data Caller supplied context to pass to the err_cb as 'data' argument.
|
||||
* \param[in] iei_strs value_string array to give IEI names in error messages passed to err_cb(), or NULL.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
int osmo_gtlvs_decode(void *decoded_struct, unsigned int obj_ofs, struct osmo_gtlv_load *gtlv, bool tlv_ordered,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)
|
||||
{
|
||||
if (!ie_coding)
|
||||
return -ENOTSUP;
|
||||
if (tlv_ordered)
|
||||
return osmo_gtlvs_decode_ordered(decoded_struct, obj_ofs, gtlv, ie_coding, err_cb, err_cb_data, iei_strs);
|
||||
else
|
||||
return osmo_gtlvs_decode_unordered(decoded_struct, obj_ofs, gtlv, ie_coding, err_cb, err_cb_data,
|
||||
iei_strs);
|
||||
}
|
||||
|
||||
/*! Encode a TLV structure from decoded struct to raw data.
|
||||
* How to encode IE values and where to read them in the decoded struct is defined by ie_coding, an array terminated by
|
||||
* a '{}' entry.
|
||||
* The IEs will be encoded in the order they appear in ie_coding.
|
||||
* \param[out] gtlv Write data using this TLV definition to gtlv->dst.
|
||||
* \param[in] decoded_struct C struct data to encode.
|
||||
* \param[in] obj_ofs Nesting offset, pass as 0.
|
||||
* \param[in] ie_coding A {} terminated list of IEI tags to encode (if present) and instructions for encoding.
|
||||
* \param[in] err_cb Function to call to report an error message, or NULL.
|
||||
* \param[in] err_cb_data Caller supplied context to pass to the err_cb as 'data' argument.
|
||||
* \param[in] iei_strs value_string array to give IEI names in error messages passed to err_cb(), or NULL.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)
|
||||
{
|
||||
void *obj = MEMB(decoded_struct, obj_ofs);
|
||||
|
||||
if (!ie_coding)
|
||||
return -ENOTSUP;
|
||||
|
||||
for (; !osmo_gtlv_coding_end(ie_coding); ie_coding++) {
|
||||
int rc;
|
||||
bool *presence_flag_p = ie_coding->has_presence_flag ? MEMB(obj, ie_coding->presence_flag_ofs) : NULL;
|
||||
unsigned int *multi_count_p = ie_coding->has_count ? MEMB(obj, ie_coding->count_ofs) : NULL;
|
||||
unsigned int n;
|
||||
unsigned int i;
|
||||
|
||||
if (presence_flag_p && !*presence_flag_p)
|
||||
continue;
|
||||
|
||||
if (multi_count_p) {
|
||||
n = *multi_count_p;
|
||||
if (!ie_coding->memb_array_pitch)
|
||||
RETURN_ERROR(-EFAULT, ie_coding->ti,
|
||||
"Error in protocol definition: The ie_coding lacks a memb_array_pitch"
|
||||
" value, cannot be used as multi-IE\n");
|
||||
} else {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
unsigned int memb_ofs;
|
||||
|
||||
osmo_gtlv_put_tli(gtlv, &ie_coding->ti, 0);
|
||||
|
||||
/* If this is a repeated IE, encode from the correct array index */
|
||||
if (multi_count_p && i >= ie_coding->count_max)
|
||||
RETURN_ERROR(-ENOTSUP, ie_coding->ti,
|
||||
"Only %u instances of this IE are supported per message", ie_coding->count_max);
|
||||
memb_ofs = ie_coding->memb_ofs + i * ie_coding->memb_array_pitch;
|
||||
|
||||
if (ie_coding->nested_ies) {
|
||||
struct osmo_gtlv_put nested_tlv = {
|
||||
.cfg = ie_coding->nested_ies_cfg ? : gtlv->cfg,
|
||||
.dst = gtlv->dst,
|
||||
};
|
||||
rc = osmo_gtlvs_encode(&nested_tlv, decoded_struct, obj_ofs + memb_ofs,
|
||||
ie_coding->nested_ies, err_cb, err_cb_data, iei_strs);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, ie_coding->ti,
|
||||
"Error while encoding TLV structure nested inside this IE");
|
||||
} else {
|
||||
rc = ie_coding->enc_func(gtlv, decoded_struct, MEMB(obj, memb_ofs));
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, ie_coding->ti, "Error while encoding this IE");
|
||||
}
|
||||
|
||||
osmo_gtlv_put_update_tl(gtlv);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Compose a human readable string describing a decoded struct.
|
||||
* How to encode IE values and where to read them in the decoded struct is defined by ie_coding, an array terminated by
|
||||
* a '{}' entry.
|
||||
* The IEs will be encoded in the order they appear in ie_coding.
|
||||
* \param[out] buf Return the string in this buffer.
|
||||
* \param[in] buflen Size of buf.
|
||||
* \param[in] decoded_struct C struct data to encode.
|
||||
* \param[in] obj_ofs Nesting offset, pass as 0.
|
||||
* \param[in] ie_coding A {} terminated list of IEI tags to encode (if present) and instructions for encoding.
|
||||
* \param[in] iei_strs value_string array to give IEI names in tag headers, or NULL.
|
||||
* \return number of characters that would be written if the buffer is large enough, like snprintf().
|
||||
*/
|
||||
int osmo_gtlvs_encode_to_str_buf(char *buf, size_t buflen, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding, const struct value_string *iei_strs)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
void *obj = MEMB(decoded_struct, obj_ofs);
|
||||
|
||||
if (!ie_coding)
|
||||
return -ENOTSUP;
|
||||
|
||||
for (; !osmo_gtlv_coding_end(ie_coding); ie_coding++) {
|
||||
bool *presence_flag_p = ie_coding->has_presence_flag ? MEMB(obj, ie_coding->presence_flag_ofs) : NULL;
|
||||
unsigned int *multi_count_p = ie_coding->has_count ? MEMB(obj, ie_coding->count_ofs) : NULL;
|
||||
unsigned int n;
|
||||
unsigned int i;
|
||||
|
||||
if (presence_flag_p && !*presence_flag_p)
|
||||
continue;
|
||||
|
||||
if (multi_count_p) {
|
||||
n = *multi_count_p;
|
||||
} else {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
if (!n)
|
||||
continue;
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, " '%s'=", get_value_string(iei_strs, ie_coding->ti.tag));
|
||||
if (multi_count_p)
|
||||
OSMO_STRBUF_PRINTF(sb, "{ ");
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
unsigned int memb_ofs;
|
||||
|
||||
/* If this is a repeated IE, encode from the correct array index */
|
||||
if (multi_count_p && i >= ie_coding->count_max)
|
||||
return -ENOTSUP;
|
||||
if (i > 0)
|
||||
OSMO_STRBUF_PRINTF(sb, ", ");
|
||||
|
||||
memb_ofs = ie_coding->memb_ofs + i * ie_coding->memb_array_pitch;
|
||||
|
||||
if (ie_coding->nested_ies) {
|
||||
OSMO_STRBUF_PRINTF(sb, "{");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_gtlvs_encode_to_str_buf, decoded_struct, obj_ofs + memb_ofs,
|
||||
ie_coding->nested_ies, iei_strs);
|
||||
OSMO_STRBUF_PRINTF(sb, " }");
|
||||
} else {
|
||||
if (ie_coding->enc_to_str_func)
|
||||
OSMO_STRBUF_APPEND(sb, ie_coding->enc_to_str_func, MEMB(obj, memb_ofs));
|
||||
else
|
||||
OSMO_STRBUF_PRINTF(sb, "(enc_to_str_func==NULL)");
|
||||
}
|
||||
}
|
||||
|
||||
if (multi_count_p)
|
||||
OSMO_STRBUF_PRINTF(sb, " }");
|
||||
}
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
/*! Compose a human readable string describing a decoded struct.
|
||||
* Like osmo_gtlvs_encode_to_str_buf() but returns a talloc allocated string.
|
||||
* \param[in] ctx talloc context to allocate from, e.g. OTC_SELECT.
|
||||
* \param[in] decoded_struct C struct data to encode.
|
||||
* \param[in] obj_ofs Nesting offset, pass as 0.
|
||||
* \param[in] ie_coding A {} terminated list of IEI tags to encode (if present) and instructions for encoding.
|
||||
* \param[in] iei_strs value_string array to give IEI names in tag headers, or NULL.
|
||||
* \return human readable string.
|
||||
*/
|
||||
char *osmo_gtlvs_encode_to_str_c(void *ctx, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding, const struct value_string *iei_strs)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 256, "ERROR", osmo_gtlvs_encode_to_str_buf, decoded_struct, obj_ofs, ie_coding, iei_strs)
|
||||
}
|
||||
@@ -1,420 +0,0 @@
|
||||
/* Write h and c source files for TLV protocol definitions, based on very sparse TLV definitions.
|
||||
* For a usage example see tests/libosmo-gtlv/test_gtlv_gen/. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv_gen.h>
|
||||
|
||||
static const struct osmo_gtlv_gen_cfg *g_cfg = NULL;
|
||||
|
||||
const struct osmo_gtlv_gen_ie osmo_gtlv_gen_ie_auto = {};
|
||||
|
||||
/* Helps avoid redundant definitions of the same type. */
|
||||
struct seen_entry {
|
||||
struct llist_head entry;
|
||||
char str[256];
|
||||
const void *from_def;
|
||||
};
|
||||
static LLIST_HEAD(seen_list);
|
||||
|
||||
static bool seen(const char *str, const void *from_def)
|
||||
{
|
||||
struct seen_entry *s;
|
||||
llist_for_each_entry(s, &seen_list, entry) {
|
||||
if (!strcmp(s->str, str)) {
|
||||
if (from_def != s->from_def) {
|
||||
fprintf(stderr, "ERROR: %s: multiple definitions use the same name: '%s'\n",
|
||||
g_cfg->proto_name, str);
|
||||
exit(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
s = talloc_zero(NULL, struct seen_entry);
|
||||
OSMO_STRLCPY_ARRAY(s->str, str);
|
||||
s->from_def = from_def;
|
||||
llist_add(&s->entry, &seen_list);
|
||||
return false;
|
||||
}
|
||||
static void clear_seen()
|
||||
{
|
||||
struct seen_entry *s;
|
||||
while ((s = llist_first_entry_or_null(&seen_list, struct seen_entry, entry))) {
|
||||
llist_del(&s->entry);
|
||||
talloc_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return "struct foo_ie_bar" from g_cfg->decoded_type_prefix and ie. */
|
||||
static inline const char *decoded_type(const struct osmo_gtlv_gen_ie_o *ie_o)
|
||||
{
|
||||
static char b[255];
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
const char *tag_name;
|
||||
if (ie && ie->decoded_type)
|
||||
return ie->decoded_type;
|
||||
/* "struct foo_ie_" + "bar" = struct foo_ie_bar*/
|
||||
tag_name = ie ? ie->tag_name : NULL;
|
||||
snprintf(b, sizeof(b), "%s%s", g_cfg->decoded_type_prefix, tag_name ? : ie_o->name);
|
||||
return b;
|
||||
}
|
||||
|
||||
/* --- .h file --- */
|
||||
|
||||
/* Write a listing of struct members like
|
||||
* bool foo_present;
|
||||
* int foo;
|
||||
* struct myproto_ie_bar bar;
|
||||
* struct abc abc[10];
|
||||
* int abc_count;
|
||||
*/
|
||||
static void write_ie_members(const struct osmo_gtlv_gen_ie_o ies[])
|
||||
{
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
if (ie_o->optional)
|
||||
printf("\tbool %s_present;\n", ie_o->name);
|
||||
printf("\t%s %s", decoded_type(ie_o), ie_o->name);
|
||||
if (ie_o->multi) {
|
||||
printf("[%u];\n", ie_o->multi);
|
||||
printf("\tunsigned int %s_count", ie_o->name);
|
||||
}
|
||||
printf(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Traverse nesting levels in the message definitions and generate the structs for all as needed. */
|
||||
static void write_ie_auto_structs(const struct osmo_gtlv_gen_ie_o ies[])
|
||||
{
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
if (!ies)
|
||||
return;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
if (!ie || !ie->nested_ies)
|
||||
continue;
|
||||
/* Recurse to write inner layers first, so that they can be referenced in outer layers. */
|
||||
write_ie_auto_structs(ie->nested_ies);
|
||||
|
||||
/* Various IE definitions can use the same underlying type. Only generate each type once. */
|
||||
if (seen(decoded_type(ie_o), NULL))
|
||||
continue;
|
||||
|
||||
/* Print:
|
||||
*
|
||||
* \* spec ref *\
|
||||
* struct myproto_ie_goo {
|
||||
* bool foo_present;
|
||||
* int foo;
|
||||
* struct myproto_ie_bar bar;
|
||||
* struct abc abc[10];
|
||||
* int abc_count;
|
||||
* };
|
||||
*/
|
||||
printf("\n");
|
||||
if (ie->spec_ref)
|
||||
printf("/* %s%s */\n", g_cfg->spec_ref_prefix, ie->spec_ref);
|
||||
printf("%s {\n", decoded_type(ie_o));
|
||||
write_ie_members(ie->nested_ies);
|
||||
printf("};\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Write all auto-generated structs, starting with the outer message definitions and nesting into all contained IE
|
||||
* definitions. */
|
||||
static void write_auto_structs()
|
||||
{
|
||||
const struct osmo_gtlv_gen_msg *gen_msg;
|
||||
clear_seen();
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
write_ie_auto_structs(gen_msg->ies);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the struct definitions for each message, i.e. for each entry in the outer PDU's message union, as well as the
|
||||
* union itself.
|
||||
*
|
||||
* struct myproto_msg_foo {
|
||||
* ...
|
||||
* }:
|
||||
* struct myproto_msg_goo {
|
||||
* ...
|
||||
* };
|
||||
* union myproto_ies {
|
||||
* myproto_msg_foo foo;
|
||||
* myproto_msg_goo goo;
|
||||
* };
|
||||
*/
|
||||
static void write_msg_union()
|
||||
{
|
||||
const struct osmo_gtlv_gen_msg *gen_msg;
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
/* "struct foo_msg" + "_%s" { *
|
||||
* struct foo_msg_goo_request { ... }; */
|
||||
printf("\nstruct %s_msg_%s {\n",
|
||||
g_cfg->proto_name,
|
||||
gen_msg->name);
|
||||
write_ie_members(gen_msg->ies);
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
printf("\nunion %s_ies {\n", g_cfg->proto_name);
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
printf("\tstruct %s_msg_%s %s;\n", g_cfg->proto_name,
|
||||
gen_msg->name, gen_msg->name);
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
/* Write the C header, myproto_ies_auto.h */
|
||||
static void write_h()
|
||||
{
|
||||
printf("/* THIS FILE IS GENERATED FROM %s */\n", __FILE__);
|
||||
printf("#include <stdint.h>\n");
|
||||
printf("#include <osmocom/gtlv/gtlv_dec_enc.h>\n");
|
||||
if (g_cfg->h_header)
|
||||
printf("\n%s\n", g_cfg->h_header);
|
||||
write_auto_structs();
|
||||
write_msg_union();
|
||||
printf("\nconst struct osmo_gtlv_coding *%s_get_msg_coding(%s message_type);\n",
|
||||
g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
printf("\n"
|
||||
"int %s_ies_decode(union %s_ies *dst, struct osmo_gtlv_load *gtlv, bool tlv_ordered,\n"
|
||||
" %s message_type, osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
printf("\n"
|
||||
"int %s_ies_encode(struct osmo_gtlv_put *gtlv, const union %s_ies *src,\n"
|
||||
" %s message_type, osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
printf("\n"
|
||||
"int %s_ies_encode_to_str(char *buf, size_t buflen, const union %s_ies *src,\n"
|
||||
" %s message_type, const struct value_string *iei_strs);\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
}
|
||||
|
||||
/* --- .c file --- */
|
||||
|
||||
/* Write a listing of:
|
||||
* extern int myproto_dec_foo(...);
|
||||
* extern int myproto_enc_foo(...);
|
||||
*/
|
||||
static void write_extern_dec_enc(const struct osmo_gtlv_gen_ie_o *ies)
|
||||
{
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
const char *dec_enc = ie_o->name;
|
||||
if (ie)
|
||||
dec_enc = ie->dec_enc ? : (ie->tag_name ? : ie_o->name);
|
||||
if (ie && ie->nested_ies) {
|
||||
write_extern_dec_enc(ie->nested_ies);
|
||||
continue;
|
||||
}
|
||||
if (seen(dec_enc, NULL))
|
||||
continue;
|
||||
printf("extern int %s_dec_%s(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv);\n",
|
||||
g_cfg->proto_name, dec_enc);
|
||||
printf("extern int %s_enc_%s(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from);\n",
|
||||
g_cfg->proto_name, dec_enc);
|
||||
if (g_cfg->add_enc_to_str)
|
||||
printf("extern int %s_enc_to_str_%s(char *buf, size_t buflen, const void *encode_from);\n",
|
||||
g_cfg->proto_name, dec_enc);
|
||||
}
|
||||
}
|
||||
|
||||
/* For a nested IE, write the struct osmo_gtlv_coding array of the inner IEs.
|
||||
* { { MYPROTO_IEI_BAR },
|
||||
* .memb_ofs = offsetof(struct myproto_foo, bar),
|
||||
* .dec_func = myproto_dec_bar,
|
||||
* .enc_func = myproto_enc_bar,
|
||||
* },
|
||||
*/
|
||||
static void write_ies_array(const char *indent, const struct osmo_gtlv_gen_ie_o *ies, const char *obj_type, const char *substruct)
|
||||
{
|
||||
#define printi(FMT, ARGS...) printf("%s" FMT, indent, ##ARGS)
|
||||
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
const char *tag_name = (ie && ie->tag_name) ? ie->tag_name : ie_o->name;
|
||||
printi("{ { %s%s", g_cfg->tag_prefix, osmo_str_toupper(tag_name));
|
||||
if (ie_o->instance)
|
||||
printf(", true, %s", ie_o->instance);
|
||||
printf(" },\n");
|
||||
printi(" .memb_ofs = offsetof(%s, %s%s),\n", obj_type, substruct, ie_o->name);
|
||||
if (ie->nested_ies) {
|
||||
printi(" .nested_ies = ies_in_%s,\n", tag_name);
|
||||
} else {
|
||||
const char *dec_enc = ie->dec_enc ? : (ie->tag_name ? : ie_o->name);
|
||||
printi(" .dec_func = %s_dec_%s,\n", g_cfg->proto_name, dec_enc);
|
||||
printi(" .enc_func = %s_enc_%s,\n", g_cfg->proto_name, dec_enc);
|
||||
if (g_cfg->add_enc_to_str)
|
||||
printi(" .enc_to_str_func = %s_enc_to_str_%s,\n", g_cfg->proto_name, dec_enc);
|
||||
}
|
||||
if (ie_o->multi) {
|
||||
printi(" .memb_array_pitch = OSMO_MEMB_ARRAY_PITCH(%s, %s%s),\n",
|
||||
obj_type, substruct, ie_o->name);
|
||||
printi(" .has_count = true, .count_max = %u,\n", ie_o->multi);
|
||||
printi(" .count_mandatory = %u,\n", ie_o->multi_mandatory);
|
||||
printi(" .count_ofs = offsetof(%s, %s%s_count),\n", obj_type, substruct, ie_o->name);
|
||||
}
|
||||
if (ie_o->optional) {
|
||||
printi(" .has_presence_flag = true,\n");
|
||||
printi(" .presence_flag_ofs = offsetof(%s, %s%s_present),\n", obj_type, substruct, ie_o->name);
|
||||
}
|
||||
printi("},\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* For a nested IE, write the struct osmo_gtlv_coding array of the inner IEs.
|
||||
* static const struct osmo_gtlv_coding ies_in_foo[] = {
|
||||
* { {MYPROTO_IEI_BAR},
|
||||
* .memb_ofs = offsetof(struct myproto_foo, bar),
|
||||
* .dec_func = myproto_dec_bar,
|
||||
* .enc_func = myproto_enc_bar,
|
||||
* },
|
||||
* ...
|
||||
* };
|
||||
*/
|
||||
static void write_nested_ies_array(const struct osmo_gtlv_gen_ie_o *ies)
|
||||
{
|
||||
const char *indent = "\t";
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
if (!ie || !ie->nested_ies)
|
||||
continue;
|
||||
write_nested_ies_array(ie->nested_ies);
|
||||
|
||||
const char *ies_in_name = ie->tag_name ? : ie_o->name;
|
||||
if (seen(ies_in_name, ie))
|
||||
continue;
|
||||
|
||||
printf("\nstatic const struct osmo_gtlv_coding ies_in_%s[] = {\n", ies_in_name);
|
||||
write_ies_array(indent, ie->nested_ies, decoded_type(ie_o), "");
|
||||
printi("{}\n");
|
||||
printf("};\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the bulk of the C code: on the basis of the list of messages (g_cfg->msg_defs), write all dec/enc function
|
||||
* declarations, all IEs arrays as well as the list of message types, first triggering to write the C code for any inner
|
||||
* layers. */
|
||||
static void write_c()
|
||||
{
|
||||
const struct osmo_gtlv_gen_msg *gen_msg;
|
||||
|
||||
printf("/* THIS FILE IS GENERATED FROM %s */\n", __FILE__);
|
||||
printf("#include <stddef.h>\n");
|
||||
printf("#include <errno.h>\n");
|
||||
printf("#include <osmocom/core/utils.h>\n");
|
||||
printf("#include <osmocom/gtlv/gtlv.h>\n");
|
||||
printf("#include <osmocom/gtlv/gtlv_dec_enc.h>\n");
|
||||
printf("#include <osmocom/gtlv/gtlv_gen.h>\n");
|
||||
if (g_cfg->c_header)
|
||||
printf("\n%s\n", g_cfg->c_header);
|
||||
|
||||
printf("\n");
|
||||
clear_seen();
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
write_extern_dec_enc(gen_msg->ies);
|
||||
}
|
||||
|
||||
clear_seen();
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
write_nested_ies_array(gen_msg->ies);
|
||||
}
|
||||
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
char *obj_type = talloc_asprintf(NULL, "union %s_ies", g_cfg->proto_name);
|
||||
char *substruct = talloc_asprintf(NULL, "%s.", gen_msg->name);
|
||||
printf("\nstatic const struct osmo_gtlv_coding ies_in_msg_%s[] = {\n", gen_msg->name);
|
||||
write_ies_array("\t", gen_msg->ies, obj_type, substruct);
|
||||
printf("\t{}\n};\n");
|
||||
talloc_free(substruct);
|
||||
talloc_free(obj_type);
|
||||
}
|
||||
printf("\nstatic const struct osmo_gtlv_coding *msg_defs[] = {\n");
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
printf("\t[%s%s] = ies_in_msg_%s,\n", g_cfg->message_type_prefix, osmo_str_toupper(gen_msg->name), gen_msg->name);
|
||||
}
|
||||
printf("};\n");
|
||||
|
||||
/* print this code snippet into the .c file, because only there can we do ARRAY_SIZE(foo_msg_coding). */
|
||||
printf("\n"
|
||||
"const struct osmo_gtlv_coding *%s_get_msg_coding(%s message_type)\n"
|
||||
"{\n"
|
||||
" if (message_type >= ARRAY_SIZE(msg_defs))\n"
|
||||
" return NULL;\n"
|
||||
" return msg_defs[message_type];\n"
|
||||
"}\n",
|
||||
g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
|
||||
printf("\n"
|
||||
"int %s_ies_decode(union %s_ies *dst, struct osmo_gtlv_load *gtlv, bool tlv_ordered,\n"
|
||||
" %s message_type,\n"
|
||||
" osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)\n"
|
||||
"{\n"
|
||||
" return osmo_gtlvs_decode(dst, 0, gtlv, tlv_ordered, %s_get_msg_coding(message_type), err_cb, err_cb_data, iei_strs);\n"
|
||||
"}\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int", g_cfg->proto_name);
|
||||
printf("\n"
|
||||
"int %s_ies_encode(struct osmo_gtlv_put *gtlv, const union %s_ies *src,\n"
|
||||
" %s message_type, osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)\n"
|
||||
"{\n"
|
||||
" return osmo_gtlvs_encode(gtlv, src, 0, %s_get_msg_coding(message_type), err_cb, err_cb_data, iei_strs);\n"
|
||||
"}\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int", g_cfg->proto_name);
|
||||
printf("\n"
|
||||
"int %s_ies_encode_to_str(char *buf, size_t buflen, const union %s_ies *src,\n"
|
||||
" %s message_type, const struct value_string *iei_strs)\n"
|
||||
"{\n"
|
||||
" return osmo_gtlvs_encode_to_str_buf(buf, buflen, src, 0, %s_get_msg_coding(message_type), iei_strs);\n"
|
||||
"}\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int", g_cfg->proto_name);
|
||||
}
|
||||
|
||||
/* Call this from your main(). */
|
||||
int osmo_gtlv_gen_main(const struct osmo_gtlv_gen_cfg *cfg, int argc, const char **argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
return 1;
|
||||
|
||||
g_cfg = cfg;
|
||||
|
||||
if (strcmp(argv[1], "h") == 0)
|
||||
write_h();
|
||||
else if (strcmp(argv[1], "c") == 0)
|
||||
write_c();
|
||||
else
|
||||
return 1;
|
||||
|
||||
clear_seen();
|
||||
return 0;
|
||||
}
|
||||
@@ -9,12 +9,14 @@ AM_CPPFLAGS = \
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGTLV_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGTLV_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
@@ -40,10 +42,7 @@ CLEANFILES = \
|
||||
pfcp_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
pfcp_ies_auto.c: $(srcdir)/gen__pfcp_ies_auto.c \
|
||||
$(top_srcdir)/src/libosmo-gtlv/gtlv_gen.c \
|
||||
$(top_srcdir)/include/osmocom/gtlv/gtlv_gen.h
|
||||
$(MAKE) -C $(top_builddir)/src/libosmo-gtlv
|
||||
pfcp_ies_auto.c: $(srcdir)/gen__pfcp_ies_auto.c
|
||||
$(MAKE) gen__pfcp_ies_auto
|
||||
$(builddir)/gen__pfcp_ies_auto c > $(builddir)/pfcp_ies_auto.c
|
||||
|
||||
@@ -54,9 +53,3 @@ noinst_PROGRAMS = \
|
||||
gen__pfcp_ies_auto_SOURCES = \
|
||||
gen__pfcp_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
gen__pfcp_ies_auto_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
SUBDIRS = \
|
||||
libosmo-gtlv \
|
||||
libosmo-pfcp \
|
||||
$(NULL)
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
SUBDIRS = \
|
||||
test_gtlv_gen \
|
||||
test_tliv \
|
||||
$(NULL)
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
gtlv_test \
|
||||
gtlv_dec_enc_test \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
gtlv_test.ok \
|
||||
gtlv_dec_enc_test.ok \
|
||||
$(NULL)
|
||||
|
||||
gtlv_test_SOURCES = \
|
||||
gtlv_test.c \
|
||||
$(NULL)
|
||||
|
||||
gtlv_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
gtlv_dec_enc_test_SOURCES = \
|
||||
gtlv_dec_enc_test.c \
|
||||
$(NULL)
|
||||
|
||||
gtlv_dec_enc_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
.PHONY: update_exp
|
||||
update_exp:
|
||||
$(builddir)/gtlv_test >$(srcdir)/gtlv_test.ok
|
||||
$(builddir)/gtlv_dec_enc_test >$(srcdir)/gtlv_dec_enc_test.ok
|
||||
$(MAKE) -C test_gtlv_gen update_exp
|
||||
$(MAKE) -C test_tliv update_exp
|
||||
@@ -1,422 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv_dec_enc.h>
|
||||
|
||||
void *ctx;
|
||||
|
||||
enum tags {
|
||||
TAG_FOO = 1,
|
||||
TAG_BAR,
|
||||
TAG_BAZ,
|
||||
TAG_REPEAT_INT,
|
||||
TAG_REPEAT_STRUCT,
|
||||
TAG_NEST,
|
||||
};
|
||||
|
||||
const struct value_string tag_names[] = {
|
||||
{ TAG_FOO, "FOO" },
|
||||
{ TAG_BAR, "BAR" },
|
||||
{ TAG_BAZ, "BAZ" },
|
||||
{ TAG_REPEAT_INT, "REPEAT_INT" },
|
||||
{ TAG_REPEAT_STRUCT, "REPEAT_STRUCT" },
|
||||
{ TAG_NEST, "NEST" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct bar {
|
||||
char str[23];
|
||||
};
|
||||
|
||||
struct baz {
|
||||
int v_int;
|
||||
bool v_bool;
|
||||
};
|
||||
|
||||
enum repeat_enum {
|
||||
R_A,
|
||||
R_B,
|
||||
R_C,
|
||||
};
|
||||
|
||||
const struct value_string repeat_enum_names[] = {
|
||||
OSMO_VALUE_STRING(R_A),
|
||||
OSMO_VALUE_STRING(R_B),
|
||||
OSMO_VALUE_STRING(R_C),
|
||||
{}
|
||||
};
|
||||
|
||||
struct repeat {
|
||||
int v_int;
|
||||
bool v_bool;
|
||||
enum repeat_enum v_enum;
|
||||
};
|
||||
|
||||
struct nested_inner_msg {
|
||||
int foo;
|
||||
struct bar bar;
|
||||
struct baz baz;
|
||||
};
|
||||
|
||||
struct decoded_msg {
|
||||
int foo;
|
||||
struct bar bar;
|
||||
|
||||
bool baz_present;
|
||||
struct baz baz;
|
||||
|
||||
unsigned int repeat_int_count;
|
||||
int repeat_int[32];
|
||||
|
||||
unsigned int repeat_struct_count;
|
||||
struct repeat repeat_struct[32];
|
||||
|
||||
bool nest_present;
|
||||
struct nested_inner_msg nest;
|
||||
};
|
||||
|
||||
int dec_u16(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
int *foo = decode_to;
|
||||
if (gtlv->len != 2)
|
||||
return -EINVAL;
|
||||
*foo = osmo_load16be(gtlv->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_u16(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const int *foo = encode_from;
|
||||
if (*foo > INT16_MAX)
|
||||
return -EINVAL;
|
||||
msgb_put_u16(gtlv->dst, *foo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_to_str_u16(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const int *foo = encode_from;
|
||||
return snprintf(buf, buflen, "%d", *foo);
|
||||
}
|
||||
|
||||
int dec_bar(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct bar *bar = decode_to;
|
||||
if (gtlv->len > sizeof(bar->str) - 1)
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(bar->str, (const char *)gtlv->val, OSMO_MIN(gtlv->len + 1, sizeof(bar->str)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_bar(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct bar *bar = encode_from;
|
||||
int len = strnlen(bar->str, sizeof(bar->str));
|
||||
memcpy(msgb_put(gtlv->dst, len), bar, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_to_str_bar(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct bar *bar = encode_from;
|
||||
return osmo_quote_str_buf3(buf, buflen, bar->str, -1);
|
||||
}
|
||||
|
||||
int dec_baz(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct baz *baz = decode_to;
|
||||
uint16_t l;
|
||||
if (gtlv->len != 2)
|
||||
return -EINVAL;
|
||||
l = osmo_load16be(gtlv->val);
|
||||
baz->v_int = l & 0x7fff;
|
||||
baz->v_bool = (l & 0x8000) ? true : false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_baz(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct baz *baz = encode_from;
|
||||
if (baz->v_int > 0x7fff)
|
||||
return -EINVAL;
|
||||
msgb_put_u16(gtlv->dst, (baz->v_bool ? 0x8000 : 0) + (baz->v_int & 0x7fff));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_to_str_baz(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct baz *baz = encode_from;
|
||||
return snprintf(buf, buflen, "{%d,%s}", baz->v_int, baz->v_bool ? "true" : "false");
|
||||
}
|
||||
|
||||
int dec_repeat_struct(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct repeat *repeat_struct = decode_to;
|
||||
if (gtlv->len != 3)
|
||||
return -EINVAL;
|
||||
repeat_struct->v_int = osmo_load16be(gtlv->val);
|
||||
repeat_struct->v_bool = gtlv->val[2] & 0x80;
|
||||
repeat_struct->v_enum = gtlv->val[2] & 0x7f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_repeat_struct(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct repeat *repeat_struct = encode_from;
|
||||
msgb_put_u16(gtlv->dst, repeat_struct->v_int);
|
||||
msgb_put_u8(gtlv->dst, (repeat_struct->v_bool ? 0x80 : 0) + (repeat_struct->v_enum & 0x7f));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_to_str_repeat_struct(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct repeat *repeat_struct = encode_from;
|
||||
return snprintf(buf, buflen, "{%d,%s,%s}", repeat_struct->v_int, repeat_struct->v_bool ? "true" : "false",
|
||||
get_value_string(repeat_enum_names, repeat_struct->v_enum));
|
||||
}
|
||||
|
||||
struct osmo_gtlv_coding nested_inner_msg_ies[] = {
|
||||
{
|
||||
.ti = { TAG_FOO },
|
||||
.dec_func = dec_u16,
|
||||
.enc_func = enc_u16,
|
||||
.enc_to_str_func = enc_to_str_u16,
|
||||
.memb_ofs = offsetof(struct nested_inner_msg, foo),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_BAR },
|
||||
.dec_func = dec_bar,
|
||||
.enc_func = enc_bar,
|
||||
.enc_to_str_func = enc_to_str_bar,
|
||||
.memb_ofs = offsetof(struct nested_inner_msg, bar),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_BAZ },
|
||||
.dec_func = dec_baz,
|
||||
.enc_func = enc_baz,
|
||||
.enc_to_str_func = enc_to_str_baz,
|
||||
.memb_ofs = offsetof(struct nested_inner_msg, baz),
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
struct osmo_gtlv_coding msg_ie_coding[] = {
|
||||
{
|
||||
.ti = { TAG_FOO },
|
||||
.dec_func = dec_u16,
|
||||
.enc_func = enc_u16,
|
||||
.enc_to_str_func = enc_to_str_u16,
|
||||
.memb_ofs = offsetof(struct decoded_msg, foo),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_BAR },
|
||||
.dec_func = dec_bar,
|
||||
.enc_func = enc_bar,
|
||||
.enc_to_str_func = enc_to_str_bar,
|
||||
.memb_ofs = offsetof(struct decoded_msg, bar),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_BAZ },
|
||||
.dec_func = dec_baz,
|
||||
.enc_func = enc_baz,
|
||||
.enc_to_str_func = enc_to_str_baz,
|
||||
.memb_ofs = offsetof(struct decoded_msg, baz),
|
||||
.has_presence_flag = true,
|
||||
.presence_flag_ofs = offsetof(struct decoded_msg, baz_present),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_REPEAT_INT },
|
||||
.dec_func = dec_u16,
|
||||
.enc_func = enc_u16,
|
||||
.enc_to_str_func = enc_to_str_u16,
|
||||
.memb_ofs = offsetof(struct decoded_msg, repeat_int),
|
||||
.memb_array_pitch = OSMO_MEMB_ARRAY_PITCH(struct decoded_msg, repeat_int),
|
||||
.has_count = true,
|
||||
.count_ofs = offsetof(struct decoded_msg, repeat_int_count),
|
||||
.count_max = ARRAY_SIZE(((struct decoded_msg *)0)->repeat_int),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_REPEAT_STRUCT },
|
||||
.dec_func = dec_repeat_struct,
|
||||
.enc_func = enc_repeat_struct,
|
||||
.enc_to_str_func = enc_to_str_repeat_struct,
|
||||
.memb_ofs = offsetof(struct decoded_msg, repeat_struct),
|
||||
.memb_array_pitch = OSMO_MEMB_ARRAY_PITCH(struct decoded_msg, repeat_struct),
|
||||
.has_count = true,
|
||||
.count_ofs = offsetof(struct decoded_msg, repeat_struct_count),
|
||||
.count_max = ARRAY_SIZE(((struct decoded_msg *)0)->repeat_struct),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_NEST },
|
||||
.memb_ofs = offsetof(struct decoded_msg, nest),
|
||||
.nested_ies = nested_inner_msg_ies,
|
||||
.has_presence_flag = true,
|
||||
.presence_flag_ofs = offsetof(struct decoded_msg, nest_present),
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
char *decoded_msg_to_str(const struct decoded_msg *m)
|
||||
{
|
||||
return osmo_gtlvs_encode_to_str_c(ctx, m, 0, msg_ie_coding, tag_names);
|
||||
}
|
||||
|
||||
|
||||
const struct decoded_msg enc_dec_tests[] = {
|
||||
{
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
},
|
||||
{
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
|
||||
.repeat_int_count = 3,
|
||||
.repeat_int = { 1, 2, 0x7fff },
|
||||
},
|
||||
{
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
|
||||
.repeat_int_count = 3,
|
||||
.repeat_int = { 1, 2, 0x7fff },
|
||||
|
||||
.repeat_struct_count = 2,
|
||||
.repeat_struct = {
|
||||
{
|
||||
.v_int = 1001,
|
||||
.v_bool = true,
|
||||
.v_enum = R_A,
|
||||
},
|
||||
{
|
||||
.v_int = 1002,
|
||||
.v_bool = false,
|
||||
.v_enum = R_B,
|
||||
},
|
||||
},
|
||||
|
||||
.nest_present = true,
|
||||
.nest = {
|
||||
.foo = 42,
|
||||
.bar = { "fortytwo" },
|
||||
.baz = {
|
||||
.v_int = 4242,
|
||||
.v_bool = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int verify_err_cb_data;
|
||||
|
||||
void err_cb(void *data, void *decoded_struct, const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
assert(data == &verify_err_cb_data);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
//printf("ERR: %s:%d ", file, line);
|
||||
printf("ERR: ");
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void test_enc_dec(const char *label, const struct osmo_gtlv_cfg *cfg, bool ordered)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(enc_dec_tests); i++) {
|
||||
int rc;
|
||||
const struct decoded_msg *orig = &enc_dec_tests[i];
|
||||
struct decoded_msg parsed = {};
|
||||
struct osmo_gtlv_load load;
|
||||
struct osmo_gtlv_put put;
|
||||
|
||||
printf("\n=== start %s %s[%d]\n", label, __func__, i);
|
||||
printf("encoded: %s\n", decoded_msg_to_str(orig));
|
||||
|
||||
put = (struct osmo_gtlv_put){
|
||||
.cfg = cfg,
|
||||
.dst = msgb_alloc(1024, __func__),
|
||||
};
|
||||
rc = osmo_gtlvs_encode(&put, (void *)orig, 0, msg_ie_coding, err_cb, &verify_err_cb_data, tag_names);
|
||||
printf("osmo_gtlvs_encode() rc = %d\n", rc);
|
||||
printf("%s.\n", osmo_hexdump(put.dst->data, put.dst->len));
|
||||
|
||||
load = (struct osmo_gtlv_load){
|
||||
.cfg = cfg,
|
||||
.src = { put.dst->data, put.dst->len },
|
||||
};
|
||||
rc = osmo_gtlvs_decode(&parsed, 0, &load, ordered, msg_ie_coding, err_cb, &verify_err_cb_data, tag_names);
|
||||
printf("osmo_gtlvs_decode() rc = %d\n", rc);
|
||||
printf("decoded: %s\n", decoded_msg_to_str(&parsed));
|
||||
if (strcmp(decoded_msg_to_str(orig), decoded_msg_to_str(&parsed))) {
|
||||
printf(" ERROR: parsed != orig\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("=== end %s %s[%d]\n", label, __func__, i);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 0, "gtlv_test");
|
||||
msgb_talloc_ctx_init(ctx, 0);
|
||||
|
||||
test_enc_dec("t8l8v ordered", &osmo_t8l8v_cfg, true);
|
||||
test_enc_dec("t8l8v unordered", &osmo_t8l8v_cfg, false);
|
||||
|
||||
test_enc_dec("t16l16v ordered", &osmo_t16l16v_cfg, true);
|
||||
test_enc_dec("t16l16v unordered", &osmo_t16l16v_cfg, false);
|
||||
|
||||
talloc_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[0]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree"
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree"
|
||||
=== end t8l8v ordered test_enc_dec[0]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[1]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
=== end t8l8v ordered test_enc_dec[1]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[2]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
=== end t8l8v ordered test_enc_dec[2]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[3]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
=== end t8l8v ordered test_enc_dec[3]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[0]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree"
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree"
|
||||
=== end t8l8v unordered test_enc_dec[0]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[1]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
=== end t8l8v unordered test_enc_dec[1]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[2]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
=== end t8l8v unordered test_enc_dec[2]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[3]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
=== end t8l8v unordered test_enc_dec[3]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[0]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree"
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree"
|
||||
=== end t16l16v ordered test_enc_dec[0]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[1]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
=== end t16l16v ordered test_enc_dec[1]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[2]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
=== end t16l16v ordered test_enc_dec[2]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[3]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
=== end t16l16v ordered test_enc_dec[3]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[0]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree"
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree"
|
||||
=== end t16l16v unordered test_enc_dec[0]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[1]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
=== end t16l16v unordered test_enc_dec[1]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[2]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
=== end t16l16v unordered test_enc_dec[2]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[3]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
=== end t16l16v unordered test_enc_dec[3]
|
||||
@@ -1,629 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
void *ctx;
|
||||
|
||||
struct ie {
|
||||
struct osmo_gtlv_tag_inst ti;
|
||||
const char *val;
|
||||
};
|
||||
|
||||
/* write all IEs to a msgb */
|
||||
struct msgb *test_tlv_enc(const struct osmo_gtlv_cfg *cfg, const struct ie *ies)
|
||||
{
|
||||
const struct ie *ie;
|
||||
struct osmo_gtlv_put gtlv = {
|
||||
.cfg = cfg,
|
||||
.dst = msgb_alloc(1024, __func__),
|
||||
};
|
||||
|
||||
for (ie = ies; ie->val; ie++) {
|
||||
/* put header without knowing length yet */
|
||||
OSMO_ASSERT(osmo_gtlv_put_tli(>lv, &ie->ti, 0) == 0);
|
||||
/* put value data, as much as desired */
|
||||
msgb_put(gtlv.dst, osmo_hexparse(ie->val, gtlv.dst->tail, msgb_tailroom(gtlv.dst)));
|
||||
/* update header len from amount of written data */
|
||||
OSMO_ASSERT(osmo_gtlv_put_update_tl(>lv) == 0);
|
||||
}
|
||||
|
||||
printf("- encoded: %s.\n", osmo_hexdump(gtlv.dst->data, gtlv.dst->len));
|
||||
return gtlv.dst;
|
||||
}
|
||||
|
||||
/* read all IEs from the msgb, and verify that it matches the given list of IEs */
|
||||
void test_tlv_dec(const struct osmo_gtlv_cfg *cfg, const struct ie *ies, struct msgb *msg)
|
||||
{
|
||||
const struct ie *ie;
|
||||
struct osmo_gtlv_load gtlv = {
|
||||
.cfg = cfg,
|
||||
.src = { msg->data, msg->len },
|
||||
};
|
||||
|
||||
printf("- decoding:\n");
|
||||
osmo_gtlv_load_start(>lv);
|
||||
|
||||
for (ie = ies; ie->val; ie++) {
|
||||
int rc = osmo_gtlv_load_next(>lv);
|
||||
if (rc) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next() rc = %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
/* end of TLV structure? */
|
||||
if (!gtlv.val)
|
||||
break;
|
||||
printf(" T=%s L=%zu", osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL), gtlv.len);
|
||||
printf(" v=%s\n", osmo_hexdump_nospc(gtlv.val, gtlv.len));
|
||||
if (gtlv.ti.tag != ie->ti.tag) {
|
||||
printf(" ERROR loading TLV structure: expected tag %u, got tag %u\n", ie->ti.tag, gtlv.ti.tag);
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len))) {
|
||||
printf(" ERROR loading TLV structure: expected val %s, got val %s\n", ie->val,
|
||||
osmo_hexdump_nospc(gtlv.val, gtlv.len));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_tlv_peek(const struct osmo_gtlv_cfg *cfg, const struct ie *ies, struct msgb *msg)
|
||||
{
|
||||
const struct ie *ie;
|
||||
struct osmo_gtlv_load gtlv = {
|
||||
.cfg = cfg,
|
||||
.src = { msg->data, msg->len },
|
||||
};
|
||||
|
||||
printf("- peeking:\n");
|
||||
osmo_gtlv_load_start(>lv);
|
||||
|
||||
ie = ies;
|
||||
while (1) {
|
||||
int rc;
|
||||
struct osmo_gtlv_tag_inst next_tag;
|
||||
rc = osmo_gtlv_load_peek_tag(>lv, &next_tag);
|
||||
if (rc == -ENOENT) {
|
||||
printf(" peek rc=-ENOENT\n");
|
||||
} else {
|
||||
printf(" peek T=%s", osmo_gtlv_tag_inst_to_str_c(ctx, &next_tag, NULL));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (ie->val && osmo_gtlv_tag_inst_cmp(&next_tag, &ie->ti)) {
|
||||
printf(" ERROR peeking tag: expected tag %s, got tag %s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &next_tag, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (!ie->val && rc != -ENOENT) {
|
||||
printf(" ERROR peeking tag: expected -ENOENT, got rc=%d, tag %s\n", rc,
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &next_tag, NULL));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (rc == -ENOENT)
|
||||
break;
|
||||
|
||||
/* go to the next TLV */
|
||||
rc = osmo_gtlv_load_next(>lv);
|
||||
if (rc) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next() rc = %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
if (ie->val)
|
||||
ie++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decode TLV in random order, each time searching for a tag in the raw data */
|
||||
void test_tlv_dec_by_tag(const struct osmo_gtlv_cfg *cfg, const struct ie *ies, struct msgb *msg)
|
||||
{
|
||||
const struct ie *last_ie;
|
||||
const struct ie *ie;
|
||||
int rc;
|
||||
struct osmo_gtlv_load gtlv = {
|
||||
.cfg = cfg,
|
||||
.src = { msg->data, msg->len },
|
||||
};
|
||||
|
||||
printf("- decoding in reverse order:\n");
|
||||
|
||||
last_ie = ies;
|
||||
while (last_ie->val) last_ie++;
|
||||
last_ie--;
|
||||
|
||||
for (ie = last_ie; ie >= ies; ie--) {
|
||||
/* each time, look from the beginning */
|
||||
osmo_gtlv_load_start(>lv);
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
if (rc) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) rc = %d\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL), rc);
|
||||
exit(1);
|
||||
}
|
||||
if (!gtlv.val) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) returned NULL val\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (osmo_gtlv_tag_inst_cmp(>lv.ti, &ie->ti)) {
|
||||
printf(" ERROR loading TLV structure: expected tag %s, got tag %s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len))) {
|
||||
while (1) {
|
||||
printf(" (mismatch: T=%s L=%zu v=%s, checking for another occurrence of T=%s)\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL),
|
||||
gtlv.len,
|
||||
osmo_hexdump_nospc(gtlv.val, gtlv.len),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
|
||||
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
if (rc || !gtlv.val) {
|
||||
printf(" ERROR val not found\n");
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len)) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf(" T=%s L=%zu v=%s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL),
|
||||
gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len));
|
||||
}
|
||||
|
||||
printf("- decoding every second tag:\n");
|
||||
|
||||
osmo_gtlv_load_start(>lv);
|
||||
for (ie = ies; ie->val; ie++) {
|
||||
/* skip one tag */
|
||||
ie++;
|
||||
if (!ie->val)
|
||||
break;
|
||||
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
if (rc) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) rc = %d\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL), rc);
|
||||
exit(1);
|
||||
}
|
||||
if (!gtlv.val) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) returned NULL val\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (osmo_gtlv_tag_inst_cmp(>lv.ti, &ie->ti)) {
|
||||
printf(" ERROR loading TLV structure: expected tag %s, got tag %s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len))) {
|
||||
while (1) {
|
||||
printf(" (mismatch: T=%s L=%zu v=%s, checking for another occurrence of T=%s)\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL),
|
||||
gtlv.len,
|
||||
osmo_hexdump_nospc(gtlv.val, gtlv.len),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
|
||||
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
if (rc || !gtlv.val) {
|
||||
printf(" ERROR val not found\n");
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len)) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf(" T=%s L=%zu v=%s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL),
|
||||
gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len));
|
||||
}
|
||||
|
||||
printf("- enforcing order: without restart, a past tag is not parsed again:\n");
|
||||
/* Try to read the first tag, expect that it isn't found because we're already halfway in the message data */
|
||||
ie = ies;
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
printf(" osmo_gtlv_load_next_by_tag_inst(%s) rc=", osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL));
|
||||
if (rc == -ENOENT) {
|
||||
printf("-ENOENT\n");
|
||||
} else {
|
||||
printf("%d\n", rc);
|
||||
printf(" ERROR: expected -ENOENT\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void test_tlv(const char *label, struct ie *tests[], size_t tests_len, const struct osmo_gtlv_cfg *cfg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < tests_len; i++) {
|
||||
const struct ie *ies = tests[i];
|
||||
struct msgb *msg;
|
||||
printf("\n=== start: %s[%d]\n", label, i);
|
||||
|
||||
msg = test_tlv_enc(cfg, ies);
|
||||
test_tlv_dec(cfg, ies, msg);
|
||||
test_tlv_peek(cfg, ies, msg);
|
||||
test_tlv_dec_by_tag(cfg, ies, msg);
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
printf("=== end: %s[%d]\n", label, i);
|
||||
}
|
||||
}
|
||||
|
||||
struct ie t8l8v_test1[] = {
|
||||
/* smallest T */
|
||||
{ {}, "2342" },
|
||||
/* largest T */
|
||||
{ {255}, "2342" },
|
||||
|
||||
/* smallest V (no V data) */
|
||||
{ {1}, "" },
|
||||
/* largest V, 255 bytes is the largest that an 8bit size length can express. */
|
||||
{ {123}, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
},
|
||||
|
||||
/* arbitrary test data */
|
||||
{ {101}, "11" },
|
||||
{ {102}, "2222" },
|
||||
{ {103}, "333333" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie t8l8v_test_multi[] = {
|
||||
{ {42}, "42" },
|
||||
{ {2}, "0101" },
|
||||
{ {2}, "2222" },
|
||||
{ {3}, "11" },
|
||||
{ {3}, "2222" },
|
||||
{ {3}, "333333" },
|
||||
{ {23}, "23" },
|
||||
{ {42}, "666f72747974776f" },
|
||||
{ {23}, "7477656e74797468726565" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie *t8l8v_tests[] = {
|
||||
t8l8v_test1,
|
||||
t8l8v_test_multi,
|
||||
};
|
||||
|
||||
void test_t8l8v()
|
||||
{
|
||||
test_tlv(__func__, t8l8v_tests, ARRAY_SIZE(t8l8v_tests), &osmo_t8l8v_cfg);
|
||||
}
|
||||
|
||||
struct ie t16l16v_test1[] = {
|
||||
/* smallest T */
|
||||
{ {}, "2342" },
|
||||
/* largest T */
|
||||
{ {65535}, "2342" },
|
||||
|
||||
/* smallest V (no V data) */
|
||||
{ {1}, "" },
|
||||
/* 256 bytes is one more than an 8bit size length can express. */
|
||||
{ {123}, "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
|
||||
/* arbitrary test data */
|
||||
{ {1001}, "11" },
|
||||
{ {1002}, "2222" },
|
||||
{ {1003}, "333333" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie t16l16v_test_multi[] = {
|
||||
{ {1042}, "42" },
|
||||
{ {102}, "0101" },
|
||||
{ {102}, "2222" },
|
||||
{ {103}, "11" },
|
||||
{ {103}, "2222" },
|
||||
{ {103}, "333333" },
|
||||
{ {1023}, "23" },
|
||||
{ {1042}, "666f72747974776f" },
|
||||
{ {1023}, "7477656e74797468726565" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie *t16l16v_tests[] = {
|
||||
t16l16v_test1,
|
||||
t16l16v_test_multi,
|
||||
};
|
||||
|
||||
void test_t16l16v()
|
||||
{
|
||||
test_tlv(__func__, t16l16v_tests, ARRAY_SIZE(t16l16v_tests), &osmo_t16l16v_cfg);
|
||||
}
|
||||
|
||||
struct ie txlxv_test1[] = {
|
||||
/* smallest T */
|
||||
{ {}, "2342" },
|
||||
/* largest T that still fits in one encoded octet (highest bit serves as flag) */
|
||||
{ {0x7f}, "2342" },
|
||||
/* smallest T that needs two octets to be encoded (first octet = 0x80 flag + 0, second octet = 0x1) */
|
||||
{ {0x80}, "2342" },
|
||||
/* largest T that can be encoded in 16bit - one flag bit. */
|
||||
{ {0x7fff}, "2342" },
|
||||
|
||||
/* smallest V (no V data) */
|
||||
{ {1}, "" },
|
||||
/* 256 bytes is one more than an 8bit size length can express. */
|
||||
{ {123}, "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
|
||||
/* arbitrary test data */
|
||||
{ {1002}, "2222" },
|
||||
{ {1003}, "333333" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie txlxv_test_multi[] = {
|
||||
{ {1042}, "42" },
|
||||
{ {1002}, "0101" },
|
||||
{ {1002}, "2222" },
|
||||
{ {103}, "11" },
|
||||
{ {103}, "2222" },
|
||||
{ {103}, "333333" },
|
||||
{ {1023}, "23" },
|
||||
{ {1042}, "666f72747974776f" },
|
||||
{ {1023}, "7477656e74797468726565" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie *txlxv_tests[] = {
|
||||
txlxv_test1,
|
||||
txlxv_test_multi,
|
||||
};
|
||||
|
||||
/* Example of defining a variable TL, where size of T and L depend on the actual tag and length values: load. */
|
||||
int txlxv_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
|
||||
{
|
||||
const uint8_t *pos = src_data;
|
||||
const uint8_t *end = src_data + src_data_len;
|
||||
if (pos[0] & 0x80) {
|
||||
if (pos + 2 > end)
|
||||
return -EINVAL;
|
||||
gtlv->ti.tag = (((int)pos[1]) << 7) + (pos[0] & 0x7f);
|
||||
pos += 2;
|
||||
} else {
|
||||
gtlv->ti.tag = pos[0];
|
||||
pos++;
|
||||
}
|
||||
|
||||
switch (gtlv->ti.tag) {
|
||||
case 1002:
|
||||
/* fixed-length IE */
|
||||
gtlv->len = 2;
|
||||
break;
|
||||
case 123:
|
||||
/* 16bit length IE */
|
||||
if (pos + 2 > end)
|
||||
return -EINVAL;
|
||||
gtlv->len = osmo_load16be(pos);
|
||||
pos += 2;
|
||||
break;
|
||||
default:
|
||||
/* 8bit length IE */
|
||||
if (pos + 1 > end)
|
||||
return -EINVAL;
|
||||
gtlv->len = *pos;
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
gtlv->val = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Example of defining a variable TL, where size of T and L depend on the actual tag and length values: store. */
|
||||
int txlxv_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
uint8_t *pos = dst_data;
|
||||
uint8_t *end = dst_data + dst_data_avail;
|
||||
unsigned int tag = ti->tag;
|
||||
if (tag < 0x80) {
|
||||
if (pos + 1 > end)
|
||||
return -ENOSPC;
|
||||
pos[0] = tag;
|
||||
pos++;
|
||||
} else {
|
||||
if (pos + 2 > end)
|
||||
return -ENOSPC;
|
||||
pos[0] = 0x80 + (tag & 0x7f);
|
||||
pos[1] = tag >> 7;
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case 1002:
|
||||
/* fixed-length IE, write no len */
|
||||
break;
|
||||
case 123:
|
||||
/* 16bit length IE */
|
||||
if (len > UINT16_MAX)
|
||||
return -ERANGE;
|
||||
if (pos + 2 > end)
|
||||
return -ENOSPC;
|
||||
osmo_store16be(len, pos);
|
||||
pos += 2;
|
||||
break;
|
||||
default:
|
||||
/* 8bit length IE */
|
||||
if (len > UINT8_MAX)
|
||||
return -ERANGE;
|
||||
if (pos + 1 > end)
|
||||
return -ENOSPC;
|
||||
pos[0] = len;
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
return pos - dst_data;
|
||||
}
|
||||
|
||||
const struct osmo_gtlv_cfg txlxv_cfg = {
|
||||
.tl_min_size = 1,
|
||||
.load_tl = txlxv_load_tl,
|
||||
.store_tl = txlxv_store_tl,
|
||||
};
|
||||
|
||||
void test_txlxv()
|
||||
{
|
||||
test_tlv(__func__, txlxv_tests, ARRAY_SIZE(txlxv_tests), &txlxv_cfg);
|
||||
}
|
||||
|
||||
/* Example of defining a TLI, with an instance indicator */
|
||||
static int tliv_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
|
||||
{
|
||||
/* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */
|
||||
gtlv->ti.tag = src_data[0];
|
||||
gtlv->len = src_data[1];
|
||||
|
||||
switch (gtlv->ti.tag) {
|
||||
/* All tags that are TLIV go here */
|
||||
case 5:
|
||||
case 7:
|
||||
case 9:
|
||||
if (src_data_len < 3)
|
||||
return -ENOSPC;
|
||||
gtlv->ti.instance_present = true;
|
||||
gtlv->ti.instance = src_data[2];
|
||||
gtlv->val = src_data + 3;
|
||||
return 0;
|
||||
default:
|
||||
gtlv->val = src_data + 2;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int tliv_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
if (ti->tag > UINT8_MAX)
|
||||
return -EINVAL;
|
||||
if (len > UINT8_MAX)
|
||||
return -EMSGSIZE;
|
||||
if (dst_data_avail < 2)
|
||||
return -ENOSPC;
|
||||
|
||||
dst_data[0] = ti->tag;
|
||||
dst_data[1] = len;
|
||||
|
||||
switch (ti->tag) {
|
||||
/* All tags that are TLIV go here */
|
||||
case 5:
|
||||
case 7:
|
||||
case 9:
|
||||
if (dst_data_avail < 3)
|
||||
return -ENOSPC;
|
||||
if (!ti->instance_present)
|
||||
return -EINVAL;
|
||||
if (ti->instance > UINT8_MAX)
|
||||
return -EINVAL;
|
||||
dst_data[2] = ti->instance;
|
||||
return 3;
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
const struct osmo_gtlv_cfg osmo_tliv_cfg = {
|
||||
.tl_min_size = 2,
|
||||
.load_tl = tliv_load_tl,
|
||||
.store_tl = tliv_store_tl,
|
||||
};
|
||||
|
||||
struct ie tliv_test1[] = {
|
||||
/* TLV */
|
||||
{ {1}, "0002" },
|
||||
/* TLIV */
|
||||
{ {5, true, 1}, "0017" },
|
||||
/* TLIV */
|
||||
{ {5, true, 2}, "0018" },
|
||||
/* TLIV */
|
||||
{ {5, true, 3}, "0019" },
|
||||
/* TLV */
|
||||
{ {6}, "001a" },
|
||||
/* TLIV */
|
||||
{ {7, true, 1}, "001b" },
|
||||
/* TLIV */
|
||||
{ {9, true, 1}, "001c" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie *tliv_tests[] = {
|
||||
tliv_test1,
|
||||
};
|
||||
|
||||
void test_tliv()
|
||||
{
|
||||
test_tlv(__func__, tliv_tests, ARRAY_SIZE(tliv_tests), &osmo_tliv_cfg);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 0, "gtlv_test");
|
||||
msgb_talloc_ctx_init(ctx, 0);
|
||||
|
||||
test_t8l8v();
|
||||
test_t16l16v();
|
||||
test_txlxv();
|
||||
test_tliv();
|
||||
|
||||
talloc_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
|
||||
=== start: test_t8l8v[0]
|
||||
- encoded: 00 02 23 42 ff 02 23 42 01 00 7b ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 65 01 11 66 02 22 22 67 03 33 33 33 .
|
||||
- decoding:
|
||||
T=0 L=2 v=2342
|
||||
T=255 L=2 v=2342
|
||||
T=1 L=0 v=
|
||||
T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
T=101 L=1 v=11
|
||||
T=102 L=2 v=2222
|
||||
T=103 L=3 v=333333
|
||||
- peeking:
|
||||
peek T=0
|
||||
peek T=255
|
||||
peek T=1
|
||||
peek T=123
|
||||
peek T=101
|
||||
peek T=102
|
||||
peek T=103
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
T=103 L=3 v=333333
|
||||
T=102 L=2 v=2222
|
||||
T=101 L=1 v=11
|
||||
T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
T=1 L=0 v=
|
||||
T=255 L=2 v=2342
|
||||
T=0 L=2 v=2342
|
||||
- decoding every second tag:
|
||||
T=255 L=2 v=2342
|
||||
T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
T=102 L=2 v=2222
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(0) rc=-ENOENT
|
||||
=== end: test_t8l8v[0]
|
||||
|
||||
=== start: test_t8l8v[1]
|
||||
- encoded: 2a 01 42 02 02 01 01 02 02 22 22 03 01 11 03 02 22 22 03 03 33 33 33 17 01 23 2a 08 66 6f 72 74 79 74 77 6f 17 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
- decoding:
|
||||
T=42 L=1 v=42
|
||||
T=2 L=2 v=0101
|
||||
T=2 L=2 v=2222
|
||||
T=3 L=1 v=11
|
||||
T=3 L=2 v=2222
|
||||
T=3 L=3 v=333333
|
||||
T=23 L=1 v=23
|
||||
T=42 L=8 v=666f72747974776f
|
||||
T=23 L=11 v=7477656e74797468726565
|
||||
- peeking:
|
||||
peek T=42
|
||||
peek T=2
|
||||
peek T=2
|
||||
peek T=3
|
||||
peek T=3
|
||||
peek T=3
|
||||
peek T=23
|
||||
peek T=42
|
||||
peek T=23
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
(mismatch: T=23 L=1 v=23, checking for another occurrence of T=23)
|
||||
T=23 L=11 v=7477656e74797468726565
|
||||
(mismatch: T=42 L=1 v=42, checking for another occurrence of T=42)
|
||||
T=42 L=8 v=666f72747974776f
|
||||
T=23 L=1 v=23
|
||||
(mismatch: T=3 L=1 v=11, checking for another occurrence of T=3)
|
||||
(mismatch: T=3 L=2 v=2222, checking for another occurrence of T=3)
|
||||
T=3 L=3 v=333333
|
||||
(mismatch: T=3 L=1 v=11, checking for another occurrence of T=3)
|
||||
T=3 L=2 v=2222
|
||||
T=3 L=1 v=11
|
||||
(mismatch: T=2 L=2 v=0101, checking for another occurrence of T=2)
|
||||
T=2 L=2 v=2222
|
||||
T=2 L=2 v=0101
|
||||
T=42 L=1 v=42
|
||||
- decoding every second tag:
|
||||
T=2 L=2 v=0101
|
||||
T=3 L=1 v=11
|
||||
(mismatch: T=3 L=2 v=2222, checking for another occurrence of T=3)
|
||||
T=3 L=3 v=333333
|
||||
T=42 L=8 v=666f72747974776f
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(42) rc=-ENOENT
|
||||
=== end: test_t8l8v[1]
|
||||
|
||||
=== start: test_t16l16v[0]
|
||||
- encoded: 00 00 00 02 23 42 ff ff 00 02 23 42 00 01 00 00 00 7b 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 e9 00 01 11 03 ea 00 02 22 22 03 eb 00 03 33 33 33 .
|
||||
- decoding:
|
||||
T=0 L=2 v=2342
|
||||
T=65535 L=2 v=2342
|
||||
T=1 L=0 v=
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1001 L=1 v=11
|
||||
T=1002 L=2 v=2222
|
||||
T=1003 L=3 v=333333
|
||||
- peeking:
|
||||
peek T=0
|
||||
peek T=65535
|
||||
peek T=1
|
||||
peek T=123
|
||||
peek T=1001
|
||||
peek T=1002
|
||||
peek T=1003
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
T=1003 L=3 v=333333
|
||||
T=1002 L=2 v=2222
|
||||
T=1001 L=1 v=11
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1 L=0 v=
|
||||
T=65535 L=2 v=2342
|
||||
T=0 L=2 v=2342
|
||||
- decoding every second tag:
|
||||
T=65535 L=2 v=2342
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1002 L=2 v=2222
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(0) rc=-ENOENT
|
||||
=== end: test_t16l16v[0]
|
||||
|
||||
=== start: test_t16l16v[1]
|
||||
- encoded: 04 12 00 01 42 00 66 00 02 01 01 00 66 00 02 22 22 00 67 00 01 11 00 67 00 02 22 22 00 67 00 03 33 33 33 03 ff 00 01 23 04 12 00 08 66 6f 72 74 79 74 77 6f 03 ff 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
- decoding:
|
||||
T=1042 L=1 v=42
|
||||
T=102 L=2 v=0101
|
||||
T=102 L=2 v=2222
|
||||
T=103 L=1 v=11
|
||||
T=103 L=2 v=2222
|
||||
T=103 L=3 v=333333
|
||||
T=1023 L=1 v=23
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
T=1023 L=11 v=7477656e74797468726565
|
||||
- peeking:
|
||||
peek T=1042
|
||||
peek T=102
|
||||
peek T=102
|
||||
peek T=103
|
||||
peek T=103
|
||||
peek T=103
|
||||
peek T=1023
|
||||
peek T=1042
|
||||
peek T=1023
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
(mismatch: T=1023 L=1 v=23, checking for another occurrence of T=1023)
|
||||
T=1023 L=11 v=7477656e74797468726565
|
||||
(mismatch: T=1042 L=1 v=42, checking for another occurrence of T=1042)
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
T=1023 L=1 v=23
|
||||
(mismatch: T=103 L=1 v=11, checking for another occurrence of T=103)
|
||||
(mismatch: T=103 L=2 v=2222, checking for another occurrence of T=103)
|
||||
T=103 L=3 v=333333
|
||||
(mismatch: T=103 L=1 v=11, checking for another occurrence of T=103)
|
||||
T=103 L=2 v=2222
|
||||
T=103 L=1 v=11
|
||||
(mismatch: T=102 L=2 v=0101, checking for another occurrence of T=102)
|
||||
T=102 L=2 v=2222
|
||||
T=102 L=2 v=0101
|
||||
T=1042 L=1 v=42
|
||||
- decoding every second tag:
|
||||
T=102 L=2 v=0101
|
||||
T=103 L=1 v=11
|
||||
(mismatch: T=103 L=2 v=2222, checking for another occurrence of T=103)
|
||||
T=103 L=3 v=333333
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(1042) rc=-ENOENT
|
||||
=== end: test_t16l16v[1]
|
||||
|
||||
=== start: test_txlxv[0]
|
||||
- encoded: 00 02 23 42 7f 02 23 42 80 01 02 23 42 ff ff 02 23 42 01 00 7b 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ea 07 22 22 eb 07 03 33 33 33 .
|
||||
- decoding:
|
||||
T=0 L=2 v=2342
|
||||
T=127 L=2 v=2342
|
||||
T=128 L=2 v=2342
|
||||
T=32767 L=2 v=2342
|
||||
T=1 L=0 v=
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1002 L=2 v=2222
|
||||
T=1003 L=3 v=333333
|
||||
- peeking:
|
||||
peek T=0
|
||||
peek T=127
|
||||
peek T=128
|
||||
peek T=32767
|
||||
peek T=1
|
||||
peek T=123
|
||||
peek T=1002
|
||||
peek T=1003
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
T=1003 L=3 v=333333
|
||||
T=1002 L=2 v=2222
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1 L=0 v=
|
||||
T=32767 L=2 v=2342
|
||||
T=128 L=2 v=2342
|
||||
T=127 L=2 v=2342
|
||||
T=0 L=2 v=2342
|
||||
- decoding every second tag:
|
||||
T=127 L=2 v=2342
|
||||
T=32767 L=2 v=2342
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1003 L=3 v=333333
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(0) rc=-ENOENT
|
||||
=== end: test_txlxv[0]
|
||||
|
||||
=== start: test_txlxv[1]
|
||||
- encoded: 92 08 01 42 ea 07 01 01 ea 07 22 22 67 01 11 67 02 22 22 67 03 33 33 33 ff 07 01 23 92 08 08 66 6f 72 74 79 74 77 6f ff 07 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
- decoding:
|
||||
T=1042 L=1 v=42
|
||||
T=1002 L=2 v=0101
|
||||
T=1002 L=2 v=2222
|
||||
T=103 L=1 v=11
|
||||
T=103 L=2 v=2222
|
||||
T=103 L=3 v=333333
|
||||
T=1023 L=1 v=23
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
T=1023 L=11 v=7477656e74797468726565
|
||||
- peeking:
|
||||
peek T=1042
|
||||
peek T=1002
|
||||
peek T=1002
|
||||
peek T=103
|
||||
peek T=103
|
||||
peek T=103
|
||||
peek T=1023
|
||||
peek T=1042
|
||||
peek T=1023
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
(mismatch: T=1023 L=1 v=23, checking for another occurrence of T=1023)
|
||||
T=1023 L=11 v=7477656e74797468726565
|
||||
(mismatch: T=1042 L=1 v=42, checking for another occurrence of T=1042)
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
T=1023 L=1 v=23
|
||||
(mismatch: T=103 L=1 v=11, checking for another occurrence of T=103)
|
||||
(mismatch: T=103 L=2 v=2222, checking for another occurrence of T=103)
|
||||
T=103 L=3 v=333333
|
||||
(mismatch: T=103 L=1 v=11, checking for another occurrence of T=103)
|
||||
T=103 L=2 v=2222
|
||||
T=103 L=1 v=11
|
||||
(mismatch: T=1002 L=2 v=0101, checking for another occurrence of T=1002)
|
||||
T=1002 L=2 v=2222
|
||||
T=1002 L=2 v=0101
|
||||
T=1042 L=1 v=42
|
||||
- decoding every second tag:
|
||||
T=1002 L=2 v=0101
|
||||
T=103 L=1 v=11
|
||||
(mismatch: T=103 L=2 v=2222, checking for another occurrence of T=103)
|
||||
T=103 L=3 v=333333
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(1042) rc=-ENOENT
|
||||
=== end: test_txlxv[1]
|
||||
|
||||
=== start: test_tliv[0]
|
||||
- encoded: 01 02 00 02 05 02 01 00 17 05 02 02 00 18 05 02 03 00 19 06 02 00 1a 07 02 01 00 1b 09 02 01 00 1c .
|
||||
- decoding:
|
||||
T=1 L=2 v=0002
|
||||
T=5[1] L=2 v=0017
|
||||
T=5[2] L=2 v=0018
|
||||
T=5[3] L=2 v=0019
|
||||
T=6 L=2 v=001a
|
||||
T=7[1] L=2 v=001b
|
||||
T=9[1] L=2 v=001c
|
||||
- peeking:
|
||||
peek T=1
|
||||
peek T=5[1]
|
||||
peek T=5[2]
|
||||
peek T=5[3]
|
||||
peek T=6
|
||||
peek T=7[1]
|
||||
peek T=9[1]
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
T=9[1] L=2 v=001c
|
||||
T=7[1] L=2 v=001b
|
||||
T=6 L=2 v=001a
|
||||
T=5[3] L=2 v=0019
|
||||
T=5[2] L=2 v=0018
|
||||
T=5[1] L=2 v=0017
|
||||
T=1 L=2 v=0002
|
||||
- decoding every second tag:
|
||||
T=5[1] L=2 v=0017
|
||||
T=5[3] L=2 v=0019
|
||||
T=7[1] L=2 v=001b
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(1) rc=-ENOENT
|
||||
=== end: test_tliv[0]
|
||||
@@ -1,60 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(bulddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
gen__myproto_ies_auto \
|
||||
gtlv_gen_test \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
myproto_ies_custom.h \
|
||||
gtlv_gen_test.ok \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = \
|
||||
myproto_ies_auto.h \
|
||||
myproto_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = \
|
||||
myproto_ies_auto.h \
|
||||
myproto_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
gen__myproto_ies_auto_SOURCES = \
|
||||
gen__myproto_ies_auto.c \
|
||||
myproto_ies_custom.c \
|
||||
$(NULL)
|
||||
|
||||
gen__myproto_ies_auto_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
myproto_ies_auto.h: $(builddir)/gen__myproto_ies_auto
|
||||
$(builddir)/gen__myproto_ies_auto h > $(builddir)/myproto_ies_auto.h
|
||||
myproto_ies_auto.c: $(builddir)/gen__myproto_ies_auto
|
||||
$(builddir)/gen__myproto_ies_auto c > $(builddir)/myproto_ies_auto.c
|
||||
|
||||
gtlv_gen_test_SOURCES = \
|
||||
gtlv_gen_test.c \
|
||||
myproto_ies_custom.c \
|
||||
myproto_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
gtlv_gen_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
.PHONY: update_exp
|
||||
update_exp:
|
||||
$(builddir)/gtlv_gen_test >$(srcdir)/gtlv_gen_test.ok
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gtlv/gtlv_gen.h>
|
||||
|
||||
#define O OSMO_GTLV_GEN_O
|
||||
#define M OSMO_GTLV_GEN_M
|
||||
#define O_MULTI OSMO_GTLV_GEN_O_MULTI
|
||||
#define M_MULTI OSMO_GTLV_GEN_M_MULTI
|
||||
|
||||
#define ALL_FROM_NAME osmo_gtlv_gen_ie_auto
|
||||
|
||||
/* An IE where the type is not a 'struct myproto_ie_${name}'. */
|
||||
static const struct osmo_gtlv_gen_ie number = {
|
||||
.decoded_type = "int", /* add 'int foo;' to the struct */
|
||||
.dec_enc = "u16", /* use myproto_dec_u16() and myproto_enc_u16() for the TLV value part */
|
||||
.spec_ref = "an int coded as uint16_t",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_moo_nest[] = {
|
||||
/* Mandatory member xxx.foo of the type defined in 'number' above. */
|
||||
M(number, "foo"),
|
||||
/* Mandatory member xxx.bar of type 'struct myproto_ie_bar', using myproto_ie_dec_bar(), myproto_ie_enc_bar(),
|
||||
* myproto_ie_enc_to_str_bar(), all defined in myproto_ies_custom.h/c. */
|
||||
M(ALL_FROM_NAME, "bar"),
|
||||
M(ALL_FROM_NAME, "baz"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie huge_number = {
|
||||
.decoded_type = "uint64_t",
|
||||
.dec_enc = "u64",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie moo_nest = {
|
||||
.tag_name = "moo_nest",
|
||||
.nested_ies = ies_in_moo_nest,
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_goo_nest[] = {
|
||||
O(huge_number, "val"),
|
||||
M(moo_nest, "nest"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie goo_nest = {
|
||||
.tag_name = "goo_nest",
|
||||
.nested_ies = ies_in_goo_nest,
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_moo_msg[] = {
|
||||
M(number, "foo"),
|
||||
M(ALL_FROM_NAME, "bar"),
|
||||
O(ALL_FROM_NAME, "baz"),
|
||||
O_MULTI(32, number, "repeat_int"),
|
||||
O_MULTI(32, ALL_FROM_NAME, "repeat_struct"),
|
||||
O(moo_nest, "nest"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_goo_msg[] = {
|
||||
M(number, "foo"),
|
||||
O(ALL_FROM_NAME, "bar"),
|
||||
O_MULTI(8, goo_nest, "nest"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_msg msg_defs[] = {
|
||||
{ "moo", ies_in_moo_msg },
|
||||
{ "goo", ies_in_goo_msg },
|
||||
{}
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct osmo_gtlv_gen_cfg cfg = {
|
||||
.proto_name = "myproto",
|
||||
.message_type_enum = "enum myproto_msg_type",
|
||||
.message_type_prefix = "MYPROTO_MSGT_",
|
||||
.tag_enum = "enum myproto_iei",
|
||||
.tag_prefix = "MYPROTO_IEI_",
|
||||
.decoded_type_prefix = "struct myproto_ie_",
|
||||
.h_header = "#include \"myproto_ies_custom.h\"",
|
||||
.c_header = "#include <myproto_ies_auto.h>",
|
||||
.msg_defs = msg_defs,
|
||||
.add_enc_to_str = true,
|
||||
};
|
||||
return osmo_gtlv_gen_main(&cfg, argc, argv);
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
#include <myproto_ies_auto.h>
|
||||
|
||||
struct myproto_msg {
|
||||
enum myproto_msg_type type;
|
||||
union myproto_ies ies;
|
||||
};
|
||||
|
||||
static void err_cb(void *data, void *decoded_struct, const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
//printf("ERR: %s:%d ", file, line);
|
||||
printf("ERR: ");
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static int myproto_msg_enc(struct msgb *dst, const struct myproto_msg *msg, const struct osmo_gtlv_cfg *cfg)
|
||||
{
|
||||
struct osmo_gtlv_put gtlv = {
|
||||
.cfg = cfg,
|
||||
.dst = dst,
|
||||
};
|
||||
|
||||
msgb_put_u8(gtlv.dst, msg->type);
|
||||
return myproto_ies_encode(>lv, (void *)&msg->ies, msg->type, err_cb, NULL, myproto_iei_names);
|
||||
}
|
||||
|
||||
static int myproto_msg_dec(struct myproto_msg *msg, const uint8_t *data, size_t data_len,
|
||||
const struct osmo_gtlv_cfg *cfg, bool ordered)
|
||||
{
|
||||
struct osmo_gtlv_load gtlv;
|
||||
if (data_len < 1)
|
||||
return -EINVAL;
|
||||
msg->type = data[0];
|
||||
gtlv = (struct osmo_gtlv_load){
|
||||
.cfg = cfg,
|
||||
.src = { data + 1, data_len - 1 },
|
||||
};
|
||||
return myproto_ies_decode(&msg->ies, >lv, ordered, msg->type, err_cb, NULL, myproto_iei_names);
|
||||
}
|
||||
|
||||
void *ctx;
|
||||
|
||||
struct myproto_msg tests[] = {
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
|
||||
.repeat_int_count = 3,
|
||||
.repeat_int = { 1, 2, 0x7fff },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
|
||||
.repeat_int_count = 3,
|
||||
.repeat_int = { 1, 2, 0x7fff },
|
||||
|
||||
.repeat_struct_count = 2,
|
||||
.repeat_struct = {
|
||||
{
|
||||
.v_int = 1001,
|
||||
.v_bool = true,
|
||||
.v_enum = R_A,
|
||||
},
|
||||
{
|
||||
.v_int = 1002,
|
||||
.v_bool = false,
|
||||
.v_enum = R_B,
|
||||
},
|
||||
},
|
||||
|
||||
.nest_present = true,
|
||||
.nest = {
|
||||
.foo = 42,
|
||||
.bar = { "fortytwo" },
|
||||
.baz = {
|
||||
.v_int = 4242,
|
||||
.v_bool = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MYPROTO_MSGT_GOO,
|
||||
{
|
||||
.goo = {
|
||||
.foo = 17,
|
||||
|
||||
.bar_present = true,
|
||||
.bar = { "gooei" },
|
||||
|
||||
.nest_count = 2,
|
||||
.nest = {
|
||||
{
|
||||
.val_present = true,
|
||||
.val = 0x0123456789abcdef,
|
||||
.nest = {
|
||||
.foo = 11,
|
||||
.bar = { "eleven" },
|
||||
.baz = {
|
||||
.v_int = 1111,
|
||||
.v_bool = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.val_present = false,
|
||||
.nest = {
|
||||
.foo = 12,
|
||||
.bar = { "twelve" },
|
||||
.baz = {
|
||||
.v_int = 1212,
|
||||
.v_bool = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
int myproto_msg_to_str_buf(char *buf, size_t buflen, const struct myproto_msg *m)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "%s={", get_value_string(myproto_msg_type_names, m->type));
|
||||
OSMO_STRBUF_APPEND(sb, osmo_gtlvs_encode_to_str_buf, &m->ies, 0, myproto_get_msg_coding(m->type),
|
||||
myproto_iei_names);
|
||||
OSMO_STRBUF_PRINTF(sb, " }");
|
||||
return sb.chars_needed;
|
||||
|
||||
}
|
||||
|
||||
char *myproto_msg_to_str(const struct myproto_msg *m)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 256, "ERROR", myproto_msg_to_str_buf, m)
|
||||
}
|
||||
|
||||
void test_enc_dec(const char *label, const struct osmo_gtlv_cfg *cfg, bool ordered)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
int rc;
|
||||
const struct myproto_msg *orig = &tests[i];
|
||||
struct myproto_msg parsed = {};
|
||||
struct msgb *msg;
|
||||
|
||||
printf("\n=== start %s %s[%d]\n", label, __func__, i);
|
||||
printf("encoded: %s\n", myproto_msg_to_str(orig));
|
||||
|
||||
msg = msgb_alloc(1024, __func__);
|
||||
rc = myproto_msg_enc(msg, orig, cfg);
|
||||
printf("myproto_msg_enc() rc = %d\n", rc);
|
||||
printf("%s.\n", osmo_hexdump(msg->data, msg->len));
|
||||
|
||||
rc = myproto_msg_dec(&parsed, msg->data, msg->len, cfg, ordered);
|
||||
printf("myproto_msg_dec() rc = %d\n", rc);
|
||||
printf("decoded: %s\n", myproto_msg_to_str(&parsed));
|
||||
if (strcmp(myproto_msg_to_str(orig), myproto_msg_to_str(&parsed))) {
|
||||
printf(" ERROR: parsed != orig\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
printf("=== end %s %s[%d]\n", label, __func__, i);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 0, "test_gen_tlv");
|
||||
msgb_talloc_ctx_init(ctx, 0);
|
||||
|
||||
test_enc_dec("t8l8v ordered", &osmo_t8l8v_cfg, true);
|
||||
test_enc_dec("t8l8v unordered", &osmo_t8l8v_cfg, false);
|
||||
|
||||
test_enc_dec("t16l16v ordered", &osmo_t16l16v_cfg, true);
|
||||
test_enc_dec("t16l16v unordered", &osmo_t16l16v_cfg, false);
|
||||
|
||||
talloc_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[0]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
|
||||
=== end t8l8v ordered test_enc_dec[0]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[1]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
|
||||
=== end t8l8v ordered test_enc_dec[1]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[2]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
|
||||
=== end t8l8v ordered test_enc_dec[2]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[3]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
|
||||
=== end t8l8v ordered test_enc_dec[3]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[4]
|
||||
encoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
|
||||
myproto_msg_enc() rc = 0
|
||||
07 01 02 00 11 02 05 67 6f 6f 65 69 08 1c 07 08 01 23 45 67 89 ab cd ef 06 10 01 02 00 0b 02 06 65 6c 65 76 65 6e 03 02 84 57 08 12 06 10 01 02 00 0c 02 06 74 77 65 6c 76 65 03 02 04 bc .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
|
||||
=== end t8l8v ordered test_enc_dec[4]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[0]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
|
||||
=== end t8l8v unordered test_enc_dec[0]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[1]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
|
||||
=== end t8l8v unordered test_enc_dec[1]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[2]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
|
||||
=== end t8l8v unordered test_enc_dec[2]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[3]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
|
||||
=== end t8l8v unordered test_enc_dec[3]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[4]
|
||||
encoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
|
||||
myproto_msg_enc() rc = 0
|
||||
07 01 02 00 11 02 05 67 6f 6f 65 69 08 1c 07 08 01 23 45 67 89 ab cd ef 06 10 01 02 00 0b 02 06 65 6c 65 76 65 6e 03 02 84 57 08 12 06 10 01 02 00 0c 02 06 74 77 65 6c 76 65 03 02 04 bc .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
|
||||
=== end t8l8v unordered test_enc_dec[4]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[0]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
|
||||
=== end t16l16v ordered test_enc_dec[0]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[1]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
|
||||
=== end t16l16v ordered test_enc_dec[1]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[2]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
|
||||
=== end t16l16v ordered test_enc_dec[2]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[3]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
|
||||
=== end t16l16v ordered test_enc_dec[3]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[4]
|
||||
encoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
|
||||
myproto_msg_enc() rc = 0
|
||||
07 00 01 00 02 00 11 00 02 00 05 67 6f 6f 65 69 00 08 00 26 00 07 00 08 01 23 45 67 89 ab cd ef 00 06 00 16 00 01 00 02 00 0b 00 02 00 06 65 6c 65 76 65 6e 00 03 00 02 84 57 00 08 00 1a 00 06 00 16 00 01 00 02 00 0c 00 02 00 06 74 77 65 6c 76 65 00 03 00 02 04 bc .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
|
||||
=== end t16l16v ordered test_enc_dec[4]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[0]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
|
||||
=== end t16l16v unordered test_enc_dec[0]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[1]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
|
||||
=== end t16l16v unordered test_enc_dec[1]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[2]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
|
||||
=== end t16l16v unordered test_enc_dec[2]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[3]
|
||||
encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
|
||||
=== end t16l16v unordered test_enc_dec[3]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[4]
|
||||
encoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
|
||||
myproto_msg_enc() rc = 0
|
||||
07 00 01 00 02 00 11 00 02 00 05 67 6f 6f 65 69 00 08 00 26 00 07 00 08 01 23 45 67 89 ab cd ef 00 06 00 16 00 01 00 02 00 0b 00 02 00 06 65 6c 65 76 65 6e 00 03 00 02 84 57 00 08 00 1a 00 06 00 16 00 01 00 02 00 0c 00 02 00 06 74 77 65 6c 76 65 00 03 00 02 04 bc .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
|
||||
=== end t16l16v unordered test_enc_dec[4]
|
||||
@@ -1,180 +0,0 @@
|
||||
/* Example for defining custom IES for gtlv_gen. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
#include <myproto_ies_custom.h>
|
||||
|
||||
int myproto_dec_u16(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
int *foo = decode_to;
|
||||
if (gtlv->len != 2)
|
||||
return -EINVAL;
|
||||
*foo = osmo_load16be(gtlv->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_u16(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
|
||||
{
|
||||
int *foo = encode_from;
|
||||
if (*foo > INT16_MAX)
|
||||
return -EINVAL;
|
||||
msgb_put_u16(gtlv->dst, *foo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_to_str_u16(char *buf, size_t buflen, void *encode_from)
|
||||
{
|
||||
int *foo = encode_from;
|
||||
return snprintf(buf, buflen, "%d", *foo);
|
||||
}
|
||||
|
||||
int myproto_dec_u64(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
uint64_t *val = decode_to;
|
||||
if (gtlv->len != sizeof(uint64_t))
|
||||
return -EINVAL;
|
||||
*val = osmo_load64be(gtlv->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_u64(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
|
||||
{
|
||||
uint64_t *val = encode_from;
|
||||
osmo_store64be(*val, msgb_put(gtlv->dst, sizeof(*val)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_to_str_u64(char *buf, size_t buflen, void *encode_from)
|
||||
{
|
||||
uint64_t *val = encode_from;
|
||||
return snprintf(buf, buflen, "0x%"PRIx64, *val);
|
||||
}
|
||||
|
||||
int myproto_dec_bar(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct myproto_ie_bar *bar = decode_to;
|
||||
if (gtlv->len > sizeof(bar->str) - 1)
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(bar->str, (const char *)gtlv->val, OSMO_MIN(gtlv->len + 1, sizeof(bar->str)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_bar(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
|
||||
{
|
||||
struct myproto_ie_bar *bar = encode_from;
|
||||
int len = strnlen(bar->str, sizeof(bar->str));
|
||||
memcpy(msgb_put(gtlv->dst, len), bar, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_to_str_bar(char *buf, size_t buflen, void *encode_from)
|
||||
{
|
||||
struct myproto_ie_bar *bar = encode_from;
|
||||
return osmo_quote_str_buf3(buf, buflen, bar->str, -1);
|
||||
}
|
||||
|
||||
int myproto_dec_baz(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct myproto_ie_baz *baz = decode_to;
|
||||
uint16_t l;
|
||||
if (gtlv->len != 2)
|
||||
return -EINVAL;
|
||||
l = osmo_load16be(gtlv->val);
|
||||
baz->v_int = l & 0x7fff;
|
||||
baz->v_bool = (l & 0x8000) ? true : false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_baz(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
|
||||
{
|
||||
struct myproto_ie_baz *baz = encode_from;
|
||||
if (baz->v_int > 0x7fff)
|
||||
return -EINVAL;
|
||||
msgb_put_u16(gtlv->dst, (baz->v_bool ? 0x8000 : 0) + (baz->v_int & 0x7fff));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_to_str_baz(char *buf, size_t buflen, void *encode_from)
|
||||
{
|
||||
struct myproto_ie_baz *baz = encode_from;
|
||||
return snprintf(buf, buflen, "{%d,%s}", baz->v_int, baz->v_bool ? "true" : "false");
|
||||
}
|
||||
|
||||
int myproto_dec_repeat_struct(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct myproto_ie_repeat_struct *repeat_struct = decode_to;
|
||||
if (gtlv->len != 3)
|
||||
return -EINVAL;
|
||||
repeat_struct->v_int = osmo_load16be(gtlv->val);
|
||||
repeat_struct->v_bool = gtlv->val[2] & 0x80;
|
||||
repeat_struct->v_enum = gtlv->val[2] & 0x7f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_repeat_struct(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
|
||||
{
|
||||
struct myproto_ie_repeat_struct *repeat_struct = encode_from;
|
||||
msgb_put_u16(gtlv->dst, repeat_struct->v_int);
|
||||
msgb_put_u8(gtlv->dst, (repeat_struct->v_bool ? 0x80 : 0) + (repeat_struct->v_enum & 0x7f));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_to_str_repeat_struct(char *buf, size_t buflen, void *encode_from)
|
||||
{
|
||||
struct myproto_ie_repeat_struct *repeat_struct = encode_from;
|
||||
return snprintf(buf, buflen, "{%d,%s,%s}",
|
||||
repeat_struct->v_int, repeat_struct->v_bool ? "true" : "false",
|
||||
get_value_string(myproto_repeat_enum_names, repeat_struct->v_enum));
|
||||
}
|
||||
|
||||
const struct value_string myproto_msg_type_names[] = {
|
||||
{ MYPROTO_MSGT_MOO, "MOO" },
|
||||
{ MYPROTO_MSGT_GOO, "GOO" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string myproto_iei_names[] = {
|
||||
{ MYPROTO_IEI_FOO, "FOO" },
|
||||
{ MYPROTO_IEI_BAR, "BAR" },
|
||||
{ MYPROTO_IEI_BAZ, "BAZ" },
|
||||
{ MYPROTO_IEI_REPEAT_INT, "REPEAT_INT" },
|
||||
{ MYPROTO_IEI_REPEAT_STRUCT, "REPEAT_STRUCT" },
|
||||
{ MYPROTO_IEI_MOO_NEST, "MOO_NEST" },
|
||||
{ MYPROTO_IEI_VAL, "VAL" },
|
||||
{ MYPROTO_IEI_GOO_NEST, "GOO_NEST" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string myproto_repeat_enum_names[] = {
|
||||
OSMO_VALUE_STRING(R_A),
|
||||
OSMO_VALUE_STRING(R_B),
|
||||
OSMO_VALUE_STRING(R_C),
|
||||
{}
|
||||
};
|
||||
@@ -1,70 +0,0 @@
|
||||
/* Definitions for decoded message IEs, to be used by the auto-generated myproto_ies_auto.c. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
enum myproto_msg_type {
|
||||
MYPROTO_MSGT_MOO = 1,
|
||||
MYPROTO_MSGT_GOO = 7,
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_msg_type_names[];
|
||||
|
||||
enum myproto_iei {
|
||||
MYPROTO_IEI_FOO = 1,
|
||||
MYPROTO_IEI_BAR,
|
||||
MYPROTO_IEI_BAZ,
|
||||
MYPROTO_IEI_REPEAT_INT,
|
||||
MYPROTO_IEI_REPEAT_STRUCT,
|
||||
MYPROTO_IEI_MOO_NEST,
|
||||
MYPROTO_IEI_VAL,
|
||||
MYPROTO_IEI_GOO_NEST,
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_iei_names[];
|
||||
|
||||
struct myproto_ie_bar {
|
||||
char str[23];
|
||||
};
|
||||
|
||||
struct myproto_ie_baz {
|
||||
int v_int;
|
||||
bool v_bool;
|
||||
};
|
||||
|
||||
enum myproto_repeat_enum {
|
||||
R_A,
|
||||
R_B,
|
||||
R_C,
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_repeat_enum_names[];
|
||||
|
||||
struct myproto_ie_repeat_struct {
|
||||
int v_int;
|
||||
bool v_bool;
|
||||
enum myproto_repeat_enum v_enum;
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(bulddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
gen__myproto_ies_auto \
|
||||
tliv_test \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
myproto_ies_custom.h \
|
||||
tliv_test.ok \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = \
|
||||
myproto_ies_auto.h \
|
||||
myproto_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = \
|
||||
myproto_ies_auto.h \
|
||||
myproto_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
gen__myproto_ies_auto_SOURCES = \
|
||||
gen__myproto_ies_auto.c \
|
||||
myproto_ies_custom.c \
|
||||
$(NULL)
|
||||
|
||||
gen__myproto_ies_auto_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
myproto_ies_auto.h: $(builddir)/gen__myproto_ies_auto
|
||||
$(builddir)/gen__myproto_ies_auto h > $(builddir)/myproto_ies_auto.h
|
||||
myproto_ies_auto.c: $(builddir)/gen__myproto_ies_auto
|
||||
$(builddir)/gen__myproto_ies_auto c > $(builddir)/myproto_ies_auto.c
|
||||
|
||||
tliv_test_SOURCES = \
|
||||
tliv_test.c \
|
||||
myproto_ies_custom.c \
|
||||
myproto_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
tliv_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
.PHONY: update_exp
|
||||
update_exp:
|
||||
$(builddir)/tliv_test >$(srcdir)/tliv_test.ok
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gtlv/gtlv_gen.h>
|
||||
|
||||
#define O OSMO_GTLV_GEN_O
|
||||
#define M OSMO_GTLV_GEN_M
|
||||
#define O_MULTI OSMO_GTLV_GEN_O_MULTI
|
||||
#define M_MULTI OSMO_GTLV_GEN_M_MULTI
|
||||
#define O_INST OSMO_GTLV_GEN_O_INST
|
||||
#define M_INST OSMO_GTLV_GEN_M_INST
|
||||
|
||||
#define AUTO osmo_gtlv_gen_ie_auto
|
||||
|
||||
static const struct osmo_gtlv_gen_ie bar = {
|
||||
.tag_name = "bar",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_moo_msg[] = {
|
||||
M_INST("MYPROTO_IEI_BAR_ALPHA", bar, "bar_alpha"),
|
||||
O_INST("MYPROTO_IEI_BAR_BETA", bar, "bar_beta"),
|
||||
M_INST("MYPROTO_IEI_BAR_GAMMA", bar, "bar_gamma"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_msg msg_defs[] = {
|
||||
{ "moo", ies_in_moo_msg },
|
||||
{}
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct osmo_gtlv_gen_cfg cfg = {
|
||||
.proto_name = "myproto",
|
||||
.message_type_enum = "enum myproto_msg_type",
|
||||
.message_type_prefix = "MYPROTO_MSGT_",
|
||||
.tag_enum = "enum myproto_iei",
|
||||
.tag_prefix = "MYPROTO_IEI_",
|
||||
.decoded_type_prefix = "struct myproto_ie_",
|
||||
.h_header = "#include \"myproto_ies_custom.h\"",
|
||||
.c_header = "#include <myproto_ies_auto.h>",
|
||||
.msg_defs = msg_defs,
|
||||
.add_enc_to_str = true,
|
||||
};
|
||||
return osmo_gtlv_gen_main(&cfg, argc, argv);
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/* Example for defining custom IES for gtlv_gen. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
#include <myproto_ies_custom.h>
|
||||
|
||||
int myproto_dec_bar(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct myproto_ie_bar *bar = decode_to;
|
||||
if (gtlv->len < 2)
|
||||
return -EINVAL;
|
||||
*bar = (struct myproto_ie_bar){
|
||||
.a = gtlv->val[0],
|
||||
.b = (gtlv->val[1] == 1),
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_bar(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
|
||||
{
|
||||
struct myproto_ie_bar *bar = encode_from;
|
||||
msgb_put_u8(gtlv->dst, bar->a);
|
||||
msgb_put_u8(gtlv->dst, bar->b ? 1 : 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int myproto_enc_to_str_bar(char *buf, size_t buflen, void *encode_from)
|
||||
{
|
||||
struct myproto_ie_bar *bar = encode_from;
|
||||
return snprintf(buf, buflen, "%d,%s", bar->a, bar->b ? "true" : "false");
|
||||
}
|
||||
|
||||
const struct value_string myproto_msg_type_names[] = {
|
||||
{ MYPROTO_MSGT_MOO, "MOO" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string myproto_iei_names[] = {
|
||||
{ MYPROTO_IEI_BAR, "BAR" },
|
||||
{}
|
||||
};
|
||||
@@ -1,50 +0,0 @@
|
||||
/* Definitions for decoded message IEs, to be used by the auto-generated myproto_ies_auto.c. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
enum myproto_msg_type {
|
||||
MYPROTO_MSGT_MOO = 1,
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_msg_type_names[];
|
||||
|
||||
enum myproto_iei {
|
||||
MYPROTO_IEI_BAR = 1,
|
||||
};
|
||||
|
||||
enum myproto_iei_bar_inst {
|
||||
MYPROTO_IEI_BAR_ALPHA = 2,
|
||||
MYPROTO_IEI_BAR_BETA = 3,
|
||||
MYPROTO_IEI_BAR_GAMMA = 5,
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_iei_names[];
|
||||
|
||||
struct myproto_ie_bar {
|
||||
int a;
|
||||
bool b;
|
||||
};
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
#include <myproto_ies_auto.h>
|
||||
|
||||
struct myproto_msg {
|
||||
enum myproto_msg_type type;
|
||||
union myproto_ies ies;
|
||||
};
|
||||
|
||||
static void err_cb(void *data, void *decoded_struct, const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
//printf("ERR: %s:%d ", file, line);
|
||||
printf("ERR: ");
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static int myproto_msg_enc(struct msgb *dst, const struct myproto_msg *msg, const struct osmo_gtlv_cfg *cfg)
|
||||
{
|
||||
struct osmo_gtlv_put gtlv = {
|
||||
.cfg = cfg,
|
||||
.dst = dst,
|
||||
};
|
||||
|
||||
msgb_put_u8(gtlv.dst, msg->type);
|
||||
return myproto_ies_encode(>lv, (void *)&msg->ies, msg->type, err_cb, NULL, myproto_iei_names);
|
||||
}
|
||||
|
||||
static int myproto_msg_dec(struct myproto_msg *msg, const uint8_t *data, size_t data_len,
|
||||
const struct osmo_gtlv_cfg *cfg, bool ordered)
|
||||
{
|
||||
struct osmo_gtlv_load gtlv;
|
||||
if (data_len < 1)
|
||||
return -EINVAL;
|
||||
msg->type = data[0];
|
||||
gtlv = (struct osmo_gtlv_load){
|
||||
.cfg = cfg,
|
||||
.src = { data + 1, data_len - 1 },
|
||||
};
|
||||
return myproto_ies_decode(&msg->ies, >lv, ordered, msg->type, err_cb, NULL, myproto_iei_names);
|
||||
}
|
||||
|
||||
void *ctx;
|
||||
|
||||
struct myproto_msg tests[] = {
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.bar_alpha = { 23, true },
|
||||
.bar_gamma = { 42, false },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.bar_alpha = { 11, true },
|
||||
.bar_beta_present = true,
|
||||
.bar_beta = { 22, false },
|
||||
.bar_gamma = { 33, true },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
int myproto_msg_to_str_buf(char *buf, size_t buflen, const struct myproto_msg *m)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "%s={", get_value_string(myproto_msg_type_names, m->type));
|
||||
OSMO_STRBUF_APPEND(sb, osmo_gtlvs_encode_to_str_buf, &m->ies, 0, myproto_get_msg_coding(m->type),
|
||||
myproto_iei_names);
|
||||
OSMO_STRBUF_PRINTF(sb, " }");
|
||||
return sb.chars_needed;
|
||||
|
||||
}
|
||||
|
||||
char *myproto_msg_to_str(const struct myproto_msg *m)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 256, "ERROR", myproto_msg_to_str_buf, m)
|
||||
}
|
||||
|
||||
void test_enc_dec(const char *label, const struct osmo_gtlv_cfg *cfg, bool ordered)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
int rc;
|
||||
const struct myproto_msg *orig = &tests[i];
|
||||
struct myproto_msg parsed = {};
|
||||
struct msgb *msg;
|
||||
|
||||
printf("\n=== start %s %s[%d]\n", label, __func__, i);
|
||||
printf("encoded: %s\n", myproto_msg_to_str(orig));
|
||||
|
||||
msg = msgb_alloc(1024, __func__);
|
||||
rc = myproto_msg_enc(msg, orig, cfg);
|
||||
printf("myproto_msg_enc() rc = %d\n", rc);
|
||||
printf("%s.\n", osmo_hexdump(msg->data, msg->len));
|
||||
|
||||
rc = myproto_msg_dec(&parsed, msg->data, msg->len, cfg, ordered);
|
||||
printf("myproto_msg_dec() rc = %d\n", rc);
|
||||
printf("decoded: %s\n", myproto_msg_to_str(&parsed));
|
||||
if (strcmp(myproto_msg_to_str(orig), myproto_msg_to_str(&parsed))) {
|
||||
printf(" ERROR: parsed != orig\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
printf("=== end %s %s[%d]\n", label, __func__, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Example of defining a TLI, with an instance indicator */
|
||||
static int tliv_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
|
||||
{
|
||||
/* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */
|
||||
gtlv->ti.tag = src_data[0];
|
||||
gtlv->len = src_data[1];
|
||||
|
||||
switch (gtlv->ti.tag) {
|
||||
/* All tags that are TLIV go here */
|
||||
case MYPROTO_IEI_BAR:
|
||||
if (src_data_len < 3)
|
||||
return -ENOSPC;
|
||||
gtlv->ti.instance_present = true;
|
||||
gtlv->ti.instance = src_data[2];
|
||||
gtlv->val = src_data + 3;
|
||||
/* In this example, the I is part of the len */
|
||||
gtlv->len--;
|
||||
return 0;
|
||||
default:
|
||||
gtlv->val = src_data + 2;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int tliv_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
if (ti->tag > UINT8_MAX)
|
||||
return -EINVAL;
|
||||
if (len > UINT8_MAX)
|
||||
return -EMSGSIZE;
|
||||
if (dst_data_avail < 2)
|
||||
return -ENOSPC;
|
||||
|
||||
dst_data[0] = ti->tag;
|
||||
|
||||
switch (ti->tag) {
|
||||
/* All tags that are TLIV go here */
|
||||
case MYPROTO_IEI_BAR:
|
||||
if (dst_data_avail < 3)
|
||||
return -ENOSPC;
|
||||
if (!ti->instance_present)
|
||||
return -EINVAL;
|
||||
if (ti->instance > UINT8_MAX)
|
||||
return -EINVAL;
|
||||
/* here, I is part of the len in L; the passed len reflects only the value, so add 1 for I */
|
||||
dst_data[1] = len + 1;
|
||||
dst_data[2] = ti->instance;
|
||||
return 3;
|
||||
default:
|
||||
dst_data[1] = len;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
const struct osmo_gtlv_cfg osmo_tliv_cfg = {
|
||||
.tl_min_size = 2,
|
||||
.load_tl = tliv_load_tl,
|
||||
.store_tl = tliv_store_tl,
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 0, "test_gen_tlv");
|
||||
msgb_talloc_ctx_init(ctx, 0);
|
||||
|
||||
test_enc_dec("tliv ordered", &osmo_tliv_cfg, true);
|
||||
test_enc_dec("tliv unordered", &osmo_tliv_cfg, false);
|
||||
|
||||
talloc_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
|
||||
=== start tliv ordered test_enc_dec[0]
|
||||
encoded: MOO={ 'BAR'=23,true 'BAR'=42,false }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 03 02 17 01 01 03 05 2a 00 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'BAR'=23,true 'BAR'=42,false }
|
||||
=== end tliv ordered test_enc_dec[0]
|
||||
|
||||
=== start tliv ordered test_enc_dec[1]
|
||||
encoded: MOO={ 'BAR'=11,true 'BAR'=22,false 'BAR'=33,true }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 03 02 0b 01 01 03 03 16 00 01 03 05 21 01 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'BAR'=11,true 'BAR'=22,false 'BAR'=33,true }
|
||||
=== end tliv ordered test_enc_dec[1]
|
||||
|
||||
=== start tliv unordered test_enc_dec[0]
|
||||
encoded: MOO={ 'BAR'=23,true 'BAR'=42,false }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 03 02 17 01 01 03 05 2a 00 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'BAR'=23,true 'BAR'=42,false }
|
||||
=== end tliv unordered test_enc_dec[0]
|
||||
|
||||
=== start tliv unordered test_enc_dec[1]
|
||||
encoded: MOO={ 'BAR'=11,true 'BAR'=22,false 'BAR'=33,true }
|
||||
myproto_msg_enc() rc = 0
|
||||
01 01 03 02 0b 01 01 03 03 16 00 01 03 05 21 01 .
|
||||
myproto_msg_dec() rc = 0
|
||||
decoded: MOO={ 'BAR'=11,true 'BAR'=22,false 'BAR'=33,true }
|
||||
=== end tliv unordered test_enc_dec[1]
|
||||
@@ -7,6 +7,7 @@ AM_CPPFLAGS = \
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGTLV_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
@@ -23,8 +24,8 @@ pfcp_test_SOURCES = \
|
||||
|
||||
pfcp_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-pfcp/libosmo-pfcp.a \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGTLV_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
.PHONY: update_exp
|
||||
|
||||
@@ -1,30 +1,6 @@
|
||||
AT_INIT
|
||||
AT_BANNER([Regression tests.])
|
||||
|
||||
AT_SETUP([gtlv])
|
||||
AT_KEYWORDS([gtlv])
|
||||
cat $abs_srcdir/libosmo-gtlv/gtlv_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/libosmo-gtlv/gtlv_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([gtlv_dec_enc])
|
||||
AT_KEYWORDS([gtlv_dec_enc])
|
||||
cat $abs_srcdir/libosmo-gtlv/gtlv_dec_enc_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/libosmo-gtlv/gtlv_dec_enc_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([gtlv_gen])
|
||||
AT_KEYWORDS([gtlv_gen])
|
||||
cat $abs_srcdir/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([tliv])
|
||||
AT_KEYWORDS([tliv])
|
||||
cat $abs_srcdir/libosmo-gtlv/test_tliv/tliv_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/libosmo-gtlv/test_tliv/tliv_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([pfcp])
|
||||
AT_KEYWORDS([pfcp])
|
||||
cat $abs_srcdir/libosmo-pfcp/pfcp_test.ok > expout
|
||||
|
||||
Reference in New Issue
Block a user