2 * copyright (c) 2001-2008 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.
28 #include <arpa/inet.h> /* for inet_aton() */
30 #include <netinet/tcp.h>
31 #include <sys/socket.h>
32 #include <stdlib.h> /* for free */
33 #include <string.h> /* for memcpy */
34 #include <ctype.h> /* isdigit() */
36 uint64_t total_packets
= 0, total_bytes
= 0;
38 static int using_localnet
= 0;
39 static int using_localnet6
= 0;
40 static in_addr_t localnet
, localmask
;
41 static struct in6_addr localnet6
, localmask6
;
43 /* Parse the net/mask specification into two IPs or die trying. */
45 acct_init_localnet(const char *spec
)
48 int num_tokens
, isnum
, j
;
49 int build_ipv6
; /* Zero for IPv4, one for IPv6. */
50 int pfxlen
, octets
, remainder
;
52 struct in6_addr addr6
;
54 tokens
= split('/', spec
, &num_tokens
);
56 errx(1, "expecting network/netmask, got \"%s\"", spec
);
58 /* Presence of a colon distinguishes address families. */
59 if (strchr(tokens
[0], ':')) {
61 if (inet_pton(AF_INET6
, tokens
[0], &addr6
) != 1)
62 errx(1, "invalid IPv6 network address \"%s\"", tokens
[0]);
63 memcpy(&localnet6
, &addr6
, sizeof(localnet6
));
66 if (inet_pton(AF_INET
, tokens
[0], &addr
) != 1)
67 errx(1, "invalid network address \"%s\"", tokens
[0]);
68 localnet
= addr
.s_addr
;
71 /* Detect a purely numeric argument. */
87 if (inet_pton(AF_INET6
, tokens
[1], &addr6
) != 1)
88 errx(1, "invalid IPv6 network mask \"%s\"", tokens
[1]);
89 memcpy(&localmask6
, &addr6
, sizeof(localmask6
));
91 if (inet_pton(AF_INET
, tokens
[1], &addr
) != 1)
92 errx(1, "invalid network mask \"%s\"", tokens
[1]);
93 localmask
= addr
.s_addr
;
98 /* Compute the prefix length. */
99 pfxlen
= strtonum(tokens
[1], 1, build_ipv6
? 128 : 32, NULL
);
101 errx(1, "invalid network prefix length \"%s\"", tokens
[1]);
103 /* Construct the network mask. */
105 remainder
= pfxlen
% 8;
106 p
= build_ipv6
? (uint8_t *) localmask6
.s6_addr
: (uint8_t *) &localmask
;
109 memset(&localmask6
, 0, sizeof(localmask6
));
111 memset(&localmask
, 0, sizeof(localmask
));
113 for (j
= 0; j
< octets
; ++j
)
116 frac
= 0xff << (8 - remainder
);
118 p
[j
] = frac
; /* Have contribution for next position. */
121 /* Register the correct netmask and calculate the correct net. */
124 for (j
= 0; j
< 16; ++j
)
125 localnet6
.s6_addr
[j
] &= localmask6
.s6_addr
[j
];
128 localnet
&= localmask
;
136 verbosef("local network address: %s", ip_to_str_af(&localnet6
, AF_INET6
));
137 verbosef(" local network mask: %s", ip_to_str_af(&localmask6
, AF_INET6
));
139 verbosef("local network address: %s", ip_to_str_af(&localnet
, AF_INET
));
140 verbosef(" local network mask: %s", ip_to_str_af(&localmask
, AF_INET
));
145 /* Account for the given packet summary. */
147 acct_for(const pktsummary
*sm
)
149 struct bucket
*hs
= NULL
, *hd
= NULL
;
150 struct bucket
*ps
, *pd
;
151 struct addr46 ipaddr
;
152 struct in6_addr scribble
;
153 int dir_in
, dir_out
, j
;
155 #if 0 /* WANT_CHATTY? */
156 printf("%15s > ", ip_to_str_af(&sm
->src_ip
, AF_INET
));
157 printf("%15s ", ip_to_str_af(&sm
->dest_ip
, AF_INET
));
158 printf("len %4d proto %2d", sm
->len
, sm
->proto
);
160 if (sm
->proto
== IPPROTO_TCP
|| sm
->proto
== IPPROTO_UDP
)
161 printf(" port %5d : %5d", sm
->src_port
, sm
->dest_port
);
162 if (sm
->proto
== IPPROTO_TCP
)
163 printf(" %s%s%s%s%s%s",
164 (sm
->tcp_flags
& TH_FIN
)?"F":"",
165 (sm
->tcp_flags
& TH_SYN
)?"S":"",
166 (sm
->tcp_flags
& TH_RST
)?"R":"",
167 (sm
->tcp_flags
& TH_PUSH
)?"P":"",
168 (sm
->tcp_flags
& TH_ACK
)?"A":"",
169 (sm
->tcp_flags
& TH_URG
)?"U":""
176 total_bytes
+= sm
->len
;
179 dir_in
= dir_out
= 0;
181 if (sm
->af
== AF_INET
) {
182 if (using_localnet
) {
183 if ((sm
->src_ip
.s_addr
& localmask
) == localnet
)
185 if ((sm
->dest_ip
.s_addr
& localmask
) == localnet
)
187 if (dir_in
== 1 && dir_out
== 1)
188 /* Traffic staying within the network isn't counted. */
189 dir_in
= dir_out
= 0;
191 if (memcmp(&sm
->src_ip
, &localip
, sizeof(localip
)) == 0)
193 if (memcmp(&sm
->dest_ip
, &localip
, sizeof(localip
)) == 0)
196 } else if (sm
->af
== AF_INET6
) {
197 if (using_localnet6
) {
198 for (j
= 0; j
< 16; ++j
)
199 scribble
.s6_addr
[j
] = sm
->src_ip6
.s6_addr
[j
] & localmask6
.s6_addr
[j
];
200 if (memcmp(&scribble
, &localnet6
, sizeof(scribble
)) == 0)
203 for (j
= 0; j
< 16; ++j
)
204 scribble
.s6_addr
[j
] = sm
->dest_ip6
.s6_addr
[j
] & localmask6
.s6_addr
[j
];
205 if (memcmp(&scribble
, &localnet6
, sizeof(scribble
)) == 0)
209 if (memcmp(&sm
->src_ip6
, &localip6
, sizeof(localip6
)) == 0)
211 if (memcmp(&sm
->dest_ip6
, &localip6
, sizeof(localip6
)) == 0)
217 daylog_acct((uint64_t)sm
->len
, GRAPH_OUT
);
218 graph_acct((uint64_t)sm
->len
, GRAPH_OUT
);
221 daylog_acct((uint64_t)sm
->len
, GRAPH_IN
);
222 graph_acct((uint64_t)sm
->len
, GRAPH_IN
);
225 if (hosts_max
== 0) return; /* skip per-host accounting */
231 memcpy(&ipaddr
.addr
.ip6
, &sm
->src_ip6
, sizeof(ipaddr
.addr
.ip6
));
235 memcpy(&ipaddr
.addr
.ip
, &sm
->src_ip
, sizeof(ipaddr
.addr
.ip
));
238 hs
= host_get(&ipaddr
);
240 hs
->total
+= sm
->len
;
241 memcpy(hs
->u
.host
.mac_addr
, sm
->src_mac
, sizeof(sm
->src_mac
));
242 hs
->u
.host
.last_seen
= now
;
246 memcpy(&ipaddr
.addr
.ip6
, &sm
->dest_ip6
, sizeof(ipaddr
.addr
.ip6
));
250 memcpy(&ipaddr
.addr
.ip
, &sm
->dest_ip
, sizeof(ipaddr
.addr
.ip
));
253 hd
= host_get(&ipaddr
); /* this can invalidate hs! */
255 hd
->total
+= sm
->len
;
256 memcpy(hd
->u
.host
.mac_addr
, sm
->dst_mac
, sizeof(sm
->dst_mac
));
257 hd
->u
.host
.last_seen
= now
;
262 memcpy(&ipaddr
.addr
.ip6
, &sm
->src_ip6
, sizeof(ipaddr
.addr
.ip6
));
266 memcpy(&ipaddr
.addr
.ip
, &sm
->src_ip
, sizeof(ipaddr
.addr
.ip
));
269 hs
= host_find(&ipaddr
);
271 ps
= host_get_ip_proto(hs
, sm
->proto
);
273 ps
->total
+= sm
->len
;
276 pd
= host_get_ip_proto(hd
, sm
->proto
);
278 pd
->total
+= sm
->len
;
280 if (ports_max
== 0) return; /* skip ports accounting */
286 if ((sm
->src_port
<= highest_port
) && (hs
!= NULL
))
288 ps
= host_get_port_tcp(hs
, sm
->src_port
);
290 ps
->total
+= sm
->len
;
293 if (sm
->dest_port
<= highest_port
)
295 pd
= host_get_port_tcp(hd
, sm
->dest_port
);
297 pd
->total
+= sm
->len
;
298 if (sm
->tcp_flags
== TH_SYN
)
299 pd
->u
.port_tcp
.syn
++;
304 if ((sm
->src_port
<= highest_port
) && (hs
!= NULL
))
306 ps
= host_get_port_udp(hs
, sm
->src_port
);
308 ps
->total
+= sm
->len
;
311 if (sm
->dest_port
<= highest_port
)
313 pd
= host_get_port_udp(hd
, sm
->dest_port
);
315 pd
->total
+= sm
->len
;
324 /* known protocol, don't complain about it */
328 verbosef("unknown IP proto (%04x)", sm
->proto
);
332 /* vim:set ts=3 sw=3 tw=78 expandtab: */