Imported Upstream version 3.0.715
[darkstat-debian] / acct.c
1 /* darkstat 3
2 * copyright (c) 2001-2011 Emil Mikulic.
3 *
4 * acct.c: traffic accounting
5 *
6 * Permission to use, copy, modify, and distribute this file for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "acct.h"
20 #include "decode.h"
21 #include "conv.h"
22 #include "daylog.h"
23 #include "err.h"
24 #include "hosts_db.h"
25 #include "localip.h"
26 #include "now.h"
27 #include "opt.h"
28
29 #define __FAVOR_BSD
30 #include <netinet/tcp.h>
31 #include <sys/socket.h>
32 #include <assert.h>
33 #include <ctype.h> /* for isdigit */
34 #include <netdb.h> /* for gai_strerror */
35 #include <stdlib.h> /* for free */
36 #include <string.h> /* for memcpy */
37
38 uint64_t acct_total_packets = 0, acct_total_bytes = 0;
39
40 static int using_localnet4 = 0, using_localnet6 = 0;
41 static struct addr localnet4, localmask4, localnet6, localmask6;
42
43 /* Parse the net/mask specification into two IPs or die trying. */
44 void
45 acct_init_localnet(const char *spec)
46 {
47 char **tokens;
48 unsigned int num_tokens;
49 int isnum, j, ret;
50 int pfxlen, octets, remainder;
51 struct addr localnet, localmask;
52
53 tokens = split('/', spec, &num_tokens);
54 if (num_tokens != 2)
55 errx(1, "expecting network/netmask, got \"%s\"", spec);
56
57 if ((ret = str_to_addr(tokens[0], &localnet)) != 0)
58 errx(1, "couldn't parse \"%s\": %s", tokens[0], gai_strerror(ret));
59
60 /* Detect a purely numeric argument. */
61 isnum = 0;
62 {
63 const char *p = tokens[1];
64 while (*p != '\0') {
65 if (isdigit(*p)) {
66 isnum = 1;
67 ++p;
68 continue;
69 } else {
70 isnum = 0;
71 break;
72 }
73 }
74 }
75
76 if (!isnum) {
77 if ((ret = str_to_addr(tokens[1], &localmask)) != 0)
78 errx(1, "couldn't parse \"%s\": %s", tokens[1], gai_strerror(ret));
79 if (localmask.family != localnet.family)
80 errx(1, "family mismatch between net and mask");
81 } else {
82 uint8_t frac, *p;
83 char *endptr;
84
85 localmask.family = localnet.family;
86
87 /* Compute the prefix length. */
88 pfxlen = (unsigned int)strtol(tokens[1], &endptr, 10);
89
90 if ((pfxlen < 0) ||
91 ((localnet.family == IPv6) && (pfxlen > 128)) ||
92 ((localnet.family == IPv4) && (pfxlen > 32)) ||
93 (tokens[1][0] == '\0') ||
94 (*endptr != '\0'))
95 errx(1, "invalid network prefix length \"%s\"", tokens[1]);
96
97 /* Construct the network mask. */
98 octets = pfxlen / 8;
99 remainder = pfxlen % 8;
100 p = (localnet.family == IPv6) ? (localmask.ip.v6.s6_addr)
101 : ((uint8_t *) &(localmask.ip.v4));
102
103 if (localnet.family == IPv6)
104 memset(p, 0, 16);
105 else
106 memset(p, 0, 4);
107
108 for (j = 0; j < octets; ++j)
109 p[j] = 0xff;
110
111 frac = (uint8_t)(0xff << (8 - remainder));
112 if (frac)
113 p[j] = frac; /* Have contribution for next position. */
114 }
115
116 free(tokens[0]);
117 free(tokens[1]);
118 free(tokens);
119
120 /* Register the correct netmask and calculate the correct net. */
121 addr_mask(&localnet, &localmask);
122 if (localnet.family == IPv6) {
123 using_localnet6 = 1;
124 localnet6 = localnet;
125 localmask6 = localmask;
126 } else {
127 using_localnet4 = 1;
128 localnet4 = localnet;
129 localmask4 = localmask;
130 }
131
132 verbosef("local network address: %s", addr_to_str(&localnet));
133 verbosef(" local network mask: %s", addr_to_str(&localmask));
134 }
135
136 static int
137 addr_is_local(const struct addr * const a)
138 {
139 if (a->family == IPv4) {
140 if (using_localnet4) {
141 if (addr_inside(a, &localnet4, &localmask4))
142 return 1;
143 } else {
144 if (addr_equal(a, &localip4))
145 return 1;
146 }
147 } else {
148 assert(a->family == IPv6);
149 if (using_localnet6) {
150 if (addr_inside(a, &localnet6, &localmask6))
151 return 1;
152 } else {
153 if (addr_equal(a, &localip6))
154 return 1;
155 }
156 }
157 return 0;
158 }
159
160 /* Account for the given packet summary. */
161 void
162 acct_for(const struct pktsummary * const sm)
163 {
164 struct bucket *hs = NULL, *hd = NULL;
165 struct bucket *ps, *pd;
166 int dir_in, dir_out;
167
168 #if 0 /* WANT_CHATTY? */
169 printf("%15s > ", addr_to_str(&sm->src));
170 printf("%15s ", addr_to_str(&sm->dst));
171 printf("len %4d proto %2d", sm->len, sm->proto);
172
173 if (sm->proto == IPPROTO_TCP || sm->proto == IPPROTO_UDP)
174 printf(" port %5d : %5d", sm->src_port, sm->dst_port);
175 if (sm->proto == IPPROTO_TCP)
176 printf(" %s%s%s%s%s%s",
177 (sm->tcp_flags & TH_FIN)?"F":"",
178 (sm->tcp_flags & TH_SYN)?"S":"",
179 (sm->tcp_flags & TH_RST)?"R":"",
180 (sm->tcp_flags & TH_PUSH)?"P":"",
181 (sm->tcp_flags & TH_ACK)?"A":"",
182 (sm->tcp_flags & TH_URG)?"U":""
183 );
184 printf("\n");
185 #endif
186
187 /* Totals. */
188 acct_total_packets++;
189 acct_total_bytes += sm->len;
190
191 /* Graphs. */
192 dir_out = addr_is_local(&(sm->src));
193 dir_in = addr_is_local(&(sm->dst));
194
195 /* Traffic staying within the network isn't counted. */
196 if (dir_in == 1 && dir_out == 1)
197 dir_in = dir_out = 0;
198
199 if (dir_out) {
200 daylog_acct((uint64_t)sm->len, GRAPH_OUT);
201 graph_acct((uint64_t)sm->len, GRAPH_OUT);
202 }
203 if (dir_in) {
204 daylog_acct((uint64_t)sm->len, GRAPH_IN);
205 graph_acct((uint64_t)sm->len, GRAPH_IN);
206 }
207
208 if (opt_hosts_max == 0) return; /* skip per-host accounting */
209
210 /* Hosts. */
211 hosts_db_reduce();
212 if (!opt_want_local_only || addr_is_local(&sm->src)) {
213 hs = host_get(&(sm->src));
214 hs->out += sm->len;
215 hs->total += sm->len;
216 memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
217 hs->u.host.lastseen = now;
218 }
219
220 if (!opt_want_local_only || addr_is_local(&sm->dst)) {
221 hd = host_get(&(sm->dst));
222 hd->in += sm->len;
223 hd->total += sm->len;
224 memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac));
225 /*
226 * Don't update recipient's last seen time, we don't know that
227 * they received successfully.
228 */
229 }
230
231 /* Protocols. */
232 if (sm->proto != IPPROTO_INVALID) {
233 if (hs) {
234 ps = host_get_ip_proto(hs, sm->proto);
235 ps->out += sm->len;
236 ps->total += sm->len;
237 }
238 if (hd) {
239 pd = host_get_ip_proto(hd, sm->proto);
240 pd->in += sm->len;
241 pd->total += sm->len;
242 }
243 }
244
245 if (opt_ports_max == 0) return; /* skip ports accounting */
246
247 /* Ports. */
248 switch (sm->proto) {
249 case IPPROTO_TCP:
250 if ((sm->src_port <= opt_highest_port) && hs) {
251 ps = host_get_port_tcp(hs, sm->src_port);
252 ps->out += sm->len;
253 ps->total += sm->len;
254 }
255 if ((sm->dst_port <= opt_highest_port) && hd) {
256 pd = host_get_port_tcp(hd, sm->dst_port);
257 pd->in += sm->len;
258 pd->total += sm->len;
259 if (sm->tcp_flags == TH_SYN)
260 pd->u.port_tcp.syn++;
261 }
262 break;
263
264 case IPPROTO_UDP:
265 if ((sm->src_port <= opt_highest_port) && hs) {
266 ps = host_get_port_udp(hs, sm->src_port);
267 ps->out += sm->len;
268 ps->total += sm->len;
269 }
270 if ((sm->dst_port <= opt_highest_port) && hd) {
271 pd = host_get_port_udp(hd, sm->dst_port);
272 pd->in += sm->len;
273 pd->total += sm->len;
274 }
275 break;
276
277 case IPPROTO_ICMP:
278 case IPPROTO_ICMPV6:
279 case IPPROTO_AH:
280 case IPPROTO_ESP:
281 case IPPROTO_OSPF:
282 /* known protocol, don't complain about it */
283 break;
284
285 default:
286 verbosef("unknown IP proto (%04x)", sm->proto);
287 }
288 }
289
290 /* vim:set ts=3 sw=3 tw=78 expandtab: */