Merge branch 'ikev1-clean' into ikev1-master
[strongswan.git] / src / libimcv / imc / imc_agent.c
index a59c028..6bba697 100644 (file)
@@ -54,6 +54,11 @@ struct private_imc_agent_t {
        TNC_IMCID id;
 
        /**
+        * List of additional IMC IDs assigned by TNCC
+        */
+       linked_list_t *additional_ids;
+
+       /**
         * list of TNCC connection entries
         */
        linked_list_t *connections;
@@ -104,6 +109,76 @@ struct private_imc_agent_t {
                                                           TNC_BufferReference msg,
                                                           TNC_UInt32 msg_len,
                                                           TNC_MessageType msg_type);
+
+
+       /**
+        * Call when an IMC-IMC message is to be sent with long message types
+        *
+        * @param imc_id                        IMC ID assigned by TNCC
+        * @param connection_id         network connection ID assigned by TNCC
+        * @param msg_flags                     message flags
+        * @param msg                           message to send
+        * @param msg_len                       message length in bytes
+        * @param msg_vid                       message vendor ID
+        * @param msg_subtype           message subtype
+        * @param dst_imc_id            destination IMV ID
+        * @return                                      TNC result code
+        */
+       TNC_Result (*send_message_long)(TNC_IMCID imc_id,
+                                                                       TNC_ConnectionID connection_id,
+                                                                       TNC_UInt32 msg_flags,
+                                                                       TNC_BufferReference msg,
+                                                                       TNC_UInt32 msg_len,
+                                                                       TNC_VendorID msg_vid,
+                                                                       TNC_MessageSubtype msg_subtype,
+                                                                       TNC_UInt32 dst_imv_id);
+
+       /**
+        * Get the value of an attribute associated with a connection
+        * or with the TNCC as a whole.
+        *
+        * @param imc_id                        IMC ID assigned by TNCC
+        * @param connection_id         network connection ID assigned by TNCC
+        * @param attribute_id          attribute ID
+        * @param buffer_len            length of buffer in bytes
+        * @param buffer                        buffer
+        * @param out_value_len         size in bytes of attribute stored in buffer
+        * @return                                      TNC result code
+        */
+       TNC_Result (*get_attribute)(TNC_IMCID imc_id,
+                                                               TNC_ConnectionID connection_id,
+                                                               TNC_AttributeID attribute_id,
+                                                               TNC_UInt32 buffer_len,
+                                                               TNC_BufferReference buffer,
+                                                               TNC_UInt32 *out_value_len);
+
+       /**
+        * Set the value of an attribute associated with a connection
+        * or with the TNCC as a whole.
+        *
+        * @param imc_id                        IMV ID assigned by TNCC
+        * @param connection_id         network connection ID assigned by TNCC
+        * @param attribute_id          attribute ID
+        * @param buffer_len            length of buffer in bytes
+        * @param buffer                        buffer
+        * @return                                      TNC result code
+        */
+       TNC_Result (*set_attribute)(TNC_IMCID imc_id,
+                                                               TNC_ConnectionID connection_id,
+                                                               TNC_AttributeID attribute_id,
+                                                               TNC_UInt32 buffer_len,
+                                                               TNC_BufferReference buffer);
+
+       /**
+        * Reserve an additional IMC ID
+        *
+        * @param imc_id                        primary IMC ID assigned by TNCC
+        * @param out_imc_id            additional IMC ID assigned by TNCC
+        * @return                                      TNC result code
+        */
+       TNC_Result (*reserve_additional_id)(TNC_IMCID imc_id,
+                                                                               TNC_UInt32 *out_imc_id);
+
 };
 
 METHOD(imc_agent_t, bind_functions, TNC_Result,
@@ -134,6 +209,26 @@ METHOD(imc_agent_t, bind_functions, TNC_Result,
        {
                this->send_message = NULL;
        }
+       if (bind_function(this->id, "TNC_TNCC_SendMessageLong",
+                       (void**)&this->send_message_long) != TNC_RESULT_SUCCESS)
+       {
+               this->send_message_long = NULL;
+       }
+       if (bind_function(this->id, "TNC_TNCC_GetAttribute",
+                       (void**)&this->get_attribute) != TNC_RESULT_SUCCESS)
+       {
+               this->get_attribute = NULL;
+       }
+       if (bind_function(this->id, "TNC_TNCC_SetAttribute",
+                       (void**)&this->set_attribute) != TNC_RESULT_SUCCESS)
+       {
+               this->set_attribute = NULL;
+       }
+       if (bind_function(this->id, "TNC_TNCC_ReserveAdditionalIMCID",
+                       (void**)&this->reserve_additional_id) != TNC_RESULT_SUCCESS)
+       {
+               this->reserve_additional_id = NULL;
+       }
        DBG2(DBG_IMC, "IMC %u \"%s\" provided with bind function",
                                  this->id, this->name);
 
@@ -206,24 +301,78 @@ static bool delete_connection(private_imc_agent_t *this, TNC_ConnectionID id)
        return found;
 }
 
+/**
+ * Read a boolean attribute
+ */
+static bool get_bool_attribute(private_imc_agent_t *this, TNC_ConnectionID id,
+                                                          TNC_AttributeID attribute_id)
+{
+       TNC_UInt32 len;
+       char buf[4];
+
+       return this->get_attribute  &&
+                  this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
+                                                          TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01;
+ }
+
+/**
+ * Read a string attribute
+ */
+static char* get_str_attribute(private_imc_agent_t *this, TNC_ConnectionID id,
+                                                               TNC_AttributeID attribute_id)
+{
+       TNC_UInt32 len;
+       char buf[BUF_LEN];
+
+       if (this->get_attribute  &&
+               this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) ==
+                                                       TNC_RESULT_SUCCESS && len <= BUF_LEN)
+       {
+               return strdup(buf);
+       }
+       return NULL;
+ }
+
 METHOD(imc_agent_t, create_state, TNC_Result,
        private_imc_agent_t *this, imc_state_t *state)
 {
-       TNC_ConnectionID connection_id;
+       TNC_ConnectionID conn_id;
+       char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL;
+       bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE;
 
-       connection_id = state->get_connection_id(state);
-       if (find_connection(this, connection_id))
+       conn_id = state->get_connection_id(state);
+       if (find_connection(this, conn_id))
        {
                DBG1(DBG_IMC, "IMC %u \"%s\" already created a state for Connection ID %u",
-                                          this->id, this->name, connection_id);
+                                          this->id, this->name, conn_id);
                state->destroy(state);
                return TNC_RESULT_OTHER;
        }
+
+       /* Get and display attributes from TNCC via IF-IMC */
+       has_long = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_LONG_TYPES);
+       has_excl = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_EXCLUSIVE);
+       has_soh  = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_SOH);
+       tnccs_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL); 
+       tnccs_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_VERSION);
+       t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL);
+       t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION);
+
+       state->set_flags(state, has_long, has_excl);
+
+       DBG2(DBG_IMC, "IMC %u \"%s\" created a state for Connection ID %u: "
+                                 "%s %s with %slong %sexcl %ssoh over %s %s",
+                                 this->id, this->name, conn_id, tnccs_p ? tnccs_p:"?",
+                                 tnccs_v ? tnccs_v:"?", has_long ? "+":"-", has_excl ? "+":"-",
+                                 has_soh ? "+":"-",  t_p ? t_p:"?", t_v ? t_v :"?");
+       free(tnccs_p);
+       free(tnccs_v);
+       free(t_p);
+       free(t_v);
+
        this->connection_lock->write_lock(this->connection_lock);
        this->connections->insert_last(this->connections, state);
        this->connection_lock->unlock(this->connection_lock);
-       DBG2(DBG_IMC, "IMC %u \"%s\" created a state for Connection ID %u",
-                                 this->id, this->name, connection_id);
        return TNC_RESULT_SUCCESS;
 }
 
@@ -303,29 +452,78 @@ METHOD(imc_agent_t, get_state, bool,
 }
 
 METHOD(imc_agent_t, send_message, TNC_Result,
-       private_imc_agent_t *this, TNC_ConnectionID connection_id, chunk_t msg)
+       private_imc_agent_t *this, TNC_ConnectionID connection_id, bool excl,
+       TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id, chunk_t msg)
 {
        TNC_MessageType type;
+       TNC_UInt32 msg_flags;
+       imc_state_t *state;
 
-       if (!this->send_message)
+       state = find_connection(this, connection_id);
+       if (!state)
        {
+               DBG1(DBG_IMV, "IMC %u \"%s\" has no state for Connection ID %u",
+                                         this->id, this->name, connection_id);
                return TNC_RESULT_FATAL;
        }
-       type = (this->vendor_id << 8) | this->subtype;
-       return this->send_message(this->id, connection_id, msg.ptr, msg.len, type);
+
+       if (state->has_long(state) && this->send_message_long)
+       {
+               if (!src_imc_id)
+               {
+                       src_imc_id = this->id;
+               }
+               msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0;
+
+               return this->send_message_long(src_imc_id, connection_id, msg_flags,
+                                                                          msg.ptr, msg.len, this->vendor_id,
+                                                                          this->subtype, dst_imv_id);
+       }
+       if (this->send_message)
+       {
+               type = (this->vendor_id << 8) | this->subtype;
+
+               return this->send_message(this->id, connection_id, msg.ptr, msg.len,
+                                                                 type);
+       }
+       return TNC_RESULT_FATAL;
 }
 
 METHOD(imc_agent_t, receive_message, TNC_Result,
-       private_imc_agent_t *this, TNC_ConnectionID connection_id, chunk_t msg,
-       TNC_MessageType msg_type, pa_tnc_msg_t **pa_tnc_msg)
+       private_imc_agent_t *this, imc_state_t *state, chunk_t msg,
+       TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype,
+       TNC_UInt32 src_imv_id, TNC_UInt32 dst_imc_id, pa_tnc_msg_t **pa_tnc_msg)
 {
        pa_tnc_msg_t *pa_msg, *error_msg;
        pa_tnc_attr_t *error_attr;
        enumerator_t *enumerator;
+       TNC_MessageType msg_type;
+       TNC_UInt32 msg_flags, src_imc_id, dst_imv_id;
+       TNC_ConnectionID connection_id;
        TNC_Result result;
 
-       DBG2(DBG_IMV, "IMC %u \"%s\" received message type 0x%08x for Connection ID %u",
-                                  this->id, this->name, msg_type, connection_id);
+       connection_id = state->get_connection_id(state);
+
+       if (state->has_long(state))
+       {
+               if (dst_imc_id != TNC_IMCID_ANY)
+               {
+                       DBG2(DBG_IMC, "IMC %u \"%s\" received message for Connection ID %u "
+                                                 "from IMV %u to IMC %u", this->id, this->name,
+                                                  connection_id, src_imv_id, dst_imc_id);
+               }
+               else
+               {
+                       DBG2(DBG_IMC, "IMC %u \"%s\" received message for Connection ID %u "
+                                                 "from IMV %u", this->id, this->name, connection_id,
+                                                  src_imv_id);
+               }
+       }
+       else
+       {
+               DBG2(DBG_IMC, "IMC %u \"%s\" received message for Connection ID %u",
+                                          this->id, this->name, connection_id);
+       }
 
        *pa_tnc_msg = NULL;
        pa_msg = pa_tnc_msg_create_from_data(msg);
@@ -336,12 +534,6 @@ METHOD(imc_agent_t, receive_message, TNC_Result,
                        *pa_tnc_msg = pa_msg;
                        break;
                case VERIFY_ERROR:
-                       if (!this->send_message)
-                       {
-                               /* TNCC doen't have a SendMessage() function */
-                               return TNC_RESULT_FATAL;
-                       }
-
                        /* build error message */
                        error_msg = pa_tnc_msg_create();
                        enumerator = pa_msg->create_error_enumerator(pa_msg);
@@ -354,9 +546,36 @@ METHOD(imc_agent_t, receive_message, TNC_Result,
                        error_msg->build(error_msg);
 
                        /* send error message */
-                       msg = error_msg->get_encoding(error_msg);
-                       result = this->send_message(this->id, connection_id,
+                       if (state->has_long(state) && this->send_message_long)
+                       {
+                               if (state->has_excl(state))
+                               {
+                                       msg_flags =     TNC_MESSAGE_FLAGS_EXCLUSIVE;
+                                       dst_imv_id = src_imv_id;
+                               }
+                               else
+                               {
+                                       msg_flags = 0;
+                                       dst_imv_id = TNC_IMVID_ANY;
+                               }
+                               src_imc_id = (dst_imc_id == TNC_IMCID_ANY) ? this->id
+                                                                                                                  : dst_imc_id;
+
+                               result = this->send_message_long(src_imc_id, connection_id,
+                                                                               msg_flags, msg.ptr, msg.len, msg_vid,
+                                                                               msg_subtype, dst_imv_id);
+                       }
+                       else if (this->send_message)
+                       {
+                               msg_type = (msg_vid << 8) | msg_subtype;
+
+                               result = this->send_message(this->id, connection_id,
                                                                                msg.ptr, msg.len, msg_type);
+                       }
+                       else
+                       {
+                               result = TNC_RESULT_FATAL;
+                       }
 
                        /* clean up */
                        error_msg->destroy(error_msg);
@@ -370,10 +589,56 @@ METHOD(imc_agent_t, receive_message, TNC_Result,
        return TNC_RESULT_SUCCESS;
 }
 
+METHOD(imc_agent_t, reserve_additional_ids, TNC_Result,
+       private_imc_agent_t *this, int count)
+{
+       TNC_Result result;
+       TNC_UInt32 id;
+       void *pointer;
+
+       if (!this->reserve_additional_id)
+       {
+               DBG1(DBG_IMC, "IMC %u \"%s\" did not detect the capability to reserve "
+                                         "additional IMC IDs from the TNCC", this->id, this->name);
+               return TNC_RESULT_ILLEGAL_OPERATION;
+       }
+       while (count > 0)
+       {
+               result = this->reserve_additional_id(this->id, &id);
+               if (result != TNC_RESULT_SUCCESS)
+               {
+                       DBG1(DBG_IMC, "IMC %u \"%s\" failed to reserve %d additional IMC IDs",
+                                                 this->id, this->name, count);
+                       return result;
+               }
+               count--;
+
+               /* store the scalar value in the pointer */
+               pointer = (void*)id;
+               this->additional_ids->insert_last(this->additional_ids, pointer);
+               DBG2(DBG_IMC, "IMC %u \"%s\" reserved additional ID %u",
+                                         this->id, this->name, id);
+       }
+       return TNC_RESULT_SUCCESS;
+}
+
+METHOD(imc_agent_t, count_additional_ids, int,
+       private_imc_agent_t *this)
+{
+       return  this->additional_ids->get_count(this->additional_ids);
+}
+
+METHOD(imc_agent_t, create_id_enumerator, enumerator_t*,
+       private_imc_agent_t *this)
+{
+       return this->additional_ids->create_enumerator(this->additional_ids);
+}
+
 METHOD(imc_agent_t, destroy, void,
        private_imc_agent_t *this)
 {
        DBG1(DBG_IMC, "IMC %u \"%s\" terminated", this->id, this->name);
+       this->additional_ids->destroy(this->additional_ids);
        this->connections->destroy_function(this->connections, free);
        this->connection_lock->destroy(this->connection_lock);
        free(this);
@@ -406,12 +671,16 @@ imc_agent_t *imc_agent_create(const char *name,
                        .get_state = _get_state,
                        .send_message = _send_message,
                        .receive_message = _receive_message,
+                       .reserve_additional_ids = _reserve_additional_ids,
+                       .count_additional_ids = _count_additional_ids,
+                       .create_id_enumerator = _create_id_enumerator,
                        .destroy = _destroy,
                },
                .name = name,
                .vendor_id = vendor_id,
                .subtype = subtype,
                .id = id,
+               .additional_ids = linked_list_create(),
                .connections = linked_list_create(),
                .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
        );