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 "traffic_stats.h"
24 #include "ui_utils.h"
25 #include "util.h"
26 #include "compat.h"
27 
28 /***************************************************************************
29  *
30  * traffic_stats_t implementation
31  *
32  **************************************************************************/
33 
34 /* initializes counters */
traffic_stats_init(traffic_stats_t * pkt_stat)35 void traffic_stats_init(traffic_stats_t *pkt_stat)
36 {
37   g_assert(pkt_stat);
38 
39   g_queue_init(&pkt_stat->pkt_list);
40 
41   basic_stats_reset(&pkt_stat->stats);
42   basic_stats_reset(&pkt_stat->stats_in);
43   basic_stats_reset(&pkt_stat->stats_out);
44 
45   protocol_stack_open(&pkt_stat->stats_protos);
46 }
47 
48 /* releases memory */
traffic_stats_reset(traffic_stats_t * pkt_stat)49 void traffic_stats_reset(traffic_stats_t *pkt_stat)
50 {
51   gpointer it;
52 
53   g_assert(pkt_stat);
54 
55   /* release items and free list */
56   while ((it = g_queue_pop_head(&pkt_stat->pkt_list)) != NULL)
57     packet_list_item_delete((packet_list_item_t *)it);
58 
59   /* purges protos */
60   protocol_stack_reset(&pkt_stat->stats_protos);
61 
62   basic_stats_reset(&pkt_stat->stats);
63   basic_stats_reset(&pkt_stat->stats_in);
64   basic_stats_reset(&pkt_stat->stats_out);
65 }
66 
67 /* adds another item stats to current element stats - doesn't copies packets! */
traffic_stats_sum(traffic_stats_t * pkt_stat,const traffic_stats_t * tosum)68 void traffic_stats_sum(traffic_stats_t *pkt_stat, const traffic_stats_t *tosum)
69 {
70   g_assert(pkt_stat);
71   g_assert(tosum);
72 
73   basic_stats_sum(&pkt_stat->stats, &tosum->stats);
74   basic_stats_sum(&pkt_stat->stats_in, &tosum->stats_in);
75   basic_stats_sum(&pkt_stat->stats_out, &tosum->stats_out);
76 
77   /* adds also to protocol stack */
78   protocol_stack_sum(&pkt_stat->stats_protos, &tosum->stats_protos);
79 
80   /* note: averages are calculated later, by update_packet_list */
81 }
82 
83 /* adds a packet */
traffic_stats_add_packet(traffic_stats_t * pkt_stat,packet_info_t * new_pkt,packet_direction dir)84 void traffic_stats_add_packet(traffic_stats_t *pkt_stat,
85                               packet_info_t *new_pkt,
86                               packet_direction dir)
87 {
88   packet_list_item_t *newit;
89 
90   g_assert(pkt_stat);
91   g_assert(new_pkt);
92 
93   /* creates a new item, incrementing refcount of new_pkt */
94   newit = packet_list_item_create(new_pkt, dir);
95 
96   /* adds to list */
97   g_queue_push_head(&pkt_stat->pkt_list, newit);
98 
99   basic_stats_add(&pkt_stat->stats, newit->info->size);
100   if (newit->direction != OUTBOUND)
101     basic_stats_add(&pkt_stat->stats_in, newit->info->size); /* in or either */
102   if (newit->direction != INBOUND)
103     basic_stats_add(&pkt_stat->stats_out, newit->info->size); /* out or either */
104 
105   /* adds also to protocol stack */
106   protocol_stack_add_pkt(&pkt_stat->stats_protos, newit->info);
107 
108   /* note: averages are calculated later, by update_packet_list */
109 }
110 
traffic_stats_purge_expired_packets(traffic_stats_t * pkt_stat,double pkt_expire_time,double proto_expire_time)111 static gboolean traffic_stats_purge_expired_packets(traffic_stats_t *pkt_stat, double pkt_expire_time, double proto_expire_time)
112 {
113   double diffms;
114   packet_list_item_t *packet;
115 
116   /* pkt queue is ordered by arrival time, so older pkts are at tail */
117   while (pkt_stat->pkt_list.head) {
118     packet = (packet_list_item_t *)g_queue_peek_tail(&pkt_stat->pkt_list);
119     diffms = subtract_times_ms(&appdata.now, &packet->info->timestamp);
120     if (diffms < pkt_expire_time)
121       break; /* packet valid, subsequent packets are younger, no need to go further */
122 
123     /* packet expired, remove from stats */
124     basic_stats_sub(&pkt_stat->stats, packet->info->size);
125     if (packet->direction != OUTBOUND)
126       basic_stats_sub(&pkt_stat->stats_in, packet->info->size); /* in or either */
127     if (packet->direction != INBOUND)
128       basic_stats_sub(&pkt_stat->stats_out, packet->info->size); /* out or either */
129 
130     /* and protocol stack */
131     protocol_stack_sub_pkt(&pkt_stat->stats_protos, packet->info);
132 
133     /* and, finally, from packet queue */
134     g_queue_pop_tail(&pkt_stat->pkt_list);
135     packet_list_item_delete(packet);
136   }
137 
138   /* purge expired protocols */
139   protocol_stack_purge_expired(&pkt_stat->stats_protos, proto_expire_time);
140 
141   if (pkt_stat->pkt_list.head == NULL) {
142     /* removed all packets */
143     pkt_stat->stats.average = 0;
144     pkt_stat->stats_in.average = 0;
145     pkt_stat->stats_out.average = 0;
146     return FALSE;
147   }
148 
149   return TRUE;  /* packet list not empty */
150 }
151 
152 
153 /* recalculate averages */
traffic_stats_calc_averages(traffic_stats_t * pkt_stat,double avg_time)154 void traffic_stats_calc_averages(traffic_stats_t *pkt_stat, double avg_time)
155 {
156   basic_stats_avg(&pkt_stat->stats, avg_time);
157   basic_stats_avg(&pkt_stat->stats_in, avg_time);
158   basic_stats_avg(&pkt_stat->stats_out, avg_time);
159   protocol_stack_avg(&pkt_stat->stats_protos, avg_time);
160 }
161 
162 /* Update stats, purging expired packets - returns FALSE if there are no
163  * active packets */
traffic_stats_update(traffic_stats_t * pkt_stat,double avg_time,double proto_expire_time)164 gboolean traffic_stats_update(traffic_stats_t *pkt_stat, double avg_time, double proto_expire_time)
165 {
166   gdouble ms_from_oldest = avg_time;
167   g_assert(pkt_stat);
168 
169   if (!traffic_stats_purge_expired_packets(pkt_stat, avg_time, proto_expire_time)) {
170     traffic_stats_calc_averages(pkt_stat, ms_from_oldest);
171     return FALSE;   /* no packets remaining */
172   }
173 
174 #if CHECK_EXPIRATION
175   /* the last packet of the list is the oldest */
176   const packet_list_item_t *packet;
177   packet = (const packet_list_item_t *)g_queue_peek_tail(&pkt_stat->pkt_list);
178   ms_from_oldest = subtract_times_ms(&now, &packet->info->timestamp);
179   if (ms_from_oldest < avg_time)
180     ms_from_oldest = avg_time;
181   else
182     g_warning("ms_to_oldest > avg_time: %f", ms_from_oldest);
183 #endif
184 
185   traffic_stats_calc_averages(pkt_stat, ms_from_oldest);
186   return TRUE;   /* there are packets */
187 }
188 
189 /* returns the name of most used protocol at the specified level, if present (NULL otherwise) */
traffic_stats_most_used_proto(const traffic_stats_t * pkt_stat,size_t level)190 const gchar *traffic_stats_most_used_proto(const traffic_stats_t *pkt_stat, size_t level)
191 {
192   if (!pkt_stat)
193       return NULL;
194   return protocol_stack_most_used(&pkt_stat->stats_protos, level);
195 }
196 
197 /* returns a newly allocated string with a dump of pkt_stat */
traffic_stats_dump(const traffic_stats_t * pkt_stat)198 gchar *traffic_stats_dump(const traffic_stats_t *pkt_stat)
199 {
200   gchar *msg;
201   gchar *msg_tot, *msg_in, *msg_out;
202   gchar *msg_proto;
203 
204   if (!pkt_stat)
205     return g_strdup("traffic_stats_t NULL");
206 
207   msg_tot = basic_stats_dump(&pkt_stat->stats);
208   msg_in = basic_stats_dump(&pkt_stat->stats_in);
209   msg_out = basic_stats_dump(&pkt_stat->stats_out);
210   msg_proto = protocol_stack_dump(&pkt_stat->stats_protos);
211   msg = g_strdup_printf("active_packets: %u\n"
212                         "  in : [%s]\n"
213                         "  out: [%s]\n"
214                         "  tot: [%s]\n"
215                         "  protocols:\n"
216                         "  %s",
217                         pkt_stat->pkt_list.length,
218                         msg_in, msg_out, msg_tot, msg_proto);
219   g_free(msg_tot);
220   g_free(msg_in);
221   g_free(msg_out);
222   g_free(msg_proto);
223   return msg;
224 }
225 
226 /* returns a newly allocated string with an xml dump of pkt_stat */
traffic_stats_xml(const traffic_stats_t * pkt_stat)227 gchar *traffic_stats_xml(const traffic_stats_t *pkt_stat)
228 {
229   gchar *msg;
230   gchar *msg_tot, *msg_in, *msg_out;
231   gchar *msg_proto;
232 
233   if (!pkt_stat)
234     return xmltag("traffic_stats", "");
235 
236   msg_tot = basic_stats_xml(&pkt_stat->stats);
237   msg_in = basic_stats_xml(&pkt_stat->stats_in);
238   msg_out = basic_stats_xml(&pkt_stat->stats_out);
239   msg_proto = protocol_stack_xml(&pkt_stat->stats_protos, "protocols");
240   msg = xmltag("traffic_stats",
241                "\n<active_packets>%u</active_packets>\n"
242                "<in>\n%s</in>\n"
243                "<out>\n%s</out>\n"
244                "<tot>\n%s</tot>\n"
245                "%s",
246                pkt_stat->pkt_list.length,
247                msg_in, msg_out, msg_tot, msg_proto);
248   g_free(msg_tot);
249   g_free(msg_in);
250   g_free(msg_out);
251   g_free(msg_proto);
252   return msg;
253 }
254