1 /* EtherApe
2 * Copyright (C) 2005 Juan Toledo, R.Ghetta
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "appdata.h"
23 #include <ctype.h>
24 #include <string.h>
25 #include "protocols.h"
26 #include "node.h"
27 #include "links.h"
28 #include "preferences.h"
29 #include "util.h"
30
31 static gint protocol_compare(gconstpointer a, gconstpointer b);
32
33
34 /***************************************************************************
35 *
36 * protocol_stack implementation
37 *
38 **************************************************************************/
39
protocol_stack_open(protostack_t * pstk)40 void protocol_stack_open(protostack_t *pstk)
41 {
42 g_assert(pstk);
43 guint i;
44 for (i = 0; i <= STACK_SIZE; ++i)
45 pstk->protostack[i] = NULL;
46 }
47
protocol_stack_reset(protostack_t * pstk)48 void protocol_stack_reset(protostack_t *pstk)
49 {
50 guint i;
51 protocol_t *protocol_info;
52
53 g_assert(pstk);
54 for (i = 0; i <= STACK_SIZE; ++i) {
55 while (pstk->protostack[i]) {
56 protocol_info = pstk->protostack[i]->data;
57
58 protocol_t_delete(protocol_info);
59 pstk->protostack[i] = g_list_delete_link(pstk->protostack[i], pstk->protostack[i]);
60 }
61 }
62 }
63
64 /* sums another stack */
protocol_stack_sum(protostack_t * pstk,const protostack_t * tosum)65 void protocol_stack_sum(protostack_t *pstk, const protostack_t *tosum)
66 {
67 GList *protocol_item;
68 protocol_t *protocol_info;
69 guint i;
70
71 g_assert(pstk);
72 g_assert(tosum);
73
74 for (i = 0; i <= STACK_SIZE; i++) {
75
76 const GList *tosum_item = tosum->protostack[i];
77 if (!tosum_item)
78 continue; /* no protocols to sum at this level */
79
80 for (; tosum_item; tosum_item = tosum_item->next) {
81
82 const protocol_t *tosum_info = tosum_item->data;
83
84 protocol_item = g_list_find_custom(pstk->protostack[i], tosum_info->name, protocol_compare);
85 if (protocol_item)
86 protocol_info = protocol_item->data;
87 else {
88 /* If there is yet not such protocol, create it */
89 protocol_info = protocol_t_create(tosum_info->name);
90 pstk->protostack[i] = g_list_prepend(pstk->protostack[i], protocol_info);
91 }
92
93 g_assert(!strcmp(protocol_info->name, tosum_info->name));
94 basic_stats_sum(&protocol_info->stats, &tosum_info->stats);
95 }
96 }
97 }
98
99
100 /* adds the given packet to the stack */
protocol_stack_add_pkt(protostack_t * pstk,const packet_info_t * packet)101 void protocol_stack_add_pkt(protostack_t *pstk, const packet_info_t *packet)
102 {
103 GList *protocol_item;
104 protocol_t *protocol_info;
105 guint i;
106
107 g_assert(packet);
108 g_assert(pstk);
109
110 for (i = 0; i <= STACK_SIZE; i++) {
111 if (!(packet->prot_desc.protonames[i]))
112 continue;
113
114 protocol_item = g_list_find_custom(pstk->protostack[i],
115 packet->prot_desc.protonames[i],
116 protocol_compare);
117 if (protocol_item)
118 protocol_info = protocol_item->data;
119 else {
120 /* If there is yet not such protocol, create it */
121 protocol_info = protocol_t_create(packet->prot_desc.protonames[i]);
122 pstk->protostack[i] = g_list_prepend(pstk->protostack[i], protocol_info);
123 }
124
125 g_assert(!strcmp(protocol_info->name, packet->prot_desc.protonames[i]));
126 basic_stats_add(&protocol_info->stats, packet->size);
127 }
128 }
129
130
protocol_stack_sub_pkt(protostack_t * pstk,const packet_info_t * packet)131 void protocol_stack_sub_pkt(protostack_t *pstk, const packet_info_t *packet)
132 {
133 guint i = 0;
134 GList *item = NULL;
135 protocol_t *protocol = NULL;
136
137 g_assert(pstk);
138
139 if (!packet)
140 return;
141
142 /* We remove protocol aggregate information */
143 while ((i <= STACK_SIZE) && packet->prot_desc.protonames[i]) {
144 item = g_list_find_custom(pstk->protostack[i],
145 packet->prot_desc.protonames[i],
146 protocol_compare);
147 if (!item) {
148 g_my_critical
149 ("Protocol not found while subtracting packet in protocol_stack_sub_pkt");
150 break;
151 }
152 protocol = item->data;
153
154 g_assert(!strcmp(protocol->name, packet->prot_desc.protonames[i]));
155 basic_stats_sub(&protocol->stats, packet->size);
156 i++;
157 }
158 }
159
160 /* calculates averages on protocol stack items */
protocol_stack_avg(protostack_t * pstk,gdouble avgtime)161 void protocol_stack_avg(protostack_t *pstk, gdouble avgtime)
162 {
163 GList *item;
164 protocol_t *protocol;
165 guint i;
166
167 g_assert(pstk);
168
169 for (i = 0; i <= STACK_SIZE; i++) {
170 item = pstk->protostack[i];
171 while (item) {
172 protocol = (protocol_t *)item->data;
173 basic_stats_avg(&protocol->stats, avgtime);
174 item = item->next;
175 }
176 }
177 }
178
179 /* checks for protocol expiration ... */
protocol_stack_purge_expired(protostack_t * pstk,double expire_time)180 void protocol_stack_purge_expired(protostack_t *pstk, double expire_time)
181 {
182 g_assert(pstk);
183
184 if (expire_time > 0) {
185 GList *item;
186 GList *next_item;
187 protocol_t *protocol;
188 double diffms;
189 guint i;
190 for (i = 0; i <= STACK_SIZE; i++) {
191 item = pstk->protostack[i];
192 while (item) {
193 protocol = (protocol_t *)item->data;
194 next_item = item->next;
195 if (protocol->stats.aver_accu <= 0) {
196 /* no traffic active on this proto, check purging */
197 diffms = subtract_times_ms(&appdata.now, &protocol->stats.last_time);
198 if (diffms >= expire_time) {
199 protocol_t_delete(protocol);
200 pstk->protostack[i] = g_list_delete_link(pstk->protostack[i], item);
201 }
202 }
203 item = next_item;
204 }
205 }
206 }
207 }
208
209
210 /* finds named protocol in the level protocols of protostack*/
protocol_stack_find(const protostack_t * pstk,size_t level,const gchar * protoname)211 const protocol_t *protocol_stack_find(const protostack_t *pstk, size_t level, const gchar *protoname)
212 {
213 GList *item;
214
215 g_assert(pstk);
216
217 if (level > STACK_SIZE || !protoname)
218 return NULL;
219
220 item = g_list_find_custom(pstk->protostack[level], protoname, protocol_compare);
221 if (item && item->data)
222 return item->data;
223
224 return NULL;
225 }
226
227 /* Comparison function to sort protocols by their accumulated traffic (descending) */
prot_freq_compare(gconstpointer a,gconstpointer b)228 static gint prot_freq_compare(gconstpointer a, gconstpointer b)
229 {
230 const protocol_t *prot_a, *prot_b;
231
232 g_assert(a != NULL);
233 g_assert(b != NULL);
234
235 prot_a = (const protocol_t *)a;
236 prot_b = (const protocol_t *)b;
237
238 if (prot_a->stats.accumulated > prot_b->stats.accumulated)
239 return -1;
240 if (prot_a->stats.accumulated < prot_b->stats.accumulated)
241 return 1;
242 return 0;
243 }
244
245
246 /* sorts stack levels on the most used protocol */
protocol_stack_sort_most_used(protostack_t * pstk)247 void protocol_stack_sort_most_used(protostack_t *pstk)
248 {
249 guint i;
250
251 for (i = 0; i <= STACK_SIZE; ++i)
252 pstk->protostack[i] = g_list_sort(pstk->protostack[i], prot_freq_compare);
253 }
254
protocol_stack_most_used(const protostack_t * pstk,size_t level)255 const gchar *protocol_stack_most_used(const protostack_t *pstk, size_t level)
256 {
257 protocol_t *protocol;
258
259 /* If we haven't recognized any protocol at that level,
260 * we say it's unknown */
261 if (level > STACK_SIZE || !pstk || !pstk->protostack[level])
262 return NULL;
263 protocol = (protocol_t *)pstk->protostack[level]->data;
264 return protocol->name;
265 }
266
267 /* returns a newly allocated string with a dump of pstk */
protocol_stack_dump(const protostack_t * pstk)268 gchar *protocol_stack_dump(const protostack_t *pstk)
269 {
270 guint i;
271 gchar *msg;
272
273 if (!pstk)
274 return g_strdup("protostack_t NULL");
275
276 msg = g_strdup("");
277 for (i = 0; i <= STACK_SIZE; ++i) {
278 gchar *msg_level;
279 gchar *tmp;
280 if (!pstk->protostack[i])
281 msg_level = g_strdup("-none-");
282 else {
283 const GList *cur_el = pstk->protostack[i];
284 msg_level = NULL;
285 while (cur_el) {
286 gchar *msg_proto;
287 const protocol_t *p = (const protocol_t *)(cur_el->data);
288 g_assert(p);
289
290 msg_proto = protocol_t_dump(p);
291 if (!msg_level)
292 msg_level = msg_proto;
293 else {
294 tmp = msg_level;
295 msg_level = g_strdup_printf("%s,[%s]", tmp, msg_proto);
296 g_free(tmp);
297 g_free(msg_proto);
298 }
299 cur_el = cur_el->next;
300 }
301 }
302 tmp = msg;
303 msg = g_strdup_printf("%slevel %d: [%s]\n", tmp, i, msg_level);
304 g_free(tmp);
305 g_free(msg_level);
306 }
307 return msg;
308 }
309
310 /* returns a newly allocated string with an xml dump of pstk */
protocol_stack_xml(const protostack_t * pstk,const gchar * tag)311 gchar *protocol_stack_xml(const protostack_t *pstk, const gchar *tag)
312 {
313 guint i;
314 gchar *msg;
315 gchar *xml;
316
317 if (!pstk)
318 return xmltag(tag, "");
319
320 msg = g_strdup("");
321 for (i = 1; i <= STACK_SIZE; ++i) {
322 gchar *msg_level;
323 gchar *tmp;
324 if (!pstk->protostack[i])
325 continue;
326
327 const GList *cur_el = pstk->protostack[i];
328 msg_level = NULL;
329 while (cur_el) {
330 gchar *msg_proto;
331 const protocol_t *p = (const protocol_t *)(cur_el->data);
332 g_assert(p);
333
334 msg_proto = protocol_t_xml(p, i);
335 if (!msg_level)
336 msg_level = msg_proto;
337 else {
338 tmp = msg_level;
339 msg_level = g_strdup_printf("%s%s", tmp, msg_proto);
340 g_free(tmp);
341 g_free(msg_proto);
342 }
343 cur_el = cur_el->next;
344 }
345 tmp = msg;
346 msg = g_strdup_printf("%s%s", tmp, msg_level);
347 g_free(tmp);
348 g_free(msg_level);
349 }
350 xml = xmltag(tag, "%s", msg);
351 g_free(msg);
352 return xml;
353 }
354
355 /***************************************************************************
356 *
357 * protocol_t implementation
358 *
359 **************************************************************************/
protocol_t_create(const gchar * protocol_name)360 protocol_t *protocol_t_create(const gchar *protocol_name)
361 {
362 protocol_t *pr = NULL;
363
364 pr = g_malloc(sizeof(protocol_t));
365 g_assert(pr);
366 pr->name = g_strdup(protocol_name);
367 basic_stats_reset(&pr->stats);
368 pr->node_names = NULL;
369
370 return pr;
371 }
372
protocol_t_delete(protocol_t * prot)373 void protocol_t_delete(protocol_t *prot)
374 {
375 g_assert(prot);
376
377 g_free(prot->name);
378 prot->name = NULL;
379
380 while (prot->node_names) {
381 GList *name_item = prot->node_names;
382 name_t *name = name_item->data;
383 node_name_delete(name);
384 prot->node_names = g_list_delete_link(prot->node_names, name_item);
385 }
386
387 g_free(prot);
388 }
389
390 /* returns a new string with a dump of prot */
protocol_t_dump(const protocol_t * prot)391 gchar *protocol_t_dump(const protocol_t *prot)
392 {
393 gchar *msg;
394 gchar *msg_stats;
395 gchar *msg_names;
396
397 if (!prot)
398 return g_strdup("protocol_t NULL");
399
400 msg_stats = basic_stats_dump(&prot->stats);
401
402 if (!prot->node_names)
403 msg_names = g_strdup("-- no names --");
404 else {
405 const GList *cur_el;
406 msg_names = NULL;
407 cur_el = prot->node_names;
408 while (cur_el) {
409 gchar *str_name;
410 const name_t *cur_name;
411
412 cur_name = (const name_t *)(cur_el->data);
413 str_name = node_name_dump(cur_name);
414 if (!msg_names)
415 msg_names = str_name;
416 else {
417 gchar *tmp = msg_names;
418 msg_names = g_strjoin(",", tmp, str_name, NULL);
419 g_free(tmp);
420 g_free(str_name);
421 }
422 cur_el = cur_el->next;
423 }
424 }
425
426 msg = g_strdup_printf("protocol name: %s, stats [%s], "
427 "node_names [%s]",
428 prot->name, msg_stats, msg_names);
429
430 g_free(msg_stats);
431 g_free(msg_names);
432 return msg;
433 }
434
435 /* returns a new string with an xml dump of prot */
protocol_t_xml(const protocol_t * prot,guint level)436 gchar *protocol_t_xml(const protocol_t *prot, guint level)
437 {
438 gchar *msg;
439 gchar *msg_stats;
440 gchar *msg_key;
441 gchar *msg_names;
442
443 if (!prot)
444 return xmltag("protocol", "");
445
446 msg_stats = basic_stats_xml(&prot->stats);
447
448 if (!prot->node_names)
449 msg_names = g_strdup("");
450 else {
451 const GList *cur_el;
452 msg_names = NULL;
453 cur_el = prot->node_names;
454 while (cur_el) {
455 gchar *str_name;
456 const name_t *cur_name;
457
458 cur_name = (const name_t *)(cur_el->data);
459 str_name = node_name_xml(cur_name);
460 if (!msg_names)
461 msg_names = str_name;
462 else {
463 gchar *tmp = msg_names;
464 msg_names = g_strjoin(",", tmp, str_name, NULL);
465 g_free(tmp);
466 g_free(str_name);
467 }
468 cur_el = cur_el->next;
469 }
470 }
471
472 msg_key = xmltag_escaped("key", "%s", prot->name);
473 msg = xmltag("protocol",
474 "\n<level>%u</level>\n%s%s%s",
475 level,
476 msg_key,
477 msg_stats, msg_names);
478
479 g_free(msg_key);
480 g_free(msg_stats);
481 g_free(msg_names);
482 return msg;
483 }
484
485
486 /* Comparison function used to compare two link protocols */
protocol_compare(gconstpointer a,gconstpointer b)487 static gint protocol_compare(gconstpointer a, gconstpointer b)
488 {
489 g_assert(a != NULL);
490 g_assert(b != NULL);
491
492 return strcmp(((const protocol_t *)a)->name, b);
493 }
494
495 /***************************************************************************
496 *
497 * protocol_summary_t implementation
498 *
499 **************************************************************************/
500 static traffic_stats_t *protosummary_stats = NULL;
501
502 /* initializes the summary */
protocol_summary_open(void)503 void protocol_summary_open(void)
504 {
505 if (protosummary_stats)
506 protocol_summary_close();
507
508 protosummary_stats = g_malloc(sizeof(traffic_stats_t));
509 g_assert(protosummary_stats);
510 traffic_stats_init(protosummary_stats);
511 }
512
513 /* frees summary, releasing resources */
protocol_summary_close(void)514 void protocol_summary_close(void)
515 {
516 if (protosummary_stats) {
517 traffic_stats_reset(protosummary_stats);
518 g_free(protosummary_stats);
519 protosummary_stats = NULL;
520 }
521 }
522
523 /* adds a new packet to summary */
protocol_summary_add_packet(packet_info_t * packet)524 void protocol_summary_add_packet(packet_info_t *packet)
525 {
526 if (!protosummary_stats)
527 protocol_summary_open();
528
529 traffic_stats_add_packet(protosummary_stats, packet, EITHERBOUND);
530 }
531
532 /* update stats on protocol summary */
protocol_summary_update_all(void)533 void protocol_summary_update_all(void)
534 {
535 if (protosummary_stats)
536 traffic_stats_update(protosummary_stats, pref.averaging_time, pref.proto_timeout_time);
537 }
538
539 /* number of protos at specified level */
protocol_summary_size(void)540 long protocol_summary_size(void)
541 {
542 long totproto = 0;
543 gint i;
544 if (!protosummary_stats)
545 return 0;
546 for (i = 0; i <= STACK_SIZE; ++i) {
547 if (protosummary_stats->stats_protos.protostack[i]) {
548 totproto +=
549 g_list_length(protosummary_stats->stats_protos.protostack[i]);
550 }
551 }
552 return totproto;
553 }
554
555
556 /* calls func for every protocol at the specified level */
protocol_summary_foreach(size_t level,GFunc func,gpointer data)557 void protocol_summary_foreach(size_t level, GFunc func, gpointer data)
558 {
559 if (!protosummary_stats || level > STACK_SIZE)
560 return;
561 g_list_foreach(protosummary_stats->stats_protos.protostack[level], func, data);
562 }
563
564
565 /* generates a summary xml */
protocol_summary_xml(void)566 gchar *protocol_summary_xml(void)
567 {
568 gchar *xml;
569
570 if (!protosummary_stats)
571 xml = g_strdup("");
572 else
573 xml = protocol_stack_xml(&protosummary_stats->stats_protos, "global_protocols");
574 return xml;
575 }
576
577 /* finds named protocol in the level protocols of protostack*/
protocol_summary_find(size_t level,const gchar * protoname)578 const protocol_t *protocol_summary_find(size_t level, const gchar *protoname)
579 {
580 if (!protosummary_stats)
581 return NULL;
582 return protocol_stack_find(&protosummary_stats->stats_protos, level, protoname);
583 }
584
585 /* access directly the stack (only for proto windows) */
protocol_summary_stack(void)586 const protostack_t *protocol_summary_stack(void)
587 {
588 if (!protosummary_stats)
589 return NULL;
590 return &protosummary_stats->stats_protos;
591 }
592
593 /* sums the link statistics to the summary */
protosum_accumulate_link(gpointer key,gpointer value,gpointer data)594 static gboolean protosum_accumulate_link(gpointer key, gpointer value, gpointer data)
595 {
596 const link_t *link = (const link_t *)value;
597 traffic_stats_sum(protosummary_stats, &link->link_stats);
598 return FALSE;
599 }
600
601 /* recalcs procotol summary stats from link statistics */
protocol_summary_recalc_fromlinks(void)602 void protocol_summary_recalc_fromlinks(void)
603 {
604 if (!protosummary_stats)
605 return;
606
607 traffic_stats_reset(protosummary_stats);
608
609 links_catalog_foreach(protosum_accumulate_link, NULL);
610
611 traffic_stats_calc_averages(protosummary_stats, pref.averaging_time);
612
613 protocol_stack_sort_most_used(&protosummary_stats->stats_protos);
614 }
615