Fix up includes in html.c
[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 total_packets = 0, 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 int num_tokens, 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
84 localmask.family = localnet.family;
85
86 /* Compute the prefix length. */
87 pfxlen = strtonum(tokens[1], 1,
88 (localnet.family == IPv6) ? 128 : 32, NULL);
89 if (pfxlen == 0)
90 errx(1, "invalid network prefix length \"%s\"", tokens[1]);
91
92 /* Construct the network mask. */
93 octets = pfxlen / 8;
94 remainder = pfxlen % 8;
95 p = (localnet.family == IPv6) ? (localmask.ip.v6.s6_addr)
96 : ((uint8_t *) &(localmask.ip.v4));
97
98 if (localnet.family == IPv6)
99 memset(p, 0, 16);
100 else
101 memset(p, 0, 4);
102
103 for (j = 0; j < octets; ++j)
104 p[j] = 0xff;
105
106 frac = 0xff << (8 - remainder);
107 if (frac)
108 p[j] = frac; /* Have contribution for next position. */
109 }
110
111 free(tokens[0]);
112 free(tokens[1]);
113 free(tokens);
114
115 /* Register the correct netmask and calculate the correct net. */
116 addr_mask(&localnet, &localmask);
117 if (localnet.family == IPv6) {
118 using_localnet6 = 1;
119 localnet6 = localnet;
120 localmask6 = localmask;
121 } else {
122 using_localnet4 = 1;
123 localnet4 = localnet;
124 localmask4 = localmask;
125 }
126
127 verbosef("local network address: %s", addr_to_str(&localnet));
128 verbosef(" local network mask: %s", addr_to_str(&localmask));
129 }
130
131 static int
132 addr_is_local(const struct addr * const a)
133 {
134 if (a->family == IPv4) {
135 if (using_localnet4) {
136 if (addr_inside(a, &localnet4, &localmask4))
137 return 1;
138 } else {
139 if (addr_equal(a, &localip4))
140 return 1;
141 }
142 } else {
143 assert(a->family == IPv6);
144 if (using_localnet6) {
145 if (addr_inside(a, &localnet6, &localmask6))
146 return 1;
147 } else {
148 if (addr_equal(a, &localip6))
149 return 1;
150 }
151 }
152 return 0;
153 }
154
155 /* Account for the given packet summary. */
156 void
157 acct_for(const struct pktsummary * const sm)
158 {
159 struct bucket *hs = NULL, *hd = NULL;
160 struct bucket *ps, *pd;
161 int dir_in, dir_out;
162
163 #if 0 /* WANT_CHATTY? */
164 printf("%15s > ", addr_to_str(&sm->src));
165 printf("%15s ", addr_to_str(&sm->dst));
166 printf("len %4d proto %2d", sm->len, sm->proto);
167
168 if (sm->proto == IPPROTO_TCP || sm->proto == IPPROTO_UDP)
169 printf(" port %5d : %5d", sm->src_port, sm->dst_port);
170 if (sm->proto == IPPROTO_TCP)
171 printf(" %s%s%s%s%s%s",
172 (sm->tcp_flags & TH_FIN)?"F":"",
173 (sm->tcp_flags & TH_SYN)?"S":"",
174 (sm->tcp_flags & TH_RST)?"R":"",
175 (sm->tcp_flags & TH_PUSH)?"P":"",
176 (sm->tcp_flags & TH_ACK)?"A":"",
177 (sm->tcp_flags & TH_URG)?"U":""
178 );
179 printf("\n");
180 #endif
181
182 /* Totals. */
183 total_packets++;
184 total_bytes += sm->len;
185
186 /* Graphs. */
187 dir_out = addr_is_local(&(sm->src));
188 dir_in = addr_is_local(&(sm->dst));
189
190 /* Traffic staying within the network isn't counted. */
191 if (dir_in == 1 && dir_out == 1)
192 dir_in = dir_out = 0;
193
194 if (dir_out) {
195 daylog_acct((uint64_t)sm->len, GRAPH_OUT);
196 graph_acct((uint64_t)sm->len, GRAPH_OUT);
197 }
198 if (dir_in) {
199 daylog_acct((uint64_t)sm->len, GRAPH_IN);
200 graph_acct((uint64_t)sm->len, GRAPH_IN);
201 }
202
203 if (hosts_max == 0) return; /* skip per-host accounting */
204
205 /* Hosts. */
206 hosts_db_reduce();
207 hs = host_get(&(sm->src));
208 hs->out += sm->len;
209 hs->total += sm->len;
210 memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
211 hs->u.host.last_seen = now;
212
213 hd = host_get(&(sm->dst));
214 hd->in += sm->len;
215 hd->total += sm->len;
216 memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac));
217 hd->u.host.last_seen = now;
218
219 /* Protocols. */
220 if (sm->proto != IPPROTO_INVALID) {
221 ps = host_get_ip_proto(hs, sm->proto);
222 ps->out += sm->len;
223 ps->total += sm->len;
224
225 pd = host_get_ip_proto(hd, sm->proto);
226 pd->in += sm->len;
227 pd->total += sm->len;
228 }
229
230 if (ports_max == 0) return; /* skip ports accounting */
231
232 /* Ports. */
233 switch (sm->proto) {
234 case IPPROTO_TCP:
235 if (sm->src_port <= highest_port) {
236 ps = host_get_port_tcp(hs, sm->src_port);
237 ps->out += sm->len;
238 ps->total += sm->len;
239 }
240
241 if (sm->dst_port <= highest_port) {
242 pd = host_get_port_tcp(hd, sm->dst_port);
243 pd->in += sm->len;
244 pd->total += sm->len;
245 if (sm->tcp_flags == TH_SYN)
246 pd->u.port_tcp.syn++;
247 }
248 break;
249
250 case IPPROTO_UDP:
251 if (sm->src_port <= highest_port) {
252 ps = host_get_port_udp(hs, sm->src_port);
253 ps->out += sm->len;
254 ps->total += sm->len;
255 }
256
257 if (sm->dst_port <= highest_port) {
258 pd = host_get_port_udp(hd, sm->dst_port);
259 pd->in += sm->len;
260 pd->total += sm->len;
261 }
262 break;
263
264 case IPPROTO_ICMP:
265 case IPPROTO_ICMPV6:
266 case IPPROTO_AH:
267 case IPPROTO_ESP:
268 case IPPROTO_OSPF:
269 /* known protocol, don't complain about it */
270 break;
271
272 default:
273 verbosef("unknown IP proto (%04x)", sm->proto);
274 }
275 }
276
277 /* vim:set ts=3 sw=3 tw=78 expandtab: */