source: tools/tracetop/tracetop.cc @ 817713f

cachetimestampsdevelopetsiliverc-4.0.3rc-4.0.4ringdecrementfixringperformance
Last change on this file since 817713f was 817713f, checked in by Shane Alcock <salcock@…>, 3 years ago

tracetop: fix warning about lack of spaces in our printf formats.

  • Property mode set to 100644
File size: 16.1 KB
Line 
1/*
2 *
3 * Copyright (c) 2007-2016 The University of Waikato, Hamilton, New Zealand.
4 * All rights reserved.
5 *
6 * This file is part of libtrace.
7 *
8 * This code has been developed by the University of Waikato WAND
9 * research group. For further information please see http://www.wand.net.nz/
10 *
11 * libtrace is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * libtrace is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 *
24 *
25 */
26
27
28/* Show the top 'n' flows from a libtrace source
29 *
30 */
31#define __STDC_FORMAT_MACROS 1
32#include "config.h"
33#include "libtrace.h"
34#include <stdio.h>
35#include <getopt.h>
36#include <stdlib.h>
37#include <map>
38#include <queue>
39#include <inttypes.h>
40#include <sys/socket.h>
41#include <netdb.h>
42#include <string.h>
43#include <assert.h>
44#ifdef HAVE_NETPACKET_PACKET_H
45#include <netpacket/packet.h>
46#include <net/ethernet.h>
47#else
48#include <net/if_dl.h>
49#endif
50
51#if HAVE_NCURSES_NCURSES_H
52#include <ncurses/ncurses.h>
53#else
54#include <ncurses.h>
55#endif
56
57typedef enum { BITS_PER_SEC, BYTES, PERCENT } display_t;
58display_t display_as = BYTES;
59float interval=2;
60double last_report=0;
61
62bool use_sip = true;
63bool use_dip = true;
64bool use_sport = true;
65bool use_dport = true;
66bool use_protocol = true;
67bool quit = false;
68bool fullspeed = false;
69bool wide_display = false;
70
71uint64_t total_bytes=0;
72uint64_t total_packets=0;
73
74int cmp_sockaddr_in6(const struct sockaddr_in6 *a, const struct sockaddr_in6 *b)
75{
76        if (a->sin6_port != b->sin6_port)
77                return a->sin6_port - b->sin6_port;
78        return memcmp(a->sin6_addr.s6_addr,b->sin6_addr.s6_addr,sizeof(a->sin6_addr.s6_addr));
79}
80
81int cmp_sockaddr_in(const struct sockaddr_in *a, const struct sockaddr_in *b)
82{
83        if (a->sin_port != b->sin_port)
84                return a->sin_port - b->sin_port;
85        return a->sin_addr.s_addr - b->sin_addr.s_addr;
86}
87
88#ifdef HAVE_NETPACKET_PACKET_H
89int cmp_sockaddr_ll(const struct sockaddr_ll *a, const struct sockaddr_ll *b)
90{
91        return memcmp(a->sll_addr, b->sll_addr, b->sll_halen);
92}
93#else
94int cmp_sockaddr_dl(const struct sockaddr_dl *a, const struct sockaddr_dl *b)
95{
96        return memcmp(a->sdl_data, b->sdl_data, b->sdl_alen);
97}
98
99#endif
100
101
102int cmp_sockaddr(const struct sockaddr *a, const struct sockaddr *b)
103{
104        if (a->sa_family != b->sa_family) {
105                return a->sa_family - b->sa_family;
106        }
107        switch (a->sa_family) {
108                case AF_INET:
109                        return cmp_sockaddr_in((struct sockaddr_in *)a,(struct sockaddr_in*)b);
110                case AF_INET6:
111                        return cmp_sockaddr_in6((struct sockaddr_in6 *)a,(struct sockaddr_in6*)b);
112#ifdef HAVE_NETPACKET_PACKET_H
113                case AF_PACKET:
114                        return cmp_sockaddr_ll((struct sockaddr_ll *)a,(struct sockaddr_ll*)b);
115#else
116                case AF_LINK:
117                        return cmp_sockaddr_dl((struct sockaddr_dl *)a, (struct sockaddr_dl *)b);
118#endif
119                case AF_UNSPEC:
120                        return 0; /* Can't compare UNSPEC's! */
121                default:
122                        fprintf(stderr,"Don't know how to compare family %d\n",a->sa_family);
123                        abort();
124        }
125}
126
127char *trace_sockaddr2string(const struct sockaddr *a, socklen_t salen, char *buffer, size_t bufflen)
128{
129        static char intbuffer[NI_MAXHOST];
130        char *mybuf = buffer ? buffer : intbuffer;
131        size_t mybufflen = buffer ? bufflen : sizeof(intbuffer);
132        int err;
133
134        /* Some systems (FreeBSD and Solaris, I'm looking at you) have a bug
135         * where they can't deal with the idea of a sockaddr_storage being
136         * passed into getnameinfo. Linux just deals by looking
137         * at sa_family and figuring out what sockaddr it is really.
138         *
139         * Anyway, the fix appears to be to manually hax the sockaddr length
140         * to be the right value for the underlying family.
141         */
142        switch (a->sa_family) {
143                case AF_INET:
144                        salen = sizeof(struct sockaddr_in);
145                        if ((err=getnameinfo(a, salen, mybuf, mybufflen, NULL, 0, NI_NUMERICHOST))!=0) {
146                                strncpy(mybuf,gai_strerror(err),mybufflen);
147                        }
148                        break;
149                case AF_INET6:
150                        salen = sizeof(struct sockaddr_in6);
151                        if ((err=getnameinfo(a, salen, mybuf, mybufflen, NULL, 0, NI_NUMERICHOST))!=0) {
152                                strncpy(mybuf,gai_strerror(err),mybufflen);
153                        }
154                        break;
155#ifdef HAVE_NETPACKET_PACKET_H
156                case AF_PACKET:
157                        trace_ether_ntoa(((struct sockaddr_ll*)a)->sll_addr, mybuf);
158                        break;
159#else
160                case AF_LINK:
161                        trace_ether_ntoa((uint8_t *)((struct sockaddr_dl *)a)->sdl_data, mybuf);
162                        break;
163#endif
164                default:
165                        snprintf(mybuf,mybufflen,"Unknown family %d",a->sa_family);
166        }
167        return mybuf;
168}
169
170static void set_port_for_sockaddr(struct sockaddr *sa,uint16_t port)
171{
172        switch (sa->sa_family) {
173                case AF_INET:
174                        ((struct sockaddr_in *)sa)->sin_port = htons(port);
175                        break;
176                case AF_INET6:
177                        ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
178                        break;
179        }
180}
181
182static void clear_addr_for_sockaddr(struct sockaddr *sa)
183{
184        switch (sa->sa_family) {
185                case AF_INET:
186                        ((struct sockaddr_in *)sa)->sin_addr.s_addr = 0;
187                        break;
188                case AF_INET6:
189                        memset((void*)&((struct sockaddr_in6 *)sa)->sin6_addr,0,sizeof(((struct sockaddr_in6 *)sa)->sin6_addr));
190                        break;
191        }
192}
193
194static uint16_t get_port_from_sockaddr(struct sockaddr *sa)
195{
196        switch (sa->sa_family) {
197                case AF_INET:
198                        return ntohs(((struct sockaddr_in *)sa)->sin_port);
199                        break;
200                case AF_INET6:
201                        return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
202                        break;
203        }
204
205        return 0;
206}
207
208struct flowkey_t {
209        struct sockaddr_storage sip;
210        struct sockaddr_storage dip;
211        uint16_t sport;
212        uint16_t dport;
213        uint8_t protocol;
214
215        bool operator <(const flowkey_t &b) const {
216                int c;
217
218                if (use_sip) {
219                        c = cmp_sockaddr((struct sockaddr*)&sip,(struct sockaddr*)&b.sip);
220                        if (c != 0) return c<0;
221                }
222                if (use_dip) {
223                        c = cmp_sockaddr((struct sockaddr*)&dip,(struct sockaddr*)&b.dip);
224                        if (c != 0) return c<0;
225                }
226
227                return protocol < b.protocol;
228        }
229};
230
231struct flowdata_t {
232        uint64_t packets;
233        uint64_t bytes;
234};
235
236typedef std::map<flowkey_t,flowdata_t> flows_t;
237
238flows_t flows;
239
240const char *nice_bandwidth(double bytespersec)
241{
242        static char ret[1024];
243        double bitspersec = bytespersec*8;
244
245        if (bitspersec>1e12)
246                snprintf(ret,sizeof(ret),"%.03fTb/s", bitspersec/1e12);
247        else if (bitspersec>1e9)
248                snprintf(ret,sizeof(ret),"%.03fGb/s", bitspersec/1e9);
249        else if (bitspersec>1e6)
250                snprintf(ret,sizeof(ret),"%.03fMb/s", bitspersec/1e6);
251        else if (bitspersec>1e3)
252                snprintf(ret,sizeof(ret),"%.03fkb/s", bitspersec/1e3);
253        else
254                snprintf(ret,sizeof(ret),"%.03fb/s", bitspersec);
255        return ret;
256}
257
258static void per_packet(libtrace_packet_t *packet)
259{
260        flowkey_t flowkey;
261        flows_t::iterator it;
262
263        if (IS_LIBTRACE_META_PACKET(packet))
264                return;
265
266        if (trace_get_source_address(packet,(struct sockaddr*)&flowkey.sip)==NULL)
267                flowkey.sip.ss_family = AF_UNSPEC;
268
269        if (trace_get_destination_address(packet,(struct sockaddr*)&flowkey.dip)==NULL)
270                flowkey.dip.ss_family = AF_UNSPEC;
271
272        if (!use_sip)
273                clear_addr_for_sockaddr((struct sockaddr *)&flowkey.sip);
274
275        if (!use_dip)
276                clear_addr_for_sockaddr((struct sockaddr *)&flowkey.dip);
277
278        if (!use_sport)
279                set_port_for_sockaddr((struct sockaddr *)&flowkey.sip,0);
280
281        if (!use_dport) 
282                set_port_for_sockaddr((struct sockaddr *)&flowkey.dip,0);
283
284        if (use_protocol && trace_get_transport(packet,&flowkey.protocol, NULL) == NULL)
285                flowkey.protocol = 255;
286
287
288        it = flows.find(flowkey);
289        if (it == flows.end()) {
290                flowdata_t flowdata = { 0, 0 };
291                flows_t::value_type insdata(flowkey,flowdata);
292                std::pair<flows_t::iterator,bool> ins= flows.insert(insdata);
293                it = ins.first;
294        }
295
296        ++it->second.packets;
297        it->second.bytes+=trace_get_wire_length(packet);
298
299        ++total_packets;
300        total_bytes+=trace_get_wire_length(packet);
301
302}
303
304struct flow_data_t {
305        uint64_t bytes;
306        uint64_t packets;
307        struct sockaddr_storage sip;
308        struct sockaddr_storage dip;
309        uint8_t protocol;
310
311        bool operator< (const flow_data_t &b) const {
312                if (bytes != b.bytes) return bytes < b.bytes;
313                return packets < b.packets;
314        }
315};
316
317static void do_report()
318{
319        typedef  std::priority_queue<flow_data_t> pq_t;
320        int row,col;
321        pq_t pq;
322        for(flows_t::const_iterator it=flows.begin();it!=flows.end();++it) {
323                flow_data_t data;
324                data.bytes = it->second.bytes,
325                data.packets = it->second.packets,
326                data.sip = it->first.sip;
327                data.dip = it->first.dip;
328                data.protocol = it->first.protocol;
329                pq.push(data);
330        }
331        getmaxyx(stdscr,row,col);
332        move(0,0);
333        printw("Total Bytes: %10" PRIu64 " (%s)\tTotal Packets: %10" PRIu64, total_bytes, nice_bandwidth(total_bytes/interval), total_packets);
334        clrtoeol();
335        attrset(A_REVERSE);
336        move(1,0);
337        if (use_sip) {
338                printw("%*s", wide_display ? 42 : 20, "source ip");
339                if (use_sport)
340                        printw("/");
341                else
342                        printw("\t");
343        }
344        if (use_sport)
345                printw("%s  ", "sport");
346        if (use_dip) {
347                printw("%*s", wide_display ? 42 : 20, "dest ip");
348                if (use_dport)
349                        printw("/");
350                else
351                        printw("\t");
352        }
353        if (use_dport)
354                printw("%s  ", "dport");
355        if (use_protocol)
356                printw("%10s\t", "proto");
357        switch(display_as) {
358                case BYTES:
359                        printw("%7s","Bytes\t");
360                        break;
361                case BITS_PER_SEC:
362                        printw("%14s\t","Bits/sec");
363                        break;
364                case PERCENT:
365                        printw("%% bytes\t");
366                        break;
367        }
368        printw("Packets");
369
370        attrset(A_NORMAL);
371        char sipstr[1024];
372        char dipstr[1024];
373        for(int i=1; i<row-3 && !pq.empty(); ++i) {
374                move(i+1,0);
375                if (use_sip) {
376                        printw("%*s", wide_display ? 42 : 20, 
377                                        trace_sockaddr2string(
378                                                (struct sockaddr*)&pq.top().sip,
379                                                sizeof(struct sockaddr_storage),
380                                                sipstr,sizeof(sipstr)));
381                        if (use_sport)
382                                printw("/");
383                        else
384                                printw("\t");
385                }
386                if (use_sport)
387                        printw("%-5d  ", get_port_from_sockaddr((struct sockaddr*)&pq.top().sip));
388                if (use_dip) {
389                        printw("%*s", wide_display ? 42 : 20, 
390                                        trace_sockaddr2string(
391                                                (struct sockaddr*)&pq.top().dip,
392                                                sizeof(struct sockaddr_storage),
393                                                dipstr,sizeof(dipstr)));
394                        if (use_dport)
395                                printw("/");
396                        else
397                                printw("\t");
398                }
399                if (use_dport)
400                        printw("%-5d  ", get_port_from_sockaddr((struct sockaddr*)&pq.top().dip));
401                if (use_protocol) {
402                        struct protoent *proto = getprotobynumber(pq.top().protocol);
403                        if (proto) 
404                                printw("%-10s  ", proto->p_name);
405                        else
406                                printw("%10d  ",pq.top().protocol);
407                }
408                switch (display_as) {
409                        case BYTES:
410                                printw("%7" PRIu64 "\t%7" PRIu64 "\n",
411                                                pq.top().bytes,
412                                                pq.top().packets);
413                                break;
414                        case BITS_PER_SEC:
415                                printw("%14.03f\t%" PRIu64 "\n",
416                                                8.0*pq.top().bytes/interval,
417                                                pq.top().packets);
418                                break;
419                        case PERCENT:
420                                printw("%6.2f%%\t%6.2f%%\n",
421                                                100.0*pq.top().bytes/total_bytes,
422                                                100.0*pq.top().packets/total_packets);
423                }
424                pq.pop();
425        }
426        flows.clear();
427        total_packets = 0;
428        total_bytes = 0;
429
430        clrtobot();
431        refresh();
432}
433
434static void run_trace(libtrace_t *trace)
435{
436        libtrace_packet_t *packet = trace_create_packet();
437        libtrace_eventobj_t obj;
438        fd_set rfds;
439        struct timeval sleep_tv;
440        struct timeval *tv = NULL;
441
442        do {
443                int maxfd=0;
444                FD_ZERO(&rfds);
445                FD_SET(0, &rfds); /* stdin */
446                tv=NULL;
447                maxfd=0;
448
449                obj = trace_event(trace, packet);
450                switch(obj.type) {
451                        case TRACE_EVENT_IOWAIT:
452                                FD_SET(obj.fd, &rfds);
453                                maxfd = obj.fd;
454                                break;
455
456                        case TRACE_EVENT_SLEEP:
457                                sleep_tv.tv_sec = (int)obj.seconds;
458                                sleep_tv.tv_usec = (int)((obj.seconds - sleep_tv.tv_sec)*1000000.0);
459
460                                tv = &sleep_tv;
461                                break;;
462
463                        case TRACE_EVENT_TERMINATE:
464                                trace_destroy_packet(packet);
465                                return;
466
467                        case TRACE_EVENT_PACKET:
468                                if (obj.size == -1)
469                                        break;
470                                if (trace_get_seconds(packet) - last_report >= interval) {
471                                        do_report();
472                                        last_report=trace_get_seconds(packet);
473                                }
474                                if (trace_read_packet(trace,packet) <= 0) {
475                                        obj.size = -1;
476                                        break;
477                                }
478                                per_packet(packet);
479                                continue;
480                }
481
482                if (tv && tv->tv_sec > interval) {
483                        tv->tv_sec = (int)interval;
484                        tv->tv_usec = 0;
485                }
486
487                select(maxfd+1, &rfds, 0, 0, tv);
488                if (FD_ISSET(0, &rfds)) {
489                        switch (getch()) {
490                                case '%':
491                                        display_as = PERCENT;
492                                        break;
493                                case 'b':
494                                        display_as = BITS_PER_SEC;
495                                        break;
496                                case 'B':
497                                        display_as = BYTES;
498                                        break;
499                                case '\x1b': /* Escape */
500                                case 'q':
501                                        quit = true;
502                                        trace_destroy_packet(packet);
503                                        return;
504                                case '1': use_sip       = !use_sip; break;
505                                case '2': use_sport     = !use_sport; break;
506                                case '3': use_dip       = !use_dip; break;
507                                case '4': use_dport     = !use_dport; break;
508                                case '5': use_protocol  = !use_protocol; break;
509                        }
510                }
511        } while (obj.type != TRACE_EVENT_TERMINATE || obj.size == -1);
512
513        trace_destroy_packet(packet);
514} 
515
516static void usage(char *argv0)
517{
518        fprintf(stderr,"usage: %s [options] libtraceuri...\n",argv0);
519        fprintf(stderr," --filter bpfexpr\n");
520        fprintf(stderr," -f bpfexpr\n");
521        fprintf(stderr,"\t\tApply a bpf filter expression\n");
522        fprintf(stderr," --snaplen snaplen\n");
523        fprintf(stderr," -s snaplen\n");
524        fprintf(stderr,"\t\tCapture only snaplen bytes\n");
525        fprintf(stderr," --promisc 0|1\n");
526        fprintf(stderr," -p 0|1\n");
527        fprintf(stderr,"\t\tEnable/Disable promiscuous mode\n");
528        fprintf(stderr," --bits-per-sec\n");
529        fprintf(stderr," -B\n");
530        fprintf(stderr,"\t\tDisplay usage in bits per second, not bytes per second\n");
531        fprintf(stderr," --percent\n");
532        fprintf(stderr," -P\n");
533        fprintf(stderr,"\t\tDisplay usage in percentage of total usage\n");
534        fprintf(stderr," --interval int\n");
535        fprintf(stderr," -i int\n");
536        fprintf(stderr,"\t\tUpdate the display every int seconds\n");
537        fprintf(stderr," --wide\n");
538        fprintf(stderr," -w\n");
539        fprintf(stderr,"\t\tExpand IP address fields to fit IPv6 addresses\n");
540}
541
542int main(int argc, char *argv[])
543{
544        libtrace_t *trace;
545        libtrace_filter_t *filter=NULL;
546        int snaplen=-1;
547        int promisc=-1;
548
549        setprotoent(1);
550
551        while(1) {
552                int option_index;
553                struct option long_options[] = {
554                        { "filter",             1, 0, 'f' },
555                        { "snaplen",            1, 0, 's' },
556                        { "promisc",            1, 0, 'p' },
557                        { "help",               0, 0, 'h' },
558                        { "libtrace-help",      0, 0, 'H' },
559                        { "bits-per-sec",       0, 0, 'B' },
560                        { "percent",            0, 0, 'P' },
561                        { "interval",           1, 0, 'i' },
562                        { "fast",               0, 0, 'F' },
563                        { "wide",               0, 0, 'w' },
564                        { NULL,                 0, 0, 0 }
565                };
566
567                int c= getopt_long(argc, argv, "BPf:Fs:p:hHi:w12345",
568                                long_options, &option_index);
569
570                if (c==-1)
571                        break;
572
573                switch (c) {
574                        case 'f':
575                                filter=trace_create_filter(optarg);
576                                break;
577                        case 'F':
578                                fullspeed = true;
579                                break;
580                        case 's':
581                                snaplen=atoi(optarg);
582                                break;
583                        case 'p':
584                                promisc=atoi(optarg);
585                                break;
586                        case 'H':
587                                trace_help();
588                                return 1;
589                        case 'B':
590                                display_as = BITS_PER_SEC;
591                                break;
592                        case 'P':
593                                display_as = PERCENT;
594                                break;
595                        case 'i':
596                                interval = atof(optarg);
597                                if (interval<=0) {
598                                        fprintf(stderr,"Interval must be >0\n");
599                                        return 1;
600                                }
601                                break;
602                        case 'w':
603                                wide_display = true;
604                                break;
605                        case '1': use_sip       = !use_sip; break;
606                        case '2': use_sport     = !use_sport; break;
607                        case '3': use_dip       = !use_dip; break;
608                        case '4': use_dport     = !use_dport; break;
609                        case '5': use_protocol  = !use_protocol; break;
610                        default:
611                                fprintf(stderr,"Unknown option: %c\n",c);
612                                /* FALL THRU */
613                        case 'h':
614                                usage(argv[0]);
615                                return 1;
616                }
617        }
618
619        if (optind>=argc) {
620                fprintf(stderr,"Missing input uri\n");
621                usage(argv[0]);
622                return 1;
623        }
624
625        initscr(); cbreak(); noecho();
626
627        while (!quit && optind<argc) {
628                trace = trace_create(argv[optind]);
629                ++optind;
630
631                if (trace_is_err(trace)) {
632                        endwin();
633                        trace_perror(trace,"Opening trace file");
634                        return 1;
635                }
636
637                if (snaplen>0)
638                        if (trace_config(trace,TRACE_OPTION_SNAPLEN,&snaplen)) {
639                                trace_perror(trace,"ignoring: ");
640                        }
641                if (filter)
642                        if (trace_config(trace,TRACE_OPTION_FILTER,filter)) {
643                                trace_perror(trace,"ignoring: ");
644                        }
645                if (promisc!=-1) {
646                        if (trace_config(trace,TRACE_OPTION_PROMISC,&promisc)) {
647                                trace_perror(trace,"ignoring: ");
648                        }
649                }
650                if (fullspeed) {
651                        int flag=1;
652                        if (trace_config(trace,TRACE_OPTION_EVENT_REALTIME,&flag)) {
653                                trace_perror(trace,"Setting EVENT_REALTIME option");
654                        }
655                }
656
657                if (trace_start(trace)) {
658                        endwin();
659                        trace_perror(trace,"Starting trace");
660                        trace_destroy(trace);
661                        return 1;
662                }
663
664                run_trace(trace);
665
666                if (trace_is_err(trace)) {
667                        trace_perror(trace,"Reading packets");
668                }
669
670                trace_destroy(trace);
671        }
672
673        endwin();
674        endprotoent();
675
676        return 0;
677}
Note: See TracBrowser for help on using the repository browser.