/* iface_lists.c * Code to manage the global list of interfaces and to update widgets/windows * displaying items from those lists * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #ifdef HAVE_LIBPCAP #include #include #include #include #include "ui/capture_ui_utils.h" #include "ui/capture_globals.h" #include "ui/iface_lists.h" #include "../log.h" /* * Used when sorting an interface list into alphabetical order by * their friendly names. */ gint if_list_comparator_alph(const void *first_arg, const void *second_arg) { const if_info_t *first = (const if_info_t *)first_arg, *second = (const if_info_t *)second_arg; if (first != NULL && first->friendly_name != NULL && second != NULL && second->friendly_name != NULL) { return g_ascii_strcasecmp(first->friendly_name, second->friendly_name); } else { return 0; } } /* * Try to populate the given device with options (like capture filter) from * the capture options that are in use for an existing capture interface. */ static void fill_from_ifaces (interface_t *device) { interface_options interface_opts; guint i; for (i = 0; i < global_capture_opts.ifaces->len; i++) { interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i); if (strcmp(interface_opts.name, device->name) != 0) { continue; } #if defined(HAVE_PCAP_CREATE) device->buffer = interface_opts.buffer_size; device->monitor_mode_enabled = interface_opts.monitor_mode; #endif device->pmode = interface_opts.promisc_mode; device->has_snaplen = interface_opts.has_snaplen; device->snaplen = interface_opts.snaplen; g_free(device->cfilter); device->cfilter = g_strdup(interface_opts.cfilter); if (interface_opts.linktype != -1) { device->active_dlt = interface_opts.linktype; } return; } } /* * Fetch the list of local interfaces with capture_interface_list() * and set the list of "all interfaces" in *capture_opts to include * those interfaces. */ void scan_local_interfaces(void (*update_cb)(void)) { GList *if_entry, *lt_entry, *if_list; if_info_t *if_info, temp; gchar *descr; if_capabilities_t *caps=NULL; gint linktype_count; gboolean monitor_mode; GSList *curr_addr; int ips = 0, i; guint count = 0, j; if_addr_t *addr, *temp_addr; link_row *link = NULL; data_link_info_t *data_link_info; interface_t device; GString *ip_str; interface_options interface_opts; gboolean found = FALSE; static gboolean running = FALSE; GHashTable *selected_devices; if (running) { /* scan_local_interfaces internally calls update_cb to process UI events to avoid stuck UI while running possibly slow operations. A side effect of this is that new interface changes can be detected before completing the last one. This return avoids recursive scan_local_interfaces operation. */ return; } running = TRUE; /* * Clear list of known interfaces (all_ifaces) that will be re-discovered on * scanning, but remember their selection state. * * XXX shouldn't this copy settings (like capture filter) from the "old" * device to the "new" device? Refreshing the interfaces list should * probably just remove disappeared devices and add discovered devices. */ selected_devices = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); if (global_capture_opts.all_ifaces->len > 0) { for (i = (int)global_capture_opts.all_ifaces->len-1; i >= 0; i--) { device = g_array_index(global_capture_opts.all_ifaces, interface_t, i); if (device.local && device.type != IF_PIPE && device.type != IF_STDIN) { global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, i); /* * Device is about to be destroyed, unmark as selected. It will * be reselected on rediscovery. */ if (device.selected) { gchar *device_name = g_strdup(device.name); /* g_hash_table_add() only exists since 2.32. */ g_hash_table_replace(selected_devices, device_name, device_name); global_capture_opts.num_selected--; } capture_opts_free_interface_t(&device); } } } /* Retrieve list of interface information (if_info_t) into if_list. */ g_free(global_capture_opts.ifaces_err_info); if_list = capture_interface_list(&global_capture_opts.ifaces_err, &global_capture_opts.ifaces_err_info, update_cb); count = 0; /* * For each discovered interface name, create a new device and add extra * information (like supported DLTs, assigned IP addresses). */ for (if_entry = if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) { memset(&device, 0, sizeof(device)); if_info = (if_info_t *)if_entry->data; ip_str = g_string_new(""); ips = 0; if (strstr(if_info->name, "rpcap:")) { continue; } device.name = g_strdup(if_info->name); if (if_info->friendly_name != NULL) { device.friendly_name = g_strdup(if_info->friendly_name); } else { device.friendly_name = NULL; } device.hidden = FALSE; device.locked = FALSE; memset(&temp, 0, sizeof(temp)); temp.name = g_strdup(if_info->name); temp.friendly_name = g_strdup(if_info->friendly_name); temp.vendor_description = g_strdup(if_info->vendor_description); temp.loopback = if_info->loopback; temp.type = if_info->type; #ifdef HAVE_EXTCAP temp.extcap = g_strdup(if_info->extcap); #endif /* Is this interface hidden and, if so, should we include it anyway? */ descr = capture_dev_user_descr_find(if_info->name); device.display_name = get_iface_display_name(descr, if_info); g_free(descr); device.selected = FALSE; if (prefs_is_capture_device_hidden(if_info->name)) { device.hidden = TRUE; } device.type = if_info->type; monitor_mode = prefs_capture_device_monitor_mode(if_info->name); caps = capture_get_if_capabilities(if_info->name, monitor_mode, NULL, NULL, update_cb); for (; (curr_addr = g_slist_nth(if_info->addrs, ips)) != NULL; ips++) { temp_addr = (if_addr_t *)g_malloc0(sizeof(if_addr_t)); if (ips != 0) { g_string_append(ip_str, "\n"); } addr = (if_addr_t *)curr_addr->data; if (addr) { address addr_str; char* temp_addr_str = NULL; temp_addr->ifat_type = addr->ifat_type; switch (addr->ifat_type) { case IF_AT_IPv4: temp_addr->addr.ip4_addr = addr->addr.ip4_addr; set_address(&addr_str, AT_IPv4, 4, &addr->addr.ip4_addr); temp_addr_str = address_to_str(NULL, &addr_str); g_string_append(ip_str, temp_addr_str); break; case IF_AT_IPv6: memcpy(temp_addr->addr.ip6_addr, addr->addr.ip6_addr, sizeof(addr->addr)); set_address(&addr_str, AT_IPv6, 16, addr->addr.ip6_addr); temp_addr_str = address_to_str(NULL, &addr_str); g_string_append(ip_str, temp_addr_str); break; default: /* In case we add non-IP addresses */ break; } wmem_free(NULL, temp_addr_str); } else { g_free(temp_addr); temp_addr = NULL; } if (temp_addr) { temp.addrs = g_slist_append(temp.addrs, temp_addr); } } #ifdef HAVE_PCAP_REMOTE device.local = TRUE; device.remote_opts.src_type = CAPTURE_IFLOCAL; device.remote_opts.remote_host_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host); device.remote_opts.remote_host_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port); device.remote_opts.remote_host_opts.auth_type = global_capture_opts.default_options.auth_type; device.remote_opts.remote_host_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username); device.remote_opts.remote_host_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password); device.remote_opts.remote_host_opts.datatx_udp = global_capture_opts.default_options.datatx_udp; device.remote_opts.remote_host_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap; device.remote_opts.remote_host_opts.nocap_local = global_capture_opts.default_options.nocap_local; #endif #ifdef HAVE_PCAP_SETSAMPLING device.remote_opts.sampling_method = global_capture_opts.default_options.sampling_method; device.remote_opts.sampling_param = global_capture_opts.default_options.sampling_param; #endif linktype_count = 0; device.links = NULL; if (caps != NULL) { #if defined(HAVE_PCAP_CREATE) device.monitor_mode_enabled = monitor_mode; device.monitor_mode_supported = caps->can_set_rfmon; #endif /* * Process the list of link-layer header types. */ 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; link = (link_row *)g_malloc(sizeof(link_row)); if (data_link_info->description != NULL) { link->dlt = data_link_info->dlt; link->name = g_strdup(data_link_info->description); } else { link->dlt = -1; link->name = g_strdup_printf("%s (not supported)", data_link_info->name); } device.links = g_list_append(device.links, link); linktype_count++; } /* * Set the active DLT for the device appropriately. */ set_active_dlt(&device, global_capture_opts.default_options.linktype); } else { #if defined(HAVE_PCAP_CREATE) device.monitor_mode_enabled = FALSE; device.monitor_mode_supported = FALSE; #endif device.active_dlt = -1; } device.addresses = g_strdup(ip_str->str); device.no_addresses = ips; device.local = TRUE; device.if_info = temp; device.last_packets = 0; if (!capture_dev_user_pmode_find(if_info->name, &device.pmode)) { device.pmode = global_capture_opts.default_options.promisc_mode; } if (!capture_dev_user_snaplen_find(if_info->name, &device.has_snaplen, &device.snaplen)) { device.has_snaplen = global_capture_opts.default_options.has_snaplen; device.snaplen = global_capture_opts.default_options.snaplen; } device.cfilter = g_strdup(global_capture_opts.default_options.cfilter); #ifdef CAN_SET_CAPTURE_BUFFER_SIZE if ((device.buffer = capture_dev_user_buffersize_find(if_info->name)) == -1) { device.buffer = global_capture_opts.default_options.buffer_size; } #endif /* Copy interface options for active capture devices. */ fill_from_ifaces(&device); /* Restore device selection (for next capture). */ if (!device.selected && g_hash_table_lookup(selected_devices, device.name)) { device.selected = TRUE; global_capture_opts.num_selected++; } #ifdef HAVE_EXTCAP /* Extcap devices start with no cached args */ device.external_cap_args_settings = NULL; #endif if (global_capture_opts.all_ifaces->len <= count) { g_array_append_val(global_capture_opts.all_ifaces, device); count = global_capture_opts.all_ifaces->len; } else { g_array_insert_val(global_capture_opts.all_ifaces, count, device); } if (caps != NULL) { free_if_capabilities(caps); } g_string_free(ip_str, TRUE); count++; } free_interface_list(if_list); /* * Pipes and stdin are not really discoverable interfaces, so re-add them to * the list of all interfaces (all_ifaces). */ for (j = 0; j < global_capture_opts.ifaces->len; j++) { interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j); /* Skip non-pipes (like Ethernet, Wi-Fi, ...). */ if (interface_opts.if_type != IF_PIPE && interface_opts.if_type != IF_STDIN) { /* Note: the interface options are not destroyed for these * interfaces in case it is briefly removed and re-added. */ continue; } found = FALSE; for (i = 0; i < (int)global_capture_opts.all_ifaces->len; i++) { device = g_array_index(global_capture_opts.all_ifaces, interface_t, i); if (strcmp(device.name, interface_opts.name) == 0) { found = TRUE; break; } } if (!found) { /* new interface, maybe a pipe */ memset(&device, 0, sizeof(device)); device.name = g_strdup(interface_opts.name); device.display_name = interface_opts.descr ? g_strdup_printf("%s: %s", device.name, interface_opts.descr) : g_strdup(device.name); device.hidden = FALSE; device.selected = TRUE; /* XXX shouldn't this be interface_opts.if_type (for IF_STDIN)? */ device.type = IF_PIPE; #ifdef CAN_SET_CAPTURE_BUFFER_SIZE device.buffer = interface_opts.buffer_size; #endif #if defined(HAVE_PCAP_CREATE) device.monitor_mode_enabled = interface_opts.monitor_mode; device.monitor_mode_supported = FALSE; #endif device.pmode = interface_opts.promisc_mode; device.has_snaplen = interface_opts.has_snaplen; device.snaplen = interface_opts.snaplen; device.cfilter = g_strdup(interface_opts.cfilter); device.active_dlt = interface_opts.linktype; device.addresses = NULL; device.no_addresses = 0; device.last_packets = 0; device.links = NULL; device.local = TRUE; device.locked = FALSE; device.if_info.name = g_strdup(interface_opts.name); device.if_info.friendly_name = NULL; device.if_info.vendor_description = g_strdup(interface_opts.descr); device.if_info.addrs = NULL; device.if_info.loopback = FALSE; #ifdef HAVE_EXTCAP device.if_info.extcap = g_strdup(interface_opts.extcap); #endif g_array_append_val(global_capture_opts.all_ifaces, device); global_capture_opts.num_selected++; } } g_hash_table_destroy(selected_devices); running = FALSE; } /* * Get the global interface list. Generate it if we haven't done so * already. This can be quite time consuming the first time, so * record how long it takes in the info log. */ void fill_in_local_interfaces(void(*update_cb)(void)) { GTimeVal start_time; GTimeVal end_time; float elapsed; static gboolean initialized = FALSE; /* record the time we started, so we can log total time later */ g_get_current_time(&start_time); g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_INFO, "fill_in_local_interfaces() starts"); if (!initialized) { /* do the actual work */ scan_local_interfaces(update_cb); initialized = TRUE; } /* log how long it took */ g_get_current_time(&end_time); elapsed = (float) ((end_time.tv_sec - start_time.tv_sec) + ((end_time.tv_usec - start_time.tv_usec) / 1e6)); g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_INFO, "fill_in_local_interfaces() ends, taking %.3fs", elapsed); } void hide_interface(gchar* new_hide) { gchar *tok; guint i; interface_t device; gboolean found = FALSE; GList *hidden_devices = NULL, *entry; if (new_hide != NULL) { for (tok = strtok (new_hide, ","); tok; tok = strtok(NULL, ",")) { hidden_devices = g_list_append(hidden_devices, tok); } } for (i = 0; i < global_capture_opts.all_ifaces->len; i++) { device = g_array_index(global_capture_opts.all_ifaces, interface_t, i); found = FALSE; for (entry = hidden_devices; entry != NULL; entry = g_list_next(entry)) { if (strcmp((char *)entry->data, device.name)==0) { device.hidden = TRUE; if (device.selected) { device.selected = FALSE; global_capture_opts.num_selected--; } found = TRUE; break; } } if (!found) { device.hidden = FALSE; } global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, i); g_array_insert_val(global_capture_opts.all_ifaces, i, device); } g_list_free(hidden_devices); g_free(new_hide); } void update_local_interfaces(void) { interface_t device; gchar *descr; guint i; for (i = 0; i < global_capture_opts.all_ifaces->len; i++) { device = g_array_index(global_capture_opts.all_ifaces, interface_t, i); device.type = capture_dev_user_linktype_find(device.name); g_free (device.display_name); descr = capture_dev_user_descr_find(device.name); device.display_name = get_iface_display_name(descr, &device.if_info); g_free (descr); device.hidden = prefs_is_capture_device_hidden(device.name); fill_from_ifaces(&device); global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, i); g_array_insert_val(global_capture_opts.all_ifaces, i, device); } } #endif /* HAVE_LIBPCAP */ /* * Editor modelines * * Local Variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * ex: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */