diff options
Diffstat (limited to 'ui/iface_lists.c')
-rw-r--r-- | ui/iface_lists.c | 396 |
1 files changed, 242 insertions, 154 deletions
diff --git a/ui/iface_lists.c b/ui/iface_lists.c index dfeb0f7f..4b21d13a 100644 --- a/ui/iface_lists.c +++ b/ui/iface_lists.c @@ -28,13 +28,13 @@ /* * Try to populate the given device with options (like capture filter) from * the capture options that are in use for an existing capture interface. - * Returns TRUE if the interface is selected for capture and FALSE otherwise. + * Returns true if the interface is selected for capture and false otherwise. */ -static gboolean +static bool fill_from_ifaces (interface_t *device) { interface_options *interface_opts; - guint i; + unsigned i; for (i = 0; i < global_capture_opts.ifaces->len; i++) { interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, i); @@ -55,13 +55,13 @@ fill_from_ifaces (interface_t *device) if (interface_opts->linktype != -1) { device->active_dlt = interface_opts->linktype; } - return TRUE; + return true; } - return FALSE; + return false; } -static gchar * -get_iface_display_name(const gchar *description, const if_info_t *if_info) +static char * +get_iface_display_name(const char *description, const if_info_t *if_info) { /* Do we have a user-supplied description? */ if (description && description[0]) { @@ -75,7 +75,7 @@ get_iface_display_name(const gchar *description, const if_info_t *if_info) * rather than the name, as the name is a string made out * of the device GUID, and not at all friendly. */ - gchar *if_string = if_info->friendly_name ? if_info->friendly_name : if_info->name; + char *if_string = if_info->friendly_name ? if_info->friendly_name : if_info->name; return ws_strdup_printf("%s: %s", description, if_string); #else /* @@ -136,22 +136,21 @@ void scan_local_interfaces_filtered(GList * allowed_types, void (*update_cb)(void)) { GList *if_entry, *lt_entry, *if_list; - if_info_t *if_info, temp; - gchar *descr; + if_info_t *if_info; + char *descr; if_capabilities_t *caps=NULL; - gboolean monitor_mode; + bool monitor_mode; GSList *curr_addr; int ips = 0, i; - guint count = 0, j; - if_addr_t *addr, *temp_addr; + unsigned count = 0, j; + if_addr_t *addr; link_row *link = NULL; data_link_info_t *data_link_info; interface_t device; GString *ip_str = NULL; interface_options *interface_opts; - gboolean found = FALSE; - static gboolean running = FALSE; - GHashTable *selected_devices; + bool found = false; + static bool running = false; if (running) { /* scan_local_interfaces internally calls update_cb to process UI events @@ -161,91 +160,207 @@ scan_local_interfaces_filtered(GList * allowed_types, void (*update_cb)(void)) This return avoids recursive scan_local_interfaces operation. */ return; } - running = TRUE; + running = true; + + /* Retrieve list of interface information (if_info_t) into if_list. */ + g_free(global_capture_opts.ifaces_err_info); + if_list = global_capture_opts.get_iface_list(&global_capture_opts.ifaces_err, + &global_capture_opts.ifaces_err_info); /* - * 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. + * For each discovered interface name, look up its list of capabilities. + * (if it supports monitor mode, supported DLTs, assigned IP addresses). + * Do this all at once to reduce the number of spawned privileged dumpcap + * processes. + * It might be even better to get this information when getting the list, + * but some devices can support different DLTs depending on whether + * monitor mode is enabled, and we have to look up the monitor mode pref. + */ + GList *if_cap_queries = NULL; + if_cap_query_t *if_cap_query; + GHashTable *capability_hash; + for (if_entry = if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) { + if_info = (if_info_t *)if_entry->data; + if (strstr(if_info->name, "rpcap:")) { + continue; + } + /* Filter out all interfaces which are not allowed to be scanned */ + if (allowed_types != NULL) + { + if(g_list_find(allowed_types, GUINT_TO_POINTER((unsigned) if_info->type)) == NULL) { + continue; + } + } + if (if_info->caps != NULL) { + continue; + } + if_cap_query = g_new(if_cap_query_t, 1); + if_cap_query->name = if_info->name; + if_cap_query->monitor_mode = prefs_capture_device_monitor_mode(if_info->name); + if_cap_query->auth_username = NULL; + if_cap_query->auth_password = NULL; + if_cap_queries = g_list_prepend(if_cap_queries, if_cap_query); + } + if_cap_queries = g_list_reverse(if_cap_queries); + capability_hash = capture_get_if_list_capabilities(if_cap_queries, NULL, NULL, update_cb); + /* The if_info->name are not copied, so we can just free the + * if_cap_query_t's and not their members. */ + g_list_free_full(if_cap_queries, g_free); + + /* + * From the existing list of known interfaces, remove devices that we + * expected to re-discover on scanning but did not (i.e., local devices, + * but not pipes, stdin, and remote 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) { + if (device.local && device.if_info.type != IF_PIPE && device.if_info.type != IF_STDIN) { + + found = false; + for (if_entry = if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) { + if_info = (if_info_t *)if_entry->data; + + if (strcmp(device.name, if_info->name) == 0) { + found = true; + break; + } + } + + if (found) { + continue; + } + 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 each discovered interface name, look for it in the list of + * devices. If not found, create a new device and add extra + * information (including the capabilities we retrieved above). + * If found, make sure that the information copied from if_info + * is still valid. */ + count = 0; 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; ips = 0; if (strstr(if_info->name, "rpcap:")) { continue; } - /* Filter out all interfaces, which are not allowed to be scanned */ + /* Filter out all interfaces which are not allowed to be scanned */ if (allowed_types != NULL) { - if(g_list_find(allowed_types, GUINT_TO_POINTER((guint) if_info->type)) == NULL) { + if(g_list_find(allowed_types, GUINT_TO_POINTER((unsigned) if_info->type)) == NULL) { continue; } } - device.name = g_strdup(if_info->name); - device.friendly_name = g_strdup(if_info->friendly_name); - device.vendor_description = g_strdup(if_info->vendor_description); - device.hidden = 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; - temp.extcap = g_strdup(if_info->extcap); + 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, if_info->name) == 0) { + found = true; + /* Remove it because we'll reinsert it below (in the proper + * index order, if that matters. Does it?) + */ + global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, i); + break; + } + } - /* Is this interface hidden and, if so, should we include it anyway? */ + if (!found) { + /* New device. Create a new one and set all the defaults. */ + memset(&device, 0, sizeof(device)); + device.name = g_strdup(if_info->name); + device.hidden = false; + if (prefs_is_capture_device_hidden(if_info->name)) { + device.hidden = true; + } + device.selected = false; + +#ifdef HAVE_PCAP_REMOTE + 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 + + device.local = true; + 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); + device.timestamp_type = g_strdup(global_capture_opts.default_options.timestamp_type); +#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 + + /* Extcap devices start with no cached args */ + device.external_cap_args_settings = NULL; + + monitor_mode = prefs_capture_device_monitor_mode(if_info->name); + device.active_dlt = -1; + } else { + /* We can divide device_t members into three categories: + * 1. Those that don't depend on if_info and the capabilities. + * Keep those the same. + * 2. Those that need to match the retrieved information. + * Free those and set them below. + * 3. Those that an option chosen from a set of options determined + * from the capabilities. We have to check if the chosen values of + * monitor mode enabled and active dlt are still supported. + * There could be a knock on effect on the capture filter, as if + * your previously chosen link-layer type isn't supported then + * your capture filter might not be either, which will result in + * it being marked invalid instead of being cleared. */ + /* XXX: We have duplicate copies of the name and we have + * the addresses and links from the if_info transformed into new + * types, but perhaps that transformation should be done when + * creating the if_info and if_capabilities. + */ + g_free(device.display_name); + g_free(device.addresses); + g_list_free_full(device.links, capture_opts_free_link_row); + g_free(device.if_info.name); + g_free(device.if_info.friendly_name); + g_free(device.if_info.vendor_description); + g_slist_free_full(device.if_info.addrs, g_free); + g_free(device.if_info.extcap); + if (device.if_info.caps) { + free_if_capabilities(device.if_info.caps); + } + monitor_mode = device.monitor_mode_enabled; + } 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, NULL, update_cb); ip_str = g_string_new(""); for (; (curr_addr = g_slist_nth(if_info->addrs, ips)) != NULL; ips++) { - temp_addr = g_new0(if_addr_t, 1); if (ips != 0) { g_string_append(ip_str, "\n"); } @@ -253,16 +368,13 @@ scan_local_interfaces_filtered(GList * allowed_types, void (*update_cb)(void)) 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); @@ -272,43 +384,30 @@ scan_local_interfaces_filtered(GList * allowed_types, void (*update_cb)(void)) 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); } } device.addresses = g_strdup(ip_str->str); g_string_free(ip_str, TRUE); -#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 device.links = NULL; - if (caps != NULL) { + caps = if_info->caps; + if (caps == NULL) { + caps = g_hash_table_lookup(capability_hash, if_info->name); + } + if (caps != NULL && !caps->primary_msg) { + GList *lt_list = caps->data_link_types; #if defined(HAVE_PCAP_CREATE) - device.monitor_mode_enabled = monitor_mode; + device.monitor_mode_enabled = monitor_mode && caps->can_set_rfmon; device.monitor_mode_supported = caps->can_set_rfmon; + if (device.monitor_mode_enabled) { + lt_list = caps->data_link_types_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)) { + bool found_active_dlt = false; + for (lt_entry = lt_list; lt_entry != NULL; lt_entry = g_list_next(lt_entry)) { data_link_info = (data_link_info_t *)lt_entry->data; link = g_new(link_row, 1); if (data_link_info->description != NULL) { @@ -318,64 +417,56 @@ scan_local_interfaces_filtered(GList * allowed_types, void (*update_cb)(void)) link->dlt = -1; link->name = ws_strdup_printf("%s (not supported)", data_link_info->name); } + if (link->dlt != -1 && link->dlt == device.active_dlt) { + found_active_dlt = true; + } device.links = g_list_append(device.links, link); } /* * Set the active DLT for the device appropriately. */ - set_active_dlt(&device, global_capture_opts.default_options.linktype); + if (!found_active_dlt) { + 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; + device.monitor_mode_enabled = false; + device.monitor_mode_supported = false; #endif device.active_dlt = -1; } 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); - device.timestamp_type = g_strdup(global_capture_opts.default_options.timestamp_type); -#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. */ - gboolean selected = fill_from_ifaces(&device); + /* Copy interface options for active capture devices. + * XXX: Not clear if we still need to do this, since we're not + * destroying the old devices. */ + bool selected = fill_from_ifaces(&device); /* Restore device selection (for next capture). */ - if (!device.selected && (selected || g_hash_table_lookup(selected_devices, device.name))) { - device.selected = TRUE; + if (!device.selected && selected) { + device.selected = true; global_capture_opts.num_selected++; } - /* Extcap devices start with no cached args */ - device.external_cap_args_settings = NULL; - + /* We shallow copy if_info and then adding to the GArray shallow + * copies it again, so free the if_info_t itself but not its members. + * Then set the GList element data to NULL so that we don't free + * it or its members when freeing the interface list. (This seems a + * little easier than removing the link from the list while iterating.) + */ + device.if_info = *if_info; + if_entry->data = NULL; + g_free(if_info); 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); - } - count++; } + g_hash_table_destroy(capability_hash); free_interface_list(if_list); /* @@ -385,7 +476,7 @@ scan_local_interfaces_filtered(GList * allowed_types, void (*update_cb)(void)) for (j = 0; j < global_capture_opts.ifaces->len; j++) { interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, j); - found = FALSE; + 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); @@ -395,26 +486,24 @@ scan_local_interfaces_filtered(GList * allowed_types, void (*update_cb)(void)) } if (strcmp(device.name, interface_opts->name) == 0) { - found = TRUE; + found = true; break; } } if (!found) { /* new interface, maybe a pipe */ memset(&device, 0, sizeof(device)); device.name = g_strdup(interface_opts->name); - device.vendor_description = g_strdup(interface_opts->hardware); device.display_name = interface_opts->descr ? ws_strdup_printf("%s: %s", device.name, interface_opts->descr) : g_strdup(device.name); - device.hidden = FALSE; - device.selected = TRUE; - device.type = interface_opts->if_type; + device.hidden = false; + device.selected = true; #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; + device.monitor_mode_supported = false; #endif device.pmode = interface_opts->promisc_mode; device.has_snaplen = interface_opts->has_snaplen; @@ -426,12 +515,13 @@ scan_local_interfaces_filtered(GList * allowed_types, void (*update_cb)(void)) device.no_addresses = 0; device.last_packets = 0; device.links = NULL; - device.local = TRUE; + device.local = true; device.if_info.name = g_strdup(interface_opts->name); + device.if_info.type = interface_opts->if_type; device.if_info.friendly_name = NULL; - device.if_info.vendor_description = g_strdup(interface_opts->descr); + device.if_info.vendor_description = g_strdup(interface_opts->hardware); device.if_info.addrs = NULL; - device.if_info.loopback = FALSE; + device.if_info.loopback = false; device.if_info.extcap = g_strdup(interface_opts->extcap); g_array_append_val(global_capture_opts.all_ifaces, device); @@ -439,8 +529,7 @@ scan_local_interfaces_filtered(GList * allowed_types, void (*update_cb)(void)) } } - g_hash_table_destroy(selected_devices); - running = FALSE; + running = false; } /* @@ -462,32 +551,31 @@ fill_in_local_interfaces(void(*update_cb)(void)) void fill_in_local_interfaces_filtered(GList * filter_list, void(*update_cb)(void)) { - gint64 start_time; + int64_t start_time; double elapsed; - static gboolean initialized = FALSE; + static bool initialized = false; /* record the time we started, so we can log total time later */ start_time = g_get_monotonic_time(); - ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "fill_in_local_interfaces() starts"); if (!initialized) { /* do the actual work */ scan_local_interfaces_filtered(filter_list, update_cb); - initialized = TRUE; + initialized = true; } /* log how long it took */ elapsed = (g_get_monotonic_time() - start_time) / 1e6; - ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "fill_in_local_interfaces() ends, taking %.3fs", elapsed); + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Finished getting the global interface list, taking %.3fs", elapsed); } void -hide_interface(gchar* new_hide) +hide_interface(char* new_hide) { - gchar *tok; - guint i; + char *tok; + unsigned i; interface_t *device; - gboolean found = FALSE; + bool found = false; GList *hidden_devices = NULL, *entry; if (new_hide != NULL) { for (tok = strtok (new_hide, ","); tok; tok = strtok(NULL, ",")) { @@ -496,20 +584,20 @@ hide_interface(gchar* new_hide) } 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; + found = false; for (entry = hidden_devices; entry != NULL; entry = g_list_next(entry)) { if (strcmp((char *)entry->data, device->name)==0) { - device->hidden = TRUE; + device->hidden = true; if (device->selected) { - device->selected = FALSE; + device->selected = false; global_capture_opts.num_selected--; } - found = TRUE; + found = true; break; } } if (!found) { - device->hidden = FALSE; + device->hidden = false; } } g_list_free(hidden_devices); @@ -520,12 +608,12 @@ void update_local_interfaces(void) { interface_t *device; - gchar *descr; - guint i; + char *descr; + unsigned 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); + device->if_info.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); |