From 1a95f230b9e348143b0251fa4bc329f07aaa5bd3 Mon Sep 17 00:00:00 2001 From: John Thacker Date: Fri, 15 Dec 2023 06:18:39 -0500 Subject: [PATCH] dumpcap: Allow retrieving interface list + caps Since we get both the non monitor mode and monitor mode link types lists of interfaces that can support it, we can allow the combination of retrieving the interface list and capabilities at the same time, because we don't need the user to specify whether to retrieve the list in or not in monitor mode. Allow any combination of retrieving the the interface list and the two capability types to be retrieved, but don't allow them in combination with the other run once arguments. When not in machine readable mode, print the capability types after the link lists (maybe we should interleave them). When in machine readable mode, add them to the serialized JSON. We don't yet pass both flags to dumpcap in capture child mode from the other applications yet. Related to #15082 --- capture/capture-pcap-util.c | 43 ++++++++++++++ capture/capture_ifinfo.h | 32 +++++----- capture_opts.c | 115 ++++++++++++++++++++++-------------- capture_opts.h | 8 ++- dumpcap.c | 98 ++++++++++++++++++++++++++---- 5 files changed, 222 insertions(+), 74 deletions(-) diff --git a/capture/capture-pcap-util.c b/capture/capture-pcap-util.c index 584810da1f..7163348ba7 100644 --- a/capture/capture-pcap-util.c +++ b/capture/capture-pcap-util.c @@ -440,9 +440,50 @@ if_info_free(if_info_t *if_info) g_free(if_info->vendor_description); g_free(if_info->extcap); g_slist_free_full(if_info->addrs, g_free); + if (if_info->caps) { + free_if_capabilities(if_info->caps); + } g_free(if_info); } +static void* +copy_linktype_cb(const void *data, void *user_data _U_) +{ + data_link_info_t *linktype_info = (data_link_info_t *)data; + + data_link_info_t *ret = g_new(data_link_info_t, 1); + ret->name = g_strdup(linktype_info->name); + ret->description = g_strdup(linktype_info->description); + return ret; +} + +static void* +copy_timestamp_cb(const void *data, void *user_data _U_) +{ + timestamp_info_t *timestamp_info = (timestamp_info_t *)data; + + timestamp_info_t *ret = g_new(timestamp_info_t, 1); + ret->name = g_strdup(timestamp_info->name); + ret->description = g_strdup(timestamp_info->description); + return ret; +} + +static if_capabilities_t * +if_capabilities_copy(const if_capabilities_t *caps) +{ + if (caps == NULL) return NULL; + + if_capabilities_t *ret = g_new(if_capabilities_t, 1); + ret->can_set_rfmon = caps->can_set_rfmon; + ret->data_link_types = g_list_copy_deep(caps->data_link_types, copy_linktype_cb, NULL); + ret->timestamp_types = g_list_copy_deep(caps->timestamp_types, copy_timestamp_cb, NULL); + ret->data_link_types_rfmon = g_list_copy_deep(caps->data_link_types_rfmon, copy_linktype_cb, NULL); + ret->primary_msg = g_strdup(caps->primary_msg); + ret->secondary_msg = g_strdup(caps->secondary_msg); + + return ret; +} + if_info_t * if_info_copy(const if_info_t *if_info) { @@ -456,6 +497,7 @@ if_info_copy(const if_info_t *if_info) new_if_info->type = if_info->type; new_if_info->loopback = if_info->loopback; new_if_info->extcap = g_strdup(if_info->extcap); + new_if_info->caps = if_capabilities_copy(if_info->caps); return new_if_info; } @@ -597,6 +639,7 @@ if_info_new(const char *name, const char *description, bool loopback) #endif if_info->loopback = loopback; if_info->addrs = NULL; + if_info->caps = NULL; return if_info; } diff --git a/capture/capture_ifinfo.h b/capture/capture_ifinfo.h index 92b7354184..53f3a5af67 100644 --- a/capture/capture_ifinfo.h +++ b/capture/capture_ifinfo.h @@ -35,6 +35,21 @@ typedef enum { IF_VIRTUAL = 9 } interface_type; +/* + * "get_if_capabilities()" and "capture_if_capabilities()" return a pointer + * to an allocated instance of this structure. "free_if_capabilities()" + * frees the returned instance. + */ +typedef struct { + bool can_set_rfmon; /* true if can be put into monitor mode */ + GList *data_link_types; /* GList of data_link_info_t's */ + GList *data_link_types_rfmon; /* GList of data_link_info_t's */ + GList *timestamp_types; /* GList of timestamp_info_t's */ + int status; + char *primary_msg; /* If non-NULL, the query failed, and a message explaing why */ + char *secondary_msg; /* An optional supplementary message */ +} if_capabilities_t; + /* * The list of interfaces returned by "get_interface_list()" is * a list of these structures. @@ -51,6 +66,7 @@ typedef struct { interface_type type; /* type of interface */ bool loopback; /* true if loopback, false otherwise */ char *extcap; /* extcap arguments, which present the data to call the extcap interface */ + if_capabilities_t *caps; } if_info_t; /* @@ -109,22 +125,6 @@ if_info_t *if_info_copy(const if_info_t *if_info); */ if_addr_t *if_addr_copy(const if_addr_t *if_addr); -/* - * "get_if_capabilities()" and "capture_if_capabilities()" return a pointer - * to an allocated instance of this structure. "free_if_capabilities()" - * frees the returned instance. - */ -typedef struct { - bool can_set_rfmon; /* true if can be put into monitor mode */ - GList *data_link_types; /* GList of data_link_info_t's */ - GList *data_link_types_rfmon; /* GList of data_link_info_t's */ - GList *timestamp_types; /* GList of timestamp_info_t's */ - /* XXX: Would there be any value in adding the status? It would - * allow the messages to have warnings, not just failures. */ - char *primary_msg; /* If non-NULL, the query failed, and a message explaing why */ - char *secondary_msg; /* An optional supplementary message */ -} if_capabilities_t; - typedef struct { const char *name; bool monitor_mode; diff --git a/capture_opts.c b/capture_opts.c index 01ff1f9aae..05f543efed 100644 --- a/capture_opts.c +++ b/capture_opts.c @@ -586,6 +586,51 @@ capture_opts_generate_display_name(const char *friendly_name, } #endif +static void +fill_in_interface_opts_defaults(interface_options *interface_opts, const capture_options *capture_opts) +{ + + 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; + interface_opts->linktype = capture_opts->default_options.linktype; + interface_opts->promisc_mode = capture_opts->default_options.promisc_mode; + interface_opts->extcap_fifo = g_strdup(capture_opts->default_options.extcap_fifo); + interface_opts->extcap_args = NULL; + interface_opts->extcap_pid = WS_INVALID_PID; + interface_opts->extcap_pipedata = NULL; + interface_opts->extcap_stderr = NULL; + interface_opts->extcap_stdout_watch = 0; + interface_opts->extcap_stderr_watch = 0; +#ifdef _WIN32 + interface_opts->extcap_pipe_h = INVALID_HANDLE_VALUE; + interface_opts->extcap_control_in_h = INVALID_HANDLE_VALUE; + interface_opts->extcap_control_out_h = INVALID_HANDLE_VALUE; +#endif + interface_opts->extcap_control_in = g_strdup(capture_opts->default_options.extcap_control_in); + interface_opts->extcap_control_out = g_strdup(capture_opts->default_options.extcap_control_out); +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + interface_opts->buffer_size = capture_opts->default_options.buffer_size; +#endif + interface_opts->monitor_mode = capture_opts->default_options.monitor_mode; +#ifdef HAVE_PCAP_REMOTE + interface_opts->src_type = capture_opts->default_options.src_type; + interface_opts->remote_host = g_strdup(capture_opts->default_options.remote_host); + interface_opts->remote_port = g_strdup(capture_opts->default_options.remote_port); + interface_opts->auth_type = capture_opts->default_options.auth_type; + interface_opts->auth_username = g_strdup(capture_opts->default_options.auth_username); + interface_opts->auth_password = g_strdup(capture_opts->default_options.auth_password); + interface_opts->datatx_udp = capture_opts->default_options.datatx_udp; + interface_opts->nocap_rpcap = capture_opts->default_options.nocap_rpcap; + interface_opts->nocap_local = capture_opts->default_options.nocap_local; +#endif +#ifdef HAVE_PCAP_SETSAMPLING + interface_opts->sampling_method = capture_opts->default_options.sampling_method; + interface_opts->sampling_param = capture_opts->default_options.sampling_param; +#endif + interface_opts->timestamp_type = capture_opts->default_options.timestamp_type; +} + static void fill_in_interface_opts_from_ifinfo(interface_options *interface_opts, const if_info_t *if_info) @@ -872,45 +917,7 @@ capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str free_interface_list(if_list); } - 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; - interface_opts.linktype = capture_opts->default_options.linktype; - interface_opts.promisc_mode = capture_opts->default_options.promisc_mode; - interface_opts.extcap_fifo = g_strdup(capture_opts->default_options.extcap_fifo); - interface_opts.extcap_args = NULL; - interface_opts.extcap_pid = WS_INVALID_PID; - interface_opts.extcap_pipedata = NULL; - interface_opts.extcap_stderr = NULL; - interface_opts.extcap_stdout_watch = 0; - interface_opts.extcap_stderr_watch = 0; -#ifdef _WIN32 - interface_opts.extcap_pipe_h = INVALID_HANDLE_VALUE; - interface_opts.extcap_control_in_h = INVALID_HANDLE_VALUE; - interface_opts.extcap_control_out_h = INVALID_HANDLE_VALUE; -#endif - interface_opts.extcap_control_in = g_strdup(capture_opts->default_options.extcap_control_in); - interface_opts.extcap_control_out = g_strdup(capture_opts->default_options.extcap_control_out); -#ifdef CAN_SET_CAPTURE_BUFFER_SIZE - interface_opts.buffer_size = capture_opts->default_options.buffer_size; -#endif - interface_opts.monitor_mode = capture_opts->default_options.monitor_mode; -#ifdef HAVE_PCAP_REMOTE - interface_opts.src_type = capture_opts->default_options.src_type; - interface_opts.remote_host = g_strdup(capture_opts->default_options.remote_host); - interface_opts.remote_port = g_strdup(capture_opts->default_options.remote_port); - interface_opts.auth_type = capture_opts->default_options.auth_type; - interface_opts.auth_username = g_strdup(capture_opts->default_options.auth_username); - interface_opts.auth_password = g_strdup(capture_opts->default_options.auth_password); - interface_opts.datatx_udp = capture_opts->default_options.datatx_udp; - interface_opts.nocap_rpcap = capture_opts->default_options.nocap_rpcap; - interface_opts.nocap_local = capture_opts->default_options.nocap_local; -#endif -#ifdef HAVE_PCAP_SETSAMPLING - interface_opts.sampling_method = capture_opts->default_options.sampling_method; - interface_opts.sampling_param = capture_opts->default_options.sampling_param; -#endif - interface_opts.timestamp_type = capture_opts->default_options.timestamp_type; + fill_in_interface_opts_defaults(&interface_opts, capture_opts); g_array_append_val(capture_opts->ifaces, interface_opts); @@ -1163,7 +1170,7 @@ capture_opts_add_opt(capture_options *capture_opts, int opt, const char *optarg_ int capture_opts_print_if_capabilities(if_capabilities_t *caps, - interface_options *interface_opts, + const interface_options *interface_opts, int queries) { GList *lt_entry, *ts_entry; @@ -1383,12 +1390,10 @@ capture_opts_output_to_pipe(const char *save_file, gboolean *is_pipe) } void -capture_opts_del_iface(capture_options *capture_opts, guint if_index) +interface_opts_free(interface_options *interface_opts) { - interface_options *interface_opts; - - interface_opts = &g_array_index(capture_opts->ifaces, interface_options, if_index); - /* XXX - check if found? */ + if (interface_opts == NULL) + return; g_free(interface_opts->name); g_free(interface_opts->descr); @@ -1416,10 +1421,30 @@ capture_opts_del_iface(capture_options *capture_opts, guint if_index) g_free(interface_opts->auth_password); } #endif +} + +void +capture_opts_del_iface(capture_options *capture_opts, guint if_index) +{ + interface_options *interface_opts; + + interface_opts = &g_array_index(capture_opts->ifaces, interface_options, if_index); + /* XXX - check if found? */ + interface_opts_free(interface_opts); + capture_opts->ifaces = g_array_remove_index(capture_opts->ifaces, if_index); } +interface_options* +interface_opts_from_if_info(capture_options *capture_opts, const if_info_t *if_info) +{ + interface_options *interface_opts = g_new(interface_options, 1); + fill_in_interface_opts_from_ifinfo(interface_opts, if_info); + fill_in_interface_opts_defaults(interface_opts, capture_opts); + + return interface_opts; +} /* * Add all non-hidden selected interfaces in the "all interfaces" list diff --git a/capture_opts.h b/capture_opts.h index e9a2780979..1600ee419f 100644 --- a/capture_opts.h +++ b/capture_opts.h @@ -372,7 +372,7 @@ enum caps_query { /* print interface capabilities, including link layer types */ extern int capture_opts_print_if_capabilities(if_capabilities_t *caps, - interface_options *interface_opts, + const interface_options *interface_opts, int queries); /* print list of interfaces */ @@ -395,6 +395,12 @@ capture_opts_default_iface_if_necessary(capture_options *capture_opts, extern void capture_opts_del_iface(capture_options *capture_opts, guint if_index); +extern void +interface_opts_free(interface_options *interface_opts); + +extern interface_options* +interface_opts_from_if_info(capture_options *capture_opts, const if_info_t *if_info); + extern void collect_ifaces(capture_options *capture_opts); diff --git a/dumpcap.c b/dumpcap.c index c8ea5da51a..38ac10b36c 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -1013,13 +1013,16 @@ show_filter_code(capture_options *capture_opts) return TRUE; } +static void +print_machine_readable_if_capabilities(json_dumper *dumper, if_capabilities_t *caps, int queries); + /* * Output a machine readable list of the interfaces * This list is retrieved by the sync_interface_list_open() function * The actual output of this function can be viewed with the command "dumpcap -D -Z none" */ static int -print_machine_readable_interfaces(GList *if_list) +print_machine_readable_interfaces(GList *if_list, int caps_queries) { GList *if_entry; if_info_t *if_info; @@ -1082,6 +1085,12 @@ print_machine_readable_interfaces(GList *if_list) json_dumper_set_member_name(&dumper, "extcap"); json_dumper_value_string(&dumper, if_info->extcap); + if (if_info->caps && caps_queries) { + json_dumper_set_member_name(&dumper, "caps"); + json_dumper_begin_object(&dumper); + print_machine_readable_if_capabilities(&dumper, if_info->caps, caps_queries); + json_dumper_end_object(&dumper); + } json_dumper_end_object(&dumper); json_dumper_end_object(&dumper); } @@ -1111,6 +1120,15 @@ print_machine_readable_if_capabilities(json_dumper *dumper, if_capabilities_t *c GList *lt_entry, *ts_entry; const gchar *desc_str; + json_dumper_set_member_name(dumper, "status"); + json_dumper_value_anyf(dumper, "%i", caps->status); + if (caps->primary_msg) { + json_dumper_set_member_name(dumper, "primary_msg"); + json_dumper_value_string(dumper, caps->primary_msg); + json_dumper_set_member_name(dumper, "secondary_msg"); + json_dumper_value_string(dumper, caps->secondary_msg); + } + if (queries & CAPS_QUERY_LINK_TYPES) { json_dumper_set_member_name(dumper, "rfmon"); json_dumper_value_anyf(dumper, "%s", caps->can_set_rfmon ? "true" : "false"); @@ -5685,20 +5703,23 @@ main(int argc, char *argv[]) break; /*** all non capture option specific ***/ case 'D': /* Print a list of capture devices and exit */ - if (!list_interfaces) { - list_interfaces = TRUE; + if (!list_interfaces && !caps_queries) { run_once_args++; } + list_interfaces = TRUE; break; case 'L': /* Print list of link-layer types and exit */ - if (!(caps_queries & CAPS_QUERY_LINK_TYPES)) { - caps_queries |= CAPS_QUERY_LINK_TYPES; + if (!list_interfaces && !caps_queries) { run_once_args++; } + caps_queries |= CAPS_QUERY_LINK_TYPES; break; case LONGOPT_LIST_TSTAMP_TYPES: - caps_queries |= CAPS_QUERY_TIMESTAMP_TYPES; - break; + if (!list_interfaces && !caps_queries) { + run_once_args++; + } + caps_queries |= CAPS_QUERY_TIMESTAMP_TYPES; + break; case 'd': /* Print BPF code for capture filter and exit */ if (!print_bpf_code) { print_bpf_code = TRUE; @@ -5855,12 +5876,64 @@ main(int argc, char *argv[]) } } - if (machine_readable) { - status = print_machine_readable_interfaces(if_list); - } else { + if (!machine_readable) { status = 0; capture_opts_print_interfaces(if_list); } + + if (caps_queries) { + if_info_t *if_info; + interface_options *interface_opts; + cap_device_open_status open_status; + gchar *open_status_str; + for (GList *if_entry = if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) { + if_info = (if_info_t *)if_entry->data; + + interface_opts = interface_opts_from_if_info(&global_capture_opts, if_info); + + if_info->caps = get_if_capabilities(interface_opts, &open_status, &open_status_str); + + if (!machine_readable) { + if (if_info->caps == NULL) { + cmdarg_err("The capabilities of the capture device " + "\"%s\" could not be obtained (%s).\n%s", + interface_opts->name, open_status_str, + get_pcap_failure_secondary_error_message(open_status, open_status_str)); + g_free(open_status_str); + /* Break after one error, as when printing selected + * interface capabilities. (XXX: We could print all + * the primary status strings, and only the unique + * set of secondary messages / suggestions; printing + * the same long secondary error is a lot.) + */ + interface_opts_free(interface_opts); + g_free(interface_opts); + break; + } else { + status = capture_opts_print_if_capabilities(if_info->caps, interface_opts, caps_queries); + if (status != 0) { + interface_opts_free(interface_opts); + g_free(interface_opts); + break; + } + } + } else { + if (if_info->caps == NULL) { + if_info->caps = g_new0(if_capabilities_t, 1); + if_info->caps->primary_msg = open_status_str; + if_info->caps->secondary_msg = g_strdup(get_pcap_failure_secondary_error_message(open_status, open_status_str)); + } + if_info->caps->status = open_status; + } + + interface_opts_free(interface_opts); + g_free(interface_opts); + } + } + + if (machine_readable) { + status = print_machine_readable_interfaces(if_list, caps_queries); + } free_interface_list(if_list); exit_main(status); } @@ -5924,15 +5997,16 @@ main(int argc, char *argv[]) open_status = CAP_DEVICE_OPEN_NO_ERR; caps = get_if_capabilities(interface_opts, &open_status, &open_status_str); - json_dumper_set_member_name(&dumper, "status"); - json_dumper_value_anyf(&dumper, "%i", open_status); if (caps == NULL) { + json_dumper_set_member_name(&dumper, "status"); + json_dumper_value_anyf(&dumper, "%i", open_status); json_dumper_set_member_name(&dumper, "primary_msg"); json_dumper_value_string(&dumper, open_status_str); json_dumper_set_member_name(&dumper, "secondary_msg"); json_dumper_value_string(&dumper, get_pcap_failure_secondary_error_message(open_status, open_status_str)); g_free(open_status_str); } else { + caps->status = open_status; print_machine_readable_if_capabilities(&dumper, caps, caps_queries); free_if_capabilities(caps); }