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
This commit is contained in:
John Thacker 2023-12-15 06:18:39 -05:00
parent dadc6f1fa0
commit 1a95f230b9
5 changed files with 222 additions and 74 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);
}