From 149e74b70d207228802a463214271c9e537476c9 Mon Sep 17 00:00:00 2001 From: Guy Harris Date: Sun, 12 Aug 2018 20:32:01 -0700 Subject: [PATCH] Put the interface descrptions into the IDB when capturing to pcapng. capture_opts_add_iface_opt(), when called in a program acting as a capture child, will fetch the description for the interface, and will also generate a "display name" for the interface. In the process, we clean up capture_opts_add_iface_opt() a bit, combining duplicate code. We rename console_display_name to just display_name, as it may also be used in the title bar of Wireshark when capturing. Change-Id: Ifd18955bb3cb41df4c0ed4362d4854068c825b96 Reviewed-on: https://code.wireshark.org/review/29117 Petri-Dish: Guy Harris Tested-by: Petri Dish Buildbot Reviewed-by: Guy Harris --- capture_opts.c | 276 +++++++++++++++++++++-------------- capture_opts.h | 6 +- caputils/capture-pcap-util.c | 153 ++++++++++++++++++- caputils/capture_ifinfo.h | 11 ++ dumpcap.c | 64 ++++++-- ui/capture_ui_utils.c | 22 +-- 6 files changed, 387 insertions(+), 145 deletions(-) diff --git a/capture_opts.c b/capture_opts.c index 8b249d6175..47f6dab00e 100644 --- a/capture_opts.c +++ b/capture_opts.c @@ -155,7 +155,7 @@ capture_opts_log(const char *log_domain, GLogLevelFlags log_level, capture_optio interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i); g_log(log_domain, log_level, "Interface name[%02d] : %s", i, interface_opts->name ? interface_opts->name : "(unspecified)"); g_log(log_domain, log_level, "Interface description[%02d] : %s", i, interface_opts->descr ? interface_opts->descr : "(unspecified)"); - g_log(log_domain, log_level, "Console display name[%02d]: %s", i, interface_opts->console_display_name ? interface_opts->console_display_name : "(unspecified)"); + g_log(log_domain, log_level, "Display name[%02d]: %s", i, interface_opts->display_name ? interface_opts->display_name : "(unspecified)"); g_log(log_domain, log_level, "Capture filter[%02d] : %s", i, interface_opts->cfilter ? interface_opts->cfilter : "(unspecified)"); g_log(log_domain, log_level, "Snap length[%02d] (%u) : %d", i, interface_opts->has_snaplen, interface_opts->snaplen); g_log(log_domain, log_level, "Link Type[%02d] : %d", i, interface_opts->linktype); @@ -499,6 +499,129 @@ get_auth_arguments(capture_options *capture_opts, const char *arg) } #endif +#ifdef _WIN32 +static char * +capture_opts_generate_display_name(const char *friendly_name, + const char *name _U_) +{ + /* + * Display the friendly name rather than the not-so-friendly + * GUID-based interface name. + */ + return g_strdup(friendly_name); +} +#else +static char * +capture_opts_generate_display_name(const char *friendly_name, + const char *name) +{ + /* + * On UN*X, however, users are more used to interface names, + * and may find it helpful to see them. + */ + return g_strdup_printf("%s: %s", friendly_name, name); +} +#endif + +static gboolean +capture_opts_search_for_interface(interface_options *interface_opts, + const char *name) +{ + gboolean matched; + GList *if_list; + int err; + GList *if_entry; + if_info_t *if_info; + size_t prefix_length; + + matched = FALSE; + if_list = capture_interface_list(&err, NULL, NULL); + if (if_list != NULL) { + /* try and do an exact match (case insensitive) */ + 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; + + /* + * Does the specified name match the interface name + * with a case-insensitive match? + */ + if (g_ascii_strcasecmp(if_info->name, name) == 0) { + /* + * Yes. + */ + matched = TRUE; + break; + } + + /* + * Does this interface have a friendly name and, if so, + * does the specified name match the friendly name with + * a case-insensitive match? + */ + if (if_info->friendly_name != NULL && + g_ascii_strcasecmp(if_info->friendly_name, name) == 0) { + /* + * Yes. + */ + matched = TRUE; + break; + } + } + + if (!matched) { + /* + * We didn't find it; attempt a case-insensitive prefix match + * of the friendly name. + */ + prefix_length = strlen(name); + 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; + + if (if_info->friendly_name != NULL && + g_ascii_strncasecmp(if_info->friendly_name, name, prefix_length) == 0) { + /* + * We found an interface whose friendly name matches + * with a case-insensitive prefix match. + */ + matched = TRUE; + break; + } + } + } + } + + if (matched) { + /* + * We found an interface that matches. + */ + interface_opts->name = g_strdup(if_info->name); + + if (if_info->friendly_name != NULL) { + /* + * We have a friendly name; remember it as the + * description... + */ + interface_opts->descr = g_strdup(if_info->friendly_name); + /* + * ...and use it in the console display name. + */ + interface_opts->display_name = capture_opts_generate_display_name(if_info->friendly_name, if_info->name); + } else { + /* fallback to the interface name */ + interface_opts->descr = NULL; + interface_opts->display_name = g_strdup(if_info->name); + } + interface_opts->if_type = if_info->type; + interface_opts->extcap = g_strdup(if_info->extcap); + } + free_interface_list(if_list); + return matched; +} + static int capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str_p) { @@ -549,6 +672,28 @@ capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str return 1; } interface_opts.name = g_strdup(if_info->name); + if (if_info->friendly_name != NULL) { + /* + * We have a friendly name for the interface, so remember that + * as the description. + */ + interface_opts.descr = g_strdup(if_info->friendly_name); + interface_opts.display_name = capture_opts_generate_display_name(if_info->friendly_name, if_info->name); + } else { + /* fallback to the interface name */ + interface_opts.descr = NULL; + interface_opts.display_name = g_strdup(if_info->name); + } + interface_opts.if_type = if_info->type; + interface_opts.extcap = g_strdup(if_info->extcap); + free_interface_list(if_list); + } else if (capture_opts->capture_child) { + /* + * In Wireshark capture child mode, so the exact interface name + * is supplied, and we don't need to look it up. + */ + if_info = if_info_get(optarg_str_p); + interface_opts.name = g_strdup(if_info->name); if (if_info->friendly_name != NULL) { /* * We have a friendly name for the interface, so display that @@ -557,126 +702,37 @@ capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str * XXX - on UN*X, the interface name is not quite so ugly, * and might be more familiar to users; display them both? */ - interface_opts.console_display_name = g_strdup(if_info->friendly_name); + interface_opts.descr = g_strdup(if_info->friendly_name); + interface_opts.display_name = g_strdup(if_info->friendly_name); } else { + interface_opts.descr = NULL; /* fallback to the interface name */ - interface_opts.console_display_name = g_strdup(if_info->name); + interface_opts.display_name = g_strdup(if_info->name); } interface_opts.if_type = if_info->type; interface_opts.extcap = g_strdup(if_info->extcap); - free_interface_list(if_list); - } else if (capture_opts->capture_child) { - /* In Wireshark capture child mode, thus proper device name is supplied. */ - /* No need for trying to match it for friendly names. */ - interface_opts.name = g_strdup(optarg_str_p); - interface_opts.console_display_name = g_strdup(optarg_str_p); - interface_opts.if_type = capture_opts->default_options.if_type; - interface_opts.extcap = g_strdup(capture_opts->default_options.extcap); + if_info_free(if_info); } else { /* - * Retrieve the interface list so that we can search for the - * specified option amongst both the interface names and the - * friendly names and so that we find the friendly name even - * if an interface name was specified. - * - * If we can't get the list, just use the specified option as - * the interface name, so that the user can try specifying an - * interface explicitly for testing purposes. + * Search for that name in the interface list and, if we found + * it, fill in fields in the interface_opts structure. */ - if_list = capture_interface_list(&err, NULL, NULL); - if (if_list != NULL) { - /* try and do an exact match (case insensitive) */ - GList *if_entry; - gboolean matched; - - matched = FALSE; - 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; - /* exact name check */ - if (g_ascii_strcasecmp(if_info->name, optarg_str_p) == 0) { - /* exact match on the interface name, use that for displaying etc */ - interface_opts.name = g_strdup(if_info->name); - - if (if_info->friendly_name != NULL) { - /* - * If we have a friendly name, use that for the - * console display name, as it is the basis for - * the auto generated temp filename. - */ - interface_opts.console_display_name = g_strdup(if_info->friendly_name); - } else { - interface_opts.console_display_name = g_strdup(if_info->name); - } - interface_opts.if_type = if_info->type; - interface_opts.extcap = g_strdup(if_info->extcap); - matched = TRUE; - break; - } - - /* exact friendly name check */ - if (if_info->friendly_name != NULL && - g_ascii_strcasecmp(if_info->friendly_name, optarg_str_p) == 0) { - /* exact match - use the friendly name for display */ - interface_opts.name = g_strdup(if_info->name); - interface_opts.console_display_name = g_strdup(if_info->friendly_name); - interface_opts.if_type = if_info->type; - interface_opts.extcap = g_strdup(if_info->extcap); - matched = TRUE; - break; - } - } - - /* didn't find, attempt a case insensitive prefix match of the friendly name*/ - if (!matched) { - size_t prefix_length; - - prefix_length = strlen(optarg_str_p); - 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; - - if (if_info->friendly_name != NULL && - g_ascii_strncasecmp(if_info->friendly_name, optarg_str_p, prefix_length) == 0) { - /* prefix match - use the friendly name for display */ - interface_opts.name = g_strdup(if_info->name); - interface_opts.console_display_name = g_strdup(if_info->friendly_name); - interface_opts.if_type = if_info->type; - interface_opts.extcap = g_strdup(if_info->extcap); - matched = TRUE; - break; - } - } - } - if (!matched) { - /* - * We didn't find the interface in the list; just use - * the specified name, so that, for example, if an - * interface doesn't show up in the list for some - * reason, the user can try specifying it explicitly - * for testing purposes. - */ - interface_opts.name = g_strdup(optarg_str_p); - interface_opts.console_display_name = g_strdup(optarg_str_p); - interface_opts.if_type = capture_opts->default_options.if_type; - interface_opts.extcap = g_strdup(capture_opts->default_options.extcap); - } - free_interface_list(if_list); - } else { + if (!capture_opts_search_for_interface(&interface_opts, optarg_str_p)) { + /* + * We didn't find the interface in the list; just use + * the specified name, so that, for example, if an + * interface doesn't show up in the list for some + * reason, the user can try specifying it explicitly + * for testing purposes. + */ interface_opts.name = g_strdup(optarg_str_p); - interface_opts.console_display_name = g_strdup(optarg_str_p); + interface_opts.descr = NULL; + interface_opts.display_name = g_strdup(optarg_str_p); interface_opts.if_type = capture_opts->default_options.if_type; interface_opts.extcap = g_strdup(capture_opts->default_options.extcap); } } - /* We don't set iface_descr here because doing so requires - * capture_ui_utils.c which requires epan/prefs.c which is - * probably a bit too much dependency for here... - */ - interface_opts.descr = g_strdup(capture_opts->default_options.descr); interface_opts.cfilter = g_strdup(capture_opts->default_options.cfilter); interface_opts.snaplen = capture_opts->default_options.snaplen; interface_opts.has_snaplen = capture_opts->default_options.has_snaplen; @@ -1119,7 +1175,7 @@ capture_opts_del_iface(capture_options *capture_opts, guint if_index) g_free(interface_opts->name); g_free(interface_opts->descr); - g_free(interface_opts->console_display_name); + g_free(interface_opts->display_name); g_free(interface_opts->cfilter); g_free(interface_opts->timestamp_type); g_free(interface_opts->extcap); @@ -1164,8 +1220,8 @@ collect_ifaces(capture_options *capture_opts) device = &g_array_index(capture_opts->all_ifaces, interface_t, i); if (!device->hidden && device->selected) { interface_opts.name = g_strdup(device->name); - interface_opts.descr = g_strdup(device->display_name); - interface_opts.console_display_name = g_strdup(device->name); + interface_opts.descr = g_strdup(device->friendly_name); + interface_opts.display_name = g_strdup(device->display_name); interface_opts.linktype = device->active_dlt; interface_opts.cfilter = g_strdup(device->cfilter); interface_opts.timestamp_type = g_strdup(device->timestamp_type); diff --git a/capture_opts.h b/capture_opts.h index 9bbd3e8599..d0b433706f 100644 --- a/capture_opts.h +++ b/capture_opts.h @@ -197,9 +197,9 @@ typedef struct link_row_tag { } link_row; typedef struct interface_options_tag { - gchar *name; /* the name of the interface provided to winpcap/libpcap to specify the interface */ - gchar *descr; - gchar *console_display_name; /* the name displayed in the console, also the basis for autonamed pcap filenames */ + gchar *name; /* the name of the interface supplied to libpcap/WinPcap/Npcap to specify the interface */ + gchar *descr; /* a more user-friendly description of the interface; may be NULL if none */ + gchar *display_name; /* the name displayed in the console and title bar */ gchar *cfilter; gboolean has_snaplen; int snaplen; diff --git a/caputils/capture-pcap-util.c b/caputils/capture-pcap-util.c index 1647511aa0..366d92e6c3 100644 --- a/caputils/capture-pcap-util.c +++ b/caputils/capture-pcap-util.c @@ -260,6 +260,150 @@ add_unix_interface_ifinfo(if_info_t *if_info, const char *name _U_, } #endif +if_info_t * +if_info_get(const char *name) +{ + char *description = NULL; + if_info_t *if_info; +#ifdef SIOCGIFDESCR + /* + * Try to fetch the description of this interface. + * XXX - this is only here because libpcap has no API to + * get the description of a *single* interface; it really + * needs both an API to get pcapng-IDB-style attributes + * for a single interface and to get a list of interfaces + * with pcapng-IDB-style attributes for each interface. + */ + int s; + struct ifreq ifrdesc; +#ifndef IFDESCRSIZE + size_t descrlen = 64; +#else + size_t descrlen = IFDESCRSIZE; +#endif /* IFDESCRSIZE */ + + /* + * Get the description for the interface. + */ + memset(&ifrdesc, 0, sizeof ifrdesc); + g_strlcpy(ifrdesc.ifr_name, name, sizeof ifrdesc.ifr_name); + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s >= 0) { +#ifdef __FreeBSD__ + /* + * On FreeBSD, if the buffer isn't big enough for the + * description, the ioctl succeeds, but the description + * isn't copied, ifr_buffer.length is set to the description + * length, and ifr_buffer.buffer is set to NULL. + */ + for (;;) { + g_free(description); + if ((description = g_malloc(descrlen)) != NULL) { + ifrdesc.ifr_buffer.buffer = description; + ifrdesc.ifr_buffer.length = descrlen; + if (ioctl(s, SIOCGIFDESCR, &ifrdesc) == 0) { + if (ifrdesc.ifr_buffer.buffer == + description) + break; + else + descrlen = ifrdesc.ifr_buffer.length; + } else { + /* + * Failed to get interface description. + */ + g_free(description); + description = NULL; + break; + } + } else + break; + } +#else /* __FreeBSD__ */ + /* + * The only other OS that currently supports + * SIOCGIFDESCR is OpenBSD, and it has no way + * to get the description length - it's clamped + * to a maximum of IFDESCRSIZE. + */ + if ((description = g_malloc(descrlen)) != NULL) { + ifrdesc.ifr_data = (caddr_t)description; + if (ioctl(s, SIOCGIFDESCR, &ifrdesc) != 0) { + /* + * Failed to get interface description. + */ + g_free(description); + description = NULL; + } + } +#endif /* __FreeBSD__ */ + close(s); + if (description != NULL && strlen(description) == 0) { + /* + * Description is empty, so discard it. + */ + g_free(description); + description = NULL; + } + } + +#ifdef __FreeBSD__ + /* + * For FreeBSD, if we didn't get a description, and this is + * a device with a name of the form usbusN, label it as a USB + * bus. + */ + if (description == NULL) { + if (strncmp(name, "usbus", 5) == 0) { + /* + * OK, it begins with "usbus". + */ + long busnum; + char *p; + + errno = 0; + busnum = strtol(name + 5, &p, 10); + if (errno == 0 && p != name + 5 && *p == '\0' && + busnum >= 0 && busnum <= INT_MAX) { + /* + * OK, it's a valid number that's not + * bigger than INT_MAX. Construct + * a description from it. + */ + static const char descr_prefix[] = "USB bus number "; + size_t descr_size; + + /* + * Allow enough room for a 32-bit bus number. + * sizeof (descr_prefix) includes the + * terminating NUL. + */ + descr_size = sizeof (descr_prefix) + 10; + description = g_malloc(descr_size); + if (description != NULL) { + pcap_snprintf(description, descr_size, + "%s%ld", descr_prefix, busnum); + } + } + } + } +#endif /* __FreeBSD__ */ +#endif /* SIOCGIFDESCR */ + if_info = if_info_new(name, description, FALSE); + g_free(description); + return if_info; +} + +void +if_info_free(if_info_t *if_info) +{ + g_free(if_info->name); + g_free(if_info->friendly_name); + g_free(if_info->vendor_description); + g_free(if_info->extcap); + g_slist_free_full(if_info->addrs, g_free); + g_free(if_info); +} + if_info_t * if_info_new(const char *name, const char *description, gboolean loopback) { @@ -528,14 +672,7 @@ get_interface_list_findalldevs(int *err, char **err_str) static void free_if_cb(gpointer data, gpointer user_data _U_) { - if_info_t *if_info = (if_info_t *)data; - - g_free(if_info->name); - g_free(if_info->friendly_name); - g_free(if_info->vendor_description); - g_free(if_info->extcap); - g_slist_free_full(if_info->addrs, g_free); - g_free(if_info); + if_info_free((if_info_t *)data); } void diff --git a/caputils/capture_ifinfo.h b/caputils/capture_ifinfo.h index 7ee447aa5c..171561d16c 100644 --- a/caputils/capture_ifinfo.h +++ b/caputils/capture_ifinfo.h @@ -75,6 +75,17 @@ extern GList *capture_interface_list(int *err, char **err_str, void (*update_cb) void free_interface_list(GList *if_list); +/** + * Get an if_info_t for a particular interface. + * (May require privilege, so should only be used by dumpcap.) + */ +extern if_info_t *if_info_get(const char *name); + +/** + * Free an if_info_t. + */ +void if_info_free(if_info_t *if_info); + /* * "get_if_capabilities()" and "capture_if_capabilities()" return a pointer * to an allocated instance of this structure. "free_if_capabilities()" diff --git a/dumpcap.c b/dumpcap.c index f17424b738..1fad72bcd8 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -3310,23 +3310,57 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, prefix = g_strdup_printf("wireshark_%d_interfaces", global_capture_opts.ifaces->len); } else { /* - * One interface; use its name to generate the temporary file - * name prefix. + * One interface; use its description, if it has one, to generate + * the temporary file name, otherwise use its name. */ gchar *basename; - basename = g_path_get_basename((&g_array_index(global_capture_opts.ifaces, interface_options, 0))->console_display_name); + const interface_options *interface_opts; + + interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, 0); + + /* + * Do we have a description? + */ + if (interface_opts->descr) { + /* + * Yes - use it. + * + * Strip off any stuff we shouldn't use in the file name, + * by getting the last component of what would be a file + * name. + */ + basename = g_path_get_basename(interface_opts->descr); + } else { + /* + * No - use the name. + * + * Strip off any stuff we shouldn't use in the file name, + * by getting the last component of what would be a file + * name. + */ + basename = g_path_get_basename(interface_opts->name); #ifdef _WIN32 - /* use the generic portion of the interface guid to form the basis of the filename */ - if (strncmp("NPF_{", basename, 5)==0) - { - /* we have a windows guid style device name, extract the guid digits as the basis of the filename */ - GString *iface; - iface = isolate_uuid(basename); - g_free(basename); - basename = g_strdup(iface->str); - g_string_free(iface, TRUE); - } + /* + * This is Windows, where we might have an ugly GUID-based + * interface name. + * + * If it's an ugly GUID-based name, use the generic portion + * of the interface GUID to form the basis of the filename. + */ + if (strncmp("NPF_{", basename, 5) == 0) { + /* + * We have a GUID-based name; extract the GUID digits + * as the basis of the filename. + */ + GString *iface; + iface = isolate_uuid(basename); + g_free(basename); + basename = g_strdup(iface->str); + g_string_free(iface, TRUE); + } #endif + } + /* generate the temp file name prefix */ prefix = g_strconcat("wireshark_", basename, NULL); g_free(basename); } @@ -3990,7 +4024,7 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct report_capture_error(errmsg, please_report); } } - report_packet_drops(received, pcap_dropped, pcap_src->dropped, pcap_src->flushed, stats->ps_ifdrop, interface_opts->console_display_name); + report_packet_drops(received, pcap_dropped, pcap_src->dropped, pcap_src->flushed, stats->ps_ifdrop, interface_opts->display_name); } /* close the input file (pcap or capture pipe) */ @@ -5141,7 +5175,7 @@ main(int argc, char *argv[]) g_string_append_printf(str, "and "); } } - g_string_append_printf(str, "'%s'", interface_opts->console_display_name); + g_string_append_printf(str, "'%s'", interface_opts->display_name); } } else { g_string_append_printf(str, "%u interfaces", global_capture_opts.ifaces->len); diff --git a/ui/capture_ui_utils.c b/ui/capture_ui_utils.c index 92ff44d77f..0f7fe1c39f 100644 --- a/ui/capture_ui_utils.c +++ b/ui/capture_ui_utils.c @@ -468,23 +468,27 @@ get_if_name(const char *if_text) return if_name; } -/* Return interface_opts->descr (after setting it if it is not set) - * This is necessary because capture_opts.c can't set descr (at least - * not without adding significant dependencies there). +/* Return a display name for the interface. */ static const char * -get_iface_description_for_interface(capture_options *capture_opts, guint i) +get_display_name_for_interface(capture_options *capture_opts, guint i) { interface_options *interface_opts; if (i < capture_opts->ifaces->len) { interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i); - if (!interface_opts->descr && interface_opts->name) { - interface_opts->descr = get_interface_descriptive_name(interface_opts->name); + if (interface_opts->display_name) { + return interface_opts->display_name; } - return (interface_opts->descr); + if (!interface_opts->display_name) { + if (interface_opts->descr && interface_opts->name) { + interface_opts->descr = get_interface_descriptive_name(interface_opts->name); + } + interface_opts->display_name = g_strdup(interface_opts->descr); + } + return interface_opts->display_name; } else { - return (NULL); + return NULL; } } @@ -569,7 +573,7 @@ get_iface_list_string(capture_options *capture_opts, guint32 style) } if (style & IFLIST_QUOTE_IF_DESCRIPTION) g_string_append_printf(iface_list_string, "'"); - g_string_append_printf(iface_list_string, "%s", get_iface_description_for_interface(capture_opts, i)); + g_string_append_printf(iface_list_string, "%s", get_display_name_for_interface(capture_opts, i)); if (style & IFLIST_QUOTE_IF_DESCRIPTION) g_string_append_printf(iface_list_string, "'"); if (style & IFLIST_SHOW_FILTER) {