Update ChangeLog for 3.0.717.
[darkstat] / 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 addr_is_local(const struct addr * const a,
137 const struct local_ips *local_ips) {
138 if (is_localip(a, local_ips))
139 return 1;
140 if (a->family == IPv4 && using_localnet4) {
141 if (addr_inside(a, &localnet4, &localmask4))
142 return 1;
143 } else if (a->family == IPv6 && using_localnet6) {
144 if (addr_inside(a, &localnet6, &localmask6))
145 return 1;
146 }
147 return 0;
148 }
149
150 /* Account for the given packet summary. */
151 void acct_for(const struct pktsummary * const sm,
152 const struct local_ips * const local_ips) {
153 struct bucket *hs = NULL, *hd = NULL;
154 struct bucket *ps, *pd;
155 int dir_in, dir_out;
156
157 #if 0 /* WANT_CHATTY? */
158 printf("%15s > ", addr_to_str(&sm->src));
159 printf("%15s ", addr_to_str(&sm->dst));
160 printf("len %4d proto %2d", sm->len, sm->proto);
161
162 if (sm->proto == IPPROTO_TCP || sm->proto == IPPROTO_UDP)
163 printf(" port %5d : %5d", sm->src_port, sm->dst_port);
164 if (sm->proto == IPPROTO_TCP)
165 printf(" %s%s%s%s%s%s",
166 (sm->tcp_flags & TH_FIN)?"F":"",
167 (sm->tcp_flags & TH_SYN)?"S":"",
168 (sm->tcp_flags & TH_RST)?"R":"",
169 (sm->tcp_flags & TH_PUSH)?"P":"",
170 (sm->tcp_flags & TH_ACK)?"A":"",
171 (sm->tcp_flags & TH_URG)?"U":""
172 );
173 printf("\n");
174 #endif
175
176 /* Totals. */
177 acct_total_packets++;
178 acct_total_bytes += sm->len;
179
180 /* Graphs. */
181 dir_out = addr_is_local(&sm->src, local_ips);
182 dir_in = addr_is_local(&sm->dst, local_ips);
183
184 /* Traffic staying within the network isn't counted. */
185 if (dir_out && !dir_in) {
186 daylog_acct((uint64_t)sm->len, GRAPH_OUT);
187 graph_acct((uint64_t)sm->len, GRAPH_OUT);
188 }
189 if (dir_in && !dir_out) {
190 daylog_acct((uint64_t)sm->len, GRAPH_IN);
191 graph_acct((uint64_t)sm->len, GRAPH_IN);
192 }
193
194 if (opt_hosts_max == 0) return; /* skip per-host accounting */
195
196 /* Hosts. */
197 hosts_db_reduce();
198 if (!opt_want_local_only || dir_out) {
199 hs = host_get(&(sm->src));
200 hs->out += sm->len;
201 hs->total += sm->len;
202 memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
203 hs->u.host.last_seen_mono = now_mono();
204 }
205
206 if (!opt_want_local_only || dir_in) {
207 hd = host_get(&(sm->dst));
208 hd->in += sm->len;
209 hd->total += sm->len;
210 memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac));
211 /*
212 * Don't update recipient's last seen time, we don't know that
213 * they received successfully.
214 */
215 }
216
217 /* Protocols. */
218 if (sm->proto != IPPROTO_INVALID) {
219 if (hs) {
220 ps = host_get_ip_proto(hs, sm->proto);
221 ps->out += sm->len;
222 ps->total += sm->len;
223 }
224 if (hd) {
225 pd = host_get_ip_proto(hd, sm->proto);
226 pd->in += sm->len;
227 pd->total += sm->len;
228 }
229 }
230
231 if (opt_ports_max == 0) return; /* skip ports accounting */
232
233 /* Ports. */
234 switch (sm->proto) {
235 case IPPROTO_TCP:
236 if ((sm->src_port <= opt_highest_port) && hs) {
237 ps = host_get_port_tcp(hs, sm->src_port);
238 ps->out += sm->len;
239 ps->total += sm->len;
240 }
241 if ((sm->dst_port <= opt_highest_port) && hd) {
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 <= opt_highest_port) && hs) {
252 ps = host_get_port_udp(hs, sm->src_port);
253 ps->out += sm->len;
254 ps->total += sm->len;
255 }
256 if ((sm->dst_port <= opt_highest_port) && hd) {
257 pd = host_get_port_udp(hd, sm->dst_port);
258 pd->in += sm->len;
259 pd->total += sm->len;
260 }
261 break;
262
263 case IPPROTO_INVALID:
264 /* proto decoding failed, don't complain in accounting */
265 break;
266
267 default:
268 verbosef("acct_for: unknown IP protocol 0x%02x", sm->proto);
269 }
270 }
271
272 /* vim:set ts=3 sw=3 tw=78 expandtab: */