diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libosmo-gtlv/gtlv.c | 94 | ||||
-rw-r--r-- | src/libosmo-gtlv/gtlv_dec_enc.c | 69 | ||||
-rw-r--r-- | src/libosmo-gtlv/gtlv_gen.c | 13 |
3 files changed, 127 insertions, 49 deletions
diff --git a/src/libosmo-gtlv/gtlv.c b/src/libosmo-gtlv/gtlv.c index 997a67a..9bdbe75 100644 --- a/src/libosmo-gtlv/gtlv.c +++ b/src/libosmo-gtlv/gtlv.c @@ -27,6 +27,45 @@ #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; @@ -102,6 +141,7 @@ int osmo_gtlv_load_next(struct osmo_gtlv_load *gtlv) /* 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; @@ -117,16 +157,19 @@ int osmo_gtlv_load_next(struct osmo_gtlv_load *gtlv) /* 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. - * \returns the tag number on success, negative on TLV parsing error, -ENOENT when no more tags - * follow. + * \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) +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) @@ -140,16 +183,26 @@ int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv) rc = gtlv->cfg->load_tl(&mtlv, ie_start, buflen_left); if (rc) return -EBADMSG; - return mtlv.tag; + 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); @@ -157,7 +210,7 @@ int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag) return rc; if (!work.val) return -ENOENT; - if (work.tag == tag) { + if (!osmo_gtlv_tag_inst_cmp(&work.ti, ti)) { *gtlv = work; return 0; } @@ -190,16 +243,25 @@ int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag) */ 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), tag, len, gtlv); + 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_tag = tag; + gtlv->last_ti = *ti; gtlv->last_tl = last_tl; gtlv->last_val = gtlv->dst->tail; return 0; @@ -212,7 +274,7 @@ int osmo_gtlv_put_tl(struct osmo_gtlv_put *gtlv, unsigned int tag, size_t len) 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, gtlv->last_tag, len, gtlv); + 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 @@ -224,22 +286,22 @@ int osmo_gtlv_put_update_tl(struct osmo_gtlv_put *gtlv) 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->tag = src_data[0]; + 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, unsigned int tag, size_t len, +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 (tag > UINT8_MAX) + if (ti->tag > UINT8_MAX) return -EINVAL; if (len > UINT8_MAX) return -EMSGSIZE; if (dst_data_avail < 2) return -ENOSPC; - dst_data[0] = tag; + dst_data[0] = ti->tag; dst_data[1] = len; return 2; } @@ -253,22 +315,22 @@ const struct osmo_gtlv_cfg osmo_t8l8v_cfg = { 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->tag = osmo_load16be(src_data); + 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, unsigned int tag, size_t len, +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 (tag > UINT16_MAX) + if (ti->tag > UINT16_MAX) return -EINVAL; if (len > UINT16_MAX) return -EMSGSIZE; if (dst_data_avail < 4) return -ENOSPC; - osmo_store16be(tag, dst_data); + osmo_store16be(ti->tag, dst_data); osmo_store16be(len, dst_data + 2); return 4; } diff --git a/src/libosmo-gtlv/gtlv_dec_enc.c b/src/libosmo-gtlv/gtlv_dec_enc.c index 28ee493..2e5509a 100644 --- a/src/libosmo-gtlv/gtlv_dec_enc.c +++ b/src/libosmo-gtlv/gtlv_dec_enc.c @@ -31,12 +31,21 @@ /* 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, IEI, FMT, ARGS...) \ +#define RETURN_ERROR(RC, TAG_INST, FMT, ARGS...) \ do {\ - if (err_cb) \ - err_cb(err_cb_data, (void *)decoded_struct, __FILE__, __LINE__, \ - "IE '%s' (0x%x): " FMT " (%d: %s)\n", \ - get_value_string(iei_strs, IEI), IEI, ##ARGS, RC, strerror((RC) > 0 ? (RC) : -(RC))); \ + 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) @@ -72,7 +81,7 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of #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->tag, \ + 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) @@ -91,7 +100,7 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of rc = osmo_gtlv_load_next(gtlv); if (rc) - RETURN_ERROR(rc, gtlv->tag, "Decoding IEs failed on or after this tag"); + RETURN_ERROR(rc, gtlv->ti, "Decoding IEs failed on or after this tag"); if (!gtlv->val) { /* End of the TLV structure */ break; @@ -103,7 +112,9 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of do { /* Find the IE coding for this tag */ - for (iec = ie_coding; !osmo_gtlv_coding_end(iec) && iec->tag != gtlv->tag; iec++); + 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; @@ -128,7 +139,7 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of 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->tag, "Only %u instances of this IE are supported per message", + 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. */ @@ -166,14 +177,14 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of 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->tag, "Error while decoding TLV structure nested inside this IE"); + 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->tag, "IE definition lacks a 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->tag, "Error while decoding this IE"); + RETURN_ERROR(rc, gtlv->ti, "Error while decoding this IE"); } if (multi_count_p) { @@ -197,14 +208,14 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of 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->tag, "Error while decoding: %u instances of this IE are mandatory, got %u", + 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->tag, "Missing mandatory IE"); + RETURN_ERROR(-EINVAL, iec->ti, "Missing mandatory IE"); } return 0; } @@ -235,19 +246,20 @@ static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs, 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(gtlv, ie_coding->tag); + 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->tag, "Missing mandatory IE"); + RETURN_ERROR(rc, ie_coding->ti, "Missing mandatory IE"); if (presence_flag) *presence_flag = false; continue; default: - RETURN_ERROR(rc, ie_coding->tag, "Error in TLV structure"); + RETURN_ERROR(rc, ie_coding->ti, "Error in TLV structure"); } for (;;) { @@ -257,7 +269,7 @@ static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs, 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->tag, "Only %u instances of this IE are supported per message", + RETURN_ERROR(-ENOTSUP, ie_coding->ti, "Only %u instances of this IE are supported per message", ie_coding->count_max); /* Decode IE value part */ @@ -286,15 +298,15 @@ static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs, 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->tag, + 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->tag, "IE definition lacks a 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->tag, "Error while decoding this IE"); + RETURN_ERROR(rc, ie_coding->ti, "Error while decoding this IE"); } if (presence_flag) @@ -312,7 +324,8 @@ static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs, (*multi_count)++; /* Does another one of these IEs follow? */ - if (osmo_gtlv_load_peek_tag(gtlv) != gtlv->tag) { + 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; } @@ -387,7 +400,7 @@ int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, un if (multi_count_p) { n = *multi_count_p; if (!ie_coding->memb_array_pitch) - RETURN_ERROR(-EFAULT, ie_coding->tag, + 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 { @@ -397,11 +410,11 @@ int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, un for (i = 0; i < n; i++) { unsigned int memb_ofs; - osmo_gtlv_put_tl(gtlv, ie_coding->tag, 0); + 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->tag, + 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; @@ -413,12 +426,12 @@ int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, un 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->tag, + 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->tag, "Error while encoding this IE"); + RETURN_ERROR(rc, ie_coding->ti, "Error while encoding this IE"); } osmo_gtlv_put_update_tl(gtlv); @@ -467,7 +480,7 @@ int osmo_gtlvs_encode_to_str_buf(char *buf, size_t buflen, const void *decoded_s if (!n) continue; - OSMO_STRBUF_PRINTF(sb, " '%s'=", get_value_string(iei_strs, ie_coding->tag)); + OSMO_STRBUF_PRINTF(sb, " '%s'=", get_value_string(iei_strs, ie_coding->ti.tag)); if (multi_count_p) OSMO_STRBUF_PRINTF(sb, "{ "); diff --git a/src/libosmo-gtlv/gtlv_gen.c b/src/libosmo-gtlv/gtlv_gen.c index 3f499f7..fd3fbd9 100644 --- a/src/libosmo-gtlv/gtlv_gen.c +++ b/src/libosmo-gtlv/gtlv_gen.c @@ -249,7 +249,7 @@ static void write_extern_dec_enc(const struct osmo_gtlv_gen_ie_o *ies) } /* For a nested IE, write the struct osmo_gtlv_coding array of the inner IEs. - * { MYPROTO_IEI_BAR, + * { { MYPROTO_IEI_BAR }, * .memb_ofs = offsetof(struct myproto_foo, bar), * .dec_func = myproto_dec_bar, * .enc_func = myproto_enc_bar, @@ -263,10 +263,13 @@ static void write_ies_array(const char *indent, const struct osmo_gtlv_gen_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,\n", g_cfg->tag_prefix, osmo_str_toupper(tag_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 && ie->nested_ies) { - printi(" .nested_ies = ies_in_%s,\n", ie->tag_name ? : 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); @@ -291,7 +294,7 @@ static void write_ies_array(const char *indent, const struct osmo_gtlv_gen_ie_o /* 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, + * { {MYPROTO_IEI_BAR}, * .memb_ofs = offsetof(struct myproto_foo, bar), * .dec_func = myproto_dec_bar, * .enc_func = myproto_enc_bar, |