moved tnc_imv plugin to libtnccs thanks to recommendation callback function
[strongswan.git] / src / libtnccs / plugins / tnc_imv / tnc_imv_manager.c
1 /*
2 * Copyright (C) 2010-2013 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 "tnc_imv_manager.h"
17 #include "tnc_imv.h"
18 #include "tnc_imv_recommendations.h"
19
20 #include <tncifimv.h>
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/mman.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <fcntl.h>
28
29 #include <utils/debug.h>
30 #include <threading/rwlock.h>
31 #include <threading/mutex.h>
32 #include <collections/linked_list.h>
33
34 typedef struct private_tnc_imv_manager_t private_tnc_imv_manager_t;
35
36 /**
37 * Private data of an imv_manager_t object.
38 */
39 struct private_tnc_imv_manager_t {
40
41 /**
42 * Public members of imv_manager_t.
43 */
44 imv_manager_t public;
45
46 /**
47 * Linked list of IMVs
48 */
49 linked_list_t *imvs;
50
51 /**
52 * Lock for IMV list
53 */
54 rwlock_t *lock;
55
56 /**
57 * Next IMV ID to be assigned
58 */
59 TNC_IMVID next_imv_id;
60
61 /**
62 * Mutex to access next IMV ID
63 */
64 mutex_t *id_mutex;
65
66 /**
67 * Policy defining how to derive final recommendation from individual ones
68 */
69 recommendation_policy_t policy;
70 };
71
72 METHOD(imv_manager_t, add, bool,
73 private_tnc_imv_manager_t *this, imv_t *imv)
74 {
75 TNC_Version version;
76 TNC_IMVID imv_id;
77
78 this->id_mutex->lock(this->id_mutex);
79 imv_id = this->next_imv_id++;
80 this->id_mutex->unlock(this->id_mutex);
81
82 imv->set_id(imv, imv_id);
83 if (imv->initialize(imv_id, TNC_IFIMV_VERSION_1,
84 TNC_IFIMV_VERSION_1, &version) != TNC_RESULT_SUCCESS)
85 {
86 DBG1(DBG_TNC, "IMV \"%s\" failed to initialize", imv->get_name(imv));
87 return FALSE;
88 }
89 this->lock->write_lock(this->lock);
90 this->imvs->insert_last(this->imvs, imv);
91 this->lock->unlock(this->lock);
92
93 if (imv->provide_bind_function(imv->get_id(imv),
94 TNC_TNCS_BindFunction) != TNC_RESULT_SUCCESS)
95 {
96 if (imv->terminate)
97 {
98 imv->terminate(imv->get_id(imv));
99 }
100 DBG1(DBG_TNC, "IMV \"%s\" failed to obtain bind function",
101 imv->get_name(imv));
102 this->lock->write_lock(this->lock);
103 this->imvs->remove_last(this->imvs, (void**)&imv);
104 this->lock->unlock(this->lock);
105 return FALSE;
106 }
107 return TRUE;
108 }
109
110 METHOD(imv_manager_t, remove_, imv_t*,
111 private_tnc_imv_manager_t *this, TNC_IMVID id)
112 {
113 enumerator_t *enumerator;
114 imv_t *imv, *removed_imv = NULL;
115
116 this->lock->write_lock(this->lock);
117 enumerator = this->imvs->create_enumerator(this->imvs);
118 while (enumerator->enumerate(enumerator, &imv))
119 {
120 if (id == imv->get_id(imv))
121 {
122 this->imvs->remove_at(this->imvs, enumerator);
123 removed_imv = imv;
124 break;
125 }
126 }
127 enumerator->destroy(enumerator);
128 this->lock->unlock(this->lock);
129
130 return removed_imv;
131 }
132
133 METHOD(imv_manager_t, load, bool,
134 private_tnc_imv_manager_t *this, char *name, char *path)
135 {
136 imv_t *imv;
137
138 imv = tnc_imv_create(name, path);
139 if (!imv)
140 {
141 return FALSE;
142 }
143 if (!add(this, imv))
144 {
145 imv->destroy(imv);
146 return FALSE;
147 }
148 DBG1(DBG_TNC, "IMV %u \"%s\" loaded from '%s'", imv->get_id(imv), name, path);
149 return TRUE;
150 }
151
152 METHOD(imv_manager_t, load_from_functions, bool,
153 private_tnc_imv_manager_t *this, char *name,
154 TNC_IMV_InitializePointer initialize,
155 TNC_IMV_NotifyConnectionChangePointer notify_connection_change,
156 TNC_IMV_ReceiveMessagePointer receive_message,
157 TNC_IMV_ReceiveMessageLongPointer receive_message_long,
158 TNC_IMV_SolicitRecommendationPointer solicit_recommendation,
159 TNC_IMV_BatchEndingPointer batch_ending,
160 TNC_IMV_TerminatePointer terminate,
161 TNC_IMV_ProvideBindFunctionPointer provide_bind_function)
162 {
163 imv_t *imv;
164
165 imv = tnc_imv_create_from_functions(name,
166 initialize,notify_connection_change,
167 receive_message, receive_message_long,
168 solicit_recommendation, batch_ending,
169 terminate, provide_bind_function);
170 if (!imv)
171 {
172 return FALSE;
173 }
174 if (!add(this, imv))
175 {
176 imv->destroy(imv);
177 return FALSE;
178 }
179 DBG1(DBG_TNC, "IMV %u \"%s\" loaded", imv->get_id(imv), name);
180 return TRUE;
181 }
182
183 METHOD(imv_manager_t, is_registered, bool,
184 private_tnc_imv_manager_t *this, TNC_IMVID id)
185 {
186 enumerator_t *enumerator;
187 imv_t *imv;
188 bool found = FALSE;
189
190 this->lock->read_lock(this->lock);
191 enumerator = this->imvs->create_enumerator(this->imvs);
192 while (enumerator->enumerate(enumerator, &imv))
193 {
194 if (imv->has_id(imv, id))
195 {
196 found = TRUE;
197 break;
198 }
199 }
200 enumerator->destroy(enumerator);
201 this->lock->unlock(this->lock);
202
203 return found;
204 }
205
206 METHOD(imv_manager_t, reserve_id, bool,
207 private_tnc_imv_manager_t *this, TNC_IMVID id, TNC_UInt32 *new_id)
208 {
209 enumerator_t *enumerator;
210 imv_t *imv;
211 bool found = FALSE;
212
213 this->lock->read_lock(this->lock);
214 enumerator = this->imvs->create_enumerator(this->imvs);
215 while (enumerator->enumerate(enumerator, &imv))
216 {
217 if (id == imv->get_id(imv))
218 {
219 found = TRUE;
220 this->id_mutex->lock(this->id_mutex);
221 *new_id = this->next_imv_id++;
222 this->id_mutex->unlock(this->id_mutex);
223 imv->add_id(imv, *new_id);
224 DBG2(DBG_TNC, "additional ID %u reserved for IMV with primary ID %u",
225 *new_id, id);
226 break;
227 }
228 }
229 enumerator->destroy(enumerator);
230 this->lock->unlock(this->lock);
231
232 return found;
233 }
234
235 METHOD(imv_manager_t, get_recommendation_policy, recommendation_policy_t,
236 private_tnc_imv_manager_t *this)
237 {
238 return this->policy;
239 }
240
241 METHOD(imv_manager_t, create_recommendations, recommendations_t*,
242 private_tnc_imv_manager_t *this)
243 {
244 return tnc_imv_recommendations_create(this->imvs);
245 }
246
247
248 METHOD(imv_manager_t, notify_connection_change, void,
249 private_tnc_imv_manager_t *this, TNC_ConnectionID id,
250 TNC_ConnectionState state)
251 {
252 enumerator_t *enumerator;
253 imv_t *imv;
254
255 this->lock->read_lock(this->lock);
256 enumerator = this->imvs->create_enumerator(this->imvs);
257 while (enumerator->enumerate(enumerator, &imv))
258 {
259 if (imv->notify_connection_change)
260 {
261 imv->notify_connection_change(imv->get_id(imv), id, state);
262 }
263 }
264 enumerator->destroy(enumerator);
265 this->lock->unlock(this->lock);
266 }
267
268 METHOD(imv_manager_t, set_message_types, TNC_Result,
269 private_tnc_imv_manager_t *this, TNC_IMVID id,
270 TNC_MessageTypeList supported_types,
271 TNC_UInt32 type_count)
272 {
273 enumerator_t *enumerator;
274 imv_t *imv;
275 TNC_Result result = TNC_RESULT_FATAL;
276
277 this->lock->read_lock(this->lock);
278 enumerator = this->imvs->create_enumerator(this->imvs);
279 while (enumerator->enumerate(enumerator, &imv))
280 {
281 if (id == imv->get_id(imv))
282 {
283 imv->set_message_types(imv, supported_types, type_count);
284 result = TNC_RESULT_SUCCESS;
285 break;
286 }
287 }
288 enumerator->destroy(enumerator);
289 this->lock->unlock(this->lock);
290 return result;
291 }
292
293 METHOD(imv_manager_t, set_message_types_long, TNC_Result,
294 private_tnc_imv_manager_t *this, TNC_IMVID id,
295 TNC_VendorIDList supported_vids,
296 TNC_MessageSubtypeList supported_subtypes,
297 TNC_UInt32 type_count)
298 {
299 enumerator_t *enumerator;
300 imv_t *imv;
301 TNC_Result result = TNC_RESULT_FATAL;
302
303 this->lock->read_lock(this->lock);
304 enumerator = this->imvs->create_enumerator(this->imvs);
305 while (enumerator->enumerate(enumerator, &imv))
306 {
307 if (id == imv->get_id(imv))
308 {
309 imv->set_message_types_long(imv, supported_vids, supported_subtypes,
310 type_count);
311 result = TNC_RESULT_SUCCESS;
312 break;
313 }
314 }
315 enumerator->destroy(enumerator);
316 this->lock->unlock(this->lock);
317 return result;
318 }
319
320 METHOD(imv_manager_t, solicit_recommendation, void,
321 private_tnc_imv_manager_t *this, TNC_ConnectionID id)
322 {
323 enumerator_t *enumerator;
324 imv_t *imv;
325
326 this->lock->read_lock(this->lock);
327 enumerator = this->imvs->create_enumerator(this->imvs);
328 while (enumerator->enumerate(enumerator, &imv))
329 {
330 imv->solicit_recommendation(imv->get_id(imv), id);
331 }
332 enumerator->destroy(enumerator);
333 this->lock->unlock(this->lock);
334 }
335
336 METHOD(imv_manager_t, receive_message, void,
337 private_tnc_imv_manager_t *this, TNC_ConnectionID connection_id,
338 bool excl,
339 TNC_BufferReference msg,
340 TNC_UInt32 msg_len,
341 TNC_VendorID msg_vid,
342 TNC_MessageSubtype msg_subtype,
343 TNC_UInt32 src_imc_id,
344 TNC_UInt32 dst_imv_id)
345 {
346 bool type_supported = FALSE;
347 TNC_MessageType msg_type;
348 TNC_UInt32 msg_flags;
349 enumerator_t *enumerator;
350 imv_t *imv;
351
352 msg_type = (msg_vid << 8) | msg_subtype;
353
354 this->lock->read_lock(this->lock);
355 enumerator = this->imvs->create_enumerator(this->imvs);
356 while (enumerator->enumerate(enumerator, &imv))
357 {
358 if (imv->type_supported(imv, msg_vid, msg_subtype) &&
359 (!excl || (excl && imv->has_id(imv, dst_imv_id))))
360 {
361 if (imv->receive_message_long && src_imc_id)
362 {
363 type_supported = TRUE;
364 msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0;
365 imv->receive_message_long(imv->get_id(imv), connection_id,
366 msg_flags, msg, msg_len, msg_vid, msg_subtype,
367 src_imc_id, dst_imv_id);
368
369 }
370 else if (imv->receive_message && msg_vid <= TNC_VENDORID_ANY &&
371 msg_subtype <= TNC_SUBTYPE_ANY)
372 {
373 type_supported = TRUE;
374 msg_type = (msg_vid << 8) | msg_subtype;
375 imv->receive_message(imv->get_id(imv), connection_id,
376 msg, msg_len, msg_type);
377 }
378 }
379 }
380 enumerator->destroy(enumerator);
381 this->lock->unlock(this->lock);
382
383 if (!type_supported)
384 {
385 DBG2(DBG_TNC, "message type 0x%06x/0x%08x not supported by any IMV",
386 msg_vid, msg_subtype);
387 }
388 }
389
390 METHOD(imv_manager_t, batch_ending, void,
391 private_tnc_imv_manager_t *this, TNC_ConnectionID id)
392 {
393 enumerator_t *enumerator;
394 imv_t *imv;
395
396 this->lock->read_lock(this->lock);
397 enumerator = this->imvs->create_enumerator(this->imvs);
398 while (enumerator->enumerate(enumerator, &imv))
399 {
400 if (imv->batch_ending)
401 {
402 imv->batch_ending(imv->get_id(imv), id);
403 }
404 }
405 enumerator->destroy(enumerator);
406 this->lock->unlock(this->lock);
407 }
408
409 METHOD(imv_manager_t, destroy, void,
410 private_tnc_imv_manager_t *this)
411 {
412 imv_t *imv;
413
414 while (this->imvs->remove_last(this->imvs, (void**)&imv) == SUCCESS)
415 {
416 if (imv->terminate &&
417 imv->terminate(imv->get_id(imv)) != TNC_RESULT_SUCCESS)
418 {
419 DBG1(DBG_TNC, "IMV \"%s\" not terminated successfully",
420 imv->get_name(imv));
421 }
422 imv->destroy(imv);
423 }
424 this->imvs->destroy(this->imvs);
425 this->lock->destroy(this->lock);
426 this->id_mutex->destroy(this->id_mutex);
427 free(this);
428 }
429
430 /**
431 * Described in header.
432 */
433 imv_manager_t* tnc_imv_manager_create(void)
434 {
435 private_tnc_imv_manager_t *this;
436 recommendation_policy_t policy;
437
438 INIT(this,
439 .public = {
440 .add = _add,
441 .remove = _remove_, /* avoid name conflict with stdio.h */
442 .load = _load,
443 .load_from_functions = _load_from_functions,
444 .is_registered = _is_registered,
445 .reserve_id = _reserve_id,
446 .get_recommendation_policy = _get_recommendation_policy,
447 .create_recommendations = _create_recommendations,
448 .notify_connection_change = _notify_connection_change,
449 .set_message_types = _set_message_types,
450 .set_message_types_long = _set_message_types_long,
451 .solicit_recommendation = _solicit_recommendation,
452 .receive_message = _receive_message,
453 .batch_ending = _batch_ending,
454 .destroy = _destroy,
455 },
456 .imvs = linked_list_create(),
457 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
458 .id_mutex = mutex_create(MUTEX_TYPE_DEFAULT),
459 .next_imv_id = 1,
460 );
461
462 policy = enum_from_name(recommendation_policy_names,
463 lib->settings->get_str(lib->settings,
464 "libtnccs.plugins.tnc-imv.recommendation_policy", "default"));
465 this->policy = (policy != -1) ? policy : RECOMMENDATION_POLICY_DEFAULT;
466 DBG1(DBG_TNC, "TNC recommendation policy is '%N'",
467 recommendation_policy_names, this->policy);
468
469 return &this->public;
470 }