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