source: tools/tracetop/tracetop.cc @ 8e11beb

cachetimestampsdevelopdpdk-ndagetsilivendag_formatrc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformance
Last change on this file since 8e11beb was 8e11beb, checked in by Shane Alcock <salcock@…>, 3 years ago

Update tools to properly ignore meta records and missing timestamps

pcapng introduces a lot of meta records that we should preserve as
long as possible. Since we aren't automatically discarding them,
we need to make sure that our tools do not try to treat them as
"real" packets, i.e. try to get a timestamp or capture length from
them.

Similarly, simple pcapng packet records do not have a timestamp
so we need to make sure the tools do the right thing when
trace_get_seconds() returns a timestamp of zero on a packet. For
starters, we don't want to set our "first" packet time to zero
in that case!

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