]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libcharon/plugins/counters/counters_listener.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libcharon / plugins / counters / counters_listener.c
1 /*
2 * Copyright (C) 2017 Tobias Brunner
3 * Copyright (C) 2012 Martin Willi
4 *
5 * Copyright (C) secunet Security Networks AG
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "counters_listener.h"
19 #include "counters_query.h"
20
21 #include <threading/spinlock.h>
22 #include <collections/hashtable.h>
23 #include <collections/array.h>
24
25 typedef struct private_counters_listener_t private_counters_listener_t;
26 typedef struct private_counters_query_t private_counters_query_t;
27
28 /**
29 * Query interface
30 */
31 struct private_counters_query_t {
32
33 /**
34 * Public interface
35 */
36 counters_query_t public;
37
38 /**
39 * Reference to this
40 */
41 private_counters_listener_t *this;
42 };
43
44 /**
45 * Private data
46 */
47 struct private_counters_listener_t {
48
49 /**
50 * Public interface
51 */
52 counters_listener_t public;
53
54 /**
55 * Query interface
56 */
57 private_counters_query_t query;
58
59 /**
60 * Global counter values
61 */
62 uint64_t counters[COUNTER_MAX];
63
64 /**
65 * Counters for specific connection names, char* => entry_t
66 */
67 hashtable_t *conns;
68
69 /**
70 * Lock for counter values
71 */
72 spinlock_t *lock;
73 };
74
75 /**
76 * Counters for a specific connection name
77 */
78 typedef struct {
79 /** connection name */
80 char *name;
81 /** counter values for connection */
82 uint64_t counters[COUNTER_MAX];
83 } entry_t;
84
85 /**
86 * Destroy named entry
87 */
88 static void destroy_entry(entry_t *this)
89 {
90 free(this->name);
91 free(this);
92 }
93
94 /**
95 * Hashtable hash function
96 */
97 static u_int hash(char *name)
98 {
99 return chunk_hash(chunk_from_str(name));
100 }
101
102 /**
103 * Hashtable equals function
104 */
105 static bool equals(char *a, char *b)
106 {
107 return streq(a, b);
108 }
109
110 /**
111 * Get the name of an IKE_SA, but return NULL if it is not known yet
112 */
113 static char *get_ike_sa_name(ike_sa_t *ike_sa)
114 {
115 peer_cfg_t *peer_cfg;
116
117 if (ike_sa)
118 {
119 peer_cfg = ike_sa->get_peer_cfg(ike_sa);
120 if (peer_cfg)
121 {
122 return peer_cfg->get_name(peer_cfg);
123 }
124 }
125 return NULL;
126 }
127
128 /**
129 * Increase a counter for a named entry
130 */
131 static void count_named(private_counters_listener_t *this,
132 ike_sa_t *ike_sa, counter_type_t type)
133 {
134 entry_t *entry;
135 char *name;
136
137 name = get_ike_sa_name(ike_sa);
138 if (name)
139 {
140 entry = this->conns->get(this->conns, name);
141 if (!entry)
142 {
143 INIT(entry,
144 .name = strdup(name),
145 );
146 this->conns->put(this->conns, entry->name, entry);
147 }
148 entry->counters[type]++;
149 }
150 }
151
152 METHOD(listener_t, alert, bool,
153 private_counters_listener_t *this, ike_sa_t *ike_sa,
154 alert_t alert, va_list args)
155 {
156 counter_type_t type;
157
158 switch (alert)
159 {
160 case ALERT_INVALID_IKE_SPI:
161 type = COUNTER_IN_INVALID_IKE_SPI;
162 break;
163 case ALERT_PARSE_ERROR_HEADER:
164 case ALERT_PARSE_ERROR_BODY:
165 type = COUNTER_IN_INVALID;
166 break;
167 default:
168 return TRUE;
169 }
170
171 this->lock->lock(this->lock);
172 this->counters[type]++;
173 count_named(this, ike_sa, type);
174 this->lock->unlock(this->lock);
175
176 return TRUE;
177 }
178
179 METHOD(listener_t, ike_rekey, bool,
180 private_counters_listener_t *this, ike_sa_t *old, ike_sa_t *new)
181 {
182 counter_type_t type;
183 ike_sa_id_t *id;
184
185 id = new->get_id(new);
186 if (id->is_initiator(id))
187 {
188 type = COUNTER_INIT_IKE_SA_REKEY;
189 }
190 else
191 {
192 type = COUNTER_RESP_IKE_SA_REKEY;
193 }
194
195 this->lock->lock(this->lock);
196 this->counters[type]++;
197 count_named(this, old, type);
198 this->lock->unlock(this->lock);
199
200 return TRUE;
201 }
202
203 METHOD(listener_t, child_rekey, bool,
204 private_counters_listener_t *this, ike_sa_t *ike_sa,
205 child_sa_t *old, child_sa_t *new)
206 {
207 this->lock->lock(this->lock);
208 this->counters[COUNTER_CHILD_SA_REKEY]++;
209 count_named(this, ike_sa, COUNTER_CHILD_SA_REKEY);
210 this->lock->unlock(this->lock);
211
212 return TRUE;
213 }
214
215 METHOD(listener_t, message_hook, bool,
216 private_counters_listener_t *this, ike_sa_t *ike_sa, message_t *message,
217 bool incoming, bool plain)
218 {
219 counter_type_t type;
220 bool request;
221
222 if ((incoming && !plain) || (!incoming && !plain))
223 { /* handle each message only once */
224 return TRUE;
225 }
226
227 request = message->get_request(message);
228 switch (message->get_exchange_type(message))
229 {
230 case IKE_SA_INIT:
231 if (incoming)
232 {
233 type = request ? COUNTER_IN_IKE_SA_INIT_REQ
234 : COUNTER_IN_IKE_SA_INIT_RSP;
235 }
236 else
237 {
238 type = request ? COUNTER_OUT_IKE_SA_INIT_REQ
239 : COUNTER_OUT_IKE_SA_INIT_RES;
240 }
241 break;
242 case IKE_AUTH:
243 if (incoming)
244 {
245 type = request ? COUNTER_IN_IKE_AUTH_REQ
246 : COUNTER_IN_IKE_AUTH_RSP;
247 }
248 else
249 {
250 type = request ? COUNTER_OUT_IKE_AUTH_REQ
251 : COUNTER_OUT_IKE_AUTH_RSP;
252 }
253 break;
254 case CREATE_CHILD_SA:
255 if (incoming)
256 {
257 type = request ? COUNTER_IN_CREATE_CHILD_SA_REQ
258 : COUNTER_IN_CREATE_CHILD_SA_RSP;
259 }
260 else
261 {
262 type = request ? COUNTER_OUT_CREATE_CHILD_SA_REQ
263 : COUNTER_OUT_CREATE_CHILD_SA_RSP;
264 }
265 break;
266 case INFORMATIONAL:
267 if (incoming)
268 {
269 type = request ? COUNTER_IN_INFORMATIONAL_REQ
270 : COUNTER_IN_INFORMATIONAL_RSP;
271 }
272 else
273 {
274 type = request ? COUNTER_OUT_INFORMATIONAL_REQ
275 : COUNTER_OUT_INFORMATIONAL_RSP;
276 }
277 break;
278 default:
279 return TRUE;
280 }
281
282 this->lock->lock(this->lock);
283 this->counters[type]++;
284 count_named(this, ike_sa, type);
285 this->lock->unlock(this->lock);
286
287 return TRUE;
288 }
289
290 CALLBACK(free_names, void,
291 array_t * names)
292 {
293 array_destroy_function(names, (void*)free, NULL);
294 }
295
296 METHOD(counters_query_t, get_names, enumerator_t*,
297 private_counters_query_t *query)
298 {
299 private_counters_listener_t *this = query->this;
300 enumerator_t *enumerator;
301 array_t *names;
302 char *name;
303
304 this->lock->lock(this->lock);
305 names = array_create(0, this->conns->get_count(this->conns));
306 enumerator = this->conns->create_enumerator(this->conns);
307 while (enumerator->enumerate(enumerator, &name, NULL))
308 {
309 array_insert(names, ARRAY_TAIL, strdup(name));
310 }
311 enumerator->destroy(enumerator);
312 this->lock->unlock(this->lock);
313
314 array_sort(names, (void*)strcmp, NULL);
315
316 return enumerator_create_cleaner(array_create_enumerator(names),
317 free_names, names);
318 }
319
320 METHOD(counters_query_t, get, bool,
321 private_counters_query_t *query, counter_type_t type, char *name,
322 uint64_t *value)
323 {
324 private_counters_listener_t *this = query->this;
325 uint64_t *counters = this->counters;
326
327 this->lock->lock(this->lock);
328 if (name)
329 {
330 entry_t *entry;
331
332 entry = this->conns->get(this->conns, name);
333 if (!entry)
334 {
335 this->lock->unlock(this->lock);
336 return FALSE;
337 }
338 counters = entry->counters;
339 }
340 if (value)
341 {
342 *value = counters[type];
343 }
344 this->lock->unlock(this->lock);
345 return TRUE;
346 }
347
348 METHOD(counters_query_t, get_all, uint64_t*,
349 private_counters_query_t *query, char *name)
350 {
351 private_counters_listener_t *this = query->this;
352 entry_t *entry;
353 uint64_t *result, *counters = this->counters;
354 counter_type_t i;
355
356 result = calloc(COUNTER_MAX, sizeof(uint64_t));
357
358 this->lock->lock(this->lock);
359 if (name)
360 {
361 entry = this->conns->get(this->conns, name);
362 if (!entry)
363 {
364 this->lock->unlock(this->lock);
365 free(result);
366 return NULL;
367 }
368 counters = &entry->counters[0];
369 }
370 for (i = 0; i < countof(this->counters); i++)
371 {
372 result[i] = counters[i];
373 }
374 this->lock->unlock(this->lock);
375 return result;
376 }
377
378 METHOD(counters_query_t, reset, void,
379 private_counters_query_t *query, char *name)
380 {
381 private_counters_listener_t *this = query->this;
382 entry_t *entry = NULL;
383
384 this->lock->lock(this->lock);
385 if (name)
386 {
387 entry = this->conns->remove(this->conns, name);
388 }
389 else
390 {
391 memset(&this->counters, 0, sizeof(this->counters));
392 }
393 this->lock->unlock(this->lock);
394
395 if (entry)
396 {
397 destroy_entry(entry);
398 }
399 }
400
401 METHOD(counters_query_t, reset_all, void,
402 private_counters_query_t *query)
403 {
404 private_counters_listener_t *this = query->this;
405 hashtable_t *new_conns, *conns;
406
407 new_conns = hashtable_create((hashtable_hash_t)hash,
408 (hashtable_equals_t)equals, 4);
409
410 this->lock->lock(this->lock);
411 conns = this->conns;
412 this->conns = new_conns;
413 this->lock->unlock(this->lock);
414
415 conns->destroy_function(conns, (void*)destroy_entry);
416 }
417
418 METHOD(counters_listener_t, destroy, void,
419 private_counters_listener_t *this)
420 {
421 lib->set(lib, "counters", NULL);
422
423 this->conns->destroy_function(this->conns, (void*)destroy_entry);
424 this->lock->destroy(this->lock);
425 free(this);
426 }
427
428 /*
429 * Described in header
430 */
431 counters_listener_t *counters_listener_create()
432 {
433 private_counters_listener_t *this;
434
435 INIT(this,
436 .public = {
437 .listener = {
438 .alert = _alert,
439 .ike_rekey = _ike_rekey,
440 .child_rekey = _child_rekey,
441 .message = _message_hook,
442 },
443 .destroy = _destroy,
444 },
445 .query = {
446 .public = {
447 .get_names = _get_names,
448 .get = _get,
449 .get_all = _get_all,
450 .reset = _reset,
451 .reset_all = _reset_all,
452 },
453 },
454 .conns = hashtable_create((hashtable_hash_t)hash,
455 (hashtable_equals_t)equals, 4),
456 .lock = spinlock_create(),
457 );
458 this->query.this = this;
459
460 lib->set(lib, "counters", &this->query);
461
462 return &this->public;
463 }