8 #if defined(HAVE_PCAP_H)
10 #elif defined(HAVE_PCAP_PCAP_H)
11 # include <pcap/pcap.h>
18 #include <sys/types.h>
19 #include <sys/ioctl.h>
20 #include <sys/socket.h>
22 #include <netinet/in.h>
32 #include "counter_hash.h"
37 #endif /* DLT_LINUX_SLL */
38 #include "threadprof.h"
45 #include "ethertype.h"
51 /* ethernet address of interface. */
53 unsigned char if_hw_addr[6];
55 /* IP address of interface */
57 struct in_addr if_ip_addr;
59 extern options_t options;
62 time_t last_timestamp;
64 pthread_mutex_t tick_mutex;
66 pcap_t* pd; /* pcap descriptor */
67 struct bpf_program pcap_filter;
68 pcap_handler packet_handler;
76 static void finish(int sig) {
84 /* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2 bytes of tcp/udp header */
85 #define CAPTURE_LENGTH 72
87 void init_counters() {
88 counters = counter_hash_create();
89 last_timestamp = time(NULL);
92 counter_type* counter_create() {
94 c = xcalloc(1, sizeof *c);
98 void close_log_file(void) {
105 void open_log_file(void) {
109 strftime(filename, 255, "/var/bandwidth/bw-%Y%m%d",localtime(&t));
110 fout = fopen(filename, "w+");
112 fprintf(stderr, "Could not open log file: %s", strerror(errno));
117 void print_counter(struct in_addr * ip, counter_type * n) {
121 fprintf(fout,"%d %s %lld %lld\n",time(NULL),inet_ntoa((struct in_addr)*ip),n->sent,n->recv);
125 void tick(int print) {
127 hash_node_type * hn = NULL;
134 if(t - last_timestamp >= DUMP_RESOLUTION) {
136 day = tt->tm_yday + tt->tm_year * 366;
137 if(day != last_day) {
144 while(hash_next_item(counters, &hn) == HASH_STATUS_OK) {
145 n = (counter_type*)hn->rec;
146 print_counter((struct in_addr*)hn->key, n);
152 int in_filter_net(struct in_addr addr) {
154 ret = ((addr.s_addr & options.netfiltermask.s_addr) == options.netfilternet.s_addr);
158 int ip_addr_match(struct in_addr addr) {
159 return addr.s_addr == if_ip_addr.s_addr;
163 static void handle_ip_packet(struct ip* iptr, int hw_dir)
165 int direction = 0; /* incoming */
166 counter_type * counter;
168 struct in_addr local_addr;
170 if(options.netfilter == 0) {
171 fprintf(stderr, "netfilter option must be specified for iftop-dump\n");
175 * Net filter on, assign direction according to netmask
177 if(in_filter_net(iptr->ip_src) && !in_filter_net(iptr->ip_dst)) {
179 local_addr = iptr->ip_src;
182 else if(in_filter_net(iptr->ip_dst) && !in_filter_net(iptr->ip_src)) {
184 local_addr = iptr->ip_dst;
193 if(hash_find(counters, &local_addr, (void**)&counter) == HASH_STATUS_KEY_NOT_FOUND) {
194 counter = counter_create();
195 hash_insert(counters, &local_addr, counter);
196 print_counter(&local_addr, counter);
200 len = ntohs(iptr->ip_len);
204 counter->recv += len;
207 counter->sent += len;
209 if(counter->recv > 2147483648ULL || counter->sent > 2147483648ULL) {
210 print_counter(&local_addr, counter);
211 counter->recv = counter->sent = 0;
212 print_counter(&local_addr, counter);
218 static void handle_raw_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
220 handle_ip_packet((struct ip*)packet, -1);
223 static void handle_llc_packet(const struct llc* llc, int dir) {
225 struct ip* ip = (struct ip*)((void*)llc + sizeof(struct llc));
227 /* Taken from tcpdump/print-llc.c */
228 if(llc->ssap == LLCSAP_SNAP && llc->dsap == LLCSAP_SNAP
229 && llc->llcui == LLC_UI) {
232 orgcode = EXTRACT_24BITS(&llc->llc_orgcode[0]);
233 et = EXTRACT_16BITS(&llc->llc_ethertype[0]);
235 case OUI_ENCAP_ETHER:
237 handle_ip_packet(ip, dir);
240 if(et == ETHERTYPE_ATALK) {
241 handle_ip_packet(ip, dir);
245 /* Not a lot we can do */
250 static void handle_tokenring_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
252 struct token_header *trp;
254 trp = (struct token_header *)packet;
256 if(IS_SOURCE_ROUTED(trp)) {
257 packet += RIF_LENGTH(trp);
259 packet += TOKEN_HDRLEN;
261 if(memcmp(trp->token_shost, if_hw_addr, 6) == 0 ) {
262 /* packet leaving this i/f */
265 else if(memcmp(trp->token_dhost, if_hw_addr, 6) == 0 || memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", trp->token_dhost, 6) == 0) {
266 /* packet entering this i/f */
270 /* Only know how to deal with LLC encapsulated packets */
271 if(FRAME_TYPE(trp) == TOKEN_FC_LLC) {
272 handle_llc_packet((struct llc*)packet, dir);
276 static void handle_ppp_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
278 register u_int length = pkthdr->len;
279 register u_int caplen = pkthdr->caplen;
285 if(packet[0] == PPP_ADDRESS) {
292 proto = EXTRACT_16BITS(packet);
296 if(proto == PPP_IP || proto == ETHERTYPE_IP) {
297 handle_ip_packet((struct ip*)packet, -1);
303 static void handle_cooked_packet(unsigned char *args, const struct pcap_pkthdr * thdr, const unsigned char * packet)
305 struct sll_header *sptr;
307 sptr = (struct sll_header *) packet;
309 switch (ntohs(sptr->sll_pkttype))
312 /*entering this interface*/
315 case LINUX_SLL_OUTGOING:
316 /*leaving this interface */
320 handle_ip_packet((struct ip*)(packet+SLL_HDR_LEN), dir);
322 #endif /* DLT_LINUX_SLL */
324 static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
326 struct ether_header *eptr;
328 const unsigned char *payload;
329 eptr = (struct ether_header*)packet;
330 ether_type = ntohs(eptr->ether_type);
331 payload = packet + sizeof(struct ether_header);
335 if(ether_type == ETHERTYPE_8021Q) {
336 struct vlan_8021q_header* vptr;
337 vptr = (struct vlan_8021q_header*)payload;
338 ether_type = ntohs(vptr->ether_type);
339 payload += sizeof(struct vlan_8021q_header);
342 if(ether_type == ETHERTYPE_IP) {
347 * Is a direction implied by the MAC addresses?
349 if(have_hw_addr && memcmp(eptr->ether_shost, if_hw_addr, 6) == 0 ) {
350 /* packet leaving this i/f */
353 else if(have_hw_addr && memcmp(eptr->ether_dhost, if_hw_addr, 6) == 0 ) {
354 /* packet entering this i/f */
357 else if (memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", eptr->ether_dhost, 6) == 0) {
358 /* broadcast packet, count as incoming */
362 iptr = (struct ip*)(payload); /* alignment? */
363 handle_ip_packet(iptr, dir);
369 * Install some filter code. Returns NULL on success or an error message on
371 char *set_filter_code(const char *filter) {
374 x = xmalloc(strlen(filter) + sizeof "() and ip");
375 sprintf(x, "(%s) and ip", filter);
378 if (pcap_compile(pd, &pcap_filter, x, 1, 0) == -1) {
380 return pcap_geterr(pd);
383 if (pcap_setfilter(pd, &pcap_filter) == -1)
384 return pcap_geterr(pd);
394 * performs pcap initialisation, called before ui is initialised
397 char errbuf[PCAP_ERRBUF_SIZE];
405 result = get_addrs_dlpi(options.interface, if_hw_addr, &if_ip_addr);
407 result = get_addrs_ioctl(options.interface, if_hw_addr, &if_ip_addr);
414 have_hw_addr = result & 1;
415 have_ip_addr = result & 2;
418 fprintf(stderr, "IP address is: %s\n", inet_ntoa(if_ip_addr));
422 fprintf(stderr, "MAC address is:");
423 for (i = 0; i < 6; ++i)
424 fprintf(stderr, "%c%02x", i ? ':' : ' ', (unsigned int)if_hw_addr[i]);
425 fprintf(stderr, "\n");
429 /* resolver_initialise(); */
431 pd = pcap_open_live(options.interface, CAPTURE_LENGTH, options.promiscuous, 1000, errbuf);
432 // DEBUG: pd = pcap_open_offline("tcpdump.out", errbuf);
434 fprintf(stderr, "pcap_open_live(%s): %s\n", options.interface, errbuf);
437 dlt = pcap_datalink(pd);
438 if(dlt == DLT_EN10MB) {
439 packet_handler = handle_eth_packet;
441 else if(dlt == DLT_RAW || dlt == DLT_NULL) {
442 packet_handler = handle_raw_packet;
444 else if(dlt == DLT_IEEE802) {
445 packet_handler = handle_tokenring_packet;
447 else if(dlt == DLT_PPP) {
448 packet_handler = handle_ppp_packet;
451 * SLL support not available in older libpcaps
454 else if(dlt == DLT_LINUX_SLL) {
455 packet_handler = handle_cooked_packet;
459 fprintf(stderr, "Unsupported datalink type: %d\n"
460 "Please email pdw@ex-parrot.com, quoting the datalink type and what you were\n"
461 "trying to do at the time\n.", dlt);
465 if ((m = set_filter_code(options.filtercode))) {
466 fprintf(stderr, "set_filter_code: %s\n", m);
474 pcap_loop(pd,-1,(pcap_handler)packet_handler,NULL);
479 * Entry point. See usage(). */
480 int main(int argc, char **argv) {
482 struct sigaction sa = {};
484 /* TODO: tidy this up */
485 /* read command line options and config file */
487 options_set_defaults();
488 options_read_args(argc, argv);
489 /* If a config was explicitly specified, whinge if it can't be found */
490 read_config(options.config_file, options.config_file_specified);
494 sa.sa_handler = finish;
495 sigaction(SIGINT, &sa, NULL);
498 pthread_mutex_init(&tick_mutex, NULL);