Get this compiling again on FreeBSD
[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 "conv.h"
22 #include "daylog.h"
23 #include "err.h"
24 #include "hosts_db.h"
25 #include "localip.h"
26 #include "now.h"
27
28 #include <arpa/inet.h> /* for inet_aton() */
29 #define __FAVOR_BSD
30 #include <netinet/tcp.h>
31 #include <sys/socket.h>
32 #include <stdlib.h> /* for free */
33 #include <string.h> /* for memcpy */
34
35 uint64_t total_packets = 0, total_bytes = 0;
36
37 static int using_localnet = 0;
38 static in_addr_t localnet, localmask;
39
40 /* Parse the net/mask specification into two IPs or die trying. */
41 void
42 acct_init_localnet(const char *spec)
43 {
44 char **tokens;
45 int num_tokens;
46 struct in_addr addr;
47
48 tokens = split('/', spec, &num_tokens);
49 if (num_tokens != 2)
50 errx(1, "expecting network/netmask, got \"%s\"", spec);
51
52 if (inet_aton(tokens[0], &addr) != 1)
53 errx(1, "invalid network address \"%s\"", tokens[0]);
54 localnet = addr.s_addr;
55
56 if (inet_aton(tokens[1], &addr) != 1)
57 errx(1, "invalid network mask \"%s\"", tokens[1]);
58 localmask = addr.s_addr;
59 /* FIXME: improve so we can accept masks like /24 for 255.255.255.0 */
60
61 using_localnet = 1;
62 free(tokens[0]);
63 free(tokens[1]);
64 free(tokens);
65
66 verbosef("local network address: %s", ip_to_str(localnet));
67 verbosef(" local network mask: %s", ip_to_str(localmask));
68
69 if ((localnet & localmask) != localnet)
70 errx(1, "this is an invalid combination of address and mask!\n"
71 "it cannot match any address!");
72 }
73
74 /* Account for the given packet summary. */
75 void
76 acct_for(const pktsummary *sm)
77 {
78 struct bucket *hs = NULL, *hd = NULL;
79 struct bucket *ps, *pd;
80 int dir_in, dir_out;
81
82 #if 0 /* WANT_CHATTY? */
83 printf("%15s > ", ip_to_str(sm->src_ip));
84 printf("%15s ", ip_to_str(sm->dest_ip));
85 printf("len %4d proto %2d", sm->len, sm->proto);
86
87 if (sm->proto == IPPROTO_TCP || sm->proto == IPPROTO_UDP)
88 printf(" port %5d : %5d", sm->src_port, sm->dest_port);
89 if (sm->proto == IPPROTO_TCP)
90 printf(" %s%s%s%s%s%s",
91 (sm->tcp_flags & TH_FIN)?"F":"",
92 (sm->tcp_flags & TH_SYN)?"S":"",
93 (sm->tcp_flags & TH_RST)?"R":"",
94 (sm->tcp_flags & TH_PUSH)?"P":"",
95 (sm->tcp_flags & TH_ACK)?"A":"",
96 (sm->tcp_flags & TH_URG)?"U":""
97 );
98 printf("\n");
99 #endif
100
101 /* Totals. */
102 total_packets++;
103 total_bytes += sm->len;
104
105 /* Graphs. */
106 dir_in = dir_out = 0;
107
108 if (sm->af == AF_INET) {
109 if (using_localnet) {
110 if ((sm->src_ip & localmask) == localnet)
111 dir_out = 1;
112 if ((sm->dest_ip & localmask) == localnet)
113 dir_in = 1;
114 if (dir_in == 1 && dir_out == 1)
115 /* Traffic staying within the network isn't counted. */
116 dir_in = dir_out = 0;
117 } else {
118 if (sm->src_ip == localip)
119 dir_out = 1;
120 if (sm->dest_ip == localip)
121 dir_in = 1;
122 }
123 } else if (sm->af == AF_INET6) {
124 /* Only exact address has been implemented. */
125 if (memcmp(&sm->src_ip6, &localip6, sizeof(localip6)) == 0)
126 dir_out = 1;
127 if (memcmp(&sm->dest_ip6, &localip6, sizeof(localip6)) == 0)
128 dir_in = 1;
129 }
130
131 if (dir_out) {
132 daylog_acct((uint64_t)sm->len, GRAPH_OUT);
133 graph_acct((uint64_t)sm->len, GRAPH_OUT);
134 }
135 if (dir_in) {
136 daylog_acct((uint64_t)sm->len, GRAPH_IN);
137 graph_acct((uint64_t)sm->len, GRAPH_IN);
138 }
139
140 if (sm->af == AF_INET6) return; /* Still no continuation for IPv6! */
141
142 if (hosts_max == 0) return; /* skip per-host accounting */
143
144 /* Hosts. */
145 hs = host_get(sm->src_ip);
146 hs->out += sm->len;
147 hs->total += sm->len;
148 memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
149 hs->u.host.last_seen = now;
150
151 hd = host_get(sm->dest_ip); /* this can invalidate hs! */
152 hd->in += sm->len;
153 hd->total += sm->len;
154 memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac));
155 hd->u.host.last_seen = now;
156
157 /* Protocols. */
158 hs = host_find(sm->src_ip);
159 if (hs != NULL) {
160 ps = host_get_ip_proto(hs, sm->proto);
161 ps->out += sm->len;
162 ps->total += sm->len;
163 }
164
165 pd = host_get_ip_proto(hd, sm->proto);
166 pd->in += sm->len;
167 pd->total += sm->len;
168
169 if (ports_max == 0) return; /* skip ports accounting */
170
171 /* Ports. */
172 switch (sm->proto)
173 {
174 case IPPROTO_TCP:
175 if ((sm->src_port <= highest_port) && (hs != NULL))
176 {
177 ps = host_get_port_tcp(hs, sm->src_port);
178 ps->out += sm->len;
179 ps->total += sm->len;
180 }
181
182 if (sm->dest_port <= highest_port)
183 {
184 pd = host_get_port_tcp(hd, sm->dest_port);
185 pd->in += sm->len;
186 pd->total += sm->len;
187 if (sm->tcp_flags == TH_SYN)
188 pd->u.port_tcp.syn++;
189 }
190 break;
191
192 case IPPROTO_UDP:
193 if ((sm->src_port <= highest_port) && (hs != NULL))
194 {
195 ps = host_get_port_udp(hs, sm->src_port);
196 ps->out += sm->len;
197 ps->total += sm->len;
198 }
199
200 if (sm->dest_port <= highest_port)
201 {
202 pd = host_get_port_udp(hd, sm->dest_port);
203 pd->in += sm->len;
204 pd->total += sm->len;
205 }
206 break;
207
208 case IPPROTO_ICMP:
209 case IPPROTO_ICMPV6:
210 /* known protocol, don't complain about it */
211 break;
212
213 default:
214 verbosef("unknown IP proto (%04x)", sm->proto);
215 }
216 }
217
218 /* vim:set ts=3 sw=3 tw=78 expandtab: */