mirror of https://gerrit.osmocom.org/libosmocore
TLV: Add one-shot TLV encoder
So far, the TLV code contained two types of functions * tlp_parse() to parse all TLVs according to definition into tlvp_parsed * various helper functions to encode individual TLVs during message generation This patch implements the inverse of tlv_parse(): tlv_encode(), which takes a full 'struct tlv_pared' and encodes all IEs found in it. The order of IEs is in numerically ascending order of the tag. As many protocols have different IE/TLV ordering requirements, let's add a tlv_encode_ordered() function where the caller can specify the TLV ordering during the one-shot encode. Change-Id: I761a30bf20355a9f80a4a8e0c60b0b0f78515efe
This commit is contained in:
parent
e0c5d700ae
commit
8006f5393e
|
@ -457,6 +457,12 @@ int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,
|
||||||
/* take a master (src) tlv def and fill up all empty slots in 'dst' */
|
/* take a master (src) tlv def and fill up all empty slots in 'dst' */
|
||||||
void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);
|
void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);
|
||||||
|
|
||||||
|
int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,
|
||||||
|
unsigned int len, const uint8_t *val);
|
||||||
|
int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp);
|
||||||
|
int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,
|
||||||
|
const uint8_t *tag_order, unsigned int tag_order_len);
|
||||||
|
|
||||||
#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
|
#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
|
||||||
#define TLVP_LEN(x, y) (x)->lv[y].len
|
#define TLVP_LEN(x, y) (x)->lv[y].len
|
||||||
#define TLVP_VAL(x, y) (x)->lv[y].val
|
#define TLVP_VAL(x, y) (x)->lv[y].val
|
||||||
|
|
|
@ -537,6 +537,9 @@ tlv_dump;
|
||||||
tlv_parse;
|
tlv_parse;
|
||||||
tlv_parse2;
|
tlv_parse2;
|
||||||
tlv_parse_one;
|
tlv_parse_one;
|
||||||
|
tlv_encode;
|
||||||
|
tlv_encode_ordered;
|
||||||
|
tlv_encode_one;
|
||||||
tvlv_att_def;
|
tvlv_att_def;
|
||||||
vtvlv_gan_att_def;
|
vtvlv_gan_att_def;
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,103 @@ int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Encode a single TLV into given message buffer
|
||||||
|
* \param[inout] msg Caller-allocated message buffer with sufficient tailroom
|
||||||
|
* \param[in] type TLV type/format to use during encode
|
||||||
|
* \param[in] tag Tag of TLV to be encoded
|
||||||
|
* \parma[in] len Length of TLV to be encoded
|
||||||
|
* \param[in] val Value part of TLV to be encoded
|
||||||
|
* \returns 0 on success; negative in case of error */
|
||||||
|
int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,
|
||||||
|
unsigned int len, const uint8_t *val)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case TLV_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
case TLV_TYPE_FIXED:
|
||||||
|
msgb_tv_fixed_put(msg, tag, len, val);
|
||||||
|
break;
|
||||||
|
case TLV_TYPE_T:
|
||||||
|
msgb_v_put(msg, tag);
|
||||||
|
break;
|
||||||
|
case TLV_TYPE_TV:
|
||||||
|
msgb_tv_put(msg, tag, val[0]);
|
||||||
|
break;
|
||||||
|
case TLV_TYPE_TLV:
|
||||||
|
msgb_tlv_put(msg, tag, len, val);
|
||||||
|
break;
|
||||||
|
case TLV_TYPE_TL16V:
|
||||||
|
msgb_tl16v_put(msg, tag, len, val);
|
||||||
|
break;
|
||||||
|
case TLV_TYPE_TvLV:
|
||||||
|
msgb_tvlv_put(msg, tag, len, val);
|
||||||
|
break;
|
||||||
|
case TLV_TYPE_SINGLE_TV:
|
||||||
|
msgb_v_put(msg, (tag << 4) | (val[0] & 0xf));
|
||||||
|
break;
|
||||||
|
case TLV_TYPE_vTvLV_GAN:
|
||||||
|
msgb_vtvlv_gan_put(msg, tag, len, val);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Encode a set of decoded TLVs according to a given definition into a message buffer
|
||||||
|
* \param[inout] msg Caller-allocated message buffer with sufficient tailroom
|
||||||
|
* \param[in] def structure defining the valid TLV tags / configurations
|
||||||
|
* \param[in] tp decoded values to be encoded
|
||||||
|
* \returns number of bytes consumed in msg; negative in case of error */
|
||||||
|
int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp)
|
||||||
|
{
|
||||||
|
unsigned int tailroom_before = msgb_tailroom(msg);
|
||||||
|
unsigned int i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(tp->lv); i++) {
|
||||||
|
/* skip entries in the array that aren't used/filled */
|
||||||
|
if (!TLVP_PRESENT(tp, i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rc = tlv_encode_one(msg, def->def[i].type, i, TLVP_LEN(tp, i), TLVP_VAL(tp, i));
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tailroom_before - msgb_tailroom(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Encode a set of decoded TLVs according to a given definition and IE order into a message buffer
|
||||||
|
* \param[inout] msg Caller-allocated message buffer with sufficient tailroom
|
||||||
|
* \param[in] def structure defining the valid TLV tags / configurations
|
||||||
|
* \param[in] tp decoded values to be encoded
|
||||||
|
* \param[in] tag_order array of tags determining the IE encoding order
|
||||||
|
* \param[in] tag_order_len length of tag_order
|
||||||
|
* \returns number of bytes consumed in msg; negative in case of error */
|
||||||
|
int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,
|
||||||
|
const uint8_t *tag_order, unsigned int tag_order_len)
|
||||||
|
{
|
||||||
|
|
||||||
|
unsigned int tailroom_before = msgb_tailroom(msg);
|
||||||
|
unsigned int i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
for (i = 0; i < tag_order_len; i++) {
|
||||||
|
uint8_t tag = tag_order[i];
|
||||||
|
|
||||||
|
/* skip entries in the array that aren't used/filled */
|
||||||
|
if (!TLVP_PRESENT(tp, tag))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rc = tlv_encode_one(msg, def->def[tag].type, tag, TLVP_LEN(tp, tag), TLVP_VAL(tp, tag));
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return tailroom_before - msgb_tailroom(msg);
|
||||||
|
}
|
||||||
|
|
||||||
/*! Parse a single TLV encoded IE
|
/*! Parse a single TLV encoded IE
|
||||||
* \param[out] o_tag the tag of the IE that was found
|
* \param[out] o_tag the tag of the IE that was found
|
||||||
* \param[out] o_len length of the IE that was found
|
* \param[out] o_len length of the IE that was found
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
#include <osmocom/gsm/tlv.h>
|
#include <osmocom/gsm/tlv.h>
|
||||||
|
#include <osmocom/gsm/gsm0808.h>
|
||||||
|
|
||||||
static void check_tlv_parse(uint8_t **data, size_t *data_len,
|
static void check_tlv_parse(uint8_t **data, size_t *data_len,
|
||||||
uint8_t exp_tag, size_t exp_len, const uint8_t *exp_val)
|
uint8_t exp_tag, size_t exp_len, const uint8_t *exp_val)
|
||||||
|
@ -286,12 +288,57 @@ static void test_tlv_repeated_ie()
|
||||||
OSMO_ASSERT(dec3[2].lv[tag].val == &test_data[2 + 3 + 3]);
|
OSMO_ASSERT(dec3[2].lv[tag].val == &test_data[2 + 3 + 3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_tlv_encoder()
|
||||||
|
{
|
||||||
|
const uint8_t enc_ies[] = {
|
||||||
|
0x17, 0x14, 0x06, 0x2b, 0x12, 0x2b, 0x0b, 0x40, 0x2b, 0xb7, 0x05, 0xd0, 0x63, 0x82, 0x95, 0x03, 0x05, 0x40,
|
||||||
|
0x07, 0x08, 0x43, 0x90,
|
||||||
|
0x2c, 0x04,
|
||||||
|
0x40, 0x42,
|
||||||
|
};
|
||||||
|
const uint8_t ie_order[] = { 0x2c, 0x40, 0x17 };
|
||||||
|
const uint8_t enc_ies_reordered[] = {
|
||||||
|
0x2c, 0x04,
|
||||||
|
0x40, 0x42,
|
||||||
|
0x17, 0x14, 0x06, 0x2b, 0x12, 0x2b, 0x0b, 0x40, 0x2b, 0xb7, 0x05, 0xd0, 0x63, 0x82, 0x95, 0x03, 0x05, 0x40,
|
||||||
|
0x07, 0x08, 0x43, 0x90,
|
||||||
|
};
|
||||||
|
struct tlv_parsed tp;
|
||||||
|
struct msgb *msg = msgb_alloc(1024, __func__);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
printf("Testing TLV encoder by decoding + re-encoding binary\n");
|
||||||
|
|
||||||
|
OSMO_ASSERT(msg);
|
||||||
|
|
||||||
|
/* decode BSSAP IEs specified above */
|
||||||
|
rc = osmo_bssap_tlv_parse(&tp, enc_ies, ARRAY_SIZE(enc_ies));
|
||||||
|
OSMO_ASSERT(rc == 3);
|
||||||
|
|
||||||
|
/* re-encode it */
|
||||||
|
rc = tlv_encode(msg, gsm0808_att_tlvdef(), &tp);
|
||||||
|
OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));
|
||||||
|
OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies, ARRAY_SIZE(enc_ies)));
|
||||||
|
|
||||||
|
msgb_reset(msg);
|
||||||
|
|
||||||
|
printf("Testing TLV encoder with IE ordering\n");
|
||||||
|
|
||||||
|
/* re-encodei in different order */
|
||||||
|
rc = tlv_encode_ordered(msg, gsm0808_att_tlvdef(), &tp, ie_order, ARRAY_SIZE(ie_order));
|
||||||
|
OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));
|
||||||
|
OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies_reordered, ARRAY_SIZE(enc_ies_reordered)));
|
||||||
|
|
||||||
|
msgb_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
//osmo_init_logging2(ctx, &info);
|
//osmo_init_logging2(ctx, &info);
|
||||||
|
|
||||||
test_tlv_shift_functions();
|
test_tlv_shift_functions();
|
||||||
test_tlv_repeated_ie();
|
test_tlv_repeated_ie();
|
||||||
|
test_tlv_encoder();
|
||||||
|
|
||||||
printf("Done.\n");
|
printf("Done.\n");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
Test shift functions
|
Test shift functions
|
||||||
|
Testing TLV encoder by decoding + re-encoding binary
|
||||||
|
Testing TLV encoder with IE ordering
|
||||||
Done.
|
Done.
|
||||||
|
|
Loading…
Reference in New Issue