aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDirk Eibach <dirk.eibach@gdsys.cc>2018-05-03 16:12:20 +0200
committerRoland Knall <rknall@gmail.com>2018-05-14 10:44:37 +0000
commite0002b0427f8b1fa88ecc0eb1d8b9d9a3da94040 (patch)
tree60f1e0280b80febeaea65b8532d5a26c3d8ecc0c
parent8f4072b481f56688ed6a971b46849de624c3a6c7 (diff)
extcap: Support for DisplayPort AUX channel monitors
Support for the generic netlink DisplayPort AUX channel monitor kernel driver. Change-Id: Iab445229ecef082968355f604993292f5f2d8d69 Reviewed-on: https://code.wireshark.org/review/27313 Petri-Dish: Dario Lombardo <lomato@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Dario Lombardo <lomato@gmail.com> Reviewed-by: Roland Knall <rknall@gmail.com>
-rw-r--r--CMakeLists.txt23
-rw-r--r--CMakeOptions.txt1
-rw-r--r--doc/dpauxmon.pod148
-rw-r--r--docbook/release-notes.asciidoc2
-rw-r--r--extcap/dpauxmon.c567
-rw-r--r--extcap/dpauxmon_user.h58
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