1 /* EtherApe
2  * Copyright (C) 2001 Juan Toledo, 2005 Riccardo 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 "protocols.h"
24 #include "links.h"
25 #include "node.h"
26 #include "preferences.h"
27 #include "conversations.h"
28 #include "util.h"
29 
30 struct xml_tvs_helper
31 {
32   double total_size;
33   unsigned long total_packets;
34   gchar *msg;
35 };
36 
37 
38 static GTree *all_links = NULL;                 /* Has all links heard on the net */
39 
40 /* sort protocols by accumulated size */
41 static void link_protocol_sort(link_t *link);
42 static gchar *link_xml(link_t *link);
43 
44 /***************************************************************************
45  *
46  * link_id_t implementation
47  *
48  **************************************************************************/
49 /* Comparison function used to order the (GTree *) links
50  * and canvas_links heard on the network */
link_id_compare(const link_id_t * a,const link_id_t * b)51 gint link_id_compare(const link_id_t *a, const link_id_t *b)
52 {
53   int i;
54   g_return_val_if_fail(a != NULL, 1);  /* This shouldn't happen.
55                                          * We arbitrarily passing 1 to
56                                          * the comparison */
57   g_return_val_if_fail(b != NULL, 1);
58 
59   i = node_id_compare(&a->src, &b->src);
60   if (i != 0)
61     return i;
62 
63   return node_id_compare(&a->dst, &b->dst);
64 }                               /* link_id_compare */
65 
66 /* returns a NEW gchar * with the node names of the link_id */
link_id_node_names(const link_id_t * link_id)67 gchar *link_id_node_names(const link_id_t *link_id)
68 {
69   const node_t *src_node, *dst_node;
70 
71   src_node = nodes_catalog_find(&link_id->src);
72   dst_node = nodes_catalog_find(&link_id->dst);
73   if (!src_node || !dst_node ||
74       !src_node->name->str || !dst_node->name->str)
75     return g_strdup(""); /* invalid info */
76 
77   return g_strdup_printf("%s-%s",
78                          src_node->name->str,
79                          dst_node->name->str);
80 }
81 
link_id_xml(const link_id_t * id)82 gchar *link_id_xml(const link_id_t *id)
83 {
84   gchar *nodea;
85   gchar *nodeb;
86   gchar *xml;
87   g_assert(id);
88 
89   nodea = node_id_xml(&id->src);
90   nodeb = node_id_xml(&id->dst);
91   xml = g_strdup_printf("<src>\n%s</src>\n<dst>\n%s</dst>\n",
92                          nodea,
93                          nodeb);
94   g_free(nodea);
95   g_free(nodeb);
96 
97   return xml;
98 }
99 
100 
101 /***************************************************************************
102  *
103  * link_t implementation
104  *
105  **************************************************************************/
106 static gint update_link(link_id_t *link_id, link_t *link, gpointer delete_list_ptr);
107 
108 /* creates a new link object */
link_create(const link_id_t * link_id)109 link_t *link_create(const link_id_t *link_id)
110 {
111   link_t *link;
112 
113   link = g_malloc(sizeof(link_t));
114   g_assert(link);
115 
116   link->link_id = *link_id;
117 
118   traffic_stats_init(&link->link_stats);
119 
120   return link;
121 }
122 
123 /* destroys a link, releasing memory */
link_delete(link_t * link)124 void link_delete(link_t *link)
125 {
126   g_assert(link);
127 
128   /* first, free any conversation belonging to the link */
129   delete_conversation_link(&link->link_id.src.addr.ip,
130                            &link->link_id.dst.addr.ip);
131 
132   traffic_stats_reset(&link->link_stats);
133 
134   g_free(link);
135 }
136 
link_dump(const link_t * link)137 gchar *link_dump(const link_t *link)
138 {
139   gchar *msg;
140   gchar *msg_idsrc;
141   gchar *msg_iddst;
142   gchar *msg_stats;
143   gchar *msg_mprot;
144   const gchar *main_prot;
145   guint i;
146 
147   if (!link)
148     return g_strdup("link_t NULL");
149 
150   msg_idsrc = node_id_dump(&link->link_id.src);
151   msg_iddst = node_id_dump(&link->link_id.dst);
152   msg_stats = traffic_stats_dump(&link->link_stats);
153 
154   main_prot = traffic_stats_most_used_proto(&link->link_stats, 0);
155   msg_mprot = g_strdup_printf("top: [%s], stack:",
156                               (main_prot) ? main_prot : "-none-");
157 
158   for (i = 1; i <= STACK_SIZE; i++) {
159     gchar *tmp = msg_mprot;
160     main_prot = traffic_stats_most_used_proto(&link->link_stats, i);
161     msg_mprot = g_strdup_printf("%s %d:>%s<", msg_mprot, i,
162                                 (main_prot) ? main_prot : "-none-");
163     g_free(tmp);
164   }
165 
166   msg = g_strdup_printf("src: %s, dst: %s, main_prot: [%s], stats [%s]",
167                         msg_idsrc, msg_iddst, msg_mprot, msg_stats);
168   g_free(msg_idsrc);
169   g_free(msg_iddst);
170   g_free(msg_stats);
171   g_free(msg_mprot);
172 
173   return msg;
174 }
175 
176 /* returns a newly allocated string with an xml dump of link */
link_xml(link_t * link)177 gchar *link_xml(link_t *link)
178 {
179   gchar *msg;
180   gchar *msg_id;
181   gchar *msg_stats;
182 
183   if (!link)
184     return xmltag("link", "");
185 
186   msg_id = link_id_xml(&link->link_id);
187   msg_stats = traffic_stats_xml(&link->link_stats);
188 
189   msg = xmltag("link", "\n<link-nodes>\n%s</link-nodes>\n%s",
190                msg_id,
191                msg_stats);
192   g_free(msg_id);
193   g_free(msg_stats);
194 
195   return msg;
196 }
197 
198 /* gfunc called by g_list_foreach to remove a link */
gfunc_remove_link(gpointer data,gpointer user_data)199 static void gfunc_remove_link(gpointer data, gpointer user_data)
200 {
201   links_catalog_remove((const link_id_t *)data);
202 }
203 
update_link(link_id_t * link_id,link_t * link,gpointer delete_list_ptr)204 static gint update_link(link_id_t *link_id, link_t *link, gpointer delete_list_ptr)
205 {
206   double diffms;
207 
208   g_assert(delete_list_ptr);
209 
210   /* update stats - returns true if there are active packets */
211   if (traffic_stats_update(&link->link_stats, pref.averaging_time,
212                            pref.proto_link_timeout_time)) {
213     /* packet(s) active, update the most used protocols for this link */
214     link_protocol_sort(link);
215   }
216   else {
217     /* no packets remaining on link - if link expiration active, see if the
218      * link is expired */
219     if (pref.proto_link_timeout_time) {
220       diffms = subtract_times_ms(&appdata.now, &link->link_stats.stats.last_time);
221       if (diffms >= pref.proto_link_timeout_time) {
222         /* link expired, remove */
223         GList * *delete_list = (GList * *)delete_list_ptr;
224 
225         /* adds current to list of links to delete */
226         *delete_list = g_list_prepend(*delete_list, link_id);
227 
228         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, _("Queuing link for remove"));
229       }
230     }
231   }
232 
233   return FALSE;
234 }
235 
236 /* sort protocols by accumulated size */
link_protocol_sort(link_t * link)237 static void link_protocol_sort(link_t *link)
238 {
239   protocol_stack_sort_most_used(&link->link_stats.stats_protos);
240 }
241 
242 
243 /***************************************************************************
244  *
245  * links catalog implementation
246  *
247  **************************************************************************/
248 
249 /* links catalog compare function */
links_catalog_compare(gconstpointer a,gconstpointer b,gpointer dummy)250 static gint links_catalog_compare(gconstpointer a, gconstpointer b, gpointer dummy)
251 {
252   return link_id_compare((const link_id_t *)a,  (const link_id_t *)b);
253 }
254 
255 /* initializes the catalog */
links_catalog_open(void)256 void links_catalog_open(void)
257 {
258   g_assert(!all_links);
259   all_links = g_tree_new_full(links_catalog_compare, NULL, NULL,
260                               (GDestroyNotify)link_delete);
261 }
262 
263 /* closes the catalog, releasing all links */
links_catalog_close(void)264 void links_catalog_close(void)
265 {
266   if (all_links) {
267     g_tree_destroy(all_links);
268     all_links = NULL;
269   }
270 }
271 
272 /* insert a new link */
links_catalog_insert(link_t * new_link)273 void links_catalog_insert(link_t *new_link)
274 {
275   g_assert(all_links);
276   g_assert(new_link);
277 
278   g_tree_insert(all_links, &new_link->link_id, new_link);
279 
280   if (DEBUG_ENABLED) {
281     gchar *str = link_id_node_names(&new_link->link_id);
282 
283     g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
284           _("New link: %s. Number of links %d"),
285           str, links_catalog_size());
286     g_free(str);
287   }
288 }
289 
290 /* removes AND DESTROYS the named link from catalog */
links_catalog_remove(const link_id_t * key)291 void links_catalog_remove(const link_id_t *key)
292 {
293   g_assert(all_links);
294   g_assert(key);
295 
296   g_tree_remove(all_links, key);
297 }
298 
299 /* finds a link */
links_catalog_find(const link_id_t * key)300 link_t *links_catalog_find(const link_id_t *key)
301 {
302   g_assert(key);
303   if (!all_links)
304     return NULL;
305 
306   return g_tree_lookup(all_links, key);
307 }
308 
309 /* finds a link, creating one if necessary */
links_catalog_find_create(const link_id_t * key)310 link_t *links_catalog_find_create(const link_id_t *key)
311 {
312   link_t *link;
313   g_assert(all_links);
314   g_assert(key);
315 
316   link = links_catalog_find(key);
317   if (!link) {
318     link = link_create(key);
319     links_catalog_insert(link);
320   }
321   return link;
322 }
323 
324 /* returns the current number of links in catalog */
links_catalog_size(void)325 gint links_catalog_size(void)
326 {
327   if (!all_links)
328     return 0;
329 
330   return g_tree_nnodes(all_links);
331 }
332 
333 /* calls the func for every link */
links_catalog_foreach(GTraverseFunc func,gpointer data)334 void links_catalog_foreach(GTraverseFunc func, gpointer data)
335 {
336   if (!all_links)
337     return;
338 
339   return g_tree_foreach(all_links, func, data);
340 }
341 
342 /* Calls update_link for every link. This is actually a function that
343  shouldn't be called often, because it might take a very long time
344  to complete */
links_catalog_update_all(void)345 void links_catalog_update_all(void)
346 {
347   GList *delete_list = NULL;
348 
349   if (!all_links)
350     return;
351 
352   /* we can't delete links while traversing the catalog, so while updating links
353    * we fill a list with the expired link_id's */
354   links_catalog_foreach((GTraverseFunc)update_link, &delete_list);
355 
356   /* after, remove all links on the list from catalog
357    * WARNING: after this call, the list items are also destroyed */
358   g_list_foreach(delete_list, gfunc_remove_link, NULL);
359 
360   /* free the list - list items are already destroyed */
361   g_list_free(delete_list);
362 
363   g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
364         _("Updated links. Active links %d"), links_catalog_size());
365 }
366 
367 /* adds a new packet to the link, creating it if necessary */
links_catalog_add_packet(const link_id_t * link_id,packet_info_t * packet,packet_direction direction)368 void links_catalog_add_packet(const link_id_t *link_id, packet_info_t *packet,
369                               packet_direction direction)
370 {
371   link_t *link;
372 
373   /* retrieves link from catalog, creating a new one if necessary */
374   link = links_catalog_find_create(link_id);
375 
376   traffic_stats_add_packet(&link->link_stats, packet, direction);
377 }
378 
link_dump_tvs(gpointer key,gpointer value,gpointer data)379 static gboolean link_dump_tvs(gpointer key, gpointer value, gpointer data)
380 {
381   gchar *msg_link;
382   gchar *tmp;
383   gchar * *msg = (gchar * *)data;
384   const link_t *link = (const link_t *)value;
385 
386   msg_link = link_dump(link);
387   tmp = *msg;
388   *msg = g_strdup_printf("%slink %p:\n%s\n", tmp, link, msg_link);
389   g_free(tmp);
390   g_free(msg_link);
391   return FALSE;
392 }
393 
links_catalog_dump(void)394 gchar *links_catalog_dump(void)
395 {
396   gchar *msg;
397 
398   msg = g_strdup("");
399   links_catalog_foreach(link_dump_tvs, &msg);
400   return msg;
401 }
402 
link_xml_tvs(gpointer key,gpointer value,gpointer data)403 static gboolean link_xml_tvs(gpointer key, gpointer value, gpointer data)
404 {
405   gchar *msg_link;
406   gchar *tmp;
407   struct xml_tvs_helper *xth = (struct xml_tvs_helper *)data;
408   link_t *link = (link_t *)value;
409 
410   msg_link = link_xml(link);
411   tmp = xth->msg;
412   xth->msg = g_strdup_printf("%s%s", tmp, msg_link);
413   g_free(tmp);
414   g_free(msg_link);
415   xth->total_size += link->link_stats.stats.accumulated;
416   xth->total_packets += link->link_stats.stats.accu_packets;
417   return FALSE;
418 }
419 
420 /* returns a newly allocated string with an xml dump of all links */
links_catalog_xml(void)421 gchar *links_catalog_xml(void)
422 {
423   gchar *xml;
424   struct xml_tvs_helper xth;
425 
426   xth.total_size = 0;
427   xth.total_packets = 0;
428   xth.msg = g_strdup("");
429 
430   links_catalog_foreach(link_xml_tvs, &xth);
431   xml = xmltag("links", "\n<accumulated>%.0f</accumulated>\n<packets>%lu</packets>\n%s", xth.total_size, xth.total_packets, xth.msg);
432   g_free(xth.msg);
433   return xml;
434 }
435