fb56034beb88aac9ab2ed3f09956bb4b94cc4d7a
[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 (using_localnet) {
108 if ((sm->src_ip & localmask) == localnet)
109 dir_out = 1;
110 if ((sm->dest_ip & localmask) == localnet)
111 dir_in = 1;
112 if (dir_in == 1 && dir_out == 1)
113 /* Traffic staying within the network isn't counted. */
114 dir_in = dir_out = 0;
115 } else {
116 if (sm->src_ip == localip)
117 dir_out = 1;
118 if (sm->dest_ip == localip)
119 dir_in = 1;
120 }
121
122 if (dir_out) {
123 daylog_acct((uint64_t)sm->len, GRAPH_OUT);
124 graph_acct((uint64_t)sm->len, GRAPH_OUT);
125 }
126 if (dir_in) {
127 daylog_acct((uint64_t)sm->len, GRAPH_IN);
128 graph_acct((uint64_t)sm->len, GRAPH_IN);
129 }
130
131 if (hosts_max == 0) return; /* skip per-host accounting */
132
133 /* Hosts. */
134 hs = host_get(sm->src_ip);
135 hs->out += sm->len;
136 hs->total += sm->len;
137 memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
138 hs->u.host.last_seen = now;
139
140 hd = host_get(sm->dest_ip); /* this can invalidate hs! */
141 hd->in += sm->len;
142 hd->total += sm->len;
143 memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac));
144 hd->u.host.last_seen = now;
145
146 /* Protocols. */
147 hs = host_find(sm->src_ip);
148 if (hs != NULL) {
149 ps = host_get_ip_proto(hs, sm->proto);
150 ps->out += sm->len;
151 ps->total += sm->len;
152 }
153
154 pd = host_get_ip_proto(hd, sm->proto);
155 pd->in += sm->len;
156 pd->total += sm->len;
157
158 if (ports_max == 0) return; /* skip ports accounting */
159
160 /* Ports. */
161 switch (sm->proto)
162 {
163 case IPPROTO_TCP:
164 if ((sm->src_port <= highest_port) && (hs != NULL))
165 {
166 ps = host_get_port_tcp(hs, sm->src_port);
167 ps->out += sm->len;
168 ps->total += sm->len;
169 }
170
171 if (sm->dest_port <= highest_port)
172 {
173 pd = host_get_port_tcp(hd, sm->dest_port);
174 pd->in += sm->len;
175 pd->total += sm->len;
176 if (sm->tcp_flags == TH_SYN)
177 pd->u.port_tcp.syn++;
178 }
179 break;
180
181 case IPPROTO_UDP:
182 if ((sm->src_port <= highest_port) && (hs != NULL))
183 {
184 ps = host_get_port_udp(hs, sm->src_port);
185 ps->out += sm->len;
186 ps->total += sm->len;
187 }
188
189 if (sm->dest_port <= highest_port)
190 {
191 pd = host_get_port_udp(hd, sm->dest_port);
192 pd->in += sm->len;
193 pd->total += sm->len;
194 }
195 break;
196
197 case IPPROTO_ICMP:
198 /* known protocol, don't complain about it */
199 break;
200
201 default:
202 verbosef("unknown IP proto (%04x)", sm->proto);
203 }
204 }
205
206 /* vim:set ts=3 sw=3 tw=78 expandtab: */