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