source: tools/tracetop/tracetop.cc @ 2216d25

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