Use strtol instead of strtonum.
[darkstat] / acct.c
1 /* darkstat 3
2 * copyright (c) 2001-2011 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 "acct.h"
20 #include "decode.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 #include "opt.h"
28
29 #include <arpa/inet.h> /* for inet_aton() */
30 #define __FAVOR_BSD
31 #include <netinet/tcp.h>
32 #include <sys/socket.h>
33 #include <assert.h>
34 #include <ctype.h> /* for isdigit */
35 #include <netdb.h> /* for gai_strerror */
36 #include <stdlib.h> /* for free */
37 #include <string.h> /* for memcpy */
38
39 uint64_t acct_total_packets = 0, acct_total_bytes = 0;
40
41 static int using_localnet4 = 0, using_localnet6 = 0;
42 static struct addr localnet4, localmask4, localnet6, localmask6;
43
44 /* Parse the net/mask specification into two IPs or die trying. */
45 void
46 acct_init_localnet(const char *spec)
47 {
48 char **tokens;
49 unsigned int num_tokens;
50 int isnum, j, ret;
51 int pfxlen, octets, remainder;
52 struct addr localnet, localmask;
53
54 tokens = split('/', spec, &num_tokens);
55 if (num_tokens != 2)
56 errx(1, "expecting network/netmask, got \"%s\"", spec);
57
58 if ((ret = str_to_addr(tokens[0], &localnet)) != 0)
59 errx(1, "couldn't parse \"%s\": %s", tokens[0], gai_strerror(ret));
60
61 /* Detect a purely numeric argument. */
62 isnum = 0;
63 {
64 const char *p = tokens[1];
65 while (*p != '\0') {
66 if (isdigit(*p)) {
67 isnum = 1;
68 ++p;
69 continue;
70 } else {
71 isnum = 0;
72 break;
73 }
74 }
75 }
76
77 if (!isnum) {
78 if ((ret = str_to_addr(tokens[1], &localmask)) != 0)
79 errx(1, "couldn't parse \"%s\": %s", tokens[1], gai_strerror(ret));
80 if (localmask.family != localnet.family)
81 errx(1, "family mismatch between net and mask");
82 } else {
83 uint8_t frac, *p;
84 char *endptr;
85
86 localmask.family = localnet.family;
87
88 /* Compute the prefix length. */
89 pfxlen = (unsigned int)strtol(tokens[1], &endptr, 10);
90
91 if ((pfxlen < 0) ||
92 ((localnet.family == IPv6) && (pfxlen > 128)) ||
93 ((localnet.family == IPv4) && (pfxlen > 32)) ||
94 (tokens[1][0] == '\0') ||
95 (*endptr != '\0'))
96 errx(1, "invalid network prefix length \"%s\"", tokens[1]);
97
98 /* Construct the network mask. */
99 octets = pfxlen / 8;
100 remainder = pfxlen % 8;
101 p = (localnet.family == IPv6) ? (localmask.ip.v6.s6_addr)
102 : ((uint8_t *) &(localmask.ip.v4));
103
104 if (localnet.family == IPv6)
105 memset(p, 0, 16);
106 else
107 memset(p, 0, 4);
108
109 for (j = 0; j < octets; ++j)
110 p[j] = 0xff;
111
112 frac = (uint8_t)(0xff << (8 - remainder));
113 if (frac)
114 p[j] = frac; /* Have contribution for next position. */
115 }
116
117 free(tokens[0]);
118 free(tokens[1]);
119 free(tokens);
120
121 /* Register the correct netmask and calculate the correct net. */
122 addr_mask(&localnet, &localmask);
123 if (localnet.family == IPv6) {
124 using_localnet6 = 1;
125 localnet6 = localnet;
126 localmask6 = localmask;
127 } else {
128 using_localnet4 = 1;
129 localnet4 = localnet;
130 localmask4 = localmask;
131 }
132
133 verbosef("local network address: %s", addr_to_str(&localnet));
134 verbosef(" local network mask: %s", addr_to_str(&localmask));
135 }
136
137 static int
138 addr_is_local(const struct addr * const a)
139 {
140 if (a->family == IPv4) {
141 if (using_localnet4) {
142 if (addr_inside(a, &localnet4, &localmask4))
143 return 1;
144 } else {
145 if (addr_equal(a, &localip4))
146 return 1;
147 }
148 } else {
149 assert(a->family == IPv6);
150 if (using_localnet6) {
151 if (addr_inside(a, &localnet6, &localmask6))
152 return 1;
153 } else {
154 if (addr_equal(a, &localip6))
155 return 1;
156 }
157 }
158 return 0;
159 }
160
161 /* Account for the given packet summary. */
162 void
163 acct_for(const struct pktsummary * const sm)
164 {
165 struct bucket *hs = NULL, *hd = NULL;
166 struct bucket *ps, *pd;
167 int dir_in, dir_out;
168
169 #if 0 /* WANT_CHATTY? */
170 printf("%15s > ", addr_to_str(&sm->src));
171 printf("%15s ", addr_to_str(&sm->dst));
172 printf("len %4d proto %2d", sm->len, sm->proto);
173
174 if (sm->proto == IPPROTO_TCP || sm->proto == IPPROTO_UDP)
175 printf(" port %5d : %5d", sm->src_port, sm->dst_port);
176 if (sm->proto == IPPROTO_TCP)
177 printf(" %s%s%s%s%s%s",
178 (sm->tcp_flags & TH_FIN)?"F":"",
179 (sm->tcp_flags & TH_SYN)?"S":"",
180 (sm->tcp_flags & TH_RST)?"R":"",
181 (sm->tcp_flags & TH_PUSH)?"P":"",
182 (sm->tcp_flags & TH_ACK)?"A":"",
183 (sm->tcp_flags & TH_URG)?"U":""
184 );
185 printf("\n");
186 #endif
187
188 /* Totals. */
189 acct_total_packets++;
190 acct_total_bytes += sm->len;
191
192 /* Graphs. */
193 dir_out = addr_is_local(&(sm->src));
194 dir_in = addr_is_local(&(sm->dst));
195
196 /* Traffic staying within the network isn't counted. */
197 if (dir_in == 1 && dir_out == 1)
198 dir_in = dir_out = 0;
199
200 if (dir_out) {
201 daylog_acct((uint64_t)sm->len, GRAPH_OUT);
202 graph_acct((uint64_t)sm->len, GRAPH_OUT);
203 }
204 if (dir_in) {
205 daylog_acct((uint64_t)sm->len, GRAPH_IN);
206 graph_acct((uint64_t)sm->len, GRAPH_IN);
207 }
208
209 if (opt_hosts_max == 0) return; /* skip per-host accounting */
210
211 /* Hosts. */
212 hosts_db_reduce();
213 hs = host_get(&(sm->src));
214 hs->out += sm->len;
215 hs->total += sm->len;
216 memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
217 hs->u.host.last_seen = now;
218
219 hd = host_get(&(sm->dst));
220 hd->in += sm->len;
221 hd->total += sm->len;
222 memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac));
223 hd->u.host.last_seen = now;
224
225 /* Protocols. */
226 if (sm->proto != IPPROTO_INVALID) {
227 ps = host_get_ip_proto(hs, sm->proto);
228 ps->out += sm->len;
229 ps->total += sm->len;
230
231 pd = host_get_ip_proto(hd, sm->proto);
232 pd->in += sm->len;
233 pd->total += sm->len;
234 }
235
236 if (opt_ports_max == 0) return; /* skip ports accounting */
237
238 /* Ports. */
239 switch (sm->proto) {
240 case IPPROTO_TCP:
241 if (sm->src_port <= opt_highest_port) {
242 ps = host_get_port_tcp(hs, sm->src_port);
243 ps->out += sm->len;
244 ps->total += sm->len;
245 }
246
247 if (sm->dst_port <= opt_highest_port) {
248 pd = host_get_port_tcp(hd, sm->dst_port);
249 pd->in += sm->len;
250 pd->total += sm->len;
251 if (sm->tcp_flags == TH_SYN)
252 pd->u.port_tcp.syn++;
253 }
254 break;
255
256 case IPPROTO_UDP:
257 if (sm->src_port <= opt_highest_port) {
258 ps = host_get_port_udp(hs, sm->src_port);
259 ps->out += sm->len;
260 ps->total += sm->len;
261 }
262
263 if (sm->dst_port <= opt_highest_port) {
264 pd = host_get_port_udp(hd, sm->dst_port);
265 pd->in += sm->len;
266 pd->total += sm->len;
267 }
268 break;
269
270 case IPPROTO_ICMP:
271 case IPPROTO_ICMPV6:
272 case IPPROTO_AH:
273 case IPPROTO_ESP:
274 case IPPROTO_OSPF:
275 /* known protocol, don't complain about it */
276 break;
277
278 default:
279 verbosef("unknown IP proto (%04x)", sm->proto);
280 }
281 }
282
283 /* vim:set ts=3 sw=3 tw=78 expandtab: */