source: tools/tracetop/tracetop.cc @ 11754a6

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