]> gitweb.fperrin.net Git - iftop.git/blobdiff - iftop.c
gitignore
[iftop.git] / iftop.c
diff --git a/iftop.c b/iftop.c
index 97020ea3f2fae72786bde4a3f7438039571ac76e..59857a5dde8c7921ac2814421f046b6abc23d0ae 100644 (file)
--- a/iftop.c
+++ b/iftop.c
@@ -5,13 +5,6 @@
 
 #include "integers.h"
 
-#if defined(HAVE_PCAP_H)
-#   include <pcap.h>
-#elif defined(HAVE_PCAP_PCAP_H)
-#   include <pcap/pcap.h>
-#else
-#   error No pcap.h
-#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <net/if.h>
-#include <net/bpf.h>
+/* include <net/bpf.h> -- this was added by the PFLOG patch but seems
+ * superfluous and breaks on Slackware */
+#if defined(HAVE_PCAP_H)
+#   include <pcap.h>
+#elif defined(HAVE_PCAP_PCAP_H)
+#   include <pcap/pcap.h>
+#else
+#   error No pcap.h
+#endif
 
 #include <pthread.h>
 #include <curses.h>
 #include <signal.h>
 #include <string.h>
 #include <unistd.h>
+#include <locale.h>
 
 #include "iftop.h"
 #include "addr_hash.h"
 #include "resolver.h"
+#include "ui_common.h"
 #include "ui.h"
+#include "tui.h"
 #include "options.h"
 #ifdef DLT_LINUX_SLL
 #include "sll.h"
 #include "ethertype.h"
 #include "cfgfile.h"
 #include "ppp.h"
+#include "addrs_ioctl.h"
 
 #include <netinet/ip6.h>
 
 /* ethernet address of interface. */
 int have_hw_addr = 0;
-unsigned char if_hw_addr[6];    
+char if_hw_addr[6];    
 
 /* IP address of interface */
 int have_ip_addr = 0;
@@ -63,6 +68,7 @@ extern options_t options;
 hash_type* history;
 history_type history_totals;
 time_t last_timestamp;
+time_t first_timestamp;
 int history_pos = 0;
 int history_len = 1;
 pthread_mutex_t tick_mutex;
@@ -80,9 +86,14 @@ static void finish(int sig) {
 
 
 
-/* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2 bytes of tcp/udp header */
+/* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2
+ * bytes of tcp/udp header */
 /* Increase with a further 20 to account for IPv6 header length.  */
-#define CAPTURE_LENGTH 92
+/* IEEE 802.11 radiotap throws in a variable length header plus 8 (radiotap
+ * header header) plus 34 (802.11 MAC) plus 40 (IPv6) = 78, plus whatever's in
+ * the radiotap payload */
+/*#define CAPTURE_LENGTH 92 */
+#define CAPTURE_LENGTH 256
 
 void init_history() {
     history = addr_hash_create();
@@ -98,6 +109,8 @@ history_type* history_create() {
 
 void history_rotate() {
     hash_node_type* n = NULL;
+    struct pcap_stat ps;
+
     history_pos = (history_pos + 1) % HISTORY_LENGTH;
     hash_next_item(history, &n);
     while(n != NULL) {
@@ -123,6 +136,9 @@ void history_rotate() {
     if(history_len < HISTORY_LENGTH) {
         history_len++;
     }
+
+    pcap_stats(pd, &ps);
+    history_totals.lost_packets = ps.ps_drop + ps.ps_ifdrop;
 }
 
 
@@ -133,14 +149,28 @@ void tick(int print) {
    
     t = time(NULL);
     if(t - last_timestamp >= RESOLUTION) {
-        //printf("TICKING\n");
         analyse_data();
-        ui_print();
+        if (options.no_curses) {
+          if (!options.timed_output || (options.timed_output && t - first_timestamp >= options.timed_output)) {
+            tui_print();
+            if (options.timed_output) {
+              finish(SIGINT);
+            }
+          }
+        }
+        else {
+          ui_print();
+        }
         history_rotate();
         last_timestamp = t;
     }
     else {
-      ui_tick(print);
+      if (options.no_curses) {
+        tui_tick(print);
+      }
+      else {
+        ui_tick(print);
+      }
     }
 
     pthread_mutex_unlock(&tick_mutex);
@@ -152,11 +182,11 @@ int in_filter_net(struct in_addr addr) {
     return ret;
 }
 
-int __inline__ ip_addr_match(struct in_addr addr) {
+static int __inline__ ip_addr_match(struct in_addr addr) {
     return addr.s_addr == if_ip_addr.s_addr;
 }
 
-int __inline__ ip6_addr_match(struct in6_addr *addr) {
+static int __inline__ ip6_addr_match(struct in6_addr *addr) {
     return IN6_ARE_ADDR_EQUAL(addr, &if_ip6_addr);
 }
 
@@ -224,16 +254,16 @@ void assign_addr_pair(addr_pair* ap, struct ip* iptr, int flip) {
   }
 }
 
-static void handle_ip_packet(struct ip* iptr, int hw_dir)
+static void handle_ip_packet(struct ip* iptr, int hw_dir, int pld_len)
 {
     int direction = 0; /* incoming */
+    int len;
     history_type* ht;
     union {
-       history_type **ht_pp;
-       void **void_pp;
+      history_type **ht_pp;
+      void **void_pp;
     } u_ht = { &ht };
     addr_pair ap;
-    unsigned int len = 0;
     struct in6_addr scribdst;   /* Scratch pad. */
     struct in6_addr scribsrc;   /* Scratch pad. */
     /* Reinterpret packet type. */
@@ -241,7 +271,23 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
 
     memset(&ap, '\0', sizeof(ap));
 
-    if( (IP_V(iptr) ==4 && options.netfilter == 0)
+    tick(0);
+
+    /*
+     * Sanity check: drop obviously short packets.
+     * pld_len comes from pcaphdr->len - sizeof(struct l2_header).
+     *
+     * It is assumed that the snaplen (currently hard-coded to 1000) is
+     * big enough to always capture the IP header past the L2 encap, and
+     * that pcap never truncates the packet to less than snaplen; in
+     * other words, that pcaphdr->caplen = MIN(pcaphdr->len, snaplen).
+     */
+    if (pld_len < sizeof (struct ip))
+       return;
+    if (IP_V(iptr) == 6 && pld_len < sizeof (struct ip6_hdr))
+       return;
+
+    if( (IP_V(iptr) == 4 && options.netfilter == 0)
             || (IP_V(iptr) == 6 && options.netfilter6 == 0) ) { 
         /*
          * Net filter is off, so assign direction based on MAC address
@@ -279,6 +325,14 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
             assign_addr_pair(&ap, iptr, 1);
             direction = 0;
         }
+        else if (IP_V(iptr) == 4 && IN_MULTICAST(iptr->ip_dst.s_addr)) {
+            assign_addr_pair(&ap, iptr, 1);
+            direction = 0;
+        }
+        else if (IP_V(iptr) == 6 && IN6_IS_ADDR_MULTICAST(&ip6tr->ip6_dst)) {
+            assign_addr_pair(&ap, iptr, 1);
+            direction = 0;
+        }
         /*
          * Cannot determine direction from hardware or IP levels.  Therefore 
          * assume that it was a packet between two other machines, assign
@@ -297,6 +351,8 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
             direction = 0;
         }
         /* Drop other uncertain packages. */
+        else
+            return;
     }
 
     if(IP_V(iptr) == 4 && options.netfilter != 0) {
@@ -328,11 +384,11 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
         /* First reduce the participating addresses using the netfilter prefix.
          * We need scratch pads to do this.
          */
-        for (j=0; j < 4; ++j) {
-            scribdst.s6_addr32[j] = ip6tr->ip6_dst.s6_addr32[j]
-                                        & options.netfilter6mask.s6_addr32[j];
-            scribsrc.s6_addr32[j] = ip6tr->ip6_src.s6_addr32[j]
-                                        & options.netfilter6mask.s6_addr32[j];
+        for (j=0; j < 16; ++j) {
+            scribdst.s6_addr[j] = ip6tr->ip6_dst.s6_addr[j]
+                                        & options.netfilter6mask.s6_addr[j];
+            scribsrc.s6_addr[j] = ip6tr->ip6_src.s6_addr[j]
+                                        & options.netfilter6mask.s6_addr[j];
         }
 
         /* Now look for any hits. */
@@ -387,21 +443,22 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
           break;
     }
 
-
     if(hash_find(history, &ap, u_ht.void_pp) == HASH_STATUS_KEY_NOT_FOUND) {
         ht = history_create();
         hash_insert(history, &ap, ht);
     }
 
     /* Do accounting. */
-    switch (IP_V(iptr)) {
-      case 4:
-          len = ntohs(iptr->ip_len);
-          break;
-      case 6:
-          len = ntohs(ip6tr->ip6_plen) + 40;
+    switch (options.bandwidth_unit) {
+      case OPTION_BW_BITS:
+      case OPTION_BW_BYTES:
+         len = pld_len;
+         break;
+      case OPTION_BW_PKTS:
+         len = 1;
+         break;
       default:
-          break;
+         return;
     }
 
     /* Update record */
@@ -431,7 +488,7 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
 
 static void handle_raw_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
 {
-    handle_ip_packet((struct ip*)packet, -1);
+    handle_ip_packet((struct ip*)packet, -1, pkthdr->len);
 }
 
 #ifdef DLT_PFLOG
@@ -445,29 +502,35 @@ static void handle_pflog_packet(unsigned char* args, const struct pcap_pkthdr* p
        hdrlen = BPF_WORDALIGN(hdr->length);
        length -= hdrlen;
        packet += hdrlen;
-       handle_ip_packet((struct ip*)packet, length);
+       handle_ip_packet((struct ip*)packet, -1, length);
 }
 #endif
 
-static void handle_llc_packet(const struct llc* llc, int dir) {
+static void handle_null_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
+{
+    handle_ip_packet((struct ip*)(packet + 4), -1, pkthdr->len);
+}
 
-    struct ip* ip = (struct ip*)((void*)llc + sizeof(struct llc));
+static void handle_llc_packet(const struct llc* llc, int dir, int llclen) {
+    int hdrlen = sizeof(struct llc);
+    int pldlen = llclen - hdrlen;
+    struct ip* ip = (struct ip*)((void*)llc + hdrlen);
 
     /* Taken from tcpdump/print-llc.c */
     if(llc->ssap == LLCSAP_SNAP && llc->dsap == LLCSAP_SNAP
        && llc->llcui == LLC_UI) {
         u_int32_t orgcode;
-        register u_short et;
+        u_int16_t et;
         orgcode = EXTRACT_24BITS(&llc->llc_orgcode[0]);
-        et = EXTRACT_16BITS(&llc->llc_ethertype[0]);
+        et = (llc->llc_ethertype[0] << 8) + llc->llc_ethertype[1];
         switch(orgcode) {
           case OUI_ENCAP_ETHER:
           case OUI_CISCO_90:
-            handle_ip_packet(ip, dir);
+             handle_ip_packet(ip, dir, pldlen);
             break;
           case OUI_APPLETALK:
             if(et == ETHERTYPE_ATALK) {
-              handle_ip_packet(ip, dir);
+               handle_ip_packet(ip, dir, pldlen);
             }
             break;
           default:;
@@ -479,52 +542,55 @@ static void handle_llc_packet(const struct llc* llc, int dir) {
 static void handle_tokenring_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
 {
     struct token_header *trp;
+    int hdrlen = 0;
     int dir = -1;
     trp = (struct token_header *)packet;
 
     if(IS_SOURCE_ROUTED(trp)) {
-      packet += RIF_LENGTH(trp);
+      hdrlen += RIF_LENGTH(trp);
     }
-    packet += TOKEN_HDRLEN;
+    hdrlen += TOKEN_HDRLEN;
+    packet += hdrlen;
 
     if(memcmp(trp->token_shost, if_hw_addr, 6) == 0 ) {
       /* packet leaving this i/f */
       dir = 1;
-    } 
-        else if(memcmp(trp->token_dhost, if_hw_addr, 6) == 0 || memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", trp->token_dhost, 6) == 0) {
+    }
+    else if(memcmp(trp->token_dhost, if_hw_addr, 6) == 0 || memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", trp->token_dhost, 6) == 0) {
       /* packet entering this i/f */
       dir = 0;
     }
 
     /* Only know how to deal with LLC encapsulated packets */
     if(FRAME_TYPE(trp) == TOKEN_FC_LLC) {
-      handle_llc_packet((struct llc*)packet, dir);
+       handle_llc_packet((struct llc*)packet, dir, pkthdr->len - hdrlen);
     }
 }
 
 static void handle_ppp_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
 {
-       register u_int length = pkthdr->len;
-       register u_int caplen = pkthdr->caplen;
-       u_int proto;
+    register u_int length = pkthdr->len;
+    register u_int caplen = pkthdr->caplen;
+    u_int proto;
 
-       if (caplen < 2) 
-        return;
+    if (caplen < 2)
+       return;
 
-       if(packet[0] == PPP_ADDRESS) {
-               if (caplen < 4) 
-            return;
+    if(packet[0] == PPP_ADDRESS) {
+       if (caplen < 4)
+           return;
 
-               packet += 2;
-               length -= 2;
+       packet += 2;
+       length -= 2;
 
-               proto = EXTRACT_16BITS(packet);
-               packet += 2;
-               length -= 2;
+       proto = EXTRACT_16BITS(packet);
+       packet += 2;
+       length -= 2;
 
-        if(proto == PPP_IP || proto == ETHERTYPE_IP || proto == ETHERTYPE_IPV6) {
-            handle_ip_packet((struct ip*)packet, -1);
-        }
+       if(proto == PPP_IP || proto == ETHERTYPE_IP
+          || proto == ETHERTYPE_IPV6) {
+           handle_ip_packet((struct ip*)packet, -1, length);
+       }
     }
 }
 
@@ -546,26 +612,25 @@ static void handle_cooked_packet(unsigned char *args, const struct pcap_pkthdr *
        dir=1;
        break;
     }
-    handle_ip_packet((struct ip*)(packet+SLL_HDR_LEN), dir);
+    handle_ip_packet((struct ip*)(packet+SLL_HDR_LEN), dir,
+                    thdr->len - SLL_HDR_LEN);
 }
 #endif /* DLT_LINUX_SLL */
 
 static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
 {
     struct ether_header *eptr;
-    int ether_type;
-    const unsigned char *payload;
+    int ether_type, hdrlen;
+
     eptr = (struct ether_header*)packet;
     ether_type = ntohs(eptr->ether_type);
-    payload = packet + sizeof(struct ether_header);
-
-    tick(0);
+    hdrlen = sizeof(struct ether_header);
 
     if(ether_type == ETHERTYPE_8021Q) {
-       struct vlan_8021q_header* vptr;
-       vptr = (struct vlan_8021q_header*)payload;
-       ether_type = ntohs(vptr->ether_type);
-        payload += sizeof(struct vlan_8021q_header);
+        struct vlan_8021q_header* vptr;
+        vptr = (struct vlan_8021q_header*) (packet + hdrlen);
+        ether_type = ntohs(vptr->ether_type);
+        hdrlen += sizeof(struct vlan_8021q_header);
     }
 
     if(ether_type == ETHERTYPE_IP || ether_type == ETHERTYPE_IPV6) {
@@ -580,20 +645,35 @@ static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkt
             dir = 1;
         }
         else if(have_hw_addr && memcmp(eptr->ether_dhost, if_hw_addr, 6) == 0 ) {
-           /* packet entering this i/f */
-           dir = 0;
-       }
-       else if (memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", eptr->ether_dhost, 6) == 0) {
-         /* broadcast packet, count as incoming */
+            /* packet entering this i/f */
+            dir = 0;
+        }
+        else if (memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", eptr->ether_dhost, 6) == 0) {
+            /* broadcast packet, count as incoming */
             dir = 0;
         }
 
         /* Distinguishing ip_hdr and ip6_hdr will be done later. */
-        iptr = (struct ip*)(payload); /* alignment? */
-        handle_ip_packet(iptr, dir);
+        iptr = (struct ip*) (packet + hdrlen); /* alignment? */
+        handle_ip_packet(iptr, dir, pkthdr->len - hdrlen);
     }
 }
 
+#ifdef DLT_IEEE802_11_RADIO
+/*
+ * Packets with a bonus radiotap header.
+ * See http://www.gsp.com/cgi-bin/man.cgi?section=9&topic=ieee80211_radiotap
+ */
+static void handle_radiotap_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
+{
+    /* 802.11 MAC header is = 34 bytes (not sure if that's universally true) */
+    /* We could try harder to figure out hardware direction from the MAC header */
+    int hdrlen = ((struct radiotap_header *)packet)->it_len + 34;
+    handle_ip_packet((struct ip*)(packet + hdrlen), -1, pkthdr->len - hdrlen);
+}
+
+
+#endif
 
 /* set_filter_code:
  * Install some filter code. Returns NULL on success or an error message on
@@ -626,8 +706,6 @@ char *set_filter_code(const char *filter) {
 void packet_init() {
     char errbuf[PCAP_ERRBUF_SIZE];
     char *m;
-    int s;
-    int i;
     int dlt;
     int result;
 
@@ -658,10 +736,7 @@ void packet_init() {
     }
 
     if(have_hw_addr) {
-      fprintf(stderr, "MAC address is:");
-      for (i = 0; i < 6; ++i)
-       fprintf(stderr, "%c%02x", i ? ':' : ' ', (unsigned int)if_hw_addr[i]);
-      fprintf(stderr, "\n");
+      fprintf(stderr, "MAC address is: %s\n", ether_ntoa(if_hw_addr));
     }
     
     //    exit(0);
@@ -682,9 +757,22 @@ void packet_init() {
                packet_handler = handle_pflog_packet;
     }
 #endif
-    else if(dlt == DLT_RAW || dlt == DLT_NULL) {
+    else if(dlt == DLT_RAW) {
         packet_handler = handle_raw_packet;
     } 
+    else if(dlt == DLT_NULL) {
+        packet_handler = handle_null_packet;
+    } 
+#ifdef DLT_LOOP
+    else if(dlt == DLT_LOOP) {
+        packet_handler = handle_null_packet;
+    }
+#endif
+#ifdef DLT_IEEE802_11_RADIO
+    else if(dlt == DLT_IEEE802_11_RADIO) {
+        packet_handler = handle_radiotap_packet;
+    }
+#endif
     else if(dlt == DLT_IEEE802) {
         packet_handler = handle_tokenring_packet;
     }
@@ -726,6 +814,8 @@ int main(int argc, char **argv) {
     pthread_t thread;
     struct sigaction sa = {};
 
+    setlocale(LC_ALL, "");
+
     /* TODO: tidy this up */
     /* read command line options and config file */   
     config_init();
@@ -744,11 +834,31 @@ int main(int argc, char **argv) {
 
     init_history();
 
-    ui_init();
+    if (options.no_curses) {
+      tui_init();
+    }
+    else {
+      ui_init();
+    }
 
     pthread_create(&thread, NULL, (void*)&packet_loop, NULL);
 
-    ui_loop();
+    /* Keep the starting time (used for timed termination) */
+    first_timestamp = time(NULL);
+
+    if (options.no_curses) {
+      if (options.timed_output) {
+        while(!foad) {
+          sleep(1);
+        }
+      }
+      else {
+        tui_loop();
+      }
+    }
+    else {
+      ui_loop();
+    }
 
     pthread_cancel(thread);