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