diff options
Diffstat (limited to 'src/libimcv/pa_tnc')
-rw-r--r-- | src/libimcv/pa_tnc/pa_tnc_attr.h | 13 | ||||
-rw-r--r-- | src/libimcv/pa_tnc/pa_tnc_attr_manager.c | 161 | ||||
-rw-r--r-- | src/libimcv/pa_tnc/pa_tnc_attr_manager.h | 26 | ||||
-rw-r--r-- | src/libimcv/pa_tnc/pa_tnc_msg.c | 221 | ||||
-rw-r--r-- | src/libimcv/pa_tnc/pa_tnc_msg.h | 6 |
5 files changed, 276 insertions, 151 deletions
diff --git a/src/libimcv/pa_tnc/pa_tnc_attr.h b/src/libimcv/pa_tnc/pa_tnc_attr.h index 1e0c339c9..be0bef32e 100644 --- a/src/libimcv/pa_tnc/pa_tnc_attr.h +++ b/src/libimcv/pa_tnc/pa_tnc_attr.h @@ -26,8 +26,12 @@ typedef struct pa_tnc_attr_t pa_tnc_attr_t; #include <library.h> #include <pen/pen.h> +#define PA_TNC_ATTR_INFO_SIZE 8 #define PA_TNC_ATTR_HEADER_SIZE 12 +#define PA_TNC_ATTR_FLAG_NONE 0x00 +#define PA_TNC_ATTR_FLAG_NOSKIP (1<<7) + /** * Interface for an RFC 5792 PA-TNC Posture Attribute. * @@ -70,12 +74,19 @@ 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 * * @return this, with an increased refcount diff --git a/src/libimcv/pa_tnc/pa_tnc_attr_manager.c b/src/libimcv/pa_tnc/pa_tnc_attr_manager.c index 900a55716..522213bd5 100644 --- a/src/libimcv/pa_tnc/pa_tnc_attr_manager.c +++ b/src/libimcv/pa_tnc/pa_tnc_attr_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Andreas Steffen + * Copyright (C) 2011-2014 Andreas Steffen * * HSR Hochschule fuer Technik Rapperswil * @@ -16,6 +16,10 @@ #include "pa_tnc_attr_manager.h" +#include "imcv.h" +#include "pa_tnc_attr.h" +#include "ietf/ietf_attr_pa_tnc_error.h" + #include <collections/linked_list.h> #include <utils/debug.h> @@ -100,14 +104,102 @@ METHOD(pa_tnc_attr_manager_t, get_names, enum_name_t*, return attr_names; } +/** + * PA-TNC attribute + * + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Flags | PA-TNC Attribute Vendor ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | PA-TNC Attribute Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | PA-TNC Attribute Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Attribute Value (Variable Length) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + METHOD(pa_tnc_attr_manager_t, create, pa_tnc_attr_t*, - private_pa_tnc_attr_manager_t *this, pen_t vendor_id, u_int32_t type, - chunk_t value) + 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, value_len; + chunk_t value; + ietf_attr_pa_tnc_error_t *error_attr; + pen_t vendor_id; + pen_type_t unsupported_type; + pen_type_t error_code = { PEN_IETF, PA_ERROR_INVALID_PARAMETER }; + enum_name_t *pa_attr_names; + pa_tnc_attr_t *attr = NULL; enumerator_t *enumerator; entry_t *entry; - pa_tnc_attr_t *attr = NULL; + /* properly initialize error return argument in case of no error */ + *error = NULL; + + if (reader->remaining(reader) < PA_TNC_ATTR_HEADER_SIZE) + { + DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute header"); + *error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + msg_info, *offset); + return NULL; + } + reader->read_uint8 (reader, &flags); + reader->read_uint24(reader, &vendor_id); + reader->read_uint32(reader, &type); + reader->read_uint32(reader, &length); + + pa_attr_names = imcv_pa_tnc_attributes->get_names(imcv_pa_tnc_attributes, + vendor_id); + if (pa_attr_names) + { + DBG2(DBG_TNC, "processing PA-TNC attribute type '%N/%N' " + "0x%06x/0x%08x", pen_names, vendor_id, + pa_attr_names, type, vendor_id, type); + } + else + { + DBG2(DBG_TNC, "processing PA-TNC attribute type '%N' " + "0x%06x/0x%08x", pen_names, vendor_id, + vendor_id, type); + } + + if (length < PA_TNC_ATTR_HEADER_SIZE) + { + DBG1(DBG_TNC, "%u bytes too small for PA-TNC attribute length", + length); + *error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + msg_info, *offset + PA_TNC_ATTR_INFO_SIZE); + return NULL; + } + length -= PA_TNC_ATTR_HEADER_SIZE; + value_len = segmented ? reader->remaining(reader) : length; + + 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, + msg_info, *offset + PA_TNC_ATTR_INFO_SIZE); + return NULL; + } + DBG3(DBG_TNC, "%B", &value); + + if (vendor_id == PEN_RESERVED) + { + *error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + msg_info, *offset + 1); + return NULL; + } + if (type == IETF_ATTR_RESERVED) + { + *error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + msg_info, *offset + 4); + return NULL; + } + + /* check if the attribute type is registered */ enumerator = this->list->create_enumerator(this->list); while (enumerator->enumerate(enumerator, &entry)) { @@ -115,13 +207,71 @@ METHOD(pa_tnc_attr_manager_t, create, pa_tnc_attr_t*, { if (entry->attr_create) { - attr = entry->attr_create(type, value); + attr = entry->attr_create(type, length, value); } break; } } enumerator->destroy(enumerator); + if (!attr) + { + if (!(flags & PA_TNC_ATTR_FLAG_NOSKIP)) + { + DBG1(DBG_TNC, "skipping unsupported PA-TNC attribute"); + (*offset) += PA_TNC_ATTR_HEADER_SIZE + length; + return NULL; + } + + DBG1(DBG_TNC, "unsupported PA-TNC attribute with NOSKIP flag"); + unsupported_type = pen_type_create(vendor_id, type); + error_code = pen_type_create(PEN_IETF, PA_ERROR_ATTR_TYPE_NOT_SUPPORTED); + *error = ietf_attr_pa_tnc_error_create(error_code, msg_info); + error_attr = (ietf_attr_pa_tnc_error_t*)(*error); + error_attr->set_unsupported_attr(error_attr, flags, unsupported_type); + return NULL; + } + (*offset) += PA_TNC_ATTR_HEADER_SIZE; + + return attr; +} + +METHOD(pa_tnc_attr_manager_t, construct, pa_tnc_attr_t*, + private_pa_tnc_attr_manager_t *this, pen_t vendor_id, uint32_t type, + chunk_t value) +{ + enum_name_t *pa_attr_names; + pa_tnc_attr_t *attr = NULL; + enumerator_t *enumerator; + entry_t *entry; + + pa_attr_names = imcv_pa_tnc_attributes->get_names(imcv_pa_tnc_attributes, + vendor_id); + if (pa_attr_names) + { + DBG2(DBG_TNC, "generating PA-TNC attribute type '%N/%N' " + "0x%06x/0x%08x", pen_names, vendor_id, + pa_attr_names, type, vendor_id, type); + } + else + { + DBG2(DBG_TNC, "generating PA-TNC attribute type '%N' " + "0x%06x/0x%08x", pen_names, vendor_id, + vendor_id, type); + } + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->vendor_id == vendor_id) + { + if (entry->attr_create) + { + attr = entry->attr_create(type, value.len, value); + } + break; + } + } + enumerator->destroy(enumerator); return attr; } @@ -145,6 +295,7 @@ pa_tnc_attr_manager_t *pa_tnc_attr_manager_create(void) .remove_vendor = _remove_vendor, .get_names = _get_names, .create = _create, + .construct = _construct, .destroy = _destroy, }, .list = linked_list_create(), diff --git a/src/libimcv/pa_tnc/pa_tnc_attr_manager.h b/src/libimcv/pa_tnc/pa_tnc_attr_manager.h index 121be7f90..8607feede 100644 --- a/src/libimcv/pa_tnc/pa_tnc_attr_manager.h +++ b/src/libimcv/pa_tnc/pa_tnc_attr_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Andreas Steffen + * Copyright (C) 2011-2014 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -26,8 +26,10 @@ typedef struct pa_tnc_attr_manager_t pa_tnc_attr_manager_t; #include "pa_tnc_attr.h" #include <library.h> +#include <bio/bio_reader.h> -typedef pa_tnc_attr_t* (*pa_tnc_attr_create_t)(u_int32_t type, chunk_t value); +typedef pa_tnc_attr_t* (*pa_tnc_attr_create_t)(u_int32_t type, size_t length, + chunk_t value); /** * Manages PA-TNC attributes for arbitrary PENs @@ -61,15 +63,29 @@ struct pa_tnc_attr_manager_t { enum_name_t* (*get_names)(pa_tnc_attr_manager_t *this, pen_t vendor_id); /** - * Create a PA-TNC attribute object from data for a given vendor ID and type + * 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, + bool segmented, uint32_t *offset, chunk_t msg_info, + pa_tnc_attr_t **error); + + /** + * Generically construct a PA-TNC attribute from type and data * * @param vendor_id Private Enterprise Number (PEN) * @param type PA-TNC attribute type * @param value PA-TNC attribute value as encoded data * @return PA-TNC attribute object if supported, NULL else */ - pa_tnc_attr_t* (*create)(pa_tnc_attr_manager_t *this, pen_t vendor_id, - u_int32_t type, chunk_t value); + pa_tnc_attr_t* (*construct)(pa_tnc_attr_manager_t *this, pen_t vendor_id, + uint32_t type, chunk_t value); /** * Destroys a pa_tnc_attr_manager_t object. diff --git a/src/libimcv/pa_tnc/pa_tnc_msg.c b/src/libimcv/pa_tnc/pa_tnc_msg.c index 77d383b93..d9b441707 100644 --- a/src/libimcv/pa_tnc/pa_tnc_msg.c +++ b/src/libimcv/pa_tnc/pa_tnc_msg.c @@ -40,26 +40,6 @@ typedef struct private_pa_tnc_msg_t private_pa_tnc_msg_t; #define PA_TNC_RESERVED 0x000000 /** - * PA-TNC attribute - * - * 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Flags | PA-TNC Attribute Vendor ID | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | PA-TNC Attribute Type | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | PA-TNC Attribute Length | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Attribute Value (Variable Length) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - -#define PA_TNC_ATTR_FLAG_NONE 0x00 -#define PA_TNC_ATTR_FLAG_NOSKIP (1<<7) -#define PA_TNC_ATTR_INFO_SIZE 8 - -/** * Private data of a pa_tnc_msg_t object. * */ @@ -96,6 +76,11 @@ struct private_pa_tnc_msg_t { size_t max_msg_len; /** + * TRUE if attribute was extracted from data + */ + bool from_data; + + /** * Encoded message */ chunk_t encoding; @@ -113,17 +98,19 @@ METHOD(pa_tnc_msg_t, add_attribute, bool, chunk_t attr_value; size_t attr_len; - attr->build(attr); - attr_value = attr->get_value(attr); - attr_len = PA_TNC_ATTR_HEADER_SIZE + attr_value.len; - - if (this->max_msg_len && this->msg_len + attr_len > this->max_msg_len) + if (!this->from_data) { - /* attribute just does not fit into this message */ - return FALSE; - } - this->msg_len += attr_len; + attr->build(attr); + attr_value = attr->get_value(attr); + attr_len = PA_TNC_ATTR_HEADER_SIZE + attr_value.len; + if (this->max_msg_len && this->msg_len + attr_len > this->max_msg_len) + { + /* attribute just does not fit into this message */ + return FALSE; + } + this->msg_len += attr_len; + } this->attributes->insert_last(this->attributes, attr); return TRUE; } @@ -201,7 +188,9 @@ METHOD(pa_tnc_msg_t, process, status_t, private_pa_tnc_msg_t *this) { bio_reader_t *reader; - pa_tnc_attr_t *error; + pa_tnc_attr_t *attr, *error; + pen_type_t attr_type; + chunk_t attr_value; uint8_t version; uint32_t reserved, offset, attr_offset; pen_type_t error_code = { PEN_IETF, PA_ERROR_INVALID_PARAMETER }; @@ -231,119 +220,38 @@ METHOD(pa_tnc_msg_t, process, status_t, offset = PA_TNC_HEADER_SIZE; /* pre-process PA-TNC attributes */ - while (reader->remaining(reader) >= PA_TNC_ATTR_HEADER_SIZE) + while (reader->remaining(reader) > 0) { - pen_t vendor_id; - uint8_t flags; - uint32_t type, length; - chunk_t value, attr_info; - pa_tnc_attr_t *attr; - enum_name_t *pa_attr_names; - ietf_attr_pa_tnc_error_t *error_attr; - - attr_info = reader->peek(reader); - attr_info.len = PA_TNC_ATTR_INFO_SIZE; - reader->read_uint8 (reader, &flags); - reader->read_uint24(reader, &vendor_id); - reader->read_uint32(reader, &type); - reader->read_uint32(reader, &length); - - pa_attr_names = imcv_pa_tnc_attributes->get_names(imcv_pa_tnc_attributes, - vendor_id); - if (pa_attr_names) - { - DBG2(DBG_TNC, "processing PA-TNC attribute type '%N/%N' " - "0x%06x/0x%08x", pen_names, vendor_id, - pa_attr_names, type, vendor_id, type); - } - else - { - DBG2(DBG_TNC, "processing PA-TNC attribute type '%N' " - "0x%06x/0x%08x", pen_names, vendor_id, - vendor_id, type); - } - - if (length < PA_TNC_ATTR_HEADER_SIZE) - { - DBG1(DBG_TNC, "%u bytes too small for PA-TNC attribute length", - length); - error = ietf_attr_pa_tnc_error_create_with_offset(error_code, - this->encoding, offset + PA_TNC_ATTR_INFO_SIZE); - goto err; - } - - if (!reader->read_data(reader, length - PA_TNC_ATTR_HEADER_SIZE, &value)) - { - DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute value"); - error = ietf_attr_pa_tnc_error_create_with_offset(error_code, - this->encoding, offset + PA_TNC_ATTR_INFO_SIZE); - goto err; - } - DBG3(DBG_TNC, "%B", &value); - - if (vendor_id == PEN_RESERVED) - { - error = ietf_attr_pa_tnc_error_create_with_offset(error_code, - this->encoding, offset + 1); - goto err; - } - if (type == IETF_ATTR_RESERVED) - { - error = ietf_attr_pa_tnc_error_create_with_offset(error_code, - this->encoding, offset + 4); - goto err; - } attr = imcv_pa_tnc_attributes->create(imcv_pa_tnc_attributes, - vendor_id, type, value); + reader, FALSE, &offset, this->encoding, &error); if (!attr) { - if (flags & PA_TNC_ATTR_FLAG_NOSKIP) - { - DBG1(DBG_TNC, "unsupported PA-TNC attribute with NOSKIP flag"); - error_code = pen_type_create(PEN_IETF, - PA_ERROR_ATTR_TYPE_NOT_SUPPORTED); - error = ietf_attr_pa_tnc_error_create(error_code, - this->encoding); - error_attr = (ietf_attr_pa_tnc_error_t*)error; - error_attr->set_attr_info(error_attr, attr_info); - goto err; - } - else - { - DBG1(DBG_TNC, "skipping unsupported PA-TNC attribute"); - offset += length; - continue; - } + goto err; } + attr_value = attr->get_value(attr); + attr_type = attr->get_type(attr); if (attr->process(attr, &attr_offset) != SUCCESS) { attr->destroy(attr); - if (vendor_id == PEN_IETF && type == IETF_ATTR_PA_TNC_ERROR) + + if (attr_type.vendor_id == PEN_IETF && + attr_type.type == IETF_ATTR_PA_TNC_ERROR) { - /* error while processing a PA-TNC error attribute - abort */ - reader->destroy(reader); - return FAILED; + /* 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_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER); error = ietf_attr_pa_tnc_error_create_with_offset(error_code, - this->encoding, - offset + PA_TNC_ATTR_HEADER_SIZE + attr_offset); + this->encoding, offset + attr_offset); goto err; } + offset += attr_value.len; this->attributes->insert_last(this->attributes, attr); - offset += length; } - - if (reader->remaining(reader) == 0) - { - reader->destroy(reader); - return SUCCESS; - } - DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute header"); - error = ietf_attr_pa_tnc_error_create_with_offset(error_code, - this->encoding, offset); + reader->destroy(reader); + return SUCCESS; err: reader->destroy(reader); @@ -352,24 +260,27 @@ err: } METHOD(pa_tnc_msg_t, process_ietf_std_errors, bool, - private_pa_tnc_msg_t *this) + private_pa_tnc_msg_t *this, linked_list_t *non_fatal_types) { - enumerator_t *enumerator; + enumerator_t *e1, *e2; + enum_name_t *pa_attr_names; pa_tnc_attr_t *attr; - pen_type_t type; + pen_type_t type, unsupported_type; + uint8_t flags; bool fatal_error = FALSE; - enumerator = this->attributes->create_enumerator(this->attributes); - while (enumerator->enumerate(enumerator, &attr)) + e1 = this->attributes->create_enumerator(this->attributes); + while (e1->enumerate(e1, &attr)) { type = attr->get_type(attr); if (type.vendor_id == PEN_IETF && type.type == IETF_ATTR_PA_TNC_ERROR) { ietf_attr_pa_tnc_error_t *error_attr; - pen_type_t error_code; - chunk_t msg_info, attr_info; + pen_type_t error_code, *non_fatal_type; + chunk_t msg_info; uint32_t offset; + bool fatal_current_error = TRUE; error_attr = (ietf_attr_pa_tnc_error_t*)attr; error_code = error_attr->get_error_code(error_attr); @@ -391,16 +302,49 @@ METHOD(pa_tnc_msg_t, process_ietf_std_errors, bool, DBG1(DBG_TNC, " occurred at offset of %u bytes", offset); break; case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED: - attr_info = error_attr->get_attr_info(error_attr); - DBG1(DBG_TNC, " unsupported attribute %#B", &attr_info); + unsupported_type = + error_attr->get_unsupported_attr(error_attr, &flags); + pa_attr_names = + imcv_pa_tnc_attributes->get_names(imcv_pa_tnc_attributes, + unsupported_type.vendor_id); + if (pa_attr_names) + { + DBG1(DBG_TNC, " unsupported attribute type '%N/%N' " + "0x%06x/0x%08x, flags 0x%02x", + pen_names, unsupported_type.vendor_id, + pa_attr_names, unsupported_type.type, + unsupported_type.vendor_id, unsupported_type.type, + flags); + } + else + { + DBG1(DBG_TNC, " unsupported attribute type '%N' " + "0x%06x/0x%08x, flags 0x%02x", + pen_names, unsupported_type.vendor_id, + unsupported_type.vendor_id, unsupported_type.type, + flags); + } + e2 = non_fatal_types->create_enumerator(non_fatal_types); + while (e2->enumerate(e2, &non_fatal_type)) + { + if (pen_type_equals(unsupported_type, *non_fatal_type)) + { + fatal_current_error = FALSE; + break; + } + } + e2->destroy(e2); break; default: break; } - fatal_error = TRUE; + if (fatal_current_error) + { + fatal_error = TRUE; + } } } - enumerator->destroy(enumerator); + e1->destroy(e1); return fatal_error; } @@ -476,6 +420,7 @@ pa_tnc_msg_t *pa_tnc_msg_create_from_data(chunk_t data) .encoding = chunk_clone(data), .attributes = linked_list_create(), .errors = linked_list_create(), + .from_data = TRUE, ); return &this->public; diff --git a/src/libimcv/pa_tnc/pa_tnc_msg.h b/src/libimcv/pa_tnc/pa_tnc_msg.h index 84814b92b..57ff1a04c 100644 --- a/src/libimcv/pa_tnc/pa_tnc_msg.h +++ b/src/libimcv/pa_tnc/pa_tnc_msg.h @@ -68,9 +68,11 @@ struct pa_tnc_msg_t { /** * Process all IETF standard error PA-TNC attributes * - * @return TRUE if at least one error attribute processed + * @param non_fatal_types list of non fatal unsupported attribute types + * @return TRUE if at least one fatal error processed */ - bool (*process_ietf_std_errors)(pa_tnc_msg_t *this); + bool (*process_ietf_std_errors)(pa_tnc_msg_t *this, + linked_list_t *non_fatal_types); /** * Enumerates over all PA-TNC attributes |