Prefix all global variables to aid debugging.
[darkstat] / acct.c
1 /* darkstat 3
2 * copyright (c) 2001-2008 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 "darkstat.h"
20 #include "acct.h"
21 #include "decode.h"
22 #include "conv.h"
23 #include "daylog.h"
24 #include "err.h"
25 #include "hosts_db.h"
26 #include "localip.h"
27 #include "now.h"
28
29 #include <arpa/inet.h> /* for inet_aton() */
30 #define __FAVOR_BSD
31 #include <netinet/tcp.h>
32 #include <sys/socket.h>
33 #include <assert.h>
34 #include <ctype.h> /* for isdigit */
35 #include <netdb.h> /* for gai_strerror */
36 #include <stdlib.h> /* for free */
37 #include <string.h> /* for memcpy */
38
39 uint64_t acct_total_packets = 0, acct_total_bytes = 0;
40
41 static int using_localnet4 = 0, using_localnet6 = 0;
42 static struct addr localnet4, localmask4, localnet6, localmask6;
43
44 /* Parse the net/mask specification into two IPs or die trying. */
45 void
46 acct_init_localnet(const char *spec)
47 {
48 char **tokens;
49 unsigned int num_tokens;
50 int isnum, j, ret;
51 int pfxlen, octets, remainder;
52 struct addr localnet, localmask;
53
54 tokens = split('/', spec, &num_tokens);
55 if (num_tokens != 2)
56 errx(1, "expecting network/netmask, got \"%s\"", spec);
57
58 if ((ret = str_to_addr(tokens[0], &localnet)) != 0)
59 errx(1, "couldn't parse \"%s\": %s", tokens[0], gai_strerror(ret));
60
61 /* Detect a purely numeric argument. */
62 isnum = 0;
63 {
64 const char *p = tokens[1];
65 while (*p != '\0') {
66 if (isdigit(*p)) {
67 isnum = 1;
68 ++p;
69 continue;
70 } else {
71 isnum = 0;
72 break;
73 }
74 }
75 }
76
77 if (!isnum) {
78 if ((ret = str_to_addr(tokens[1], &localmask)) != 0)
79 errx(1, "couldn't parse \"%s\": %s", tokens[1], gai_strerror(ret));
80 if (localmask.family != localnet.family)
81 errx(1, "family mismatch between net and mask");
82 } else {
83 uint8_t frac, *p;
84
85 localmask.family = localnet.family;
86
87 /* Compute the prefix length. */
88 pfxlen = (int)strtonum(tokens[1], 1,
89 (localnet.family == IPv6) ? 128 : 32, NULL);
90
91 if (pfxlen == 0)
92 errx(1, "invalid network prefix length \"%s\"", tokens[1]);
93
94 /* Construct the network mask. */
95 octets = pfxlen / 8;
96 remainder = pfxlen % 8;
97 p = (localnet.family == IPv6) ? (localmask.ip.v6.s6_addr)
98 : ((uint8_t *) &(localmask.ip.v4));
99
100 if (localnet.family == IPv6)
101 memset(p, 0, 16);
102 else
103 memset(p, 0, 4);
104
105 for (j = 0; j < octets; ++j)
106 p[j] = 0xff;
107
108 frac = (uint8_t)(0xff << (8 - remainder));
109 if (frac)
110 p[j] = frac; /* Have contribution for next position. */
111 }
112
113 free(tokens[0]);
114 free(tokens[1]);
115 free(tokens);
116
117 /* Register the correct netmask and calculate the correct net. */
118 addr_mask(&localnet, &localmask);
119 if (localnet.family == IPv6) {
120 using_localnet6 = 1;
121 localnet6 = localnet;
122 localmask6 = localmask;
123 } else {
124 using_localnet4 = 1;
125 localnet4 = localnet;
126 localmask4 = localmask;
127 }
128
129 verbosef("local network address: %s", addr_to_str(&localnet));
130 verbosef(" local network mask: %s", addr_to_str(&localmask));
131 }
132
133 static int
134 addr_is_local(const struct addr * const a)
135 {
136 if (a->family == IPv4) {
137 if (using_localnet4) {
138 if (addr_inside(a, &localnet4, &localmask4))
139 return 1;
140 } else {
141 if (addr_equal(a, &localip4))
142 return 1;
143 }
144 } else {
145 assert(a->family == IPv6);
146 if (using_localnet6) {
147 if (addr_inside(a, &localnet6, &localmask6))
148 return 1;
149 } else {
150 if (addr_equal(a, &localip6))
151 return 1;
152 }
153 }
154 return 0;
155 }
156
157 /* Account for the given packet summary. */
158 void
159 acct_for(const struct pktsummary * const sm)
160 {
161 struct bucket *hs = NULL, *hd = NULL;
162 struct bucket *ps, *pd;
163 int dir_in, dir_out;
164
165 #if 0 /* WANT_CHATTY? */
166 printf("%15s > ", addr_to_str(&sm->src));
167 printf("%15s ", addr_to_str(&sm->dst));
168 printf("len %4d proto %2d", sm->len, sm->proto);
169
170 if (sm->proto == IPPROTO_TCP || sm->proto == IPPROTO_UDP)
171 printf(" port %5d : %5d", sm->src_port, sm->dst_port);
172 if (sm->proto == IPPROTO_TCP)
173 printf(" %s%s%s%s%s%s",
174 (sm->tcp_flags & TH_FIN)?"F":"",
175 (sm->tcp_flags & TH_SYN)?"S":"",
176 (sm->tcp_flags & TH_RST)?"R":"",
177 (sm->tcp_flags & TH_PUSH)?"P":"",
178 (sm->tcp_flags & TH_ACK)?"A":"",
179 (sm->tcp_flags & TH_URG)?"U":""
180 );
181 printf("\n");
182 #endif
183
184 /* Totals. */
185 acct_total_packets++;
186 acct_total_bytes += sm->len;
187
188 /* Graphs. */
189 dir_out = addr_is_local(&(sm->src));
190 dir_in = addr_is_local(&(sm->dst));
191
192 /* Traffic staying within the network isn't counted. */
193 if (dir_in == 1 && dir_out == 1)
194 dir_in = dir_out = 0;
195
196 if (dir_out) {
197 daylog_acct((uint64_t)sm->len, GRAPH_OUT);
198 graph_acct((uint64_t)sm->len, GRAPH_OUT);
199 }
200 if (dir_in) {
201 daylog_acct((uint64_t)sm->len, GRAPH_IN);
202 graph_acct((uint64_t)sm->len, GRAPH_IN);
203 }
204
205 if (opt_hosts_max == 0) return; /* skip per-host accounting */
206
207 /* Hosts. */
208 hosts_db_reduce();
209 hs = host_get(&(sm->src));
210 hs->out += sm->len;
211 hs->total += sm->len;
212 memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
213 hs->u.host.last_seen = now;
214
215 hd = host_get(&(sm->dst));
216 hd->in += sm->len;
217 hd->total += sm->len;
218 memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac));
219 hd->u.host.last_seen = now;
220
221 /* Protocols. */
222 if (sm->proto != IPPROTO_INVALID) {
223 ps = host_get_ip_proto(hs, sm->proto);
224 ps->out += sm->len;
225 ps->total += sm->len;
226
227 pd = host_get_ip_proto(hd, sm->proto);
228 pd->in += sm->len;
229 pd->total += sm->len;
230 }
231
232 if (opt_ports_max == 0) return; /* skip ports accounting */
233
234 /* Ports. */
235 switch (sm->proto) {
236 case IPPROTO_TCP:
237 if (sm->src_port <= opt_highest_port) {
238 ps = host_get_port_tcp(hs, sm->src_port);
239 ps->out += sm->len;
240 ps->total += sm->len;
241 }
242
243 if (sm->dst_port <= opt_highest_port) {
244 pd = host_get_port_tcp(hd, sm->dst_port);
245 pd->in += sm->len;
246 pd->total += sm->len;
247 if (sm->tcp_flags == TH_SYN)
248 pd->u.port_tcp.syn++;
249 }
250 break;
251
252 case IPPROTO_UDP:
253 if (sm->src_port <= opt_highest_port) {
254 ps = host_get_port_udp(hs, sm->src_port);
255 ps->out += sm->len;
256 ps->total += sm->len;
257 }
258
259 if (sm->dst_port <= opt_highest_port) {
260 pd = host_get_port_udp(hd, sm->dst_port);
261 pd->in += sm->len;
262 pd->total += sm->len;
263 }
264 break;
265
266 case IPPROTO_ICMP:
267 case IPPROTO_ICMPV6:
268 case IPPROTO_AH:
269 case IPPROTO_ESP:
270 case IPPROTO_OSPF:
271 /* known protocol, don't complain about it */
272 break;
273
274 default:
275 verbosef("unknown IP proto (%04x)", sm->proto);
276 }
277 }
278
279 /* vim:set ts=3 sw=3 tw=78 expandtab: */