2 * udpdump is an extcap tool used to get packets exported from a source (like a network device or a GSMTAP producer) that
3 * are dumped to a pcap file
5 * Copyright 2016, Dario Lombardo <lomato@gmail.com>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
15 #define WS_LOG_DOMAIN "udpdump"
17 #include <extcap/extcap-base.h>
20 #include <glib/gprintf.h>
23 #ifdef HAVE_SYS_TIME_H
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
38 #include <writecap/pcapio.h>
39 #include <wiretap/wtap.h>
40 #include <wsutil/strtoi.h>
41 #include <wsutil/inet_addr.h>
42 #include <wsutil/filesystem.h>
43 #include <wsutil/privileges.h>
44 #include <wsutil/socket.h>
45 #include <wsutil/please_report_bug.h>
46 #include <wsutil/wslog.h>
47 #include <wsutil/pint.h>
48 #include <wsutil/exported_pdu_tlvs.h>
52 #define PCAP_SNAPLEN 0xffff
54 #define UDPDUMP_DEFAULT_PORT 5555
56 #define UDPDUMP_EXTCAP_INTERFACE "udpdump"
57 #define UDPDUMP_VERSION_MAJOR "0"
58 #define UDPDUMP_VERSION_MINOR "1"
59 #define UDPDUMP_VERSION_RELEASE "0"
61 #define PKT_BUF_SIZE 65535
63 #define UDPDUMP_EXPORT_HEADER_LEN 40
66 EXTCAP_BASE_OPTIONS_ENUM
,
73 static struct ws_option longopts
[] = {
75 /* Generic application options */
76 { "help", ws_no_argument
, NULL
, OPT_HELP
},
77 { "version", ws_no_argument
, NULL
, OPT_VERSION
},
78 /* Interfaces options */
79 { "port", ws_required_argument
, NULL
, OPT_PORT
},
80 { "payload", ws_required_argument
, NULL
, OPT_PAYLOAD
},
84 static int list_config(char *interface
)
89 ws_warning("No interface specified.");
93 printf("arg {number=%u}{call=--port}{display=Listen port}"
94 "{type=unsigned}{range=1,65535}{default=%u}{tooltip=The port the receiver listens on}\n",
95 inc
++, UDPDUMP_DEFAULT_PORT
);
96 printf("arg {number=%u}{call=--payload}{display=Payload type}"
97 "{type=string}{default=data}{tooltip=The type used to describe the payload in the exported pdu format}\n",
100 extcap_config_debug(&inc
);
105 static int setup_listener(const uint16_t port
, socket_handle_t
* sock
)
108 struct sockaddr_in serveraddr
;
110 struct timeval timeout
= { 1, 0 };
113 *sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
115 if (*sock
== INVALID_SOCKET
) {
116 ws_warning("Error opening socket: %s", strerror(errno
));
121 if (setsockopt(*sock
, SOL_SOCKET
, SO_REUSEADDR
, (char*)&optval
, (socklen_t
)sizeof(int)) < 0) {
122 ws_warning("Can't set socket option SO_REUSEADDR: %s", strerror(errno
));
123 goto cleanup_setup_listener
;
127 if (setsockopt (*sock
, SOL_SOCKET
, SO_RCVTIMEO
, (char*)&timeout
, (socklen_t
)sizeof(timeout
)) < 0) {
128 ws_warning("Can't set socket option SO_RCVTIMEO: %s", strerror(errno
));
129 goto cleanup_setup_listener
;
133 memset(&serveraddr
, 0x0, sizeof(serveraddr
));
134 serveraddr
.sin_family
= AF_INET
;
135 serveraddr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
136 serveraddr
.sin_port
= htons(port
);
138 if (bind(*sock
, (struct sockaddr
*)&serveraddr
, (socklen_t
)sizeof(serveraddr
)) < 0) {
139 ws_warning("Error on binding: %s", strerror(errno
));
140 goto cleanup_setup_listener
;
145 cleanup_setup_listener
:
151 static int setup_dumpfile(const char* fifo
, FILE** fp
)
153 uint64_t bytes_written
= 0;
156 if (!g_strcmp0(fifo
, "-")) {
161 *fp
= fopen(fifo
, "wb");
163 ws_warning("Error creating output file: %s", g_strerror(errno
));
167 if (!libpcap_write_file_header(*fp
, 252, PCAP_SNAPLEN
, false, &bytes_written
, &err
)) {
168 ws_warning("Can't write pcap file header: %s", g_strerror(err
));
177 static void add_proto_name(uint8_t* mbuf
, unsigned* offset
, const char* proto_name
)
179 size_t proto_str_len
= strlen(proto_name
);
180 uint16_t proto_name_len
= (uint16_t)((proto_str_len
+ 3) & 0xfffffffc);
182 phton16(mbuf
+ *offset
, EXP_PDU_TAG_DISSECTOR_NAME
);
184 phton16(mbuf
+ *offset
, proto_name_len
);
187 memcpy(mbuf
+ *offset
, proto_name
, proto_str_len
);
188 *offset
+= proto_name_len
;
191 static void add_ip_source_address(uint8_t* mbuf
, unsigned* offset
, uint32_t source_address
)
193 phton16(mbuf
+ *offset
, EXP_PDU_TAG_IPV4_SRC
);
195 phton16(mbuf
+ *offset
, 4);
197 memcpy(mbuf
+ *offset
, &source_address
, 4);
201 static void add_ip_dest_address(uint8_t* mbuf
, unsigned* offset
, uint32_t dest_address
)
203 phton16(mbuf
+ *offset
, EXP_PDU_TAG_IPV4_DST
);
205 phton16(mbuf
+ *offset
, 4);
207 memcpy(mbuf
+ *offset
, &dest_address
, 4);
211 static void add_udp_source_port(uint8_t* mbuf
, unsigned* offset
, uint16_t src_port
)
213 uint32_t port
= htonl(src_port
);
215 phton16(mbuf
+ *offset
, EXP_PDU_TAG_SRC_PORT
);
217 phton16(mbuf
+ *offset
, 4);
219 memcpy(mbuf
+ *offset
, &port
, 4);
223 static void add_udp_dst_port(uint8_t* mbuf
, unsigned* offset
, uint16_t dst_port
)
225 uint32_t port
= htonl(dst_port
);
227 phton16(mbuf
+ *offset
, EXP_PDU_TAG_DST_PORT
);
229 phton16(mbuf
+ *offset
, 4);
231 memcpy(mbuf
+ *offset
, &port
, 4);
235 static void add_end_options(uint8_t* mbuf
, unsigned* offset
)
237 memset(mbuf
+ *offset
, 0x0, 4);
241 static int dump_packet(const char* proto_name
, const uint16_t listenport
, const char* buf
,
242 const ssize_t buflen
, const struct sockaddr_in clientaddr
, FILE* fp
)
246 int64_t curtime
= g_get_real_time();
247 uint64_t bytes_written
= 0;
249 int ret
= EXIT_SUCCESS
;
251 /* The space we need is the standard header + variable lengths */
252 mbuf
= (uint8_t*)g_malloc0(UDPDUMP_EXPORT_HEADER_LEN
+ ((strlen(proto_name
) + 3) & 0xfffffffc) + buflen
);
254 add_proto_name(mbuf
, &offset
, proto_name
);
255 add_ip_source_address(mbuf
, &offset
, clientaddr
.sin_addr
.s_addr
);
256 add_ip_dest_address(mbuf
, &offset
, g_htonl(INADDR_LOOPBACK
));
257 add_udp_source_port(mbuf
, &offset
, clientaddr
.sin_port
);
258 add_udp_dst_port(mbuf
, &offset
, listenport
);
259 add_end_options(mbuf
, &offset
);
261 memcpy(mbuf
+ offset
, buf
, buflen
);
262 offset
+= (unsigned)buflen
;
264 if (!libpcap_write_packet(fp
,
265 (uint32_t)(curtime
/ G_USEC_PER_SEC
), (uint32_t)(curtime
% G_USEC_PER_SEC
),
266 offset
, offset
, mbuf
, &bytes_written
, &err
)) {
267 ws_warning("Can't write packet: %s", g_strerror(err
));
277 static void run_listener(const char* fifo
, const uint16_t port
, const char* proto_name
)
279 struct sockaddr_in clientaddr
;
280 socklen_t clientlen
= sizeof(clientaddr
);
281 socket_handle_t sock
;
286 if (setup_dumpfile(fifo
, &fp
) == EXIT_FAILURE
) {
292 if (setup_listener(port
, &sock
) == EXIT_FAILURE
)
295 ws_debug("Listener running on port %u", port
);
297 buf
= (char*)g_malloc(PKT_BUF_SIZE
);
298 while(!extcap_end_application
) {
299 memset(buf
, 0x0, PKT_BUF_SIZE
);
301 buflen
= recvfrom(sock
, buf
, PKT_BUF_SIZE
, 0, (struct sockaddr
*)&clientaddr
, &clientlen
);
310 wchar_t *errmsg
= NULL
;
311 int err
= WSAGetLastError();
312 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
,
314 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
315 (LPWSTR
)&errmsg
, 0, NULL
);
316 ws_warning("Error in recvfrom: %S (err=%d)", errmsg
, err
);
320 ws_warning("Error in recvfrom: %s (errno=%d)", strerror(errno
), errno
);
322 extcap_end_application
= true;
326 if (dump_packet(proto_name
, port
, buf
, buflen
, clientaddr
, fp
) == EXIT_FAILURE
)
327 extcap_end_application
= true;
336 int main(int argc
, char *argv
[])
342 int ret
= EXIT_FAILURE
;
343 extcap_parameters
* extcap_conf
= g_new0(extcap_parameters
, 1);
345 char* help_header
= NULL
;
346 char* payload
= NULL
;
347 char* port_msg
= NULL
;
349 /* Initialize log handler early so we can have proper logging during startup. */
350 extcap_log_init("udpdump");
353 * Get credential information for later use.
355 init_process_policies();
358 * Attempt to get the pathname of the directory containing the
361 err_msg
= configuration_init(argv
[0], NULL
);
362 if (err_msg
!= NULL
) {
363 ws_warning("Can't get pathname of directory containing the extcap program: %s.",
368 help_url
= data_file_url("udpdump.html");
369 extcap_base_set_util_info(extcap_conf
, argv
[0], UDPDUMP_VERSION_MAJOR
, UDPDUMP_VERSION_MINOR
, UDPDUMP_VERSION_RELEASE
,
372 extcap_base_register_interface(extcap_conf
, UDPDUMP_EXTCAP_INTERFACE
, "UDP Listener remote capture", 252, "Exported PDUs");
374 help_header
= ws_strdup_printf(
375 " %s --extcap-interfaces\n"
376 " %s --extcap-interface=%s --extcap-dlts\n"
377 " %s --extcap-interface=%s --extcap-config\n"
378 " %s --extcap-interface=%s --port 5555 --fifo myfifo --capture",
379 argv
[0], argv
[0], UDPDUMP_EXTCAP_INTERFACE
, argv
[0], UDPDUMP_EXTCAP_INTERFACE
, argv
[0], UDPDUMP_EXTCAP_INTERFACE
);
380 extcap_help_add_header(extcap_conf
, help_header
);
382 extcap_help_add_option(extcap_conf
, "--help", "print this help");
383 extcap_help_add_option(extcap_conf
, "--version", "print the version");
384 port_msg
= ws_strdup_printf("the port to listens on. Default: %u", UDPDUMP_DEFAULT_PORT
);
385 extcap_help_add_option(extcap_conf
, "--port <port>", port_msg
);
392 extcap_help_print(extcap_conf
);
396 while ((result
= ws_getopt_long(argc
, argv
, ":", longopts
, &option_idx
)) != -1) {
400 extcap_help_print(extcap_conf
);
405 extcap_version_print(extcap_conf
);
409 if (!ws_strtou16(ws_optarg
, NULL
, &port
)) {
410 ws_warning("Invalid port: %s", ws_optarg
);
417 payload
= g_strdup(ws_optarg
);
421 /* missing option argument */
422 ws_warning("Option '%s' requires an argument", argv
[ws_optind
- 1]);
426 if (!extcap_base_parse_options(extcap_conf
, result
- EXTCAP_OPT_LIST_INTERFACES
, ws_optarg
)) {
427 ws_warning("Invalid option: %s", argv
[ws_optind
- 1]);
433 extcap_cmdline_debug(argv
, argc
);
435 if (ws_optind
!= argc
) {
436 ws_warning("Unexpected extra option: %s", argv
[ws_optind
]);
440 if (extcap_base_handle_interface(extcap_conf
)) {
445 if (!extcap_base_register_graceful_shutdown_cb(extcap_conf
, NULL
)) {
450 if (extcap_conf
->show_config
) {
451 ret
= list_config(extcap_conf
->interface
);
456 payload
= g_strdup("data");
458 err_msg
= ws_init_sockets();
459 if (err_msg
!= NULL
) {
460 ws_warning("Error: %s", err_msg
);
462 ws_warning("%s", please_report_bug());
467 port
= UDPDUMP_DEFAULT_PORT
;
469 if (extcap_conf
->capture
)
470 run_listener(extcap_conf
->fifo
, port
, payload
);
474 extcap_base_cleanup(&extcap_conf
);
480 * Editor modelines - https://www.wireshark.org/tools/modelines.html
485 * indent-tabs-mode: t
488 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
489 * :indentSize=8:tabSize=8:noTabs=false: