Annotation of embedaddon/iftop/iftop.c, revision 1.1
1.1 ! misho 1: /*
! 2: * iftop.c:
! 3: *
! 4: */
! 5:
! 6: #include "integers.h"
! 7:
! 8: #if defined(HAVE_PCAP_H)
! 9: # include <pcap.h>
! 10: #elif defined(HAVE_PCAP_PCAP_H)
! 11: # include <pcap/pcap.h>
! 12: #else
! 13: # error No pcap.h
! 14: #endif
! 15: #include <stdio.h>
! 16: #include <stdlib.h>
! 17: #include <time.h>
! 18: #include <sys/types.h>
! 19: #include <sys/ioctl.h>
! 20: #include <sys/socket.h>
! 21: #include <net/if.h>
! 22:
! 23: #include <pthread.h>
! 24: #include <curses.h>
! 25: #include <signal.h>
! 26: #include <string.h>
! 27: #include <unistd.h>
! 28:
! 29: #include "iftop.h"
! 30: #include "addr_hash.h"
! 31: #include "resolver.h"
! 32: #include "ui.h"
! 33: #include "options.h"
! 34: #ifdef DLT_LINUX_SLL
! 35: #include "sll.h"
! 36: #endif /* DLT_LINUX_SLL */
! 37: #include "threadprof.h"
! 38: #include "ether.h"
! 39: #include "ip.h"
! 40: #include "tcp.h"
! 41: #include "token.h"
! 42: #include "llc.h"
! 43: #include "extract.h"
! 44: #include "ethertype.h"
! 45: #include "cfgfile.h"
! 46: #include "ppp.h"
! 47:
! 48:
! 49: /* ethernet address of interface. */
! 50: int have_hw_addr = 0;
! 51: unsigned char if_hw_addr[6];
! 52:
! 53: /* IP address of interface */
! 54: int have_ip_addr = 0;
! 55: struct in_addr if_ip_addr;
! 56:
! 57: extern options_t options;
! 58:
! 59: hash_type* history;
! 60: history_type history_totals;
! 61: time_t last_timestamp;
! 62: int history_pos = 0;
! 63: int history_len = 1;
! 64: pthread_mutex_t tick_mutex;
! 65:
! 66: pcap_t* pd; /* pcap descriptor */
! 67: struct bpf_program pcap_filter;
! 68: pcap_handler packet_handler;
! 69:
! 70: sig_atomic_t foad;
! 71:
! 72: static void finish(int sig) {
! 73: foad = sig;
! 74: }
! 75:
! 76:
! 77:
! 78:
! 79: /* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2 bytes of tcp/udp header */
! 80: #define CAPTURE_LENGTH 72
! 81:
! 82: void init_history() {
! 83: history = addr_hash_create();
! 84: last_timestamp = time(NULL);
! 85: memset(&history_totals, 0, sizeof history_totals);
! 86: }
! 87:
! 88: history_type* history_create() {
! 89: history_type* h;
! 90: h = xcalloc(1, sizeof *h);
! 91: return h;
! 92: }
! 93:
! 94: void history_rotate() {
! 95: hash_node_type* n = NULL;
! 96: history_pos = (history_pos + 1) % HISTORY_LENGTH;
! 97: hash_next_item(history, &n);
! 98: while(n != NULL) {
! 99: hash_node_type* next = n;
! 100: history_type* d = (history_type*)n->rec;
! 101: hash_next_item(history, &next);
! 102:
! 103: if(d->last_write == history_pos) {
! 104: addr_pair key = *(addr_pair*)(n->key);
! 105: hash_delete(history, &key);
! 106: free(d);
! 107: }
! 108: else {
! 109: d->recv[history_pos] = 0;
! 110: d->sent[history_pos] = 0;
! 111: }
! 112: n = next;
! 113: }
! 114:
! 115: history_totals.sent[history_pos] = 0;
! 116: history_totals.recv[history_pos] = 0;
! 117:
! 118: if(history_len < HISTORY_LENGTH) {
! 119: history_len++;
! 120: }
! 121: }
! 122:
! 123:
! 124: void tick(int print) {
! 125: time_t t;
! 126:
! 127: pthread_mutex_lock(&tick_mutex);
! 128:
! 129: t = time(NULL);
! 130: if(t - last_timestamp >= RESOLUTION) {
! 131: //printf("TICKING\n");
! 132: analyse_data();
! 133: ui_print();
! 134: history_rotate();
! 135: last_timestamp = t;
! 136: }
! 137: else {
! 138: ui_tick(print);
! 139: }
! 140:
! 141: pthread_mutex_unlock(&tick_mutex);
! 142: }
! 143:
! 144: int in_filter_net(struct in_addr addr) {
! 145: int ret;
! 146: ret = ((addr.s_addr & options.netfiltermask.s_addr) == options.netfilternet.s_addr);
! 147: return ret;
! 148: }
! 149:
! 150: int ip_addr_match(struct in_addr addr) {
! 151: return addr.s_addr == if_ip_addr.s_addr;
! 152: }
! 153:
! 154: /**
! 155: * Creates an addr_pair from an ip (and tcp/udp) header, swapping src and dst
! 156: * if required
! 157: */
! 158: void assign_addr_pair(addr_pair* ap, struct ip* iptr, int flip) {
! 159: unsigned short int src_port = 0;
! 160: unsigned short int dst_port = 0;
! 161:
! 162: /* Does this protocol use ports? */
! 163: if(iptr->ip_p == IPPROTO_TCP || iptr->ip_p == IPPROTO_UDP) {
! 164: /* We take a slight liberty here by treating UDP the same as TCP */
! 165:
! 166: /* Find the TCP/UDP header */
! 167: struct tcphdr* thdr = ((void*)iptr) + IP_HL(iptr) * 4;
! 168: src_port = ntohs(thdr->th_sport);
! 169: dst_port = ntohs(thdr->th_dport);
! 170: }
! 171:
! 172: if(flip == 0) {
! 173: ap->src = iptr->ip_src;
! 174: ap->src_port = src_port;
! 175: ap->dst = iptr->ip_dst;
! 176: ap->dst_port = dst_port;
! 177: }
! 178: else {
! 179: ap->src = iptr->ip_dst;
! 180: ap->src_port = dst_port;
! 181: ap->dst = iptr->ip_src;
! 182: ap->dst_port = src_port;
! 183: }
! 184:
! 185: }
! 186:
! 187: static void handle_ip_packet(struct ip* iptr, int hw_dir)
! 188: {
! 189: int direction = 0; /* incoming */
! 190: history_type* ht;
! 191: union {
! 192: history_type **ht_pp;
! 193: void **void_pp;
! 194: } u_ht = { &ht };
! 195: addr_pair ap;
! 196: int len;
! 197:
! 198: if(options.netfilter == 0) {
! 199: /*
! 200: * Net filter is off, so assign direction based on MAC address
! 201: */
! 202: if(hw_dir == 1) {
! 203: /* Packet leaving this interface. */
! 204: assign_addr_pair(&ap, iptr, 0);
! 205: direction = 1;
! 206: }
! 207: else if(hw_dir == 0) {
! 208: /* Packet incoming */
! 209: assign_addr_pair(&ap, iptr, 1);
! 210: direction = 0;
! 211: }
! 212: /* Packet direction is not given away by h/ware layer. Try IP
! 213: * layer
! 214: */
! 215: else if(have_ip_addr && ip_addr_match(iptr->ip_src)) {
! 216: /* outgoing */
! 217: assign_addr_pair(&ap, iptr, 0);
! 218: direction = 1;
! 219: }
! 220: else if(have_ip_addr && ip_addr_match(iptr->ip_dst)) {
! 221: /* incoming */
! 222: assign_addr_pair(&ap, iptr, 1);
! 223: direction = 0;
! 224: }
! 225: /*
! 226: * Cannot determine direction from hardware or IP levels. Therefore
! 227: * assume that it was a packet between two other machines, assign
! 228: * source and dest arbitrarily (by numerical value) and account as
! 229: * incoming.
! 230: */
! 231: else if (options.promiscuous_but_choosy) {
! 232: return; /* junk it */
! 233: }
! 234: else if(iptr->ip_src.s_addr < iptr->ip_dst.s_addr) {
! 235: assign_addr_pair(&ap, iptr, 1);
! 236: direction = 0;
! 237: }
! 238: else {
! 239: assign_addr_pair(&ap, iptr, 0);
! 240: direction = 0;
! 241: }
! 242: }
! 243: else {
! 244: /*
! 245: * Net filter on, assign direction according to netmask
! 246: */
! 247: if(in_filter_net(iptr->ip_src) && !in_filter_net(iptr->ip_dst)) {
! 248: /* out of network */
! 249: assign_addr_pair(&ap, iptr, 0);
! 250: direction = 1;
! 251: }
! 252: else if(in_filter_net(iptr->ip_dst) && !in_filter_net(iptr->ip_src)) {
! 253: /* into network */
! 254: assign_addr_pair(&ap, iptr, 1);
! 255: direction = 0;
! 256: }
! 257: else {
! 258: /* drop packet */
! 259: return ;
! 260: }
! 261: }
! 262:
! 263: ap.protocol = iptr->ip_p;
! 264:
! 265: /* Add the addresses to be resolved */
! 266: resolve(&iptr->ip_dst, NULL, 0);
! 267: resolve(&iptr->ip_src, NULL, 0);
! 268:
! 269: if(hash_find(history, &ap, u_ht.void_pp) == HASH_STATUS_KEY_NOT_FOUND) {
! 270: ht = history_create();
! 271: hash_insert(history, &ap, ht);
! 272: }
! 273:
! 274: len = ntohs(iptr->ip_len);
! 275:
! 276: /* Update record */
! 277: ht->last_write = history_pos;
! 278: if(iptr->ip_src.s_addr == ap.src.s_addr) {
! 279: ht->sent[history_pos] += len;
! 280: ht->total_sent += len;
! 281: }
! 282: else {
! 283: ht->recv[history_pos] += len;
! 284: ht->total_recv += len;
! 285: }
! 286:
! 287: if(direction == 0) {
! 288: /* incoming */
! 289: history_totals.recv[history_pos] += len;
! 290: history_totals.total_recv += len;
! 291: }
! 292: else {
! 293: history_totals.sent[history_pos] += len;
! 294: history_totals.total_sent += len;
! 295: }
! 296:
! 297: }
! 298:
! 299: static void handle_raw_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
! 300: {
! 301: handle_ip_packet((struct ip*)packet, -1);
! 302: }
! 303:
! 304: static void handle_llc_packet(const struct llc* llc, int dir) {
! 305:
! 306: struct ip* ip = (struct ip*)((void*)llc + sizeof(struct llc));
! 307:
! 308: /* Taken from tcpdump/print-llc.c */
! 309: if(llc->ssap == LLCSAP_SNAP && llc->dsap == LLCSAP_SNAP
! 310: && llc->llcui == LLC_UI) {
! 311: u_int32_t orgcode;
! 312: register u_short et;
! 313: orgcode = EXTRACT_24BITS(&llc->llc_orgcode[0]);
! 314: et = EXTRACT_16BITS(&llc->llc_ethertype[0]);
! 315: switch(orgcode) {
! 316: case OUI_ENCAP_ETHER:
! 317: case OUI_CISCO_90:
! 318: handle_ip_packet(ip, dir);
! 319: break;
! 320: case OUI_APPLETALK:
! 321: if(et == ETHERTYPE_ATALK) {
! 322: handle_ip_packet(ip, dir);
! 323: }
! 324: break;
! 325: default:;
! 326: /* Not a lot we can do */
! 327: }
! 328: }
! 329: }
! 330:
! 331: static void handle_tokenring_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
! 332: {
! 333: struct token_header *trp;
! 334: int dir = -1;
! 335: trp = (struct token_header *)packet;
! 336:
! 337: if(IS_SOURCE_ROUTED(trp)) {
! 338: packet += RIF_LENGTH(trp);
! 339: }
! 340: packet += TOKEN_HDRLEN;
! 341:
! 342: if(memcmp(trp->token_shost, if_hw_addr, 6) == 0 ) {
! 343: /* packet leaving this i/f */
! 344: dir = 1;
! 345: }
! 346: else if(memcmp(trp->token_dhost, if_hw_addr, 6) == 0 || memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", trp->token_dhost, 6) == 0) {
! 347: /* packet entering this i/f */
! 348: dir = 0;
! 349: }
! 350:
! 351: /* Only know how to deal with LLC encapsulated packets */
! 352: if(FRAME_TYPE(trp) == TOKEN_FC_LLC) {
! 353: handle_llc_packet((struct llc*)packet, dir);
! 354: }
! 355: }
! 356:
! 357: static void handle_ppp_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
! 358: {
! 359: register u_int length = pkthdr->len;
! 360: register u_int caplen = pkthdr->caplen;
! 361: u_int proto;
! 362:
! 363: if (caplen < 2)
! 364: return;
! 365:
! 366: if(packet[0] == PPP_ADDRESS) {
! 367: if (caplen < 4)
! 368: return;
! 369:
! 370: packet += 2;
! 371: length -= 2;
! 372:
! 373: proto = EXTRACT_16BITS(packet);
! 374: packet += 2;
! 375: length -= 2;
! 376:
! 377: if(proto == PPP_IP || proto == ETHERTYPE_IP) {
! 378: handle_ip_packet((struct ip*)packet, -1);
! 379: }
! 380: }
! 381: }
! 382:
! 383: #ifdef DLT_LINUX_SLL
! 384: static void handle_cooked_packet(unsigned char *args, const struct pcap_pkthdr * thdr, const unsigned char * packet)
! 385: {
! 386: struct sll_header *sptr;
! 387: int dir = -1;
! 388: sptr = (struct sll_header *) packet;
! 389:
! 390: switch (ntohs(sptr->sll_pkttype))
! 391: {
! 392: case LINUX_SLL_HOST:
! 393: /*entering this interface*/
! 394: dir = 0;
! 395: break;
! 396: case LINUX_SLL_OUTGOING:
! 397: /*leaving this interface */
! 398: dir=1;
! 399: break;
! 400: }
! 401: handle_ip_packet((struct ip*)(packet+SLL_HDR_LEN), dir);
! 402: }
! 403: #endif /* DLT_LINUX_SLL */
! 404:
! 405: static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
! 406: {
! 407: struct ether_header *eptr;
! 408: int ether_type;
! 409: const unsigned char *payload;
! 410: eptr = (struct ether_header*)packet;
! 411: ether_type = ntohs(eptr->ether_type);
! 412: payload = packet + sizeof(struct ether_header);
! 413:
! 414: tick(0);
! 415:
! 416: if(ether_type == ETHERTYPE_8021Q) {
! 417: struct vlan_8021q_header* vptr;
! 418: vptr = (struct vlan_8021q_header*)payload;
! 419: ether_type = ntohs(vptr->ether_type);
! 420: payload += sizeof(struct vlan_8021q_header);
! 421: }
! 422:
! 423: if(ether_type == ETHERTYPE_IP) {
! 424: struct ip* iptr;
! 425: int dir = -1;
! 426:
! 427: /*
! 428: * Is a direction implied by the MAC addresses?
! 429: */
! 430: if(have_hw_addr && memcmp(eptr->ether_shost, if_hw_addr, 6) == 0 ) {
! 431: /* packet leaving this i/f */
! 432: dir = 1;
! 433: }
! 434: else if(have_hw_addr && memcmp(eptr->ether_dhost, if_hw_addr, 6) == 0 ) {
! 435: /* packet entering this i/f */
! 436: dir = 0;
! 437: }
! 438: else if (memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", eptr->ether_dhost, 6) == 0) {
! 439: /* broadcast packet, count as incoming */
! 440: dir = 0;
! 441: }
! 442:
! 443: iptr = (struct ip*)(payload); /* alignment? */
! 444: handle_ip_packet(iptr, dir);
! 445: }
! 446: }
! 447:
! 448:
! 449: /* set_filter_code:
! 450: * Install some filter code. Returns NULL on success or an error message on
! 451: * failure. */
! 452: char *set_filter_code(const char *filter) {
! 453: char *x;
! 454: if (filter) {
! 455: x = xmalloc(strlen(filter) + sizeof "() and ip");
! 456: sprintf(x, "(%s) and ip", filter);
! 457: } else
! 458: x = xstrdup("ip");
! 459: if (pcap_compile(pd, &pcap_filter, x, 1, 0) == -1) {
! 460: xfree(x);
! 461: return pcap_geterr(pd);
! 462: }
! 463: xfree(x);
! 464: if (pcap_setfilter(pd, &pcap_filter) == -1)
! 465: return pcap_geterr(pd);
! 466: else
! 467: return NULL;
! 468: }
! 469:
! 470:
! 471:
! 472: /*
! 473: * packet_init:
! 474: *
! 475: * performs pcap initialisation, called before ui is initialised
! 476: */
! 477: void packet_init() {
! 478: char errbuf[PCAP_ERRBUF_SIZE];
! 479: char *m;
! 480: int s;
! 481: int i;
! 482: int dlt;
! 483: int result;
! 484:
! 485: #ifdef HAVE_DLPI
! 486: result = get_addrs_dlpi(options.interface, if_hw_addr, &if_ip_addr);
! 487: #else
! 488: result = get_addrs_ioctl(options.interface, if_hw_addr, &if_ip_addr);
! 489: #endif
! 490:
! 491: if (result < 0) {
! 492: exit(1);
! 493: }
! 494:
! 495: have_hw_addr = result & 1;
! 496: have_ip_addr = result & 2;
! 497:
! 498: if(have_ip_addr) {
! 499: fprintf(stderr, "IP address is: %s\n", inet_ntoa(if_ip_addr));
! 500: }
! 501:
! 502: if(have_hw_addr) {
! 503: fprintf(stderr, "MAC address is:");
! 504: for (i = 0; i < 6; ++i)
! 505: fprintf(stderr, "%c%02x", i ? ':' : ' ', (unsigned int)if_hw_addr[i]);
! 506: fprintf(stderr, "\n");
! 507: }
! 508:
! 509: // exit(0);
! 510: resolver_initialise();
! 511:
! 512: pd = pcap_open_live(options.interface, CAPTURE_LENGTH, options.promiscuous, 1000, errbuf);
! 513: // DEBUG: pd = pcap_open_offline("tcpdump.out", errbuf);
! 514: if(pd == NULL) {
! 515: fprintf(stderr, "pcap_open_live(%s): %s\n", options.interface, errbuf);
! 516: exit(1);
! 517: }
! 518: dlt = pcap_datalink(pd);
! 519: if(dlt == DLT_EN10MB) {
! 520: packet_handler = handle_eth_packet;
! 521: }
! 522: else if(dlt == DLT_RAW || dlt == DLT_NULL) {
! 523: packet_handler = handle_raw_packet;
! 524: }
! 525: else if(dlt == DLT_IEEE802) {
! 526: packet_handler = handle_tokenring_packet;
! 527: }
! 528: else if(dlt == DLT_PPP) {
! 529: packet_handler = handle_ppp_packet;
! 530: }
! 531: /*
! 532: * SLL support not available in older libpcaps
! 533: */
! 534: #ifdef DLT_LINUX_SLL
! 535: else if(dlt == DLT_LINUX_SLL) {
! 536: packet_handler = handle_cooked_packet;
! 537: }
! 538: #endif
! 539: else {
! 540: fprintf(stderr, "Unsupported datalink type: %d\n"
! 541: "Please email pdw@ex-parrot.com, quoting the datalink type and what you were\n"
! 542: "trying to do at the time\n.", dlt);
! 543: exit(1);
! 544: }
! 545:
! 546: if ((m = set_filter_code(options.filtercode))) {
! 547: fprintf(stderr, "set_filter_code: %s\n", m);
! 548: exit(1);
! 549: return;
! 550: }
! 551: }
! 552:
! 553: /* packet_loop:
! 554: * Worker function for packet capture thread. */
! 555: void packet_loop(void* ptr) {
! 556: pcap_loop(pd,-1,(pcap_handler)packet_handler,NULL);
! 557: }
! 558:
! 559:
! 560: /* main:
! 561: * Entry point. See usage(). */
! 562: int main(int argc, char **argv) {
! 563: pthread_t thread;
! 564: struct sigaction sa = {};
! 565:
! 566: /* TODO: tidy this up */
! 567: /* read command line options and config file */
! 568: config_init();
! 569: options_set_defaults();
! 570: options_read_args(argc, argv);
! 571: /* If a config was explicitly specified, whinge if it can't be found */
! 572: read_config(options.config_file, options.config_file_specified);
! 573: options_make();
! 574:
! 575: sa.sa_handler = finish;
! 576: sigaction(SIGINT, &sa, NULL);
! 577:
! 578: pthread_mutex_init(&tick_mutex, NULL);
! 579:
! 580: packet_init();
! 581:
! 582: init_history();
! 583:
! 584: ui_init();
! 585:
! 586: pthread_create(&thread, NULL, (void*)&packet_loop, NULL);
! 587:
! 588: ui_loop();
! 589:
! 590: pthread_cancel(thread);
! 591:
! 592: ui_finish();
! 593:
! 594: return 0;
! 595: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>