8d1e70716e8b3cb285ec0bccbbc0e08cc5a2c80a
[strongswan.git] / src / libimcv / imc / imc_agent.c
1 /*
2 * Copyright (C) 2011-2012 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "imcv.h"
17 #include "imc_agent.h"
18
19 #include <tncif_names.h>
20
21 #include <debug.h>
22 #include <threading/rwlock.h>
23
24 typedef struct private_imc_agent_t private_imc_agent_t;
25
26 /**
27 * Private data of an imc_agent_t object.
28 */
29 struct private_imc_agent_t {
30
31 /**
32 * Public members of imc_agent_t
33 */
34 imc_agent_t public;
35
36 /**
37 * name of IMC
38 */
39 const char *name;
40
41 /**
42 * message vendor ID of IMC
43 */
44 TNC_VendorID vendor_id;
45
46 /**
47 * message subtype of IMC
48 */
49 TNC_MessageSubtype subtype;
50
51 /**
52 * ID of IMC as assigned by TNCC
53 */
54 TNC_IMCID id;
55
56 /**
57 * List of additional IMC IDs assigned by TNCC
58 */
59 linked_list_t *additional_ids;
60
61 /**
62 * list of TNCC connection entries
63 */
64 linked_list_t *connections;
65
66 /**
67 * rwlock to lock TNCC connection entries
68 */
69 rwlock_t *connection_lock;
70
71 /**
72 * Inform a TNCC about the set of message types the IMC is able to receive
73 *
74 * @param imc_id IMC ID assigned by TNCC
75 * @param supported_types list of supported message types
76 * @param type_count number of list elements
77 * @return TNC result code
78 */
79 TNC_Result (*report_message_types)(TNC_IMCID imc_id,
80 TNC_MessageTypeList supported_types,
81 TNC_UInt32 type_count);
82
83 /**
84 * Inform a TNCC about the set of message types the IMC is able to receive
85 *
86 * @param imc_id IMC ID assigned by TNCC
87 * @param supported_vids list of supported message vendor IDs
88 * @param supported_subtypes list of supported message subtypes
89 * @param type_count number of list elements
90 * @return TNC result code
91 */
92 TNC_Result (*report_message_types_long)(TNC_IMCID imc_id,
93 TNC_VendorIDList supported_vids,
94 TNC_MessageSubtypeList supported_subtypes,
95 TNC_UInt32 type_count);
96
97 /**
98 * Call when an IMC-IMC message is to be sent
99 *
100 * @param imc_id IMC ID assigned by TNCC
101 * @param connection_id network connection ID assigned by TNCC
102 * @param msg message to send
103 * @param msg_len message length in bytes
104 * @param msg_type message type
105 * @return TNC result code
106 */
107 TNC_Result (*send_message)(TNC_IMCID imc_id,
108 TNC_ConnectionID connection_id,
109 TNC_BufferReference msg,
110 TNC_UInt32 msg_len,
111 TNC_MessageType msg_type);
112
113
114 /**
115 * Call when an IMC-IMC message is to be sent with long message types
116 *
117 * @param imc_id IMC ID assigned by TNCC
118 * @param connection_id network connection ID assigned by TNCC
119 * @param msg_flags message flags
120 * @param msg message to send
121 * @param msg_len message length in bytes
122 * @param msg_vid message vendor ID
123 * @param msg_subtype message subtype
124 * @param dst_imc_id destination IMV ID
125 * @return TNC result code
126 */
127 TNC_Result (*send_message_long)(TNC_IMCID imc_id,
128 TNC_ConnectionID connection_id,
129 TNC_UInt32 msg_flags,
130 TNC_BufferReference msg,
131 TNC_UInt32 msg_len,
132 TNC_VendorID msg_vid,
133 TNC_MessageSubtype msg_subtype,
134 TNC_UInt32 dst_imv_id);
135
136 /**
137 * Get the value of an attribute associated with a connection
138 * or with the TNCC as a whole.
139 *
140 * @param imc_id IMC ID assigned by TNCC
141 * @param connection_id network connection ID assigned by TNCC
142 * @param attribute_id attribute ID
143 * @param buffer_len length of buffer in bytes
144 * @param buffer buffer
145 * @param out_value_len size in bytes of attribute stored in buffer
146 * @return TNC result code
147 */
148 TNC_Result (*get_attribute)(TNC_IMCID imc_id,
149 TNC_ConnectionID connection_id,
150 TNC_AttributeID attribute_id,
151 TNC_UInt32 buffer_len,
152 TNC_BufferReference buffer,
153 TNC_UInt32 *out_value_len);
154
155 /**
156 * Set the value of an attribute associated with a connection
157 * or with the TNCC as a whole.
158 *
159 * @param imc_id IMV ID assigned by TNCC
160 * @param connection_id network connection ID assigned by TNCC
161 * @param attribute_id attribute ID
162 * @param buffer_len length of buffer in bytes
163 * @param buffer buffer
164 * @return TNC result code
165 */
166 TNC_Result (*set_attribute)(TNC_IMCID imc_id,
167 TNC_ConnectionID connection_id,
168 TNC_AttributeID attribute_id,
169 TNC_UInt32 buffer_len,
170 TNC_BufferReference buffer);
171
172 /**
173 * Reserve an additional IMC ID
174 *
175 * @param imc_id primary IMC ID assigned by TNCC
176 * @param out_imc_id additional IMC ID assigned by TNCC
177 * @return TNC result code
178 */
179 TNC_Result (*reserve_additional_id)(TNC_IMCID imc_id,
180 TNC_UInt32 *out_imc_id);
181
182 };
183
184 METHOD(imc_agent_t, bind_functions, TNC_Result,
185 private_imc_agent_t *this, TNC_TNCC_BindFunctionPointer bind_function)
186 {
187 if (!bind_function)
188 {
189 DBG1(DBG_IMC, "TNC client failed to provide bind function");
190 return TNC_RESULT_INVALID_PARAMETER;
191 }
192 if (bind_function(this->id, "TNC_TNCC_ReportMessageTypes",
193 (void**)&this->report_message_types) != TNC_RESULT_SUCCESS)
194 {
195 this->report_message_types = NULL;
196 }
197 if (bind_function(this->id, "TNC_TNCC_ReportMessageTypesLong",
198 (void**)&this->report_message_types_long) != TNC_RESULT_SUCCESS)
199 {
200 this->report_message_types_long = NULL;
201 }
202 if (bind_function(this->id, "TNC_TNCC_RequestHandshakeRetry",
203 (void**)&this->public.request_handshake_retry) != TNC_RESULT_SUCCESS)
204 {
205 this->public.request_handshake_retry = NULL;
206 }
207 if (bind_function(this->id, "TNC_TNCC_SendMessage",
208 (void**)&this->send_message) != TNC_RESULT_SUCCESS)
209 {
210 this->send_message = NULL;
211 }
212 if (bind_function(this->id, "TNC_TNCC_SendMessageLong",
213 (void**)&this->send_message_long) != TNC_RESULT_SUCCESS)
214 {
215 this->send_message_long = NULL;
216 }
217 if (bind_function(this->id, "TNC_TNCC_GetAttribute",
218 (void**)&this->get_attribute) != TNC_RESULT_SUCCESS)
219 {
220 this->get_attribute = NULL;
221 }
222 if (bind_function(this->id, "TNC_TNCC_SetAttribute",
223 (void**)&this->set_attribute) != TNC_RESULT_SUCCESS)
224 {
225 this->set_attribute = NULL;
226 }
227 if (bind_function(this->id, "TNC_TNCC_ReserveAdditionalIMCID",
228 (void**)&this->reserve_additional_id) != TNC_RESULT_SUCCESS)
229 {
230 this->reserve_additional_id = NULL;
231 }
232 DBG2(DBG_IMC, "IMC %u \"%s\" provided with bind function",
233 this->id, this->name);
234
235 if (this->report_message_types_long)
236 {
237 this->report_message_types_long(this->id, &this->vendor_id,
238 &this->subtype, 1);
239 }
240 else if (this->report_message_types &&
241 this->vendor_id <= TNC_VENDORID_ANY &&
242 this->subtype <= TNC_SUBTYPE_ANY)
243 {
244 TNC_MessageType type;
245
246 type = (this->vendor_id << 8) | this->subtype;
247 this->report_message_types(this->id, &type, 1);
248 }
249 return TNC_RESULT_SUCCESS;
250 }
251
252 /**
253 * finds a connection state based on its Connection ID
254 */
255 static imc_state_t* find_connection(private_imc_agent_t *this,
256 TNC_ConnectionID id)
257 {
258 enumerator_t *enumerator;
259 imc_state_t *state, *found = NULL;
260
261 this->connection_lock->read_lock(this->connection_lock);
262 enumerator = this->connections->create_enumerator(this->connections);
263 while (enumerator->enumerate(enumerator, &state))
264 {
265 if (id == state->get_connection_id(state))
266 {
267 found = state;
268 break;
269 }
270 }
271 enumerator->destroy(enumerator);
272 this->connection_lock->unlock(this->connection_lock);
273
274 return found;
275 }
276
277 /**
278 * delete a connection state with a given Connection ID
279 */
280 static bool delete_connection(private_imc_agent_t *this, TNC_ConnectionID id)
281 {
282 enumerator_t *enumerator;
283 imc_state_t *state;
284 bool found = FALSE;
285
286 this->connection_lock->write_lock(this->connection_lock);
287 enumerator = this->connections->create_enumerator(this->connections);
288 while (enumerator->enumerate(enumerator, &state))
289 {
290 if (id == state->get_connection_id(state))
291 {
292 found = TRUE;
293 state->destroy(state);
294 this->connections->remove_at(this->connections, enumerator);
295 break;
296 }
297 }
298 enumerator->destroy(enumerator);
299 this->connection_lock->unlock(this->connection_lock);
300
301 return found;
302 }
303
304 /**
305 * Read a boolean attribute
306 */
307 static bool get_bool_attribute(private_imc_agent_t *this, TNC_ConnectionID id,
308 TNC_AttributeID attribute_id)
309 {
310 TNC_UInt32 len;
311 char buf[4];
312
313 return this->get_attribute &&
314 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
315 TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01;
316 }
317
318 /**
319 * Read a string attribute
320 */
321 static char* get_str_attribute(private_imc_agent_t *this, TNC_ConnectionID id,
322 TNC_AttributeID attribute_id)
323 {
324 TNC_UInt32 len;
325 char buf[BUF_LEN];
326
327 if (this->get_attribute &&
328 this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) ==
329 TNC_RESULT_SUCCESS && len <= BUF_LEN)
330 {
331 return strdup(buf);
332 }
333 return NULL;
334 }
335
336 /**
337 * Read an UInt32 attribute
338 */
339 static u_int32_t get_uint_attribute(private_imc_agent_t *this, TNC_ConnectionID id,
340 TNC_AttributeID attribute_id)
341 {
342 TNC_UInt32 len;
343 char buf[4];
344
345 if (this->get_attribute &&
346 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
347 TNC_RESULT_SUCCESS && len == 4)
348 {
349 return untoh32(buf);
350 }
351 return 0;
352 }
353
354 METHOD(imc_agent_t, create_state, TNC_Result,
355 private_imc_agent_t *this, imc_state_t *state)
356 {
357 TNC_ConnectionID conn_id;
358 char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL;
359 bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE;
360 u_int32_t max_msg_len;
361
362 conn_id = state->get_connection_id(state);
363 if (find_connection(this, conn_id))
364 {
365 DBG1(DBG_IMC, "IMC %u \"%s\" already created a state for Connection ID %u",
366 this->id, this->name, conn_id);
367 state->destroy(state);
368 return TNC_RESULT_OTHER;
369 }
370
371 /* Get and display attributes from TNCC via IF-IMC */
372 has_long = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_LONG_TYPES);
373 has_excl = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_EXCLUSIVE);
374 has_soh = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_SOH);
375 tnccs_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL);
376 tnccs_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_VERSION);
377 t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL);
378 t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION);
379 max_msg_len = get_uint_attribute(this, conn_id, TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE);
380
381 state->set_flags(state, has_long, has_excl);
382 state->set_max_msg_len(state, max_msg_len);
383
384 DBG2(DBG_IMC, "IMC %u \"%s\" created a state for %s %s Connection ID %u: "
385 "%slong %sexcl %ssoh", this->id, this->name,
386 tnccs_p ? tnccs_p:"?", tnccs_v ? tnccs_v:"?", conn_id,
387 has_long ? "+":"-", has_excl ? "+":"-", has_soh ? "+":"-");
388 DBG2(DBG_IMC, " over %s %s with maximum PA-TNC message size of %u bytes",
389 t_p ? t_p:"?", t_v ? t_v :"?", max_msg_len);
390
391 free(tnccs_p);
392 free(tnccs_v);
393 free(t_p);
394 free(t_v);
395
396 this->connection_lock->write_lock(this->connection_lock);
397 this->connections->insert_last(this->connections, state);
398 this->connection_lock->unlock(this->connection_lock);
399 return TNC_RESULT_SUCCESS;
400 }
401
402 METHOD(imc_agent_t, delete_state, TNC_Result,
403 private_imc_agent_t *this, TNC_ConnectionID connection_id)
404 {
405 if (!delete_connection(this, connection_id))
406 {
407 DBG1(DBG_IMC, "IMC %u \"%s\" has no state for Connection ID %u",
408 this->id, this->name, connection_id);
409 return TNC_RESULT_FATAL;
410 }
411 DBG2(DBG_IMC, "IMC %u \"%s\" deleted the state of Connection ID %u",
412 this->id, this->name, connection_id);
413 return TNC_RESULT_SUCCESS;
414 }
415
416 METHOD(imc_agent_t, change_state, TNC_Result,
417 private_imc_agent_t *this, TNC_ConnectionID connection_id,
418 TNC_ConnectionState new_state,
419 imc_state_t **state_p)
420 {
421 imc_state_t *state;
422
423 switch (new_state)
424 {
425 case TNC_CONNECTION_STATE_HANDSHAKE:
426 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
427 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
428 case TNC_CONNECTION_STATE_ACCESS_NONE:
429 state = find_connection(this, connection_id);
430
431 if (!state)
432 {
433 DBG1(DBG_IMC, "IMC %u \"%s\" has no state for Connection ID %u",
434 this->id, this->name, connection_id);
435 return TNC_RESULT_FATAL;
436 }
437 state->change_state(state, new_state);
438 DBG2(DBG_IMC, "IMC %u \"%s\" changed state of Connection ID %u to '%N'",
439 this->id, this->name, connection_id,
440 TNC_Connection_State_names, new_state);
441 if (state_p)
442 {
443 *state_p = state;
444 }
445 break;
446 case TNC_CONNECTION_STATE_CREATE:
447 DBG1(DBG_IMC, "state '%N' should be handled by create_state()",
448 TNC_Connection_State_names, new_state);
449 return TNC_RESULT_FATAL;
450 case TNC_CONNECTION_STATE_DELETE:
451 DBG1(DBG_IMC, "state '%N' should be handled by delete_state()",
452 TNC_Connection_State_names, new_state);
453 return TNC_RESULT_FATAL;
454 default:
455 DBG1(DBG_IMC, "IMC %u \"%s\" was notified of unknown state %u "
456 "for Connection ID %u",
457 this->id, this->name, new_state, connection_id);
458 return TNC_RESULT_INVALID_PARAMETER;
459 }
460 return TNC_RESULT_SUCCESS;
461 }
462
463 METHOD(imc_agent_t, get_state, bool,
464 private_imc_agent_t *this, TNC_ConnectionID connection_id,
465 imc_state_t **state)
466 {
467 *state = find_connection(this, connection_id);
468 if (!*state)
469 {
470 DBG1(DBG_IMC, "IMC %u \"%s\" has no state for Connection ID %u",
471 this->id, this->name, connection_id);
472 return FALSE;
473 }
474 return TRUE;
475 }
476
477 METHOD(imc_agent_t, send_message, TNC_Result,
478 private_imc_agent_t *this, TNC_ConnectionID connection_id, bool excl,
479 TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id, linked_list_t *attr_list)
480 {
481 TNC_MessageType type;
482 TNC_UInt32 msg_flags;
483 TNC_Result result = TNC_RESULT_FATAL;
484 imc_state_t *state;
485 pa_tnc_attr_t *attr;
486 pa_tnc_msg_t *pa_tnc_msg;
487 chunk_t msg;
488 enumerator_t *enumerator;
489 bool attr_added;
490
491 state = find_connection(this, connection_id);
492 if (!state)
493 {
494 DBG1(DBG_IMV, "IMC %u \"%s\" has no state for Connection ID %u",
495 this->id, this->name, connection_id);
496 return TNC_RESULT_FATAL;
497 }
498
499 while (attr_list->get_count(attr_list))
500 {
501 pa_tnc_msg = pa_tnc_msg_create(state->get_max_msg_len(state));
502 attr_added = FALSE;
503
504 enumerator = attr_list->create_enumerator(attr_list);
505 while (enumerator->enumerate(enumerator, &attr))
506 {
507 if (pa_tnc_msg->add_attribute(pa_tnc_msg, attr))
508 {
509 attr_added = TRUE;
510 }
511 else
512 {
513 if (attr_added)
514 {
515 break;
516 }
517 else
518 {
519 DBG1(DBG_IMC, "PA-TNC attribute too large to send, deleted");
520 attr->destroy(attr);
521 }
522 }
523 attr_list->remove_at(attr_list, enumerator);
524 }
525 enumerator->destroy(enumerator);
526
527 /* build and send the PA-TNC message via the IF-IMC interface */
528 if (!pa_tnc_msg->build(pa_tnc_msg))
529 {
530 pa_tnc_msg->destroy(pa_tnc_msg);
531 return TNC_RESULT_FATAL;
532 }
533 msg = pa_tnc_msg->get_encoding(pa_tnc_msg);
534
535 if (state->has_long(state) && this->send_message_long)
536 {
537 if (!src_imc_id)
538 {
539 src_imc_id = this->id;
540 }
541 msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0;
542
543 result = this->send_message_long(src_imc_id, connection_id,
544 msg_flags, msg.ptr, msg.len, this->vendor_id,
545 this->subtype, dst_imv_id);
546 }
547 else if (this->send_message)
548 {
549 type = (this->vendor_id << 8) | this->subtype;
550
551 result = this->send_message(this->id, connection_id, msg.ptr,
552 msg.len, type);
553 }
554
555 pa_tnc_msg->destroy(pa_tnc_msg);
556
557 if (result != TNC_RESULT_SUCCESS)
558 {
559 break;
560 }
561 }
562 return result;
563 }
564
565 METHOD(imc_agent_t, receive_message, TNC_Result,
566 private_imc_agent_t *this, imc_state_t *state, chunk_t msg,
567 TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype,
568 TNC_UInt32 src_imv_id, TNC_UInt32 dst_imc_id, pa_tnc_msg_t **pa_tnc_msg)
569 {
570 pa_tnc_msg_t *pa_msg;
571 pa_tnc_attr_t *error_attr;
572 linked_list_t *error_attr_list;
573 enumerator_t *enumerator;
574 TNC_UInt32 src_imc_id, dst_imv_id;
575 TNC_ConnectionID connection_id;
576 TNC_Result result;
577
578 connection_id = state->get_connection_id(state);
579
580 if (state->has_long(state))
581 {
582 if (dst_imc_id != TNC_IMCID_ANY)
583 {
584 DBG2(DBG_IMC, "IMC %u \"%s\" received message for Connection ID %u "
585 "from IMV %u to IMC %u", this->id, this->name,
586 connection_id, src_imv_id, dst_imc_id);
587 }
588 else
589 {
590 DBG2(DBG_IMC, "IMC %u \"%s\" received message for Connection ID %u "
591 "from IMV %u", this->id, this->name, connection_id,
592 src_imv_id);
593 }
594 }
595 else
596 {
597 DBG2(DBG_IMC, "IMC %u \"%s\" received message for Connection ID %u",
598 this->id, this->name, connection_id);
599 }
600
601 *pa_tnc_msg = NULL;
602 pa_msg = pa_tnc_msg_create_from_data(msg);
603
604 switch (pa_msg->process(pa_msg))
605 {
606 case SUCCESS:
607 *pa_tnc_msg = pa_msg;
608 break;
609 case VERIFY_ERROR:
610 /* extract and copy by refence all error attributes */
611 error_attr_list = linked_list_create();
612
613 enumerator = pa_msg->create_error_enumerator(pa_msg);
614 while (enumerator->enumerate(enumerator, &error_attr))
615 {
616 error_attr_list->insert_last(error_attr_list,
617 error_attr->get_ref(error_attr));
618 }
619 enumerator->destroy(enumerator);
620
621 src_imc_id = (dst_imc_id == TNC_IMCID_ANY) ? this->id : dst_imc_id;
622 dst_imv_id = state->has_excl(state) ? src_imv_id : TNC_IMVID_ANY;
623
624 result = send_message(this, connection_id, state->has_excl(state),
625 src_imc_id, dst_imv_id, error_attr_list);
626
627 error_attr_list->destroy(error_attr_list);
628 pa_msg->destroy(pa_msg);
629 return result;
630 case FAILED:
631 default:
632 pa_msg->destroy(pa_msg);
633 return TNC_RESULT_FATAL;
634 }
635 return TNC_RESULT_SUCCESS;
636 }
637
638 METHOD(imc_agent_t, reserve_additional_ids, TNC_Result,
639 private_imc_agent_t *this, int count)
640 {
641 TNC_Result result;
642 TNC_UInt32 id;
643 void *pointer;
644
645 if (!this->reserve_additional_id)
646 {
647 DBG1(DBG_IMC, "IMC %u \"%s\" did not detect the capability to reserve "
648 "additional IMC IDs from the TNCC", this->id, this->name);
649 return TNC_RESULT_ILLEGAL_OPERATION;
650 }
651 while (count > 0)
652 {
653 result = this->reserve_additional_id(this->id, &id);
654 if (result != TNC_RESULT_SUCCESS)
655 {
656 DBG1(DBG_IMC, "IMC %u \"%s\" failed to reserve %d additional IMC IDs",
657 this->id, this->name, count);
658 return result;
659 }
660 count--;
661
662 /* store the scalar value in the pointer */
663 pointer = (void*)id;
664 this->additional_ids->insert_last(this->additional_ids, pointer);
665 DBG2(DBG_IMC, "IMC %u \"%s\" reserved additional ID %u",
666 this->id, this->name, id);
667 }
668 return TNC_RESULT_SUCCESS;
669 }
670
671 METHOD(imc_agent_t, count_additional_ids, int,
672 private_imc_agent_t *this)
673 {
674 return this->additional_ids->get_count(this->additional_ids);
675 }
676
677 METHOD(imc_agent_t, create_id_enumerator, enumerator_t*,
678 private_imc_agent_t *this)
679 {
680 return this->additional_ids->create_enumerator(this->additional_ids);
681 }
682
683 METHOD(imc_agent_t, destroy, void,
684 private_imc_agent_t *this)
685 {
686 DBG1(DBG_IMC, "IMC %u \"%s\" terminated", this->id, this->name);
687 this->additional_ids->destroy(this->additional_ids);
688 this->connections->destroy_function(this->connections, free);
689 this->connection_lock->destroy(this->connection_lock);
690 free(this);
691
692 /* decrease the reference count or terminate */
693 libimcv_deinit();
694 }
695
696 /**
697 * Described in header.
698 */
699 imc_agent_t *imc_agent_create(const char *name,
700 pen_t vendor_id, u_int32_t subtype,
701 TNC_IMCID id, TNC_Version *actual_version)
702 {
703 private_imc_agent_t *this;
704
705 /* initialize or increase the reference count */
706 if (!libimcv_init())
707 {
708 return NULL;
709 }
710
711 INIT(this,
712 .public = {
713 .bind_functions = _bind_functions,
714 .create_state = _create_state,
715 .delete_state = _delete_state,
716 .change_state = _change_state,
717 .get_state = _get_state,
718 .send_message = _send_message,
719 .receive_message = _receive_message,
720 .reserve_additional_ids = _reserve_additional_ids,
721 .count_additional_ids = _count_additional_ids,
722 .create_id_enumerator = _create_id_enumerator,
723 .destroy = _destroy,
724 },
725 .name = name,
726 .vendor_id = vendor_id,
727 .subtype = subtype,
728 .id = id,
729 .additional_ids = linked_list_create(),
730 .connections = linked_list_create(),
731 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
732 );
733
734 *actual_version = TNC_IFIMC_VERSION_1;
735 DBG1(DBG_IMC, "IMC %u \"%s\" initialized", this->id, this->name);
736
737 return &this->public;
738 }
739