When dumpcap is run to get an interface list, interface capabilities, or
[obnox/wireshark/wip.git] / dumpcap.c
index e6de0d1fac635de8307644fc2d40e536738ba43b..0a6f5fff10072b30a79b797b8f0d65379c4a7683 100644 (file)
--- a/dumpcap.c
+++ b/dumpcap.c
 #include <unistd.h>
 #endif
 
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
 #if defined(__APPLE__) && defined(__LP64__)
 #include <sys/utsname.h>
 #endif
 #include <sys/un.h>
 #endif
 
+#ifdef NEED_INET_V6DEFS_H
+# include "inet_v6defs.h"
+#endif
+
 #include <wsutil/privileges.h>
 
 #include "sync_pipe.h"
@@ -327,7 +335,7 @@ print_usage(gboolean print_ver) {
   fprintf(output, "  -I                       capture in monitor mode, if available\n");
 #endif
 #if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
-  fprintf(output, "  -B <buffer size>         size of kernel buffer (def: platform-dependent)\n");
+  fprintf(output, "  -B <buffer size>         size of kernel buffer (def: 1MB)\n");
 #endif
   fprintf(output, "  -y <link type>           link layer type (def: first appropriate)\n");
   fprintf(output, "  -D                       print list of interfaces and exit\n");
@@ -494,29 +502,111 @@ create_data_link_info(int dlt)
     return data_link_info;
 }
 
-static GList *
-get_pcap_linktype_list(const char *devname, char **err_str)
+static if_capabilities_t *
+get_if_capabilities(const char *devname, gboolean monitor_mode
+#ifndef HAVE_PCAP_CREATE
+       _U_
+#endif
+, char **err_str)
 {
-    GList *linktype_list = NULL;
+    if_capabilities_t *caps;
+    char errbuf[PCAP_ERRBUF_SIZE];
     pcap_t *pch;
+#ifdef HAVE_PCAP_CREATE
+    int status;
+#endif
     int deflt;
-    char errbuf[PCAP_ERRBUF_SIZE];
 #ifdef HAVE_PCAP_LIST_DATALINKS
     int *linktypes;
     int i, nlt;
 #endif
     data_link_info_t *data_link_info;
 
+    /*
+     * Allocate the interface capabilities structure.
+     */
+    caps = g_malloc(sizeof *caps);
+
 #ifdef HAVE_PCAP_OPEN
     pch = pcap_open(devname, MIN_PACKET_SIZE, 0, 0, NULL, errbuf);
+    caps->can_set_rfmon = FALSE;
+    if (pch == NULL) {
+        if (err_str != NULL)
+            *err_str = g_strdup(errbuf);
+        g_free(caps);
+        return NULL;
+    }
+#elif defined(HAVE_PCAP_CREATE)
+    pch = pcap_create(devname, errbuf);
+    if (pch == NULL) {
+        if (err_str != NULL)
+            *err_str = g_strdup(errbuf);
+        g_free(caps);
+        return NULL;
+    }
+    status = pcap_can_set_rfmon(pch); 
+    switch (status) {
+
+    case 0:
+        caps->can_set_rfmon = FALSE;
+        break;
+
+    case 1:
+        caps->can_set_rfmon = TRUE;
+        if (monitor_mode)
+               pcap_set_rfmon(pch, 1);
+        break;
+
+    case PCAP_ERROR_NO_SUCH_DEVICE:
+        if (err_str != NULL)
+            *err_str = g_strdup_printf("There is no capture device named \"%s\"", devname);
+        pcap_close(pch);
+        g_free(caps);
+        return NULL;
+
+    case PCAP_ERROR:
+        if (err_str != NULL)
+            *err_str = g_strdup_printf("pcap_can_set_rfmon on \"%s\" failed: %s",
+                                       devname, pcap_geterr(pch));
+        pcap_close(pch);
+        g_free(caps);
+        return NULL;
+
+    default:
+        if (err_str != NULL)
+            *err_str = g_strdup_printf("pcap_can_set_rfmon on \"%s\" failed: %s",
+                                       devname, pcap_statustostr(status));
+        pcap_close(pch);
+        g_free(caps);
+        return NULL;
+    }
+
+    status = pcap_activate(pch);
+    if (status < 0) {
+        /* Error.  We ignore warnings (status > 0). */
+        if (err_str != NULL) {
+            if (status == PCAP_ERROR) {
+                *err_str = g_strdup_printf("pcap_activate on %s failed: %s",
+                                           devname, pcap_geterr(pch));
+            } else {
+                *err_str = g_strdup_printf("pcap_activate on %s failed: %s",
+                                           devname, pcap_statustostr(status));
+            }
+        }
+        pcap_close(pch);
+        g_free(caps);
+        return NULL;
+    }
 #else
     pch = pcap_open_live(devname, MIN_PACKET_SIZE, 0, 0, errbuf);
-#endif
+    caps->can_set_rfmon = FALSE;
     if (pch == NULL) {
         if (err_str != NULL)
             *err_str = g_strdup(errbuf);
-            return NULL;
+        g_free(caps);
+        return NULL;
     }
+#endif
     deflt = get_pcap_linktype(pch, devname);
 #ifdef HAVE_PCAP_LIST_DATALINKS
     nlt = pcap_list_datalinks(pch, &linktypes);
@@ -526,6 +616,7 @@ get_pcap_linktype_list(const char *devname, char **err_str)
             *err_str = NULL; /* an empty list doesn't mean an error */
         return NULL;
     }
+    caps->data_link_types = NULL;
     for (i = 0; i < nlt; i++) {
         data_link_info = create_data_link_info(linktypes[i]);
 
@@ -535,9 +626,11 @@ get_pcap_linktype_list(const char *devname, char **err_str)
          * device has as the default?
          */
         if (linktypes[i] == deflt)
-            linktype_list = g_list_prepend(linktype_list, data_link_info);
+            caps->data_link_types = g_list_prepend(caps->data_link_types,
+                                                   data_link_info);
         else
-            linktype_list = g_list_append(linktype_list, data_link_info);
+            caps->data_link_types = g_list_append(caps->data_link_types,
+                                                  data_link_info);
     }
 #ifdef HAVE_PCAP_FREE_DATALINKS
     pcap_free_datalinks(linktypes);
@@ -567,28 +660,105 @@ get_pcap_linktype_list(const char *devname, char **err_str)
 #else /* HAVE_PCAP_LIST_DATALINKS */
 
     data_link_info = create_data_link_info(deflt);
-    linktype_list = g_list_append(linktype_list, data_link_info);
+    caps->data_link_types = g_list_append(caps->data_link_types,
+                                          data_link_info);
 #endif /* HAVE_PCAP_LIST_DATALINKS */
 
     pcap_close(pch);
 
     if (err_str != NULL)
         *err_str = NULL;
-    return linktype_list;
+    return caps;
+}
+
+#define ADDRSTRLEN 46 /* Covers IPv4 & IPv6 */
+static void
+print_machine_readable_interfaces(GList *if_list)
+{
+    int         i;
+    GList       *if_entry;
+    if_info_t   *if_info;
+    GSList      *addr;
+    if_addr_t   *if_addr;
+    char        addr_str[ADDRSTRLEN];
+
+    /* Let our parent know we succeeded. */
+    pipe_write_block(2, SP_SUCCESS, NULL);
+
+    i = 1;  /* Interface id number */
+    for (if_entry = g_list_first(if_list); if_entry != NULL;
+         if_entry = g_list_next(if_entry)) {
+        if_info = (if_info_t *)if_entry->data;
+        printf("%d. %s", i++, if_info->name);
+
+        /*
+         * Print the contents of the if_entry struct in a parseable format.
+         * Each if_entry element is tab-separated.  Addresses are comma-
+         * separated.
+         */
+        /* XXX - Make sure our description doesn't contain a tab */
+        if (if_info->description != NULL)
+            printf("\t%s\t", if_info->description);
+        else
+            printf("\t\t");
+
+        for(addr = g_slist_nth(if_info->addrs, 0); addr != NULL;
+                    addr = g_slist_next(addr)) {
+            if (addr != g_slist_nth(if_info->addrs, 0))
+                printf(",");
+
+            if_addr = (if_addr_t *)addr->data;
+            switch(if_addr->ifat_type) {
+            case IF_AT_IPv4:
+                if (inet_ntop(AF_INET, &if_addr->addr.ip4_addr, addr_str,
+                              ADDRSTRLEN)) {
+                    printf("%s", addr_str);
+                } else {
+                    printf("<unknown IPv4>");
+                }
+                break;
+            case IF_AT_IPv6:
+                if (inet_ntop(AF_INET6, &if_addr->addr.ip6_addr,
+                              addr_str, ADDRSTRLEN)) {
+                    printf("%s", addr_str);
+                } else {
+                    printf("<unknown IPv6>");
+                }
+                break;
+            default:
+                printf("<type unknown %u>", if_addr->ifat_type);
+            }
+        }
+
+        if (if_info->loopback)
+            printf("\tloopback");
+        else
+            printf("\tnetwork");
+
+        printf("\n");
+    }
 }
 
 /*
  * If you change the machine-readable output format of this function,
- * you MUST update capture_sync.c:sync_linktype_list_open() accordingly!
+ * you MUST update capture_ifinfo.c:capture_get_if_capabilities() accordingly!
  */
 static void
-print_machine_readable_link_layer_types(GList *lt_list)
+print_machine_readable_if_capabilities(if_capabilities_t *caps)
 {
     GList *lt_entry;
     data_link_info_t *data_link_info;
     const gchar *desc_str;
 
-    for (lt_entry = lt_list; lt_entry != NULL; lt_entry = g_list_next(lt_entry)) {
+    /* Let our parent know we succeeded. */
+    pipe_write_block(2, SP_SUCCESS, NULL);
+
+    if (caps->can_set_rfmon)
+        printf("1\n");
+    else
+        printf("0\n");
+    for (lt_entry = caps->data_link_types; lt_entry != NULL;
+         lt_entry = g_list_next(lt_entry)) {
       data_link_info = (data_link_info_t *)lt_entry->data;
       if (data_link_info->description != NULL)
         desc_str = data_link_info->description;
@@ -653,6 +823,9 @@ print_statistics_loop(gboolean machine_readable)
         return 2;
     }
 
+    /* Let our parent know we succeeded. */
+    pipe_write_block(2, SP_SUCCESS, NULL);
+
     if (!machine_readable) {
         printf("%-15s  %10s  %10s\n", "Interface", "Received",
             "Dropped");
@@ -1613,6 +1786,10 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
      the error buffer, and check if it's still a null string.  */
   open_err_str[0] = '\0';
 #ifdef HAVE_PCAP_OPEN
+  /*
+   * If we're opening a remote device, use pcap_open(); that's currently
+   * the only open routine that supports remote devices.
+   */
   if (strncmp (capture_opts->iface, "rpcap://", 8) == 0) {
     auth.type = capture_opts->auth_type == CAPTURE_AUTH_PWD ?
       RPCAP_RMTAUTH_PWD : RPCAP_RMTAUTH_NULL;
@@ -1628,8 +1805,14 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
                  (capture_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0),
                  CAP_READ_TIMEOUT, &auth, open_err_str);
   } else
-#elif defined(HAVE_PCAP_CREATE)
+#endif /* HAVE_PCAP_OPEN */
   {
+    /*
+     * If we're not opening a remote device, use pcap_create() and
+     * pcap_activate() if we have them, so that we can set the buffer
+     * size, otherwise use pcap_open_live().
+     */
+#ifdef HAVE_PCAP_CREATE
     ld->pcap_h = pcap_create(capture_opts->iface, open_err_str);
     if (ld->pcap_h != NULL) {
       pcap_set_snaplen(ld->pcap_h, capture_opts->has_snaplen ? capture_opts->snaplen : WTAP_MAX_PACKET_SIZE);
@@ -1647,20 +1830,20 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
         ld->pcap_h = NULL;
       }
     }
-  }
 #else
-  ld->pcap_h = pcap_open_live(capture_opts->iface,
-                              capture_opts->has_snaplen ? capture_opts->snaplen :
-                                                          WTAP_MAX_PACKET_SIZE,
-                              capture_opts->promisc_mode, CAP_READ_TIMEOUT,
-                              open_err_str);
-#endif
-
-/* If not using libcap: we now can now set euid/egid to ruid/rgid         */
-/*  to remove any suid privileges.                                        */
-/* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities  */
-/*  (euid/egid have already previously been set to ruid/rgid.             */
-/* (See comment in main() for details)                                    */
+    ld->pcap_h = pcap_open_live(capture_opts->iface,
+                                capture_opts->has_snaplen ? capture_opts->snaplen :
+                                                            WTAP_MAX_PACKET_SIZE,
+                                capture_opts->promisc_mode, CAP_READ_TIMEOUT,
+                                open_err_str);
+#endif
+  }
+
+  /* If not using libcap: we now can now set euid/egid to ruid/rgid         */
+  /*  to remove any suid privileges.                                        */
+  /* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities  */
+  /*  (euid/egid have already previously been set to ruid/rgid.             */
+  /* (See comment in main() for details)                                    */
 #ifndef HAVE_LIBCAP
   relinquish_special_privs_perm();
 #else
@@ -3270,29 +3453,56 @@ main(int argc, char *argv[])
   g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", global_capture_opts.iface);
 
   if (list_interfaces) {
-    status = capture_opts_list_interfaces(machine_readable);
-    exit_main(status);
+    /* Get the list of interfaces */
+    GList       *if_list;
+    int         err;
+    gchar       *err_str;
+
+    if_list = capture_interface_list(&err, &err_str);
+    if (if_list == NULL) {
+        switch (err) {
+        case CANT_GET_INTERFACE_LIST:
+            cmdarg_err("%s", err_str);
+            g_free(err_str);
+            break;
+
+        case NO_INTERFACES_FOUND:
+            cmdarg_err("There are no interfaces on which a capture can be done");
+            break;
+        }
+        exit_main(2);
+    }
+
+    if (machine_readable)      /* tab-separated values to stdout */
+      print_machine_readable_interfaces(if_list);
+    else
+      capture_opts_print_interfaces(if_list);
+    free_interface_list(if_list);
+    exit_main(0);
   } else if (list_link_layer_types) {
     /* Get the list of link-layer types for the capture device. */
-    GList *lt_list;
+    if_capabilities_t *caps;
     gchar *err_str;
 
-    lt_list = get_pcap_linktype_list(global_capture_opts.iface, &err_str);
-    if (lt_list == NULL) {
-      if (err_str != NULL) {
-        cmdarg_err("The list of data link types for the capture device \"%s\" could not be obtained (%s)."
-         "Please check to make sure you have sufficient permissions, and that\n"
-         "you have the proper interface or pipe specified.\n", global_capture_opts.iface, err_str);
-        g_free(err_str);
-      } else
-        cmdarg_err("The capture device \"%s\" has no data link types.", global_capture_opts.iface);
+    caps = get_if_capabilities(global_capture_opts.iface,
+                               global_capture_opts.monitor_mode, &err_str);
+    if (caps == NULL) {
+      cmdarg_err("The capabilities of the capture device \"%s\" could not be obtained (%s).\n"
+       "Please check to make sure you have sufficient permissions, and that\n"
+       "you have the proper interface or pipe specified.", global_capture_opts.iface, err_str);
+      g_free(err_str);
+      exit_main(2);
+    }
+    if (caps->data_link_types == NULL) {
+      cmdarg_err("The capture device \"%s\" has no data link types.", global_capture_opts.iface);
       exit_main(2);
     }
     if (machine_readable)      /* tab-separated values to stdout */
-      print_machine_readable_link_layer_types(lt_list);
+      print_machine_readable_if_capabilities(caps);
     else
-      capture_opts_print_link_layer_types(lt_list);
-    free_pcap_linktype_list(lt_list);
+      capture_opts_print_if_capabilities(caps,
+                                         global_capture_opts.monitor_mode);
+    free_if_capabilities(caps);
     exit_main(0);
   } else if (print_statistics) {
     status = print_statistics_loop(machine_readable);