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