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