From: Andreas Steffen Date: Wed, 1 Oct 2014 21:11:30 +0000 (+0200) Subject: Implemented add_segment method for PA-TNC attributes X-Git-Tag: 5.2.1dr1~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eba0cbcee3b96f77fc51f460c25e3bc06eee9934;p=thirdparty%2Fstrongswan.git Implemented add_segment method for PA-TNC attributes --- diff --git a/src/libimcv/pa_tnc/pa_tnc_attr.h b/src/libimcv/pa_tnc/pa_tnc_attr.h index a5296692cb..be0bef32ed 100644 --- a/src/libimcv/pa_tnc/pa_tnc_attr.h +++ b/src/libimcv/pa_tnc/pa_tnc_attr.h @@ -26,6 +26,7 @@ typedef struct pa_tnc_attr_t pa_tnc_attr_t; #include #include +#define PA_TNC_ATTR_INFO_SIZE 8 #define PA_TNC_ATTR_HEADER_SIZE 12 #define PA_TNC_ATTR_FLAG_NONE 0x00 @@ -73,11 +74,18 @@ struct pa_tnc_attr_t { /** * Process the value of an PA-TNC attribute to extract its parameters * - * @param relative error offset within attribute body + * @param offset relative error offset within attribute body * @return result status */ status_t (*process)(pa_tnc_attr_t *this, uint32_t *offset); + /** + * Add a data segment to an attribute allowing incremental processing + * + * @param segment data segment to be appended + */ + void (*add_segment)(pa_tnc_attr_t *this, chunk_t segment); + /** * Get a new reference to the PA-TNC attribute * diff --git a/src/libimcv/pa_tnc/pa_tnc_attr_manager.c b/src/libimcv/pa_tnc/pa_tnc_attr_manager.c index 71f294d104..2d914f42ac 100644 --- a/src/libimcv/pa_tnc/pa_tnc_attr_manager.c +++ b/src/libimcv/pa_tnc/pa_tnc_attr_manager.c @@ -120,14 +120,12 @@ METHOD(pa_tnc_attr_manager_t, get_names, enum_name_t*, * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -#define PA_TNC_ATTR_INFO_SIZE 8 - METHOD(pa_tnc_attr_manager_t, create, pa_tnc_attr_t*, - private_pa_tnc_attr_manager_t *this, bio_reader_t *reader, uint32_t *offset, - chunk_t msg_info, pa_tnc_attr_t **error) + private_pa_tnc_attr_manager_t *this, bio_reader_t *reader, bool segmented, + uint32_t *offset, chunk_t msg_info, pa_tnc_attr_t **error) { uint8_t flags; - uint32_t type, length, attr_offset; + uint32_t type, length, value_len; chunk_t value; ietf_attr_pa_tnc_error_t *error_attr; pen_t vendor_id; @@ -177,8 +175,9 @@ METHOD(pa_tnc_attr_manager_t, create, pa_tnc_attr_t*, return NULL; } length -= PA_TNC_ATTR_HEADER_SIZE; + value_len = segmented ? reader->remaining(reader) : length; - if (!reader->read_data(reader, length, &value)) + if (!reader->read_data(reader, value_len, &value)) { DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute value"); *error = ietf_attr_pa_tnc_error_create_with_offset(error_code, @@ -220,7 +219,7 @@ METHOD(pa_tnc_attr_manager_t, create, pa_tnc_attr_t*, if (!(flags & PA_TNC_ATTR_FLAG_NOSKIP)) { DBG1(DBG_TNC, "skipping unsupported PA-TNC attribute"); - offset += length; + (*offset) += PA_TNC_ATTR_HEADER_SIZE + length; return NULL; } @@ -232,21 +231,7 @@ METHOD(pa_tnc_attr_manager_t, create, pa_tnc_attr_t*, error_attr->set_unsupported_attr(error_attr, flags, unsupported_type); return NULL; } - if (attr->process(attr, &attr_offset) != SUCCESS) - { - attr->destroy(attr); - attr = NULL; - if (vendor_id == PEN_IETF && type == IETF_ATTR_PA_TNC_ERROR) - { - /* error while processing a PA-TNC error attribute - abort */ - return NULL; - } - error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER); - *error = ietf_attr_pa_tnc_error_create_with_offset(error_code, - msg_info, *offset + PA_TNC_ATTR_HEADER_SIZE + attr_offset); - return NULL; - } - (*offset) += length; + (*offset) += PA_TNC_ATTR_HEADER_SIZE; return attr; } diff --git a/src/libimcv/pa_tnc/pa_tnc_attr_manager.h b/src/libimcv/pa_tnc/pa_tnc_attr_manager.h index c38face6a9..048ad19413 100644 --- a/src/libimcv/pa_tnc/pa_tnc_attr_manager.h +++ b/src/libimcv/pa_tnc/pa_tnc_attr_manager.h @@ -66,13 +66,14 @@ struct pa_tnc_attr_manager_t { * Create and pre-parse a PA-TNC attribute object from data * * @param reader PA-TNC attribute as encoded data + * @param segmented TRUE if attribute is segmented * @param offset Offset in bytes where an error has been found * @param msg_info Message info added to an error attribute * @param error Error attribute if an error occurred * @return PA-TNC attribute object if supported, NULL else */ pa_tnc_attr_t* (*create)(pa_tnc_attr_manager_t *this, bio_reader_t *reader, - uint32_t *offset, chunk_t msg_info, + bool segmented, uint32_t *offset, chunk_t msg_info, pa_tnc_attr_t **error); /** diff --git a/src/libimcv/pa_tnc/pa_tnc_msg.c b/src/libimcv/pa_tnc/pa_tnc_msg.c index 5a0afcdc06..d9b4417076 100644 --- a/src/libimcv/pa_tnc/pa_tnc_msg.c +++ b/src/libimcv/pa_tnc/pa_tnc_msg.c @@ -37,6 +37,8 @@ typedef struct private_pa_tnc_msg_t private_pa_tnc_msg_t; * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ +#define PA_TNC_RESERVED 0x000000 + /** * Private data of a pa_tnc_msg_t object. * @@ -187,8 +189,10 @@ METHOD(pa_tnc_msg_t, process, status_t, { bio_reader_t *reader; pa_tnc_attr_t *attr, *error; + pen_type_t attr_type; + chunk_t attr_value; uint8_t version; - uint32_t reserved, offset; + uint32_t reserved, offset, attr_offset; pen_type_t error_code = { PEN_IETF, PA_ERROR_INVALID_PARAMETER }; /* process message header */ @@ -219,15 +223,32 @@ METHOD(pa_tnc_msg_t, process, status_t, while (reader->remaining(reader) > 0) { attr = imcv_pa_tnc_attributes->create(imcv_pa_tnc_attributes, - reader, &offset, this->encoding, &error); - if (error) + reader, FALSE, &offset, this->encoding, &error); + if (!attr) { goto err; } - if (attr) + attr_value = attr->get_value(attr); + attr_type = attr->get_type(attr); + + if (attr->process(attr, &attr_offset) != SUCCESS) { - this->attributes->insert_last(this->attributes, attr); + attr->destroy(attr); + + if (attr_type.vendor_id == PEN_IETF && + attr_type.type == IETF_ATTR_PA_TNC_ERROR) + { + /* suppress error while processing a PA-TNC error attribute */ + offset += attr_value.len; + continue; + } + error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER); + error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + this->encoding, offset + attr_offset); + goto err; } + offset += attr_value.len; + this->attributes->insert_last(this->attributes, attr); } reader->destroy(reader); return SUCCESS; diff --git a/src/libimcv/pa_tnc/pa_tnc_msg.h b/src/libimcv/pa_tnc/pa_tnc_msg.h index a33690890c..57ff1a04cf 100644 --- a/src/libimcv/pa_tnc/pa_tnc_msg.h +++ b/src/libimcv/pa_tnc/pa_tnc_msg.h @@ -25,9 +25,6 @@ typedef struct pa_tnc_msg_t pa_tnc_msg_t; #define PA_TNC_VERSION 0x01 #define PA_TNC_HEADER_SIZE 8 -#define PA_TNC_RESERVED 0x000000 - - #include "pa_tnc_attr.h" diff --git a/src/libimcv/seg/seg_contract.c b/src/libimcv/seg/seg_contract.c index 38e954d955..df25e64e99 100644 --- a/src/libimcv/seg/seg_contract.c +++ b/src/libimcv/seg/seg_contract.c @@ -15,6 +15,7 @@ #include "seg_contract.h" #include "seg_env.h" +#include "ietf/ietf_attr_pa_tnc_error.h" #include "tcg/seg/tcg_seg_attr_seg_env.h" #include @@ -195,9 +196,10 @@ METHOD(seg_contract_t, add_segment, pa_tnc_attr_t*, tcg_seg_attr_seg_env_t *seg_env_attr; seg_env_t *current, *seg_env = NULL; pa_tnc_attr_t *base_attr; + pen_type_t error_code; uint32_t base_attr_id; uint8_t flags; - chunk_t segment_data; + chunk_t segment_data, msg_info; enumerator_t *enumerator; seg_env_attr = (tcg_seg_attr_seg_env_t*)attr; @@ -212,10 +214,7 @@ METHOD(seg_contract_t, add_segment, pa_tnc_attr_t*, if (current->get_base_attr_id(current) == base_attr_id) { seg_env = current; - if (!(*more)) - { - this->seg_envs->remove_at(this->seg_envs, enumerator); - } + this->seg_envs->remove_at(this->seg_envs, enumerator); break; } } @@ -227,13 +226,17 @@ METHOD(seg_contract_t, add_segment, pa_tnc_attr_t*, { DBG1(DBG_TNC, "base attribute ID %d is already in use", base_attr_id); + this->seg_envs->insert_last(this->seg_envs, seg_env); return NULL; } DBG2(DBG_TNC, "received first segment for base attribute ID %d " "(%d bytes)", base_attr_id, segment_data.len); seg_env = seg_env_create_from_data(base_attr_id, segment_data, - this->max_seg_size); - this->seg_envs->insert_last(this->seg_envs, seg_env); + this->max_seg_size, error); + if (!seg_env) + { + return NULL; + } } else { @@ -245,19 +248,36 @@ METHOD(seg_contract_t, add_segment, pa_tnc_attr_t*, DBG2(DBG_TNC, "received %s segment for base attribute ID %d " "(%d bytes)", (*more) ? "next" : "last", base_attr_id, segment_data.len); - seg_env->add_segment(seg_env, segment_data); + if (!seg_env->add_segment(seg_env, segment_data, error)) + { + seg_env->destroy(seg_env); + return NULL; + } } + base_attr = seg_env->get_base_attr(seg_env); + if (*more) { - return NULL; + /* reinsert into list since more segments are to come */ + this->seg_envs->insert_last(this->seg_envs, seg_env); + } + else + { + /* added the last segment */ + if (!base_attr) + { + /* base attribute waits for more data */ + DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute value"); + msg_info = seg_env->get_base_attr_info(seg_env); + error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER); + *error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + msg_info, PA_TNC_ATTR_INFO_SIZE); + } + seg_env->destroy(seg_env); } - base_attr = seg_env->get_base_attr(seg_env, error); - seg_env->destroy(seg_env); - return base_attr; } - METHOD(seg_contract_t, is_issuer, bool, private_seg_contract_t *this) { diff --git a/src/libimcv/seg/seg_env.c b/src/libimcv/seg/seg_env.c index 1ec8a367b4..b961589bfe 100644 --- a/src/libimcv/seg/seg_env.c +++ b/src/libimcv/seg/seg_env.c @@ -17,6 +17,7 @@ #include "imcv.h" #include "pa_tnc/pa_tnc_msg.h" +#include "ietf/ietf_attr_pa_tnc_error.h" #include "tcg/seg/tcg_seg_attr_seg_env.h" #include @@ -48,20 +49,25 @@ struct private_seg_env_t { pa_tnc_attr_t *base_attr; /** - * Maximum PA-TNC attribute segment size + * Base Attribute Info to be used for PA-TNC error messages */ - uint32_t max_seg_size; + u_char base_attr_info[8]; /** - * TRUE if attribute is assembled from data + * Base Attribute needs more segment data */ - bool from_data; + bool need_more; /** - * Remaining attribute data to be sent or received data being accumulated + * Pointer to remaining attribute data to be sent */ chunk_t data; + /** + * Maximum PA-TNC attribute segment size + */ + uint32_t max_seg_size; + }; METHOD(seg_env_t, get_base_attr_id, uint32_t, @@ -71,33 +77,15 @@ METHOD(seg_env_t, get_base_attr_id, uint32_t, } METHOD(seg_env_t, get_base_attr, pa_tnc_attr_t*, - private_seg_env_t *this, pa_tnc_attr_t** error) + private_seg_env_t *this) { - *error = NULL; + return this->need_more ? NULL : this->base_attr->get_ref(this->base_attr); +} - if (!this->base_attr) - { - bio_writer_t *writer; - bio_reader_t *reader; - chunk_t msg_info; - uint32_t offset = 0; - - writer = bio_writer_create(8); - writer->write_uint8 (writer, PA_TNC_VERSION); - writer->write_uint24(writer, PA_TNC_RESERVED); - writer->write_uint8 (writer, BASE_ATTR_ID_PREFIX); - writer->write_uint24(writer, this->base_attr_id); - msg_info = writer->extract_buf(writer); - writer->destroy(writer); - - reader = bio_reader_create(this->data); - this->base_attr = imcv_pa_tnc_attributes->create(imcv_pa_tnc_attributes, - reader, &offset, msg_info, error); - chunk_free(&msg_info); - reader->destroy(reader); - } - - return this->base_attr ? this->base_attr->get_ref(this->base_attr) : NULL; +METHOD(seg_env_t, get_base_attr_info, chunk_t, + private_seg_env_t *this) +{ + return chunk_create(this->base_attr_info, 8); } METHOD(seg_env_t, first_segment, pa_tnc_attr_t*, @@ -175,19 +163,44 @@ METHOD(seg_env_t, next_segment, pa_tnc_attr_t*, return seg_env_attr; } -METHOD(seg_env_t, add_segment, void, - private_seg_env_t *this, chunk_t segment_data) +METHOD(seg_env_t, add_segment, bool, + private_seg_env_t *this, chunk_t segment, pa_tnc_attr_t **error) { - this->data = chunk_cat("mc", this->data, segment_data); + pen_type_t type, error_code; + uint32_t attr_offset; + chunk_t msg_info; + status_t status; + + /* not all attributes might have implemented the add_segment method */ + if (!this->base_attr->add_segment) + { + return FALSE; + } + this->base_attr->add_segment(this->base_attr, segment); + status = this->base_attr->process(this->base_attr, &attr_offset); + + if (status != SUCCESS && status != NEED_MORE) + { + type = this->base_attr->get_type(this->base_attr); + if (type.vendor_id == PEN_IETF && type.type == IETF_ATTR_PA_TNC_ERROR) + { + /* error while processing a PA-TNC error attribute - abort */ + return FALSE; + } + error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER); + msg_info = get_base_attr_info(this); + *error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + msg_info, PA_TNC_ATTR_HEADER_SIZE + attr_offset); + return FALSE; + } + this->need_more = (status == NEED_MORE); + + return TRUE; } METHOD(seg_env_t, destroy, void, private_seg_env_t *this) { - if (this->from_data) - { - chunk_free(&this->data); - } DESTROY_IF(this->base_attr); free(this); } @@ -218,6 +231,7 @@ seg_env_t *seg_env_create(uint32_t base_attr_id, pa_tnc_attr_t *base_attr, .public = { .get_base_attr_id = _get_base_attr_id, .get_base_attr = _get_base_attr, + .get_base_attr_info = _get_base_attr_info, .first_segment = _first_segment, .next_segment = _next_segment, .add_segment = _add_segment, @@ -236,14 +250,20 @@ seg_env_t *seg_env_create(uint32_t base_attr_id, pa_tnc_attr_t *base_attr, * See header */ seg_env_t *seg_env_create_from_data(uint32_t base_attr_id, chunk_t data, - uint32_t max_seg_size) + uint32_t max_seg_size, pa_tnc_attr_t** error) { private_seg_env_t *this; + pen_type_t type, error_code; + bio_reader_t *reader; + chunk_t msg_info; + uint32_t offset = 0, attr_offset; + status_t status; INIT(this, .public = { .get_base_attr_id = _get_base_attr_id, .get_base_attr = _get_base_attr, + .get_base_attr_info = _get_base_attr_info, .first_segment = _first_segment, .next_segment = _next_segment, .add_segment = _add_segment, @@ -251,10 +271,41 @@ seg_env_t *seg_env_create_from_data(uint32_t base_attr_id, chunk_t data, }, .base_attr_id = base_attr_id, .max_seg_size = max_seg_size, - .data = chunk_clone(data), - .from_data = TRUE, ); + /* create info field to be used by PA-TNC error messages */ + memset(this->base_attr_info, 0xff, 4); + htoun32(this->base_attr_info + 4, base_attr_id); + msg_info = get_base_attr_info(this); + + /* extract from base attribute segment from data */ + reader = bio_reader_create(data); + this->base_attr = imcv_pa_tnc_attributes->create(imcv_pa_tnc_attributes, + reader, TRUE, &offset, msg_info, error); + reader->destroy(reader); + + if (!this->base_attr) + { + destroy(this); + return NULL; + } + status = this->base_attr->process(this->base_attr, &attr_offset); + + if (status != SUCCESS && status != NEED_MORE) + { + type = this->base_attr->get_type(this->base_attr); + if (!(type.vendor_id == PEN_IETF && + type.type == IETF_ATTR_PA_TNC_ERROR)) + { + error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER); + *error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + msg_info, PA_TNC_ATTR_HEADER_SIZE + attr_offset); + } + destroy(this); + return NULL; + } + this->need_more = (status == NEED_MORE); + return &this->public; } diff --git a/src/libimcv/seg/seg_env.h b/src/libimcv/seg/seg_env.h index 0dcefdde8b..dd057ae471 100644 --- a/src/libimcv/seg/seg_env.h +++ b/src/libimcv/seg/seg_env.h @@ -50,12 +50,18 @@ struct seg_env_t { uint32_t (*get_base_attr_id)(seg_env_t *this); /** - * Get Base Attribute + * Get Base Attribute if it contains processed [incremental] data * - * @param error Error attribute if an error occurred or NULL * @return Base Attribute (must be destroyed) or NULL */ - pa_tnc_attr_t* (*get_base_attr)(seg_env_t *this, pa_tnc_attr_t **error); + pa_tnc_attr_t* (*get_base_attr)(seg_env_t *this); + + /** + * Base Attribute Info to be used by PA-TNC error messages + * + * @return Message info string + */ + chunk_t (*get_base_attr_info)(seg_env_t *this); /** * Generate the first segment envelope of the base attribute @@ -76,8 +82,11 @@ struct seg_env_t { * Generate the first segment envelope of the base attribute * * @param segment Attribute segment to be added + * @param error Error attribute if a parsing error occurred + * return TRUE if segment was successfully added */ - void (*add_segment)(seg_env_t *this, chunk_t segment); + bool (*add_segment)(seg_env_t *this, chunk_t segment, + pa_tnc_attr_t** error); /** * Destroys a seg_env_t object. @@ -101,8 +110,10 @@ seg_env_t* seg_env_create(uint32_t base_attr_id, pa_tnc_attr_t *base_attr, * @param base_attr_id Base Attribute ID * @param data First attribute segment * @param max_seg_size Maximum segment size + * @param error Error attribute if a parsing error occurred */ seg_env_t* seg_env_create_from_data(uint32_t base_attr_id, chunk_t data, - uint32_t max_seg_size); + uint32_t max_seg_size, + pa_tnc_attr_t** error); #endif /** SEG_ENV_H_ @}*/ diff --git a/src/libimcv/tcg/swid/tcg_swid_attr_req.c b/src/libimcv/tcg/swid/tcg_swid_attr_req.c index 35254cc327..561242758e 100644 --- a/src/libimcv/tcg/swid/tcg_swid_attr_req.c +++ b/src/libimcv/tcg/swid/tcg_swid_attr_req.c @@ -220,6 +220,12 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, add_segment, void, + private_tcg_swid_attr_req_t *this, chunk_t segment) +{ + this->value = chunk_cat("mc", this->value, segment); +} + METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, private_tcg_swid_attr_req_t *this) { @@ -285,6 +291,7 @@ pa_tnc_attr_t *tcg_swid_attr_req_create(u_int8_t flags, u_int32_t request_id, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .add_segment = _add_segment, .get_ref = _get_ref, .destroy = _destroy, }, @@ -321,6 +328,7 @@ pa_tnc_attr_t *tcg_swid_attr_req_create_from_data(size_t length, chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .add_segment = _add_segment, .get_ref = _get_ref, .destroy = _destroy, }, diff --git a/src/libimcv/tcg/swid/tcg_swid_attr_tag_id_inv.c b/src/libimcv/tcg/swid/tcg_swid_attr_tag_id_inv.c index 6e9ebd43ce..4d1316cc74 100644 --- a/src/libimcv/tcg/swid/tcg_swid_attr_tag_id_inv.c +++ b/src/libimcv/tcg/swid/tcg_swid_attr_tag_id_inv.c @@ -224,6 +224,12 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, add_segment, void, + private_tcg_swid_attr_tag_id_inv_t *this, chunk_t segment) +{ + this->value = chunk_cat("mc", this->value, segment); +} + METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, private_tcg_swid_attr_tag_id_inv_t *this) { @@ -288,6 +294,7 @@ pa_tnc_attr_t *tcg_swid_attr_tag_id_inv_create(uint32_t request_id, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .add_segment = _add_segment, .get_ref = _get_ref, .destroy = _destroy, }, @@ -325,6 +332,7 @@ pa_tnc_attr_t *tcg_swid_attr_tag_id_inv_create_from_data(size_t length, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .add_segment = _add_segment, .get_ref = _get_ref, .destroy = _destroy, }, diff --git a/src/libimcv/tcg/swid/tcg_swid_attr_tag_inv.c b/src/libimcv/tcg/swid/tcg_swid_attr_tag_inv.c index 5923f56196..f616967885 100644 --- a/src/libimcv/tcg/swid/tcg_swid_attr_tag_inv.c +++ b/src/libimcv/tcg/swid/tcg_swid_attr_tag_inv.c @@ -213,6 +213,12 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, add_segment, void, + private_tcg_swid_attr_tag_inv_t *this, chunk_t segment) +{ + this->value = chunk_cat("mc", this->value, segment); +} + METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, private_tcg_swid_attr_tag_inv_t *this) { @@ -276,6 +282,7 @@ pa_tnc_attr_t *tcg_swid_attr_tag_inv_create(uint32_t request_id, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .add_segment = _add_segment, .get_ref = _get_ref, .destroy = _destroy, }, @@ -312,6 +319,7 @@ pa_tnc_attr_t *tcg_swid_attr_tag_inv_create_from_data(size_t length, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .add_segment = _add_segment, .get_ref = _get_ref, .destroy = _destroy, },