aboutsummaryrefslogtreecommitdiffstats
path: root/include/osmocom/gtlv/gtlv_dec_enc.h
blob: 132239f9fed11075ebddc42b278069144090e984 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/* 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;
}