65438fc97a094f773d89c5ec38ccc1ec4e3d2782
[strongswan.git] / src / libimcv / imv / imv_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 "imv_agent.h"
18 #include "ietf/ietf_attr_assess_result.h"
19
20 #include <tncif_names.h>
21
22 #include <utils/debug.h>
23 #include <threading/rwlock.h>
24
25 typedef struct private_imv_agent_t private_imv_agent_t;
26
27 /**
28 * Private data of an imv_agent_t object.
29 */
30 struct private_imv_agent_t {
31
32 /**
33 * Public members of imv_agent_t
34 */
35 imv_agent_t public;
36
37 /**
38 * name of IMV
39 */
40 const char *name;
41
42 /**
43 * message types registered by IMV
44 */
45 pen_type_t *supported_types;
46
47 /**
48 * number of message types registered by IMV
49 */
50 u_int32_t type_count;
51
52 /**
53 * ID of IMV as assigned by TNCS
54 */
55 TNC_IMVID id;
56
57 /**
58 * List of additional IMV IDs assigned by TNCS
59 */
60 linked_list_t *additional_ids;
61
62 /**
63 * list of TNCS connection entries
64 */
65 linked_list_t *connections;
66
67 /**
68 * rwlock to lock TNCS connection entries
69 */
70 rwlock_t *connection_lock;
71
72 /**
73 * Inform a TNCS about the set of message types the IMV is able to receive
74 *
75 * @param imv_id IMV ID assigned by TNCS
76 * @param supported_types list of supported message types
77 * @param type_count number of list elements
78 * @return TNC result code
79 */
80 TNC_Result (*report_message_types)(TNC_IMVID imv_id,
81 TNC_MessageTypeList supported_types,
82 TNC_UInt32 type_count);
83
84 /**
85 * Inform a TNCS about the set of message types the IMV is able to receive
86 *
87 * @param imv_id IMV ID assigned by TNCS
88 * @param supported_vids list of supported message vendor IDs
89 * @param supported_subtypes list of supported message subtypes
90 * @param type_count number of list elements
91 * @return TNC result code
92 */
93 TNC_Result (*report_message_types_long)(TNC_IMVID imv_id,
94 TNC_VendorIDList supported_vids,
95 TNC_MessageSubtypeList supported_subtypes,
96 TNC_UInt32 type_count);
97
98 /**
99 * Deliver IMV Action Recommendation and IMV Evaluation Results to the TNCS
100 *
101 * @param imv_id IMV ID assigned by TNCS
102 # @param connection_id network connection ID assigned by TNCS
103 * @param rec IMV action recommendation
104 * @param eval IMV evaluation result
105 * @return TNC result code
106 */
107 TNC_Result (*provide_recommendation)(TNC_IMVID imv_id,
108 TNC_ConnectionID connection_id,
109 TNC_IMV_Action_Recommendation rec,
110 TNC_IMV_Evaluation_Result eval);
111
112 /**
113 * Get the value of an attribute associated with a connection
114 * or with the TNCS as a whole.
115 *
116 * @param imv_id IMV ID assigned by TNCS
117 * @param connection_id network connection ID assigned by TNCS
118 * @param attribute_id attribute ID
119 * @param buffer_len length of buffer in bytes
120 * @param buffer buffer
121 * @param out_value_len size in bytes of attribute stored in buffer
122 * @return TNC result code
123 */
124 TNC_Result (*get_attribute)(TNC_IMVID imv_id,
125 TNC_ConnectionID connection_id,
126 TNC_AttributeID attribute_id,
127 TNC_UInt32 buffer_len,
128 TNC_BufferReference buffer,
129 TNC_UInt32 *out_value_len);
130
131 /**
132 * Set the value of an attribute associated with a connection
133 * or with the TNCS as a whole.
134 *
135 * @param imv_id IMV ID assigned by TNCS
136 * @param connection_id network connection ID assigned by TNCS
137 * @param attribute_id attribute ID
138 * @param buffer_len length of buffer in bytes
139 * @param buffer buffer
140 * @return TNC result code
141 */
142 TNC_Result (*set_attribute)(TNC_IMVID imv_id,
143 TNC_ConnectionID connection_id,
144 TNC_AttributeID attribute_id,
145 TNC_UInt32 buffer_len,
146 TNC_BufferReference buffer);
147
148 /**
149 * Reserve an additional IMV ID
150 *
151 * @param imv_id primary IMV ID assigned by TNCS
152 * @param out_imv_id additional IMV ID assigned by TNCS
153 * @return TNC result code
154 */
155 TNC_Result (*reserve_additional_id)(TNC_IMVID imv_id,
156 TNC_UInt32 *out_imv_id);
157
158 };
159
160 METHOD(imv_agent_t, bind_functions, TNC_Result,
161 private_imv_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function)
162 {
163 if (!bind_function)
164 {
165 DBG1(DBG_IMV, "TNC server failed to provide bind function");
166 return TNC_RESULT_INVALID_PARAMETER;
167 }
168 if (bind_function(this->id, "TNC_TNCS_ReportMessageTypes",
169 (void**)&this->report_message_types) != TNC_RESULT_SUCCESS)
170 {
171 this->report_message_types = NULL;
172 }
173 if (bind_function(this->id, "TNC_TNCS_ReportMessageTypesLong",
174 (void**)&this->report_message_types_long) != TNC_RESULT_SUCCESS)
175 {
176 this->report_message_types_long = NULL;
177 }
178 if (bind_function(this->id, "TNC_TNCS_RequestHandshakeRetry",
179 (void**)&this->public.request_handshake_retry) != TNC_RESULT_SUCCESS)
180 {
181 this->public.request_handshake_retry = NULL;
182 }
183 if (bind_function(this->id, "TNC_TNCS_SendMessage",
184 (void**)&this->public.send_message) != TNC_RESULT_SUCCESS)
185 {
186 this->public.send_message = NULL;
187 }
188 if (bind_function(this->id, "TNC_TNCS_SendMessageLong",
189 (void**)&this->public.send_message_long) != TNC_RESULT_SUCCESS)
190 {
191 this->public.send_message_long = NULL;
192 }
193 if (bind_function(this->id, "TNC_TNCS_ProvideRecommendation",
194 (void**)&this->provide_recommendation) != TNC_RESULT_SUCCESS)
195 {
196 this->provide_recommendation = NULL;
197 }
198 if (bind_function(this->id, "TNC_TNCS_GetAttribute",
199 (void**)&this->get_attribute) != TNC_RESULT_SUCCESS)
200 {
201 this->get_attribute = NULL;
202 }
203 if (bind_function(this->id, "TNC_TNCS_SetAttribute",
204 (void**)&this->set_attribute) != TNC_RESULT_SUCCESS)
205 {
206 this->set_attribute = NULL;
207 }
208 if (bind_function(this->id, "TNC_TNCC_ReserveAdditionalIMVID",
209 (void**)&this->reserve_additional_id) != TNC_RESULT_SUCCESS)
210 {
211 this->reserve_additional_id = NULL;
212 }
213 DBG2(DBG_IMV, "IMV %u \"%s\" provided with bind function",
214 this->id, this->name);
215
216 if (this->report_message_types_long)
217 {
218 TNC_VendorIDList vendor_id_list;
219 TNC_MessageSubtypeList subtype_list;
220 int i;
221
222 vendor_id_list = malloc(this->type_count * sizeof(TNC_UInt32));
223 subtype_list = malloc(this->type_count * sizeof(TNC_UInt32));
224
225 for (i = 0; i < this->type_count; i++)
226 {
227 vendor_id_list[i] = this->supported_types[i].vendor_id;
228 subtype_list[i] = this->supported_types[i].type;
229 }
230 this->report_message_types_long(this->id, vendor_id_list, subtype_list,
231 this->type_count);
232 free(vendor_id_list);
233 free(subtype_list);
234 }
235 else if (this->report_message_types)
236 {
237 TNC_MessageTypeList type_list;
238 int i;
239
240 type_list = malloc(this->type_count * sizeof(TNC_UInt32));
241
242 for (i = 0; i < this->type_count; i++)
243 {
244 type_list[i] = (this->supported_types[i].vendor_id << 8) |
245 (this->supported_types[i].type & 0xff);
246 }
247 this->report_message_types(this->id, type_list, this->type_count);
248 free(type_list);
249 }
250 return TNC_RESULT_SUCCESS;
251 }
252
253 /**
254 * finds a connection state based on its Connection ID
255 */
256 static imv_state_t* find_connection(private_imv_agent_t *this,
257 TNC_ConnectionID id)
258 {
259 enumerator_t *enumerator;
260 imv_state_t *state, *found = NULL;
261
262 this->connection_lock->read_lock(this->connection_lock);
263 enumerator = this->connections->create_enumerator(this->connections);
264 while (enumerator->enumerate(enumerator, &state))
265 {
266 if (id == state->get_connection_id(state))
267 {
268 found = state;
269 break;
270 }
271 }
272 enumerator->destroy(enumerator);
273 this->connection_lock->unlock(this->connection_lock);
274
275 return found;
276 }
277
278 /**
279 * delete a connection state with a given Connection ID
280 */
281 static bool delete_connection(private_imv_agent_t *this, TNC_ConnectionID id)
282 {
283 enumerator_t *enumerator;
284 imv_state_t *state;
285 bool found = FALSE;
286
287 this->connection_lock->write_lock(this->connection_lock);
288 enumerator = this->connections->create_enumerator(this->connections);
289 while (enumerator->enumerate(enumerator, &state))
290 {
291 if (id == state->get_connection_id(state))
292 {
293 found = TRUE;
294 state->destroy(state);
295 this->connections->remove_at(this->connections, enumerator);
296 break;
297 }
298 }
299 enumerator->destroy(enumerator);
300 this->connection_lock->unlock(this->connection_lock);
301
302 return found;
303 }
304
305 /**
306 * Read a boolean attribute
307 */
308 static bool get_bool_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
309 TNC_AttributeID attribute_id)
310 {
311 TNC_UInt32 len;
312 char buf[4];
313
314 return this->get_attribute &&
315 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
316 TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01;
317 }
318
319 /**
320 * Read a string attribute
321 */
322 static char* get_str_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
323 TNC_AttributeID attribute_id)
324 {
325 TNC_UInt32 len;
326 char buf[BUF_LEN];
327
328 if (this->get_attribute &&
329 this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) ==
330 TNC_RESULT_SUCCESS && len <= BUF_LEN)
331 {
332 return strdup(buf);
333 }
334 return NULL;
335 }
336
337 /**
338 * Read an UInt32 attribute
339 */
340 static u_int32_t get_uint_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
341 TNC_AttributeID attribute_id)
342 {
343 TNC_UInt32 len;
344 char buf[4];
345
346 if (this->get_attribute &&
347 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
348 TNC_RESULT_SUCCESS && len == 4)
349 {
350 return untoh32(buf);
351 }
352 return 0;
353 }
354
355 METHOD(imv_agent_t, create_state, TNC_Result,
356 private_imv_agent_t *this, imv_state_t *state)
357 {
358 TNC_ConnectionID conn_id;
359 char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL;
360 bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE;
361 u_int32_t max_msg_len;
362
363 conn_id = state->get_connection_id(state);
364 if (find_connection(this, conn_id))
365 {
366 DBG1(DBG_IMV, "IMV %u \"%s\" already created a state for Connection ID %u",
367 this->id, this->name, conn_id);
368 state->destroy(state);
369 return TNC_RESULT_OTHER;
370 }
371
372 /* Get and display attributes from TNCS via IF-IMV */
373 has_long = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_LONG_TYPES);
374 has_excl = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_EXCLUSIVE);
375 has_soh = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_SOH);
376 tnccs_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL);
377 tnccs_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_VERSION);
378 t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL);
379 t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION);
380 max_msg_len = get_uint_attribute(this, conn_id, TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE);
381
382 state->set_flags(state, has_long, has_excl);
383 state->set_max_msg_len(state, max_msg_len);
384
385 DBG2(DBG_IMV, "IMV %u \"%s\" created a state for %s %s Connection ID %u: "
386 "%slong %sexcl %ssoh", this->id, this->name,
387 tnccs_p ? tnccs_p:"?", tnccs_v ? tnccs_v:"?", conn_id,
388 has_long ? "+":"-", has_excl ? "+":"-", has_soh ? "+":"-");
389 DBG2(DBG_IMV, " over %s %s with maximum PA-TNC message size of %u bytes",
390 t_p ? t_p:"?", t_v ? t_v :"?", max_msg_len);
391
392 free(tnccs_p);
393 free(tnccs_v);
394 free(t_p);
395 free(t_v);
396
397 this->connection_lock->write_lock(this->connection_lock);
398 this->connections->insert_last(this->connections, state);
399 this->connection_lock->unlock(this->connection_lock);
400 return TNC_RESULT_SUCCESS;
401 }
402
403 METHOD(imv_agent_t, delete_state, TNC_Result,
404 private_imv_agent_t *this, TNC_ConnectionID connection_id)
405 {
406 if (!delete_connection(this, connection_id))
407 {
408 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
409 this->id, this->name, connection_id);
410 return TNC_RESULT_FATAL;
411 }
412 DBG2(DBG_IMV, "IMV %u \"%s\" deleted the state of Connection ID %u",
413 this->id, this->name, connection_id);
414 return TNC_RESULT_SUCCESS;
415 }
416
417 METHOD(imv_agent_t, change_state, TNC_Result,
418 private_imv_agent_t *this, TNC_ConnectionID connection_id,
419 TNC_ConnectionState new_state,
420 imv_state_t **state_p)
421 {
422 imv_state_t *state;
423
424 switch (new_state)
425 {
426 case TNC_CONNECTION_STATE_HANDSHAKE:
427 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
428 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
429 case TNC_CONNECTION_STATE_ACCESS_NONE:
430 state = find_connection(this, connection_id);
431 if (!state)
432 {
433 DBG1(DBG_IMV, "IMV %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_IMV, "IMV %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_IMV, "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_IMV, "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_IMV, "IMV %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(imv_agent_t, get_state, bool,
464 private_imv_agent_t *this, TNC_ConnectionID connection_id,
465 imv_state_t **state)
466 {
467 *state = find_connection(this, connection_id);
468 if (!*state)
469 {
470 DBG1(DBG_IMV, "IMV %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(imv_agent_t, get_name, const char*,
478 private_imv_agent_t *this)
479 {
480 return this->name;
481 }
482
483 METHOD(imv_agent_t, get_id, TNC_IMVID,
484 private_imv_agent_t *this)
485 {
486 return this->id;
487 }
488
489 METHOD(imv_agent_t, reserve_additional_ids, TNC_Result,
490 private_imv_agent_t *this, int count)
491 {
492 TNC_Result result;
493 TNC_UInt32 id;
494 void *pointer;
495
496 if (!this->reserve_additional_id)
497 {
498 DBG1(DBG_IMV, "IMV %u \"%s\" did not detect the capability to reserve "
499 "additional IMV IDs from the TNCS", this->id, this->name);
500 return TNC_RESULT_ILLEGAL_OPERATION;
501 }
502 while (count > 0)
503 {
504 result = this->reserve_additional_id(this->id, &id);
505 if (result != TNC_RESULT_SUCCESS)
506 {
507 DBG1(DBG_IMV, "IMV %u \"%s\" failed to reserve %d additional IMV IDs",
508 this->id, this->name, count);
509 return result;
510 }
511 count--;
512
513 /* store the scalar value in the pointer */
514 pointer = (void*)id;
515 this->additional_ids->insert_last(this->additional_ids, pointer);
516 DBG2(DBG_IMV, "IMV %u \"%s\" reserved additional ID %u",
517 this->id, this->name, id);
518 }
519 return TNC_RESULT_SUCCESS;
520 }
521
522 METHOD(imv_agent_t, count_additional_ids, int,
523 private_imv_agent_t *this)
524 {
525 return this->additional_ids->get_count(this->additional_ids);
526 }
527
528 METHOD(imv_agent_t, create_id_enumerator, enumerator_t*,
529 private_imv_agent_t *this)
530 {
531 return this->additional_ids->create_enumerator(this->additional_ids);
532 }
533
534 typedef struct {
535 /**
536 * implements enumerator_t
537 */
538 enumerator_t public;
539
540 /**
541 * language length
542 */
543 TNC_UInt32 lang_len;
544
545 /**
546 * language buffer
547 */
548 char lang_buf[BUF_LEN];
549
550 /**
551 * position pointer into language buffer
552 */
553 char *lang_pos;
554
555 } language_enumerator_t;
556
557 /**
558 * Implementation of language_enumerator.destroy.
559 */
560 static void language_enumerator_destroy(language_enumerator_t *this)
561 {
562 free(this);
563 }
564
565 /**
566 * Implementation of language_enumerator.enumerate
567 */
568 static bool language_enumerator_enumerate(language_enumerator_t *this, ...)
569 {
570 char *pos, *cur_lang, **lang;
571 TNC_UInt32 len;
572 va_list args;
573
574 if (!this->lang_len)
575 {
576 return FALSE;
577 }
578 cur_lang = this->lang_pos;
579 pos = strchr(this->lang_pos, ',');
580 if (pos)
581 {
582 len = pos - this->lang_pos;
583 this->lang_pos += len + 1,
584 this->lang_len -= len + 1;
585 }
586 else
587 {
588 len = this->lang_len;
589 pos = this->lang_pos + len;
590 this->lang_pos = NULL;
591 this->lang_len = 0;
592 }
593
594 /* remove preceding whitespace */
595 while (*cur_lang == ' ' && len--)
596 {
597 cur_lang++;
598 }
599
600 /* remove trailing whitespace */
601 while (len && *(--pos) == ' ')
602 {
603 len--;
604 }
605 cur_lang[len] = '\0';
606
607 va_start(args, this);
608 lang = va_arg(args, char**);
609 *lang = cur_lang;
610 va_end(args);
611
612 return TRUE;
613 }
614
615 METHOD(imv_agent_t, create_language_enumerator, enumerator_t*,
616 private_imv_agent_t *this, imv_state_t *state)
617 {
618 language_enumerator_t *e;
619
620 /* Create a language enumerator instance */
621 e = malloc_thing(language_enumerator_t);
622 e->public.enumerate = (void*)language_enumerator_enumerate;
623 e->public.destroy = (void*)language_enumerator_destroy;
624
625 if (!this->get_attribute ||
626 !this->get_attribute(this->id, state->get_connection_id(state),
627 TNC_ATTRIBUTEID_PREFERRED_LANGUAGE, BUF_LEN,
628 e->lang_buf, &e->lang_len) == TNC_RESULT_SUCCESS ||
629 e->lang_len >= BUF_LEN)
630 {
631 e->lang_len = 0;
632 }
633 e->lang_buf[e->lang_len] = '\0';
634 e->lang_pos = e->lang_buf;
635
636 return (enumerator_t*)e;
637 }
638
639 METHOD(imv_agent_t, provide_recommendation, TNC_Result,
640 private_imv_agent_t *this, imv_state_t *state)
641 {
642 TNC_IMV_Action_Recommendation rec;
643 TNC_IMV_Evaluation_Result eval;
644 TNC_ConnectionID connection_id;
645 char *reason_string, *reason_lang;
646 enumerator_t *e;
647
648 state->get_recommendation(state, &rec, &eval);
649 connection_id = state->get_connection_id(state);
650
651 /* send a reason string if action recommendation is not allow */
652 if (rec != TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
653 {
654 /* find a reason string for the preferred language and set it */
655 if (this->set_attribute)
656 {
657 e = create_language_enumerator(this, state);
658 if (state->get_reason_string(state, e, &reason_string, &reason_lang))
659 {
660 this->set_attribute(this->id, connection_id,
661 TNC_ATTRIBUTEID_REASON_STRING,
662 strlen(reason_string), reason_string);
663 this->set_attribute(this->id, connection_id,
664 TNC_ATTRIBUTEID_REASON_LANGUAGE,
665 strlen(reason_lang), reason_lang);
666 }
667 e->destroy(e);
668 }
669 }
670 return this->provide_recommendation(this->id, connection_id, rec, eval);
671 }
672
673 METHOD(imv_agent_t, destroy, void,
674 private_imv_agent_t *this)
675 {
676 DBG1(DBG_IMV, "IMV %u \"%s\" terminated", this->id, this->name);
677 this->additional_ids->destroy(this->additional_ids);
678 this->connections->destroy_offset(this->connections,
679 offsetof(imv_state_t, destroy));
680 this->connection_lock->destroy(this->connection_lock);
681 free(this);
682
683 /* decrease the reference count or terminate */
684 libimcv_deinit();
685 }
686
687 /**
688 * Described in header.
689 */
690 imv_agent_t *imv_agent_create(const char *name,
691 pen_type_t *supported_types, u_int32_t type_count,
692 TNC_IMVID id, TNC_Version *actual_version)
693 {
694 private_imv_agent_t *this;
695
696 /* initialize or increase the reference count */
697 if (!libimcv_init())
698 {
699 return NULL;
700 }
701
702 INIT(this,
703 .public = {
704 .bind_functions = _bind_functions,
705 .create_state = _create_state,
706 .delete_state = _delete_state,
707 .change_state = _change_state,
708 .get_state = _get_state,
709 .get_name = _get_name,
710 .get_id = _get_id,
711 .reserve_additional_ids = _reserve_additional_ids,
712 .count_additional_ids = _count_additional_ids,
713 .create_id_enumerator = _create_id_enumerator,
714 .create_language_enumerator = _create_language_enumerator,
715 .provide_recommendation = _provide_recommendation,
716 .destroy = _destroy,
717 },
718 .name = name,
719 .supported_types = supported_types,
720 .type_count = type_count,
721 .id = id,
722 .additional_ids = linked_list_create(),
723 .connections = linked_list_create(),
724 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
725 );
726
727 *actual_version = TNC_IFIMV_VERSION_1;
728 DBG1(DBG_IMV, "IMV %u \"%s\" initialized", this->id, this->name);
729
730 return &this->public;
731 }
732
733