diff options
-rw-r--r-- | CMakeLists.txt | 23 | ||||
-rw-r--r-- | CMakeOptions.txt | 1 | ||||
-rw-r--r-- | doc/dpauxmon.pod | 148 | ||||
-rw-r--r-- | docbook/release-notes.asciidoc | 2 | ||||
-rw-r--r-- | extcap/dpauxmon.c | 567 | ||||
-rw-r--r-- | extcap/dpauxmon_user.h | 58 |
6 files changed, 799 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 68492b3ca2..ad2d0f0cbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2769,6 +2769,29 @@ elseif (BUILD_ciscodump) #message( WARNING "Cannot find libssh, cannot build ciscodump" ) endif() +if(BUILD_dpauxmon AND HAVE_LIBNL3) + set(dpauxmon_LIBS + ${GLIB2_LIBRARIES} + ${CMAKE_DL_LIBS} + wsutil + writecap + ${NL_LIBRARIES} + ) + set(dpauxmon_FILES + extcap/dpauxmon.c + extcap/extcap-base.c + ) + + add_executable(dpauxmon WIN32 ${dpauxmon_FILES}) + set_extcap_executable_properties(dpauxmon) + target_link_libraries(dpauxmon ${dpauxmon_LIBS}) + target_include_directories(dpauxmon PUBLIC ${NL_INCLUDE_DIR}) + install(TARGETS dpauxmon RUNTIME DESTINATION ${EXTCAP_DIR}) + add_dependencies(extcaps dpauxmon) +elseif (BUILD_dpauxmon) + #message( WARNING "Cannot find libnl3, cannot build dpauxmon" ) +endif() + if(BUILD_udpdump) set(udpdump_LIBS ${GLIB2_LIBRARIES} diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 5aedb7fa1e..53d68fa693 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -19,6 +19,7 @@ option(BUILD_xxx2deb "Build xxx2deb" OFF) option(BUILD_androiddump "Build androiddump" ON) option(BUILD_sshdump "Build sshdump" ON) option(BUILD_ciscodump "Build ciscodump" ON) +option(BUILD_dpauxmon "Build dpauxmon" ON) option(BUILD_randpktdump "Build randpktdump" ON) option(BUILD_udpdump "Build udpdump" ON) option(BUILD_sharkd "Build sharkd" ON) diff --git a/doc/dpauxmon.pod b/doc/dpauxmon.pod new file mode 100644 index 0000000000..c486d1e503 --- /dev/null +++ b/doc/dpauxmon.pod @@ -0,0 +1,148 @@ + +=head1 NAME + +dpauxmon - Provide interfaces to capture DisplayPort AUX channel data. + +=head1 SYNOPSIS + +B<dpauxmon> +S<[ B<--help> ]> +S<[ B<--version> ]> +S<[ B<--extcap-interfaces> ]> +S<[ B<--extcap-dlts> ]> +S<[ B<--extcap-interface>=E<lt>interfaceE<gt> ]> +S<[ B<--extcap-config> ]> +S<[ B<--extcap-capture-filter>=E<lt>capture filterE<gt> ]> +S<[ B<--capture> ]> +S<[ B<--fifo>=E<lt>path to file or pipeE<gt> ]> +S<[ B<--interface_id>=E<lt>Interface ID to captureE<gt> ]> + +B<dpauxmon> +S<B<--extcap-interfaces>> + +B<dpauxmon> +S<B<--extcap-interface>=E<lt>interfaceE<gt>> +S<B<--extcap-dlts>> + +B<dpauxmon> +S<B<--extcap-interface>=E<lt>interfaceE<gt>> +S<B<--extcap-config>> + +B<dpauxmon> +S<B<--extcap-interface>=E<lt>interfaceE<gt>> +S<B<--fifo>=E<lt>path to file or pipeE<gt>> +S<B<--capture>> +S<B<--interface_id=interface_id>> + +=head1 DESCRIPTION + +B<dpauxmon> is an extcap tool that can capture DisplayPort AUX channel data +from linux kernel drivers using the generic netlink interface. + +Supported interfaces: + +=over 4 + +=item 1. dpauxmon + +=back + +=head1 OPTIONS + +=over 4 + +=item --help + +Print program arguments. + +=item --version + +Print program version. + +=item --extcap-interfaces + +List available interfaces. + +=item --extcap-interface=E<lt>interfaceE<gt> + +Use specified interfaces. + +=item --extcap-dlts + +List DLTs of specified interface. + +=item --extcap-config + +List configuration options of specified interface. + +=item --capture + +Start capturing from specified interface and save it in place specified by --fifo. + +=item --fifo=E<lt>path to file or pipeE<gt> + +Save captured packet to file or send it through pipe. + +=item --interface_idt=E<lt>interface idE<gt> + +The interface for capture. + +=back + +=head1 EXAMPLES + +To see program arguments: + + dpauxmon --help + +To see program version: + + dpauxmon --version + +To see interfaces: + + dpauxmon --extcap-interfaces + +Only one interface (dpauxmon) is supported. + + Output: + interface {value=dpauxmon}{display=DisplayPort AUX channel capture} + +To see interface DLTs: + + dpauxmon --extcap-interface=dpauxmon --extcap-dlts + + Output: + dlt {number=275}{name=dpauxmon}{display=DisplayPort AUX channel monitor DLT} + +To see interface configuration options: + + dpauxmon --extcap-interface=dpauxmon --extcap-config + + Output: + dpauxmon --extcap-interface=dpauxmon --extcap-config + arg {number=0}{call=--interface_id}{display=Interface Id} + {type=unsigned}{tooltip=The Interface Id} + {required=true} + +To capture: + + dpauxmon --extcap-interface=dpauxmon --fifo=/tmp/dpauxmon.pcap --capture --interface_id 0 + +=head1 SEE ALSO + +wireshark(1), tshark(1), dumpcap(1), extcap(4) + +=head1 NOTES + +B<dpauxmon> is part of the B<Wireshark> distribution. The latest version +of B<Wireshark> can be found at L<https://www.wireshark.org>. + +HTML versions of the Wireshark project man pages are available at: +L<https://www.wireshark.org/docs/man-pages>. + +=head1 AUTHORS + + Original Author + -------- ------ + Dirk Eibach <dirk.eibach[AT]gdsys.cc> diff --git a/docbook/release-notes.asciidoc b/docbook/release-notes.asciidoc index 655f635623..f11c527540 100644 --- a/docbook/release-notes.asciidoc +++ b/docbook/release-notes.asciidoc @@ -83,6 +83,8 @@ Too many protocols have been updated to list here. //_Non-empty section placeholder._ [commaize] -- +A new extcap has been added: dpauxmon. It allows capturing DisplayPort AUX channel +data from linux kernel drivers. -- //=== Major API Changes diff --git a/extcap/dpauxmon.c b/extcap/dpauxmon.c new file mode 100644 index 0000000000..487401d38e --- /dev/null +++ b/extcap/dpauxmon.c @@ -0,0 +1,567 @@ +/* dpauxmon.c + * dpauxmon is an extcap tool used to monitor DisplayPort AUX channel traffic + * coming in from the kernel via generic netlink + * Copyright 2018, Dirk Eibach, Guntermann & Drunck GmbH <dirk.eibach@gdsys.cc> + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "extcap-base.h" + +#include <wsutil/strtoi.h> +#include <wsutil/filesystem.h> +#include <writecap/pcapio.h> + +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <netlink/genl/mngt.h> + +#include <signal.h> +#include <errno.h> + +#include <linux/genetlink.h> + +#include "dpauxmon_user.h" + +#define PCAP_SNAPLEN 128 + +#define DPAUXMON_EXTCAP_INTERFACE "dpauxmon" +#define DPAUXMON_VERSION_MAJOR "0" +#define DPAUXMON_VERSION_MINOR "1" +#define DPAUXMON_VERSION_RELEASE "0" + +static gboolean run_loop = TRUE; +FILE* pcap_fp = NULL; + +enum { + EXTCAP_BASE_OPTIONS_ENUM, + OPT_HELP, + OPT_VERSION, + OPT_INTERFACE_ID, +}; + +static struct option longopts[] = { + EXTCAP_BASE_OPTIONS, + /* Generic application options */ + { "help", no_argument, NULL, OPT_HELP}, + { "version", no_argument, NULL, OPT_VERSION}, + /* Interfaces options */ + { "interface_id", required_argument, NULL, OPT_INTERFACE_ID}, + { 0, 0, 0, 0 } +}; + +static struct nla_policy dpauxmon_attr_policy[DPAUXMON_ATTR_MAX + 1] = { + [DPAUXMON_ATTR_IFINDEX] = { .type = NLA_U32 }, + [DPAUXMON_ATTR_FROM_SOURCE] = { .type = NLA_FLAG }, + [DPAUXMON_ATTR_TIMESTAMP] = { .type = NLA_MSECS }, +}; + +struct family_handler_args { + const char *group; + int id; +}; + +static int list_config(char *interface) +{ + unsigned inc = 0; + + if (!interface) { + g_warning("No interface specified."); + return EXIT_FAILURE; + } + + if (g_strcmp0(interface, DPAUXMON_EXTCAP_INTERFACE)) { + g_warning("interface must be %s", DPAUXMON_EXTCAP_INTERFACE); + return EXIT_FAILURE; + } + + printf("arg {number=%u}{call=--interface_id}{display=Interface index}" + "{type=unsigned}{range=1,65535}{default=%u}{tooltip=The dpauxmon interface index}\n", + inc++, 0); + + extcap_config_debug(&inc); + + return EXIT_SUCCESS; +} + +static void exit_from_loop(int signo _U_) +{ + run_loop = FALSE; +} + +static int setup_dumpfile(const char* fifo, FILE** fp) +{ + guint64 bytes_written = 0; + int err; + + if (!g_strcmp0(fifo, "-")) { + *fp = stdout; + return EXIT_SUCCESS; + } + + *fp = fopen(fifo, "w"); + if (!(*fp)) { + g_warning("Error creating output file: %s", g_strerror(errno)); + return EXIT_FAILURE; + } + + if (!libpcap_write_file_header(*fp, 275, PCAP_SNAPLEN, FALSE, &bytes_written, &err)) { + g_warning("Can't write pcap file header"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int dump_packet(FILE* fp, const char* buf, const ssize_t buflen, guint64 ts_usecs) +{ + guint64 bytes_written = 0; + int err; + int ret = EXIT_SUCCESS; + + if (!libpcap_write_packet(fp, ts_usecs / 1000000, ts_usecs % 1000000, buflen, buflen, buf, &bytes_written, &err)) { + g_warning("Can't write packet"); + ret = EXIT_FAILURE; + } + + fflush(fp); + + return ret; +} + +static int error_handler(struct sockaddr_nl *nla _U_, struct nlmsgerr *err, + void *arg) +{ + int *ret = (int*)arg; + *ret = err->error; + return NL_STOP; +} + +static int ack_handler(struct nl_msg *msg _U_, void *arg) +{ + int *ret = (int*)arg; + *ret = 0; + return NL_STOP; +} + +static int family_handler(struct nl_msg *msg, void *arg) +{ + struct family_handler_args *grp = (struct family_handler_args *)arg; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int rem_mcgrp; + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return NL_SKIP; + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { + struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1]; + + nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, + (struct nlattr *)nla_data(mcgrp), nla_len(mcgrp), NULL); + + if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || + !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) + continue; + + if (strncmp((const char*)nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]), + grp->group, + nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) + continue; + + grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); + + break; + } + + return NL_SKIP; +} + +static int nl_get_multicast_id(struct nl_sock *sock, int family, + const char *group) +{ + struct nl_msg *msg; + struct nl_cb *cb; + int ret, ctrlid; + struct family_handler_args grp = { + .group = group, + .id = -ENOENT, + }; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) { + ret = -ENOMEM; + goto out_fail_cb; + } + + ctrlid = genl_ctrl_resolve(sock, "nlctrl"); + + genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); + + ret = -ENOBUFS; + NLA_PUT_U16(msg, CTRL_ATTR_FAMILY_ID, family); + + ret = nl_send_auto_complete(sock, msg); + if (ret < 0) + goto nla_put_failure; + + ret = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, family_handler, &grp); + + while (ret > 0) + nl_recvmsgs(sock, cb); + + if (ret == 0) + ret = grp.id; +nla_put_failure: + nl_cb_put(cb); +out_fail_cb: + nlmsg_free(msg); + return ret; +} + +/* + * netlink callback handlers + */ + +int nl_receive_timeout(struct nl_sock* sk, struct sockaddr_nl* nla, unsigned char** buf, struct ucred** creds) +{ + struct pollfd fds = {nl_socket_get_fd(sk), POLLIN, 0}; + int poll_res = poll(&fds, 1, 500); + + if (poll_res < 0) { + g_debug("poll() failed in nl_receive_timeout"); + g_usleep(500000); + return -nl_syserr2nlerr(errno); + } + + return poll_res ? nl_recv(sk, nla, buf, creds) : 0; +} + +static int send_start(struct nl_sock *sock, int family, unsigned int interface_id) +{ + struct nl_msg *msg; + void *hdr; + int err; + int res = 0; + + msg = nlmsg_alloc(); + if (msg == NULL) { + g_critical("Unable to allocate netlink message"); + return -ENOMEM; + } + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, 0, + DPAUXMON_CMD_START, 1); + if (hdr == NULL) { + g_critical("Unable to write genl header"); + res = -ENOMEM; + goto out_free; + } + + if ((err = nla_put_u32(msg, DPAUXMON_ATTR_IFINDEX, interface_id)) < 0) { + g_critical("Unable to add attribute: %s", nl_geterror(err)); + res = -EIO; + goto out_free; + } + + if ((err = nl_send_auto_complete(sock, msg)) < 0) + g_debug("Starting monitor failed, already running?"); + +out_free: + nlmsg_free(msg); + return res; +} + +static void send_stop(struct nl_sock *sock, int family, unsigned int interface_id) +{ + struct nl_msg *msg; + void *hdr; + int err; + + msg = nlmsg_alloc(); + if (msg == NULL) { + g_critical("Unable to allocate netlink message"); + return; + } + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, 0, + DPAUXMON_CMD_STOP, 1); + if (hdr == NULL) { + g_critical("Unable to write genl header"); + goto out_free; + } + + if ((err = nla_put_u32(msg, DPAUXMON_ATTR_IFINDEX, interface_id)) < 0) { + g_critical("Unable to add attribute: %s", nl_geterror(err)); + goto out_free; + } + + if ((err = nl_send_auto_complete(sock, msg)) < 0) { + g_critical("Unable to send message: %s", nl_geterror(err)); + goto out_free; + } + +out_free: + nlmsg_free(msg); +} + +static int handle_data(struct nl_cache_ops *unused _U_, struct genl_cmd *cmd _U_, + struct genl_info *info, void *arg _U_) +{ + unsigned char *data; + int data_size; + guint64 ts = 0; + guint8 packet[21] = { 0x00 }; + + if (!info->attrs[DPAUXMON_ATTR_DATA]) + return NL_SKIP; + + data = (unsigned char*)nla_data(info->attrs[DPAUXMON_ATTR_DATA]); + data_size = nla_len(info->attrs[DPAUXMON_ATTR_DATA]); + + if (data_size > 19) { + g_debug("Invalid packet size %u", data_size); + return NL_SKIP; + } + + if (info->attrs[DPAUXMON_ATTR_TIMESTAMP]) + ts = nla_get_msecs(info->attrs[DPAUXMON_ATTR_TIMESTAMP]); + + packet[1] = info->attrs[DPAUXMON_ATTR_FROM_SOURCE] ? 0x01 : 0x00; + + memcpy(&packet[2], data, data_size); + + if (dump_packet(pcap_fp, packet, data_size + 2, ts) == EXIT_FAILURE) + run_loop = FALSE; + + return NL_OK; +} + +static int parse_cb(struct nl_msg *msg, void *arg _U_) +{ + return genl_handle_msg(msg, NULL); +} + +static struct genl_cmd cmds[] = { +#if 0 + { + .c_id = DPAUXMON_CMD_START, + .c_name = "dpauxmon start", + .c_maxattr = DPAUXMON_ATTR_MAX, + .c_attr_policy = dpauxmon_attr_policy, + .c_msg_parser = &handle_start, + }, + { + .c_id = DPAUXMON_CMD_STOP, + .c_name = "dpauxmon stop", + .c_maxattr = DPAUXMON_ATTR_MAX, + .c_attr_policy = dpauxmon_attr_policy, + .c_msg_parser = &handle_stop, + }, +#endif + { + .c_id = DPAUXMON_CMD_DATA, + .c_name = "dpauxmon data", + .c_maxattr = DPAUXMON_ATTR_MAX, + .c_attr_policy = dpauxmon_attr_policy, + .c_msg_parser = &handle_data, + }, +}; + +#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) + +static struct genl_ops ops = { + .o_name = "dpauxmon", + .o_cmds = cmds, + .o_ncmds = ARRAY_SIZE(cmds), +}; + +struct nl_sock *sock; + +static void run_listener(const char* fifo, unsigned int interface_id) +{ + int err; + int grp; + struct sigaction int_handler = { .sa_handler = exit_from_loop }; + struct nl_cb *socket_cb; + + if (sigaction(SIGINT, &int_handler, 0)) { + g_warning("Can't set signal handler"); + return; + } + + if (setup_dumpfile(fifo, &pcap_fp) == EXIT_FAILURE) { + if (pcap_fp) + goto close_out; + } + + if (!(sock = nl_socket_alloc())) { + g_critical("Unable to allocate netlink socket"); + goto close_out; + } + + if ((err = nl_connect(sock, NETLINK_GENERIC)) < 0) { + g_critical("Unable to connect netlink socket: %s", + nl_geterror(err)); + goto free_out; + } + + if ((err = genl_register_family(&ops)) < 0) { + g_critical("Unable to register Generic Netlink family"); + goto err_out; + } + + if ((err = genl_ops_resolve(sock, &ops)) < 0) { + g_critical("Unable to resolve family name"); + goto err_out; + } + + /* register notification handler callback */ + if ((err = nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, + parse_cb, NULL)) < 0) { + g_critical("Unable to modify valid message callback"); + goto err_out; + } + + grp = nl_get_multicast_id(sock, ops.o_id, "notify"); + nl_socket_add_membership(sock, grp); + + if (!(socket_cb = nl_socket_get_cb(sock))) { + g_warning("Can't overwrite recv callback"); + } else { + nl_cb_overwrite_recv(socket_cb, nl_receive_timeout); + nl_cb_put(socket_cb); + } + + err = send_start(sock, ops.o_id, interface_id); + if (err) + goto err_out; + + nl_socket_disable_seq_check(sock); + + g_debug("DisplayPort AUX monitor running on interface %u", interface_id); + + while(run_loop == TRUE) { + if ((err = nl_recvmsgs_default(sock)) < 0) + g_warning("Unable to receive message: %s", nl_geterror(err)); + } + + send_stop(sock, ops.o_id, interface_id); + +err_out: + nl_close(sock); +free_out: + nl_socket_free(sock); +close_out: + fclose(pcap_fp); +} + +int main(int argc, char *argv[]) +{ + int option_idx = 0; + int result; + unsigned int interface_id = 0; + int ret = EXIT_FAILURE; + extcap_parameters* extcap_conf = g_new0(extcap_parameters, 1); + char* help_header = NULL; + + extcap_base_set_util_info(extcap_conf, argv[0], DPAUXMON_VERSION_MAJOR, DPAUXMON_VERSION_MINOR, DPAUXMON_VERSION_RELEASE, + NULL); + extcap_base_register_interface(extcap_conf, DPAUXMON_EXTCAP_INTERFACE, "DisplayPort AUX channel monitor capture", 275, "DisplayPort AUX channel monitor"); + + help_header = g_strdup_printf( + " %s --extcap-interfaces\n" + " %s --extcap-interface=%s --extcap-dlts\n" + " %s --extcap-interface=%s --extcap-config\n" + " %s --extcap-interface=%s --interface_id 0 --fifo myfifo --capture", + argv[0], argv[0], DPAUXMON_EXTCAP_INTERFACE, argv[0], DPAUXMON_EXTCAP_INTERFACE, argv[0], DPAUXMON_EXTCAP_INTERFACE); + extcap_help_add_header(extcap_conf, help_header); + g_free(help_header); + extcap_help_add_option(extcap_conf, "--help", "print this help"); + extcap_help_add_option(extcap_conf, "--version", "print the version"); + extcap_help_add_option(extcap_conf, "--port <port> ", "the dpauxmon interface index"); + + opterr = 0; + optind = 0; + + if (argc == 1) { + extcap_help_print(extcap_conf); + goto end; + } + + while ((result = getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) { + switch (result) { + + case OPT_HELP: + extcap_help_print(extcap_conf); + ret = EXIT_SUCCESS; + goto end; + + case OPT_VERSION: + printf("%s\n", extcap_conf->version); + goto end; + + case OPT_INTERFACE_ID: + if (!ws_strtou32(optarg, NULL, &interface_id)) { + g_warning("Invalid interface id: %s", optarg); + goto end; + } + break; + + case ':': + /* missing option argument */ + g_warning("Option '%s' requires an argument", argv[optind - 1]); + break; + + default: + if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, optarg)) { + g_warning("Invalid option: %s", argv[optind - 1]); + goto end; + } + } + } + + extcap_cmdline_debug(argv, argc); + + if (optind != argc) { + g_warning("Unexpected extra option: %s", argv[optind]); + goto end; + } + + if (extcap_base_handle_interface(extcap_conf)) { + ret = EXIT_SUCCESS; + goto end; + } + + if (extcap_conf->show_config) { + ret = list_config(extcap_conf->interface); + goto end; + } + + if (extcap_conf->capture) + run_listener(extcap_conf->fifo, interface_id); + +end: + /* clean up stuff */ + extcap_base_cleanup(&extcap_conf); + return ret; +} diff --git a/extcap/dpauxmon_user.h b/extcap/dpauxmon_user.h new file mode 100644 index 0000000000..818898148a --- /dev/null +++ b/extcap/dpauxmon_user.h @@ -0,0 +1,58 @@ +/* + * Copyright 2018, Dirk Eibach, Guntermann & Drunck GmbH <dirk.eibach@gdsys.cc> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DPAUXMON_USER_H_ +#define DPAUXMON_USER_H_ + +#include <linux/types.h> + +/* + * Generic Netlink Interface for DisplayPort AUX channel monitoring + */ + +/* + * enum dpauxmon_cmd - supported dpauxmon netlink commands + * + * @__DPAUXMON_CMD_UNSPEC: unspecified command to catch errors + * + * @DPAUXMON_CMD_START: start monitoring on %DPAUXMON_ATTR_IFINDEX + * @DPAUXMON_CMD_STOP: stop monitoring on %DPAUXMON_ATTR_IFINDEX + * @DPAUXMON_CMD_DATA: captured data from %DPAUXMON_ATTR_IFINDEX + */ +enum dpauxmon_cmd { + __DPAUXMON_CMD_UNSPEC, + DPAUXMON_CMD_START, + DPAUXMON_CMD_STOP, + DPAUXMON_CMD_DATA, + + /* keep last */ + __DPAUXMON_CMD_MAX, + DPAUXMON_CMD_MAX = __DPAUXMON_CMD_MAX - 1, +}; + +/* + * enum dpauxmon_attr - dpauxmon netlink attributes + * + * @__DPAUXMON_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @DPAUXMON_ATTR_IFINDEX: index of dpauxmon unit to operate on + * @DPAUXMON_ATTR_DATA: dpauxmon data payload + * @DPAUXMON_ATTR_FROM_SOURCE: data payload is sent from source + * @DPAUXMON_ATTR_TIMESTAMP: data payload is sent from source + */ +enum dpauxmon_attr { + __DPAUXMON_ATTR_UNSPEC, + DPAUXMON_ATTR_IFINDEX, /* NLA_U32 */ + DPAUXMON_ATTR_DATA, /* NLA_BINARY */ + DPAUXMON_ATTR_FROM_SOURCE, /* NLA_FLAG */ + DPAUXMON_ATTR_TIMESTAMP, /* NLA_MSECS */ + + /* keep last */ + __DPAUXMON_ATTR_AFTER_LAST, + DPAUXMON_ATTR_MAX = __DPAUXMON_ATTR_AFTER_LAST - 1 +}; + +#endif |