2 * dpauxmon is an extcap tool used to monitor DisplayPort AUX channel traffic
3 * coming in from the kernel via generic netlink
4 * Copyright 2018, Dirk Eibach, Guntermann & Drunck GmbH <dirk.eibach@gdsys.cc>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
15 #include "extcap-base.h"
17 #include <wsutil/strtoi.h>
18 #include <wsutil/filesystem.h>
19 #include <writecap/pcapio.h>
21 #include <netlink/netlink.h>
22 #include <netlink/genl/genl.h>
23 #include <netlink/genl/ctrl.h>
24 #include <netlink/genl/mngt.h>
29 #include <linux/genetlink.h>
31 #include "dpauxmon_user.h"
33 #define PCAP_SNAPLEN 128
35 #define DPAUXMON_EXTCAP_INTERFACE "dpauxmon"
36 #define DPAUXMON_VERSION_MAJOR "0"
37 #define DPAUXMON_VERSION_MINOR "1"
38 #define DPAUXMON_VERSION_RELEASE "0"
40 static gboolean run_loop = TRUE;
44 EXTCAP_BASE_OPTIONS_ENUM,
50 static struct option longopts[] = {
52 /* Generic application options */
53 { "help", no_argument, NULL, OPT_HELP},
54 { "version", no_argument, NULL, OPT_VERSION},
55 /* Interfaces options */
56 { "interface_id", required_argument, NULL, OPT_INTERFACE_ID},
60 static struct nla_policy dpauxmon_attr_policy[DPAUXMON_ATTR_MAX + 1] = {
61 [DPAUXMON_ATTR_IFINDEX] = { .type = NLA_U32 },
62 [DPAUXMON_ATTR_FROM_SOURCE] = { .type = NLA_FLAG },
63 [DPAUXMON_ATTR_TIMESTAMP] = { .type = NLA_MSECS },
66 struct family_handler_args {
71 static int list_config(char *interface)
76 g_warning("No interface specified.");
80 if (g_strcmp0(interface, DPAUXMON_EXTCAP_INTERFACE)) {
81 g_warning("interface must be %s", DPAUXMON_EXTCAP_INTERFACE);
85 printf("arg {number=%u}{call=--interface_id}{display=Interface index}"
86 "{type=unsigned}{range=1,65535}{default=%u}{tooltip=The dpauxmon interface index}\n",
89 extcap_config_debug(&inc);
94 static void exit_from_loop(int signo _U_)
99 static int setup_dumpfile(const char* fifo, FILE** fp)
101 guint64 bytes_written = 0;
104 if (!g_strcmp0(fifo, "-")) {
109 *fp = fopen(fifo, "w");
111 g_warning("Error creating output file: %s", g_strerror(errno));
115 if (!libpcap_write_file_header(*fp, 275, PCAP_SNAPLEN, FALSE, &bytes_written, &err)) {
116 g_warning("Can't write pcap file header");
123 static int dump_packet(FILE* fp, const char* buf, const ssize_t buflen, guint64 ts_usecs)
125 guint64 bytes_written = 0;
127 int ret = EXIT_SUCCESS;
129 if (!libpcap_write_packet(fp, ts_usecs / 1000000, ts_usecs % 1000000, buflen, buflen, buf, &bytes_written, &err)) {
130 g_warning("Can't write packet");
139 static int error_handler(struct sockaddr_nl *nla _U_, struct nlmsgerr *err,
142 int *ret = (int*)arg;
147 static int ack_handler(struct nl_msg *msg _U_, void *arg)
149 int *ret = (int*)arg;
154 static int family_handler(struct nl_msg *msg, void *arg)
156 struct family_handler_args *grp = (struct family_handler_args *)arg;
157 struct nlattr *tb[CTRL_ATTR_MAX + 1];
158 struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
159 struct nlattr *mcgrp;
162 nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
163 genlmsg_attrlen(gnlh, 0), NULL);
165 if (!tb[CTRL_ATTR_MCAST_GROUPS])
168 nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
169 struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
171 nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
172 (struct nlattr *)nla_data(mcgrp), nla_len(mcgrp), NULL);
174 if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] ||
175 !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
178 if (strncmp((const char*)nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
180 nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])))
183 grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
191 static int nl_get_multicast_id(struct nl_sock *sock, int family,
197 struct family_handler_args grp = {
206 cb = nl_cb_alloc(NL_CB_DEFAULT);
212 ctrlid = genl_ctrl_resolve(sock, "nlctrl");
214 genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
217 NLA_PUT_U16(msg, CTRL_ATTR_FAMILY_ID, family);
219 ret = nl_send_auto_complete(sock, msg);
221 goto nla_put_failure;
225 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
226 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
227 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, family_handler, &grp);
230 nl_recvmsgs(sock, cb);
242 * netlink callback handlers
245 int nl_receive_timeout(struct nl_sock* sk, struct sockaddr_nl* nla, unsigned char** buf, struct ucred** creds)
247 struct pollfd fds = {nl_socket_get_fd(sk), POLLIN, 0};
248 int poll_res = poll(&fds, 1, 500);
251 g_debug("poll() failed in nl_receive_timeout");
253 return -nl_syserr2nlerr(errno);
256 return poll_res ? nl_recv(sk, nla, buf, creds) : 0;
259 static int send_start(struct nl_sock *sock, int family, unsigned int interface_id)
268 g_critical("Unable to allocate netlink message");
272 hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, 0,
273 DPAUXMON_CMD_START, 1);
275 g_critical("Unable to write genl header");
280 if ((err = nla_put_u32(msg, DPAUXMON_ATTR_IFINDEX, interface_id)) < 0) {
281 g_critical("Unable to add attribute: %s", nl_geterror(err));
286 if ((err = nl_send_auto_complete(sock, msg)) < 0)
287 g_debug("Starting monitor failed, already running?");
294 static void send_stop(struct nl_sock *sock, int family, unsigned int interface_id)
302 g_critical("Unable to allocate netlink message");
306 hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, 0,
307 DPAUXMON_CMD_STOP, 1);
309 g_critical("Unable to write genl header");
313 if ((err = nla_put_u32(msg, DPAUXMON_ATTR_IFINDEX, interface_id)) < 0) {
314 g_critical("Unable to add attribute: %s", nl_geterror(err));
318 if ((err = nl_send_auto_complete(sock, msg)) < 0) {
319 g_critical("Unable to send message: %s", nl_geterror(err));
327 static int handle_data(struct nl_cache_ops *unused _U_, struct genl_cmd *cmd _U_,
328 struct genl_info *info, void *arg _U_)
333 guint8 packet[21] = { 0x00 };
335 if (!info->attrs[DPAUXMON_ATTR_DATA])
338 data = (unsigned char*)nla_data(info->attrs[DPAUXMON_ATTR_DATA]);
339 data_size = nla_len(info->attrs[DPAUXMON_ATTR_DATA]);
341 if (data_size > 19) {
342 g_debug("Invalid packet size %u", data_size);
346 if (info->attrs[DPAUXMON_ATTR_TIMESTAMP])
347 ts = nla_get_msecs(info->attrs[DPAUXMON_ATTR_TIMESTAMP]);
349 packet[1] = info->attrs[DPAUXMON_ATTR_FROM_SOURCE] ? 0x01 : 0x00;
351 memcpy(&packet[2], data, data_size);
353 if (dump_packet(pcap_fp, packet, data_size + 2, ts) == EXIT_FAILURE)
359 static int parse_cb(struct nl_msg *msg, void *arg _U_)
361 return genl_handle_msg(msg, NULL);
364 static struct genl_cmd cmds[] = {
367 .c_id = DPAUXMON_CMD_START,
368 .c_name = "dpauxmon start",
369 .c_maxattr = DPAUXMON_ATTR_MAX,
370 .c_attr_policy = dpauxmon_attr_policy,
371 .c_msg_parser = &handle_start,
374 .c_id = DPAUXMON_CMD_STOP,
375 .c_name = "dpauxmon stop",
376 .c_maxattr = DPAUXMON_ATTR_MAX,
377 .c_attr_policy = dpauxmon_attr_policy,
378 .c_msg_parser = &handle_stop,
382 .c_id = DPAUXMON_CMD_DATA,
383 .c_name = "dpauxmon data",
384 .c_maxattr = DPAUXMON_ATTR_MAX,
385 .c_attr_policy = dpauxmon_attr_policy,
386 .c_msg_parser = &handle_data,
390 #define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
392 static struct genl_ops ops = {
393 .o_name = "dpauxmon",
395 .o_ncmds = ARRAY_SIZE(cmds),
398 struct nl_sock *sock;
400 static void run_listener(const char* fifo, unsigned int interface_id)
404 struct sigaction int_handler = { .sa_handler = exit_from_loop };
405 struct nl_cb *socket_cb;
407 if (sigaction(SIGINT, &int_handler, 0)) {
408 g_warning("Can't set signal handler");
412 if (setup_dumpfile(fifo, &pcap_fp) == EXIT_FAILURE) {
417 if (!(sock = nl_socket_alloc())) {
418 g_critical("Unable to allocate netlink socket");
422 if ((err = nl_connect(sock, NETLINK_GENERIC)) < 0) {
423 g_critical("Unable to connect netlink socket: %s",
428 if ((err = genl_register_family(&ops)) < 0) {
429 g_critical("Unable to register Generic Netlink family");
433 if ((err = genl_ops_resolve(sock, &ops)) < 0) {
434 g_critical("Unable to resolve family name");
438 /* register notification handler callback */
439 if ((err = nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM,
440 parse_cb, NULL)) < 0) {
441 g_critical("Unable to modify valid message callback");
445 grp = nl_get_multicast_id(sock, ops.o_id, "notify");
446 nl_socket_add_membership(sock, grp);
448 if (!(socket_cb = nl_socket_get_cb(sock))) {
449 g_warning("Can't overwrite recv callback");
451 nl_cb_overwrite_recv(socket_cb, nl_receive_timeout);
452 nl_cb_put(socket_cb);
455 err = send_start(sock, ops.o_id, interface_id);
459 nl_socket_disable_seq_check(sock);
461 g_debug("DisplayPort AUX monitor running on interface %u", interface_id);
463 while(run_loop == TRUE) {
464 if ((err = nl_recvmsgs_default(sock)) < 0)
465 g_warning("Unable to receive message: %s", nl_geterror(err));
468 send_stop(sock, ops.o_id, interface_id);
473 nl_socket_free(sock);
478 int main(int argc, char *argv[])
482 unsigned int interface_id = 0;
483 int ret = EXIT_FAILURE;
484 extcap_parameters* extcap_conf = g_new0(extcap_parameters, 1);
485 char* help_header = NULL;
487 extcap_base_set_util_info(extcap_conf, argv[0], DPAUXMON_VERSION_MAJOR, DPAUXMON_VERSION_MINOR, DPAUXMON_VERSION_RELEASE,
489 extcap_base_register_interface(extcap_conf, DPAUXMON_EXTCAP_INTERFACE, "DisplayPort AUX channel monitor capture", 275, "DisplayPort AUX channel monitor");
491 help_header = g_strdup_printf(
492 " %s --extcap-interfaces\n"
493 " %s --extcap-interface=%s --extcap-dlts\n"
494 " %s --extcap-interface=%s --extcap-config\n"
495 " %s --extcap-interface=%s --interface_id 0 --fifo myfifo --capture",
496 argv[0], argv[0], DPAUXMON_EXTCAP_INTERFACE, argv[0], DPAUXMON_EXTCAP_INTERFACE, argv[0], DPAUXMON_EXTCAP_INTERFACE);
497 extcap_help_add_header(extcap_conf, help_header);
499 extcap_help_add_option(extcap_conf, "--help", "print this help");
500 extcap_help_add_option(extcap_conf, "--version", "print the version");
501 extcap_help_add_option(extcap_conf, "--port <port> ", "the dpauxmon interface index");
507 extcap_help_print(extcap_conf);
511 while ((result = getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) {
515 extcap_help_print(extcap_conf);
520 printf("%s\n", extcap_conf->version);
523 case OPT_INTERFACE_ID:
524 if (!ws_strtou32(optarg, NULL, &interface_id)) {
525 g_warning("Invalid interface id: %s", optarg);
531 /* missing option argument */
532 g_warning("Option '%s' requires an argument", argv[optind - 1]);
536 if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, optarg)) {
537 g_warning("Invalid option: %s", argv[optind - 1]);
543 extcap_cmdline_debug(argv, argc);
545 if (optind != argc) {
546 g_warning("Unexpected extra option: %s", argv[optind]);
550 if (extcap_base_handle_interface(extcap_conf)) {
555 if (extcap_conf->show_config) {
556 ret = list_config(extcap_conf->interface);
560 if (extcap_conf->capture)
561 run_listener(extcap_conf->fifo, interface_id);
565 extcap_base_cleanup(&extcap_conf);