Migration from 'in_addr_t' to 'struct addr46'.
[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_af(&localnet, AF_INET));
67 verbosef(" local network mask: %s", ip_to_str_af(&localmask, AF_INET));
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 struct addr46 ipaddr;
81 int dir_in, dir_out;
82
83 #if 0 /* WANT_CHATTY? */
84 printf("%15s > ", ip_to_str_af(&sm->src_ip, AF_INET));
85 printf("%15s ", ip_to_str_af(&sm->dest_ip, AF_INET));
86 printf("len %4d proto %2d", sm->len, sm->proto);
87
88 if (sm->proto == IPPROTO_TCP || sm->proto == IPPROTO_UDP)
89 printf(" port %5d : %5d", sm->src_port, sm->dest_port);
90 if (sm->proto == IPPROTO_TCP)
91 printf(" %s%s%s%s%s%s",
92 (sm->tcp_flags & TH_FIN)?"F":"",
93 (sm->tcp_flags & TH_SYN)?"S":"",
94 (sm->tcp_flags & TH_RST)?"R":"",
95 (sm->tcp_flags & TH_PUSH)?"P":"",
96 (sm->tcp_flags & TH_ACK)?"A":"",
97 (sm->tcp_flags & TH_URG)?"U":""
98 );
99 printf("\n");
100 #endif
101
102 /* Totals. */
103 total_packets++;
104 total_bytes += sm->len;
105
106 /* Graphs. */
107 dir_in = dir_out = 0;
108
109 if (sm->af == AF_INET) {
110 if (using_localnet) {
111 if ((sm->src_ip.s_addr & localmask) == localnet)
112 dir_out = 1;
113 if ((sm->dest_ip.s_addr & localmask) == localnet)
114 dir_in = 1;
115 if (dir_in == 1 && dir_out == 1)
116 /* Traffic staying within the network isn't counted. */
117 dir_in = dir_out = 0;
118 } else {
119 if (memcmp(&sm->src_ip, &localip, sizeof(localip)) == 0)
120 dir_out = 1;
121 if (memcmp(&sm->dest_ip, &localip, sizeof(localip)) == 0)
122 dir_in = 1;
123 }
124 } else if (sm->af == AF_INET6) {
125 /* Only exact address has been implemented. */
126 if (memcmp(&sm->src_ip6, &localip6, sizeof(localip6)) == 0)
127 dir_out = 1;
128 if (memcmp(&sm->dest_ip6, &localip6, sizeof(localip6)) == 0)
129 dir_in = 1;
130 }
131
132 if (dir_out) {
133 daylog_acct((uint64_t)sm->len, GRAPH_OUT);
134 graph_acct((uint64_t)sm->len, GRAPH_OUT);
135 }
136 if (dir_in) {
137 daylog_acct((uint64_t)sm->len, GRAPH_IN);
138 graph_acct((uint64_t)sm->len, GRAPH_IN);
139 }
140
141 if (sm->af == AF_INET6) return; /* Still no continuation for IPv6! */
142
143 if (hosts_max == 0) return; /* skip per-host accounting */
144
145 /* Hosts. */
146 ipaddr.af = sm->af; /* TODO */
147 ipaddr.addr.ip = sm->src_ip; /* TODO */
148 hs = host_get(&ipaddr);
149 hs->out += sm->len;
150 hs->total += sm->len;
151 memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
152 hs->u.host.last_seen = now;
153
154 ipaddr.addr.ip = sm->dest_ip; /* TODO */
155 hd = host_get(&ipaddr); /* this can invalidate hs! */
156 hd->in += sm->len;
157 hd->total += sm->len;
158 memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac));
159 hd->u.host.last_seen = now;
160
161 /* Protocols. */
162 ipaddr.addr.ip = sm->src_ip; /* TODO */
163 hs = host_find(&ipaddr);
164 if (hs != NULL) {
165 ps = host_get_ip_proto(hs, sm->proto);
166 ps->out += sm->len;
167 ps->total += sm->len;
168 }
169
170 pd = host_get_ip_proto(hd, sm->proto);
171 pd->in += sm->len;
172 pd->total += sm->len;
173
174 if (ports_max == 0) return; /* skip ports accounting */
175
176 /* Ports. */
177 switch (sm->proto)
178 {
179 case IPPROTO_TCP:
180 if ((sm->src_port <= highest_port) && (hs != NULL))
181 {
182 ps = host_get_port_tcp(hs, sm->src_port);
183 ps->out += sm->len;
184 ps->total += sm->len;
185 }
186
187 if (sm->dest_port <= highest_port)
188 {
189 pd = host_get_port_tcp(hd, sm->dest_port);
190 pd->in += sm->len;
191 pd->total += sm->len;
192 if (sm->tcp_flags == TH_SYN)
193 pd->u.port_tcp.syn++;
194 }
195 break;
196
197 case IPPROTO_UDP:
198 if ((sm->src_port <= highest_port) && (hs != NULL))
199 {
200 ps = host_get_port_udp(hs, sm->src_port);
201 ps->out += sm->len;
202 ps->total += sm->len;
203 }
204
205 if (sm->dest_port <= highest_port)
206 {
207 pd = host_get_port_udp(hd, sm->dest_port);
208 pd->in += sm->len;
209 pd->total += sm->len;
210 }
211 break;
212
213 case IPPROTO_ICMP:
214 case IPPROTO_ICMPV6:
215 /* known protocol, don't complain about it */
216 break;
217
218 default:
219 verbosef("unknown IP proto (%04x)", sm->proto);
220 }
221 }
222
223 /* vim:set ts=3 sw=3 tw=78 expandtab: */