libosmo-pfcp/include/osmocom/gtlv/gtlv_dec_enc.h

205 lines
9.1 KiB
C

/* 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, size_t decoded_struct_size,
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, size_t decoded_struct_size,
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, size_t decoded_struct_size, 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, size_t decoded_struct_size, 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;
}