2 * copyright (c) 2001-2012 Emil Mikulic.
4 * acct.c: traffic accounting
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.
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.
30 #include <netinet/tcp.h>
31 #include <sys/socket.h>
33 #include <ctype.h> /* for isdigit */
34 #include <netdb.h> /* for gai_strerror */
35 #include <stdlib.h> /* for free */
36 #include <string.h> /* for memcpy */
38 uint64_t acct_total_packets
= 0, acct_total_bytes
= 0;
40 static int using_localnet4
= 0, using_localnet6
= 0;
41 static struct addr localnet4
, localmask4
, localnet6
, localmask6
;
43 /* Parse the net/mask specification into two IPs or die trying. */
45 acct_init_localnet(const char *spec
)
48 unsigned int num_tokens
;
50 int pfxlen
, octets
, remainder
;
51 struct addr localnet
, localmask
;
53 tokens
= split('/', spec
, &num_tokens
);
55 errx(1, "expecting network/netmask, got \"%s\"", spec
);
57 if ((ret
= str_to_addr(tokens
[0], &localnet
)) != 0)
58 errx(1, "couldn't parse \"%s\": %s", tokens
[0], gai_strerror(ret
));
60 /* Detect a purely numeric argument. */
63 const char *p
= tokens
[1];
77 if ((ret
= str_to_addr(tokens
[1], &localmask
)) != 0)
78 errx(1, "couldn't parse \"%s\": %s", tokens
[1], gai_strerror(ret
));
79 if (localmask
.family
!= localnet
.family
)
80 errx(1, "family mismatch between net and mask");
85 localmask
.family
= localnet
.family
;
87 /* Compute the prefix length. */
88 pfxlen
= (unsigned int)strtol(tokens
[1], &endptr
, 10);
91 ((localnet
.family
== IPv6
) && (pfxlen
> 128)) ||
92 ((localnet
.family
== IPv4
) && (pfxlen
> 32)) ||
93 (tokens
[1][0] == '\0') ||
95 errx(1, "invalid network prefix length \"%s\"", tokens
[1]);
97 /* Construct the network mask. */
99 remainder
= pfxlen
% 8;
100 p
= (localnet
.family
== IPv6
) ? (localmask
.ip
.v6
.s6_addr
)
101 : ((uint8_t *) &(localmask
.ip
.v4
));
103 if (localnet
.family
== IPv6
)
108 for (j
= 0; j
< octets
; ++j
)
111 frac
= (uint8_t)(0xff << (8 - remainder
));
113 p
[j
] = frac
; /* Have contribution for next position. */
120 /* Register the correct netmask and calculate the correct net. */
121 addr_mask(&localnet
, &localmask
);
122 if (localnet
.family
== IPv6
) {
124 localnet6
= localnet
;
125 localmask6
= localmask
;
128 localnet4
= localnet
;
129 localmask4
= localmask
;
132 verbosef("local network address: %s", addr_to_str(&localnet
));
133 verbosef(" local network mask: %s", addr_to_str(&localmask
));
136 static int addr_is_local(const struct addr
* const a
,
137 const struct local_ips
*local_ips
) {
138 if (is_localip(a
, local_ips
))
140 if (a
->family
== IPv4
&& using_localnet4
) {
141 if (addr_inside(a
, &localnet4
, &localmask4
))
143 } else if (a
->family
== IPv6
&& using_localnet6
) {
144 if (addr_inside(a
, &localnet6
, &localmask6
))
150 /* Account for the given packet summary. */
151 void acct_for(const struct pktsummary
* const sm
,
152 const struct local_ips
* const local_ips
) {
153 struct bucket
*hs
= NULL
; // Source host.
154 struct bucket
*hd
= NULL
; // Dest host.
157 #if 0 /* WANT_CHATTY? */
158 printf("%15s > ", addr_to_str(&sm
->src
));
159 printf("%15s ", addr_to_str(&sm
->dst
));
160 printf("len %4d proto %2d", sm
->len
, sm
->proto
);
162 if (sm
->proto
== IPPROTO_TCP
|| sm
->proto
== IPPROTO_UDP
)
163 printf(" port %5d : %5d", sm
->src_port
, sm
->dst_port
);
164 if (sm
->proto
== IPPROTO_TCP
)
165 printf(" %s%s%s%s%s%s",
166 (sm
->tcp_flags
& TH_FIN
)?"F":"",
167 (sm
->tcp_flags
& TH_SYN
)?"S":"",
168 (sm
->tcp_flags
& TH_RST
)?"R":"",
169 (sm
->tcp_flags
& TH_PUSH
)?"P":"",
170 (sm
->tcp_flags
& TH_ACK
)?"A":"",
171 (sm
->tcp_flags
& TH_URG
)?"U":""
177 acct_total_packets
++;
178 acct_total_bytes
+= sm
->len
;
181 dir_out
= addr_is_local(&sm
->src
, local_ips
);
182 dir_in
= addr_is_local(&sm
->dst
, local_ips
);
184 /* Traffic staying within the network isn't counted. */
185 if (dir_out
&& !dir_in
) {
186 daylog_acct((uint64_t)sm
->len
, GRAPH_OUT
);
187 graph_acct((uint64_t)sm
->len
, GRAPH_OUT
);
189 if (dir_in
&& !dir_out
) {
190 daylog_acct((uint64_t)sm
->len
, GRAPH_IN
);
191 graph_acct((uint64_t)sm
->len
, GRAPH_IN
);
194 if (opt_hosts_max
== 0) return; /* skip per-host accounting */
198 if (!opt_want_local_only
|| dir_out
) {
199 hs
= host_get(&(sm
->src
));
201 hs
->total
+= sm
->len
;
202 memcpy(hs
->u
.host
.mac_addr
, sm
->src_mac
, sizeof(sm
->src_mac
));
203 hs
->u
.host
.last_seen_mono
= now_mono();
206 if (!opt_want_local_only
|| dir_in
) {
207 hd
= host_get(&(sm
->dst
));
209 hd
->total
+= sm
->len
;
210 memcpy(hd
->u
.host
.mac_addr
, sm
->dst_mac
, sizeof(sm
->dst_mac
));
212 * Don't update recipient's last seen time, we don't know that
213 * they received successfully.
218 if (sm
->proto
!= IPPROTO_INVALID
) {
220 struct bucket
*ps
= host_get_ip_proto(hs
, sm
->proto
);
222 ps
->total
+= sm
->len
;
225 struct bucket
*pd
= host_get_ip_proto(hd
, sm
->proto
);
227 pd
->total
+= sm
->len
;
231 if (opt_ports_max
== 0) return; /* skip ports accounting */
236 // Local ports on host.
237 if ((sm
->src_port
<= opt_highest_port
) && hs
) {
238 struct bucket
*ps
= host_get_port_tcp(hs
, sm
->src_port
);
240 ps
->total
+= sm
->len
;
242 if ((sm
->dst_port
<= opt_highest_port
) && hd
) {
243 struct bucket
*pd
= host_get_port_tcp(hd
, sm
->dst_port
);
245 pd
->total
+= sm
->len
;
246 if (sm
->tcp_flags
== TH_SYN
)
247 pd
->u
.port_tcp
.syn
++;
251 if ((sm
->src_port
<= opt_highest_port
) && hd
) {
252 struct bucket
*pdr
= host_get_port_tcp_remote(hd
, sm
->src_port
);
254 pdr
->total
+= sm
->len
;
256 if ((sm
->dst_port
<= opt_highest_port
) && hs
) {
257 struct bucket
*psr
= host_get_port_tcp_remote(hs
, sm
->dst_port
);
259 psr
->total
+= sm
->len
;
260 if (sm
->tcp_flags
== TH_SYN
)
261 psr
->u
.port_tcp
.syn
++;
266 // Local ports on host.
267 if ((sm
->src_port
<= opt_highest_port
) && hs
) {
268 struct bucket
*ps
= host_get_port_udp(hs
, sm
->src_port
);
270 ps
->total
+= sm
->len
;
272 if ((sm
->dst_port
<= opt_highest_port
) && hd
) {
273 struct bucket
*pd
= host_get_port_udp(hd
, sm
->dst_port
);
275 pd
->total
+= sm
->len
;
279 if ((sm
->src_port
<= opt_highest_port
) && hd
) {
280 struct bucket
*pdr
= host_get_port_udp_remote(hd
, sm
->src_port
);
282 pdr
->total
+= sm
->len
;
284 if ((sm
->dst_port
<= opt_highest_port
) && hs
) {
285 struct bucket
*psr
= host_get_port_udp_remote(hs
, sm
->dst_port
);
287 psr
->total
+= sm
->len
;
291 case IPPROTO_INVALID
:
292 /* proto decoding failed, don't complain in accounting */
297 /* vim:set ts=3 sw=3 tw=78 expandtab: */