source: tools/tracetop/tracetop.cc @ 5c866be

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 5c866be was 5c866be, checked in by Perry Lorier <perry@…>, 12 years ago

Support undocumented initialisation flags

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