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