source: tools/tracetop/tracetop.cc @ 524ebb5

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