summaryrefslogtreecommitdiffstats
path: root/extcap/ciscodump.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /extcap/ciscodump.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'extcap/ciscodump.c')
-rw-r--r--extcap/ciscodump.c2512
1 files changed, 2512 insertions, 0 deletions
diff --git a/extcap/ciscodump.c b/extcap/ciscodump.c
new file mode 100644
index 00000000..54751ed0
--- /dev/null
+++ b/extcap/ciscodump.c
@@ -0,0 +1,2512 @@
+/* ciscodump.c
+ * ciscodump is extcap tool used to capture data using a ssh on a remote cisco router
+ *
+ * Copyright 2015, Dario Lombardo
+ *
+ * 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"
+#define WS_LOG_DOMAIN "ciscodump"
+
+#include <extcap/extcap-base.h>
+#include <wsutil/interface.h>
+#include <wsutil/strtoi.h>
+#include <wsutil/filesystem.h>
+#include <wsutil/privileges.h>
+#include <wsutil/please_report_bug.h>
+#include <wsutil/wslog.h>
+#include <extcap/ssh-base.h>
+#include <writecap/pcapio.h>
+
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <wsutil/time_util.h>
+#include <wsutil/ws_strptime.h>
+
+#include <cli_main.h>
+
+#define CISCODUMP_VERSION_MAJOR "1"
+#define CISCODUMP_VERSION_MINOR "0"
+#define CISCODUMP_VERSION_RELEASE "0"
+
+/* The read timeout in msec */
+#define CISCODUMP_READ_TIMEOUT_MSEC 300
+
+#define CISCODUMP_EXTCAP_INTERFACE "ciscodump"
+#define SSH_READ_BLOCK_SIZE 1024
+#define SSH_READ_TIMEOUT_MSES 10000
+#define SSH_READ_TIMEOUT_USEC (SSH_READ_TIMEOUT_MSES*1000)
+
+#define WIRESHARK_CAPTURE "WSC"
+#define WIRESHARK_CAPTURE_POINT "WSC_P"
+#define WIRESHARK_CAPTURE_BUFFER "WSC_B"
+#define WIRESHARK_CAPTURE_ACCESSLIST "WSC_ACL"
+
+#define PCAP_SNAPLEN 0xffff
+
+#define PACKET_MAX_SIZE 65535
+
+#define MINIMUM_IOS_MAJOR 12
+#define MINIMUM_IOS_MINOR 4
+#define MINIMUM_IOS_XE_MAJOR_16 16
+#define MINIMUM_IOS_XE_MINOR_16 1
+#define MINIMUM_IOS_XE_MAJOR_17 17
+#define MINIMUM_IOS_XE_MINOR_17 1
+#define MINIMUM_ASA_MAJOR 8
+#define MINIMUM_ASA_MINOR 4
+
+#define READ_PROMPT_ERROR -1
+#define READ_PROMPT_EOLN 0
+#define READ_PROMPT_PROMPT 1
+#define READ_PROMPT_TOO_LONG 2
+
+#define READ_LINE_ERROR -1
+#define READ_LINE_EOLN 0
+#define READ_LINE_TIMEOUT 1
+#define READ_LINE_TOO_LONG 2
+
+/* Type of Cisco device */
+typedef enum {
+ CISCO_UNKNOWN,
+ CISCO_IOS,
+ CISCO_IOS_XE_16,
+ CISCO_IOS_XE_17,
+ CISCO_ASA
+} CISCO_SW_TYPE;
+
+/* Status of the parser */
+enum {
+ CISCODUMP_PARSER_STARTING,
+ CISCODUMP_PARSER_IN_PACKET,
+ CISCODUMP_PARSER_IN_HEADER,
+ CISCODUMP_PARSER_END_PACKET,
+ CISCODUMP_PARSER_UNKNOWN
+};
+
+enum {
+ EXTCAP_BASE_OPTIONS_ENUM,
+ OPT_HELP,
+ OPT_VERSION,
+ OPT_REMOTE_HOST,
+ OPT_REMOTE_PORT,
+ OPT_REMOTE_USERNAME,
+ OPT_REMOTE_PASSWORD,
+ OPT_REMOTE_INTERFACE,
+ OPT_REMOTE_FILTER,
+ OPT_SSHKEY,
+ OPT_SSHKEY_PASSPHRASE,
+ OPT_PROXYCOMMAND,
+ OPT_REMOTE_COUNT
+};
+
+static char prompt_str[SSH_READ_BLOCK_SIZE + 1];
+static int32_t prompt_len = -1;
+CISCO_SW_TYPE global_sw_type = CISCO_UNKNOWN;
+static bool send_output_quit = false; /* IOS XE 17: send quit during output */
+
+static struct ws_option longopts[] = {
+ EXTCAP_BASE_OPTIONS,
+ { "help", ws_no_argument, NULL, OPT_HELP},
+ { "version", ws_no_argument, NULL, OPT_VERSION},
+ SSH_BASE_OPTIONS,
+ { 0, 0, 0, 0}
+};
+
+static void graceful_shutdown_cb(void)
+{
+ if (global_sw_type == CISCO_IOS_XE_17) {
+ send_output_quit = true;
+ }
+}
+
+/* Replaces needle with rep in line */
+static char* str_replace_char(char *line, char needle, char rep)
+{
+ for(int i = 0; line[i] != '\0'; i++) {
+ if (line[i] == needle) {
+ line[i] = rep;
+ }
+ }
+
+ return line;
+}
+
+/* Replaces CR with LN */
+static char* crtoln(char *line)
+{
+ return str_replace_char(line, '\r', '\n');
+}
+
+static char* interfaces_list_to_filter(GSList* interfaces, unsigned int remote_port)
+{
+ GString* filter = g_string_new(NULL);
+ GSList* cur;
+
+ if (interfaces) {
+ g_string_append_printf(filter, "deny tcp host %s any eq %u, deny tcp any eq %u host %s",
+ (char*)interfaces->data, remote_port, remote_port, (char*)interfaces->data);
+ cur = g_slist_next(interfaces);
+ while (cur) {
+ g_string_append_printf(filter, ", deny tcp host %s any eq %u, deny tcp any eq %u host %s",
+ (char*)cur->data, remote_port, remote_port, (char*)cur->data);
+ cur = g_slist_next(cur);
+ }
+ g_string_append_printf(filter, ", permit ip any any");
+ }
+
+ return g_string_free(filter, false);
+}
+
+static char* local_interfaces_to_filter(const unsigned int remote_port)
+{
+ GSList* interfaces = local_interfaces_to_list();
+ char* filter = interfaces_list_to_filter(interfaces, remote_port);
+ g_slist_free_full(interfaces, g_free);
+ return filter;
+}
+
+/* Read bytes from the channel with no escape character.
+ * If bytes == -1, read all data (until timeout). If outbuf != NULL, data are stored there
+ */
+static int read_output_bytes_any(ssh_channel channel, int bytes, char* outbuf)
+{
+ char chr;
+ int total;
+ int bytes_read;
+
+ total = (bytes > 0 ? bytes : G_MAXINT);
+ bytes_read = 0;
+
+ while(ssh_channel_read_timeout(channel, &chr, 1, 0, CISCODUMP_READ_TIMEOUT_MSEC) > 0 && bytes_read < total) {
+ ws_noisy("%c %02x", chr, chr);
+ if (outbuf)
+ outbuf[bytes_read] = chr;
+ bytes_read++;
+ }
+ if (outbuf)
+ outbuf[bytes_read+1] = '\0';
+ return EXIT_SUCCESS;
+}
+
+/* Read bytes from the channel. Recognize escape char '^'.
+ * If bytes == -1, read all data (until timeout). If outbuf != NULL, data are stored there
+ */
+static int read_output_bytes(ssh_channel channel, int bytes, char* outbuf)
+{
+ char chr;
+ int total;
+ int bytes_read;
+
+ total = (bytes > 0 ? bytes : G_MAXINT);
+ bytes_read = 0;
+
+ while(ssh_channel_read_timeout(channel, &chr, 1, 0, CISCODUMP_READ_TIMEOUT_MSEC) > 0 && bytes_read < total) {
+ ws_noisy("%c %02x", chr, chr);
+ if (chr == '^')
+ return EXIT_FAILURE;
+ if (outbuf)
+ outbuf[bytes_read] = chr;
+ bytes_read++;
+ }
+ return EXIT_SUCCESS;
+}
+
+/* Reads input to buffer and parses EOL
+ * If line is NULL, just received count of characters in len is calculated
+ * It returns:
+ * READ_LINE_ERROR - any ssh error occured
+ * READ_LINE_EOLN - EOLN found, line/len contains \0 terminated string
+ * READ_LINE_TIMEOUT - reading ended with timeout, line/len contains \0 terminate prompt
+ * READ_LINE_TOO_LONG - buffer is full with no EOLN nor PROMPT found, line is filled with NOT \0 terminated data
+ */
+static int ssh_channel_read_line_timeout(ssh_channel channel, char *line, int *len, int max_len) {
+ char chr;
+ int rlen = 0;
+
+ *len = 0;
+ do {
+ rlen = ssh_channel_read_timeout(channel, &chr, 1, false, CISCODUMP_READ_TIMEOUT_MSEC);
+ ws_noisy("%c %02x %d", chr, chr, rlen);
+ if (rlen == SSH_ERROR) {
+ ws_warning("Error reading from channel");
+ return READ_LINE_ERROR;
+ } else if (rlen > 0) {
+ if (chr != '\n') {
+ /* Ignore \r */
+ if (chr != '\r') {
+ if (line) {
+ line[*len] = chr;
+ }
+ (*len)++;
+ }
+ } else {
+ /* Parse the current line */
+ if (line) {
+ line[*len] = '\0';
+ }
+ return READ_LINE_EOLN;
+ }
+ } else {
+ return READ_LINE_TIMEOUT;
+ }
+ } while (*len < max_len);
+
+ return READ_LINE_TOO_LONG;
+}
+
+/* Reads input to buffer and parses EOL or prompt_str PROMPT
+ * It returns:
+ * READ_PROMPT_ERROR - any ssh error occured
+ * READ_PROMPT_EOLN - EOLN found, line/len contains \0 terminated string
+ * READ_PROMPT_PROMPT - reading ended and it ends with PROMPT, line/len contains \0 terminate prompt
+ * READ_PROMPT_TOO_LONG - buffer is full with no EOLN nor PROMPT found, line is filled with NOT \0 terminated data
+ */
+static int ssh_channel_read_prompt(ssh_channel channel, char *line, uint32_t *len, uint32_t max_len) {
+ char chr;
+ int rlen = 0;
+ int64_t start_time = g_get_monotonic_time();
+
+ do {
+ rlen = ssh_channel_read_timeout(channel, &chr, 1, false, CISCODUMP_READ_TIMEOUT_MSEC);
+ ws_noisy("%c %02x %d", chr, chr, rlen);
+ if (rlen == SSH_ERROR) {
+ ws_warning("Error reading from channel");
+ return READ_PROMPT_ERROR;
+ } else if (rlen > 0) {
+ if (chr != '\n') {
+ line[*len] = chr;
+ (*len)++;
+ } else {
+ /* Parse the current line */
+ line[*len] = '\0';
+ ws_noisy(" exiting: READ_PROMPT_EOLN (%d/%d)", *len, max_len);
+ return READ_PROMPT_EOLN;
+ }
+ } else {
+ int64_t cur_time = g_get_monotonic_time();
+
+ /* ssh timeout, we might be on prompt */
+ /* IOS, IOS-XE: check if line has same length as prompt and if it match prompt */
+ if ((*len == (uint32_t)prompt_len) && (0 == strncmp(line, prompt_str, prompt_len))) {
+ line[*len] = '\0';
+ ws_noisy(" exiting: READ_PROMPT_PROMPT (%d/%d)", *len, max_len);
+ return READ_PROMPT_PROMPT;
+ }
+ /* ASA: check if line begins with \r and has same length as prompt and if it match prompt */
+ if ((line[0] == '\r') && (*len == (uint32_t)prompt_len+1) && (0 == strncmp(line+1, prompt_str, prompt_len))) {
+ line[*len] = '\0';
+ ws_noisy(" exiting: READ_PROMPT_PROMPT (%d/%d)", *len, max_len);
+ return READ_PROMPT_PROMPT;
+ }
+ /* no prompt found, so we continue in waiting for data, but we should check global timeout */
+ if ((cur_time-start_time) > SSH_READ_TIMEOUT_USEC) {
+ line[*len] = '\0';
+ ws_noisy(" exiting: READ_PROMPT_ERROR");
+ return READ_PROMPT_ERROR;
+ }
+ }
+ } while (!extcap_end_application && (*len < max_len));
+
+ ws_noisy(" exiting: READ_PROMPT_TOO_LONG (%d/%d/%d)", *len, max_len, extcap_end_application);
+ line[*len] = '\0';
+ return READ_PROMPT_TOO_LONG;
+}
+
+static int ssh_channel_wait_prompt(ssh_channel channel, char *line, uint32_t *len, uint32_t max_len) {
+ char line2[SSH_READ_BLOCK_SIZE + 1];
+ uint32_t len2;
+ int status;
+
+ memset(line2, 0x0, SSH_READ_BLOCK_SIZE + 1);
+ line[0] = '\0';
+ *len = 0;
+ do {
+ len2 = 0;
+ switch (status = ssh_channel_read_prompt(channel, line2, &len2, SSH_READ_BLOCK_SIZE)) {
+ case READ_PROMPT_EOLN:
+ *len = (uint32_t)g_strlcat(line, line2, max_len);
+ len2 = 0;
+ break;
+ case READ_PROMPT_PROMPT:
+ *len = (uint32_t)g_strlcat(line, line2, max_len);
+ len2 = 0;
+ break;
+ default:
+ /* We do not have better solution for that cases */
+ /* Just terminate the line and return error */
+ *len = (uint32_t)g_strlcat(line, line2, max_len);
+ line[max_len] = '\0';
+ ws_noisy("Returning READ_PROMPT_ERROR (%d/%d)", *len, max_len);
+ return READ_PROMPT_ERROR;
+ }
+ } while (status == READ_PROMPT_EOLN);
+
+ ws_noisy("Returning READ_PROMPT_PROMPT (%d/%d)", *len, max_len);
+ return READ_PROMPT_PROMPT;
+}
+
+/* true if prompt and no error text in response. false otherwise */
+/* Note: It do not catch all CISCO CLI errors, but many of them */
+static bool ssh_channel_wait_prompt_check_error(ssh_channel channel, char *line, uint32_t *len, uint32_t max_len, char *error_re) {
+ /* Did we received prompt? */
+ if (ssh_channel_wait_prompt(channel, line, len, max_len) != READ_PROMPT_PROMPT) {
+ return false;
+ }
+
+ /* Is there ERROR: text in output? */
+ if (NULL != g_strstr_len(line, -1, "ERROR:")) {
+ return false;
+ }
+
+ /* Is there ERROR: text in output? */
+ if (NULL != g_strstr_len(line, -1, "% Invalid input detected at")) {
+ return false;
+ }
+
+ /* Is there error_re text in output? */
+ if (error_re &&
+ g_regex_match_simple(error_re, line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)
+ ) {
+ return false;
+ }
+
+ return true;
+}
+
+static void ciscodump_cleanup_ios(ssh_channel channel, const char* iface, const char* cfilter)
+{
+ char* iface_copy = g_strdup(iface);
+ char* iface_one;
+ char* str = NULL;
+ int wscp_cnt = 1;
+ char* wscp_str = NULL;
+
+ extcap_end_application = false;
+ if (channel) {
+ ws_debug("Removing configuration...");
+ read_output_bytes(channel, -1, NULL);
+
+ wscp_cnt = 1;
+ for (str = iface_copy; ; str = NULL) {
+ iface_one = strtok(str, ",");
+ if (iface_one == NULL)
+ break;
+
+ wscp_str = g_strdup_printf("%s_%d", WIRESHARK_CAPTURE_POINT, wscp_cnt);
+ wscp_cnt++;
+
+ ssh_channel_printf(channel, "monitor capture point stop %s\n", wscp_str);
+ ssh_channel_printf(channel, "no monitor capture point ip cef %s %s\n", wscp_str, iface_one);
+
+ g_free(wscp_str);
+ wscp_str = NULL;
+ }
+
+ ssh_channel_printf(channel, "no monitor capture buffer %s\n", WIRESHARK_CAPTURE_BUFFER);
+ if (cfilter) {
+ ssh_channel_printf(channel, "configure terminal\n");
+ ssh_channel_printf(channel, "no ip access-list ex %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
+ }
+
+ read_output_bytes(channel, -1, NULL);
+ ws_debug("Configuration removed");
+ }
+
+ g_free(iface_copy);
+}
+
+static void ciscodump_cleanup_ios_xe_16(ssh_channel channel, const char* cfilter)
+{
+ if (channel) {
+ ws_debug("Removing configuration...");
+ read_output_bytes(channel, -1, NULL);
+
+ ssh_channel_printf(channel, "monitor capture %s stop\n", WIRESHARK_CAPTURE);
+ ssh_channel_printf(channel, "no monitor capture %s\n", WIRESHARK_CAPTURE);
+ if (cfilter) {
+ ssh_channel_printf(channel, "configure terminal\n");
+ ssh_channel_printf(channel, "no ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
+ ssh_channel_printf(channel, "\nend\n");
+ }
+
+ read_output_bytes(channel, -1, NULL);
+ ws_debug("Configuration removed");
+ }
+}
+
+static void ciscodump_cleanup_ios_xe_17(ssh_channel channel, const char* cfilter)
+{
+ if (channel) {
+ ws_debug("Removing configuration...");
+ read_output_bytes(channel, -1, NULL);
+
+ ssh_channel_printf(channel, "monitor capture %s stop\n", WIRESHARK_CAPTURE);
+ ssh_channel_printf(channel, "no monitor capture %s\n", WIRESHARK_CAPTURE);
+ if (cfilter) {
+ ssh_channel_printf(channel, "configure terminal\n");
+ ssh_channel_printf(channel, "no ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
+ ssh_channel_printf(channel, "\nend\n");
+ }
+
+ read_output_bytes(channel, -1, NULL);
+ ws_debug("Configuration removed");
+ }
+}
+
+static void ciscodump_cleanup_asa(ssh_channel channel, const char* cfilter)
+{
+ if (channel) {
+ ws_debug("Removing configuration...");
+ read_output_bytes(channel, -1, NULL);
+
+ ssh_channel_printf(channel, "no capture %s\n", WIRESHARK_CAPTURE);
+ if (cfilter) {
+ ssh_channel_printf(channel, "configure terminal\n");
+ ssh_channel_printf(channel, "clear configure access-list %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
+ ssh_channel_printf(channel, "\nend\n", WIRESHARK_CAPTURE_ACCESSLIST);
+ }
+
+ read_output_bytes(channel, -1, NULL);
+ ws_debug("Configuration removed");
+ }
+}
+
+static void ciscodump_cleanup(ssh_channel channel, const char* iface, const char* cfilter, CISCO_SW_TYPE sw_type)
+{
+ ws_debug("Starting config cleanup");
+ switch (sw_type) {
+ case CISCO_IOS:
+ ciscodump_cleanup_ios(channel, iface, cfilter);
+ break;
+ case CISCO_IOS_XE_16:
+ ciscodump_cleanup_ios_xe_16(channel, cfilter);
+ break;
+ case CISCO_IOS_XE_17:
+ ciscodump_cleanup_ios_xe_17(channel, cfilter);
+ break;
+ case CISCO_ASA:
+ ciscodump_cleanup_asa(channel, cfilter);
+ break;
+ case CISCO_UNKNOWN:
+ break;
+ }
+ ws_debug("Config cleanup finished");
+}
+
+static void packets_captured_count_ios(char *line, uint32_t *max, bool *running) {
+ char** part;
+
+ *max = 0;
+
+ ws_debug("Analyzing response: %s", line);
+
+ /* Read count of packets */
+ part = g_regex_split_simple(
+ "Packets :\\s*(\\d+)",
+ line, G_REGEX_CASELESS, 0);
+ if (*part && *(part+1)) {
+ /* RE matched */
+ if (strlen(*(part+1)) > 0) {
+ ws_strtou32(*(part+1), NULL, max);
+ }
+ }
+ g_strfreev(part);
+
+ *running = false;
+ if (g_regex_match_simple("Status : Active", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
+ *running = true;
+ }
+ ws_debug("Count of packets: %d", *max);
+ ws_debug("Capture is running: %d", *running);
+}
+
+static void packets_captured_count_ios_xe_16(char *line, uint32_t *max, bool *running) {
+ char** part;
+
+ *max = 0;
+
+ ws_debug("Analyzing response: %s", line);
+
+ part = g_regex_split_simple(
+ "packets in buf\\s+:\\s+(\\d+)",
+ line, G_REGEX_CASELESS, 0);
+ if (*part && *(part+1)) {
+ /* RE matched */
+ if (strlen(*(part+1)) > 0) {
+ ws_strtou32(*(part+1), NULL, max);
+ }
+ }
+ g_strfreev(part);
+
+ *running = false;
+ /* Check if capture is running */
+ if (g_regex_match_simple("Status : Active", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
+ *running = true;
+ }
+ ws_debug("Count of packets: %d", *max);
+ ws_debug("Capture is running: %d", *running);
+}
+
+static void packets_captured_count_asa(char *line, uint32_t *max, bool *running) {
+ char** part;
+
+ *max = 0;
+
+ ws_debug("Analyzing response: %s", line);
+
+ /* Read count of packets */
+ part = g_regex_split_simple(
+ "(\\d+) packets captured",
+ line, G_REGEX_CASELESS, 0);
+ if (*part && *(part+1)) {
+ /* RE matched */
+ if (strlen(*(part+1)) > 0) {
+ ws_strtou32(*(part+1), NULL, max);
+ }
+ }
+ g_strfreev(part);
+
+ if (running != NULL) {
+ *running = false;
+ /* Check if capture is running */
+ if (g_regex_match_simple("\\[Capturing -", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
+ *running = true;
+ }
+ ws_debug("Capture is running: %d", *running);
+ }
+ ws_debug("Count of packets: %d", *max);
+}
+
+static int parse_line_ios(uint8_t* packet, unsigned* offset, char* line, int status, time_t *pkt_time, uint32_t *pkt_usec)
+{
+ char** parts;
+ char** part;
+ uint32_t value;
+ size_t size;
+
+ if (strlen(line) <= 1) {
+ if (status == CISCODUMP_PARSER_IN_PACKET)
+ return CISCODUMP_PARSER_END_PACKET;
+ else
+ return status;
+ }
+
+/*
+22:45:44.700 UTC Nov 27 2021 : IPv4 LES CEF : Fa4.320 None
+
+0F1A2C00: B0FAEBC7 A8620050 0zkG(b.P
+0F1A2C10: 568494FB 08004588 004EF201 40003F11 V..{..E..Nr.@.?.
+0F1A2C20: 5263AC10 1F60AC10 7F31D0DF 00A1003A Rc,..`,..1P_.!.:
+0F1A2C30: D1753030 02010104 07707269 76617465 Qu00.....private
+0F1A2C40: A0220204 1A00A2E8 02010002 01003014 "...."h......0.
+0F1A2C50: 3012060E 2B060104 0182CB21 01040103 0...+.....K!....
+0F1A2C60: 06000500 00 .....
+
+22:45:44.700 UTC Nov 27 2021 : IPv4 LES CEF : Fa4.320 None
+
+0F1A2C00: B0FAEBC7 A8620050 0zkG(b.P
+0F1A2C10: 568494FB 08004588 004E7FFB 40003F11 V..{..E..N.{@.?.
+0F1A2C20: EB72AC10 1F60AC10 5828E872 00A1003A kr,..`,.X(hr.!.:
+0F1A2C30: 2B393030 02010104 07707269 76617465 +900.....private
+0F1A2C40: A0220204 07836B18 02010002 01003014 "....k.......0.
+0F1A2C50: 3012060E 2B060104 0182CB21 01040103 0...+.....K!....
+0F1A2C60: 06000500 00 .....
+*/
+
+ /* we got the packet header */
+ /* The packet header is a line like: */
+ /* 16:09:37.171 ITA Mar 18 2016 : IPv4 LES CEF : Gi0/1 None */
+ parts = g_regex_split_simple(
+ "^(\\d{2}:\\d{2}:\\d{2}).(\\d+) (\\w+) (\\w+ \\d+ \\d+) :",
+ line, G_REGEX_CASELESS, 0);
+ if (parts && *(parts+1)) {
+ /* RE matched */
+ char* cp;
+ struct tm tm;
+ /* Date without msec, with timezone */
+ char* d1 = g_strdup_printf("%s %s %s", *(parts+1), *(parts+3), *(parts+4));
+ /* Date without msec, without timezone */
+ char* d2 = g_strdup_printf("%s %s", *(parts+1), *(parts+4));
+
+ memset(&tm, 0x0, sizeof(struct tm));
+
+ cp = ws_strptime_p(d1, "%H:%M:%S %Z %b %d %Y", &tm);
+ if (!cp || (*cp != '\0')) {
+ /* Time zone parse failed */
+ cp = ws_strptime_p(d2, "%H:%M:%S %b %d %Y", &tm);
+ if (!cp || (*cp != '\0')) {
+ /* Time parse failed, use now */
+ time_t t;
+ struct tm *tm2;
+
+ t = time(0);
+ tm2 = localtime(&t);
+ memcpy(&tm, tm2, sizeof(struct tm));
+ }
+ }
+ ws_strtou32(*(parts+2), NULL, pkt_usec);
+ *pkt_usec *= 1000;
+ *pkt_time = mktime(&tm);
+
+ g_strfreev(parts);
+ return CISCODUMP_PARSER_IN_HEADER;
+ }
+ g_strfreev(parts);
+
+ /* we got a line of the packet */
+ /* A line looks like */
+ /* <address>: <1st group> <2nd group> <3rd group> <4th group> <ascii representation> */
+ /* ABCDEF01: 01020304 05060708 090A0B0C 0D0E0F10 ................ */
+ /* Note that any of the 4 groups are optional and that a group can be 1 to 4 bytes long */
+ parts = g_regex_split_simple(
+ "^[\\dA-F]{8,8}:\\s+([\\dA-F]{2,8})\\s+([\\dA-F]{2,8}){0,1}\\s+([\\dA-F]{2,8}){0,1}\\s+([\\dA-F]{2,8}){0,1}.*",
+ line, G_REGEX_CASELESS, 0);
+
+ part = parts;
+ if (*part && *(part+1)) {
+ /* There is at least one match. Skip first string */
+ part++;
+ while(*part) {
+ /* RE matched */
+ if (strlen(*part) > 1) {
+ ws_hexstrtou32(*part, NULL, &value);
+ value = g_ntohl(value);
+ size = strlen(*part) / 2;
+ memcpy(packet + *offset, &value, size);
+ *offset += (uint32_t)size;
+ }
+ part++;
+ }
+ }
+ g_strfreev(parts);
+ return CISCODUMP_PARSER_IN_PACKET;
+}
+
+static int parse_line_ios_xe_16(uint8_t* packet, unsigned* offset, char* line)
+{
+ char** parts;
+ char** part;
+ uint32_t value;
+ size_t size;
+
+ if (strlen(line) <= 1) {
+ return CISCODUMP_PARSER_END_PACKET;
+ }
+
+ /*
+0
+ 0000: 00000C07 AC154C5D 3C259068 08004500 ......L]<%.h..E.
+ 0010: 00547549 40003F01 B582C0A8 4983C0A8 .TuI@.?.....I...
+ 0020: 46090800 B28E456E 00030000 00000000 F.....En........
+ 0030: 00000000 00000000 00000000 00000000 ................
+ 0040: 00000000 00000000 00000000 00000000 ................
+ 0050: 00000000 00000000 00000000 00000000 ................
+ 0060: 0000 ..
+
+1
+ 0000: 4C5D3C25 9068A49B CD904C74 08004500 L]<%.h....Lt..E.
+ 0010: 00547549 4000FF01 F581C0A8 4609C0A8 .TuI@.......F...
+ 0020: 49830000 BA8E456E 00030000 00000000 I.....En........
+ 0030: 00000000 00000000 00000000 00000000 ................
+ 0040: 00000000 00000000 00000000 00000000 ................
+ 0050: 00000000 00000000 00000000 00000000 ................
+ 0060: 0000 ..
+*/
+
+ /* we got the packet header */
+ /*0*/
+ if (g_regex_match_simple("^\\d+$", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
+ return CISCODUMP_PARSER_IN_HEADER;
+ }
+
+ /* we got a line of the packet */
+ /* A line looks like */
+ /* 0000: 00000C07 AC154C5D 3C259068 08004500 ......L]<%.h..E. */
+ /* ... */
+ /* 0060: 0000 .. */
+ /* Note that any of the 4 groups are optional and that a group can be 1 to 8 bytes long */
+ parts = g_regex_split_simple(
+ "^\\s+[0-9A-F]{4,4}: ([0-9A-F]{2,8}) ([0-9A-F]{2,8}){0,1} ([0-9A-F]{2,8}){0,1} ([0-9A-F]{2,8}){0,1}\\s+.*",
+ line, G_REGEX_CASELESS, 0);
+
+ part = parts;
+ if (*part && *(part+1)) {
+ /* There is at least one match. Skip first string */
+ part++;
+ while(*part) {
+ if (strlen(*part) > 1) {
+ ws_hexstrtou32(*part, NULL, &value);
+ value = g_ntohl(value);
+ size = strlen(*part) / 2;
+ memcpy(packet + *offset, &value, size);
+ *offset += (uint32_t)size;
+ }
+ part++;
+ }
+ }
+ g_strfreev(parts);
+
+ return CISCODUMP_PARSER_IN_PACKET;
+}
+
+static int parse_line_ios_xe_17(uint8_t* packet, unsigned* offset, char* line)
+{
+ char** parts;
+ char** part;
+ uint8_t value;
+
+ if (strlen(line) <= 1) {
+ return CISCODUMP_PARSER_END_PACKET;
+ }
+
+ /*
+0000 6c 5e 3b 88 5e 80 6c 5e 3b 88 5e 80 08 00 45 00 l^;.^.l^;.^...E.
+0010 00 28 9b 00 00 00 ff 06 0f 02 c0 a8 c8 01 c0 a8 .(..............
+0020 c8 7a 7e 77 00 31 4c ba ba a5 92 d3 0a eb 50 10 .z~w.1L.......P.
+0030 10 20 6a 20 00 00 00 00 00 00 00 00 . j ........
+*/
+
+ /* we got a line of the packet */
+ /* A line looks like */
+ /* 0000 6c 5e 3b 88 5e 80 6c 5e 3b 88 5e 80 08 00 45 00 l^;.^.l^;.^...E */
+ /* ... */
+ /* 0030 10 20 6a 20 00 00 00 00 00 00 00 00 . j ........ */
+ /* Note that any of the 4 groups are optional and that a group can be 1 to 8 bytes long */
+ parts = g_regex_split_simple(
+ "^\\s*[0-9A-F]{4,4} ([0-9A-F]{2}) ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1} ([0-9A-F]{2}){0,1}\\s+.*",
+ line, G_REGEX_CASELESS, 0);
+
+ part = parts;
+ if (*part && *(part+1)) {
+ /* There is at least one match. Skip first string */
+ part++;
+ while(*part) {
+ if (strlen(*part) > 1) {
+ ws_hexstrtou8(*part, NULL, &value);
+ memcpy(packet + *offset, &value, 1);
+ *offset += 1;
+ }
+ part++;
+ }
+ }
+ g_strfreev(parts);
+
+ return CISCODUMP_PARSER_IN_PACKET;
+}
+
+static int parse_line_asa(uint8_t* packet, unsigned* offset, char* line, uint32_t *current_max, time_t *pkt_time, uint32_t *pkt_usec)
+{
+ char** parts;
+ char** part;
+ uint16_t value;
+ size_t size;
+ uint32_t new_max;
+
+ if (strlen(line) <= 1) {
+ return CISCODUMP_PARSER_UNKNOWN;
+ }
+
+ /*
+4599 packets captured
+
+ 1: 20:40:01.108469 10.124.255.212 > 10.124.255.5 icmp: echo request
+0x0000 a453 0ef3 7fc0 74ad 98e5 0004 0800 4500 .S....t.......E.
+0x0010 0054 dc73 4000 4001 4a63 0a7c ffd4 0a7c .T.s@.@.Jc.|...|
+0x0020 ff05 0800 275b d0a4 0000 0000 0000 0000 ....'[..........
+0x0030 0000 0000 0000 0000 0000 0000 0000 0000 ................
+0x0040 0000 0000 0000 0000 0000 0000 0000 0000 ................
+0x0050 0000 0000 0000 0000 0000 0000 0000 0000 ................
+0x0060 0000 ..
+1 packet shown
+*/
+
+ /* Update count of available packets */
+ /* 4599 packets captured */
+ packets_captured_count_asa(line, &new_max, NULL);
+ if (new_max > 0) {
+ *current_max = new_max;
+ return CISCODUMP_PARSER_IN_HEADER;
+ }
+
+ /* we got the packet header */
+ /* 1: 20:40:01.108469 10.124.255.212 > 10.124.255.5 icmp: echo request */
+ parts = g_regex_split_simple("^\\s*\\d+:\\s+(\\d+):(\\d+):(\\d+)\\.(\\d+)\\s+", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0);
+ if (parts && *(parts+1)) {
+ /* RE matched */
+ struct tm *tm;
+ time_t t;
+
+ t = time(0);
+ tm = localtime(&t);
+ ws_strtoi32(*(parts+1), NULL, &(tm->tm_hour));
+ ws_strtoi32(*(parts+2), NULL, &(tm->tm_min));
+ ws_strtoi32(*(parts+3), NULL, &(tm->tm_sec));
+ ws_strtou32(*(parts+4), NULL, pkt_usec);
+ *pkt_time = mktime(tm);
+
+ g_strfreev(parts);
+ return CISCODUMP_PARSER_IN_HEADER;
+ }
+ g_strfreev(parts);
+
+ /* we got the packet tail */
+ /* 1 packet shown */
+ if (g_regex_match_simple("^\\s*1 packet shown.*$", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
+ return CISCODUMP_PARSER_END_PACKET;
+ }
+
+ /* we got a line of the packet */
+ /* A line looks like */
+ /* 0x<address>: <1st group> <...> <8th group> <5th group> <ascii representation> */
+ /* 0x0000 a453 0ef3 7fc0 74ad 98e5 0004 0800 4500 -.S....t.......E. */
+ /* 0x0060 0000 .. */
+ /* Note that any of the 8 groups are optional and that a group can be 1 to 8 bytes long */
+ parts = g_regex_split_simple(
+ "^0x[0-9A-F]{4,4}\\s+([0-9A-F]{2,4}) ([0-9A-F]{2,4}){0,1} ([0-9A-F]{2,4}){0,1} ([0-9A-F]{2,4}){0,1} ([0-9A-F]{2,4}){0,1} ([0-9A-F]{2,4}){0,1} ([0-9A-F]{2,4}){0,1} ([0-9A-F]{2,4}){0,1}\\s+.*",
+ line, G_REGEX_CASELESS, 0);
+
+ part = parts;
+ if (*part && *(part+1)) {
+ /* There is at least one match. Skip first string */
+ part++;
+ while(*part) {
+ if (strlen(*part) > 1) {
+ ws_hexstrtou16(*part, NULL, &value);
+ value = g_ntohs(value);
+ size = strlen(*part) / 2;
+ memcpy(packet + *offset, &value, size);
+ *offset += (uint32_t)size;
+ }
+ part++;
+ }
+ }
+ g_strfreev(parts);
+
+ return CISCODUMP_PARSER_IN_PACKET;
+}
+
+/* IOS: Reads response and parses buffer till prompt received */
+static int process_buffer_response_ios(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets)
+{
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint32_t read_packets = 1;
+ int status = CISCODUMP_PARSER_STARTING;
+ int loop_end = 0;
+ unsigned packet_size = 0;
+ time_t pkt_time = 0;
+ uint32_t pkt_usec = 0;
+ uint32_t len = 0;
+
+ /* Process response */
+ do {
+
+ loop_end = 0;
+ /* Read input till EOLN or prompt */
+ switch (ssh_channel_read_prompt(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
+ case READ_PROMPT_EOLN:
+ status = parse_line_ios(packet, &packet_size, line, status, &pkt_time, &pkt_usec);
+
+ if (status == CISCODUMP_PARSER_END_PACKET) {
+ ws_debug("Read packet %d\n", read_packets);
+ if (read_packets > *processed_packets) {
+ int err;
+ uint64_t bytes_written;
+
+ ws_debug("Exporting packet %d\n", *processed_packets);
+ /* dump the packet to the pcap file */
+ if (!libpcap_write_packet(fp,
+ pkt_time, pkt_usec,
+ packet_size, packet_size, packet, &bytes_written, &err)) {
+ ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
+ break;
+ }
+ fflush(fp);
+ ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
+ (*processed_packets)++;
+ }
+ packet_size = 0;
+ read_packets++;
+ }
+ break;
+ case READ_PROMPT_PROMPT:
+ ws_debug("Prompt found");
+ loop_end = 1;
+ break;
+ default:
+ /* We do not have better solution for that cases */
+ if (extcap_end_application) {
+ /* End by signal causes timeout so we decrease priority */
+ ws_debug("Timeout or response was too long\n");
+ } else {
+ ws_warning("Timeout or response was too long\n");
+ }
+ return false;
+ }
+ len = 0;
+ ws_debug("loop end detection %d %d %d %d", extcap_end_application, loop_end, *processed_packets, count);
+ } while ((!extcap_end_application) && (!loop_end) && (*processed_packets < count));
+
+ return true;
+}
+
+/* IOS: Queries buffer content and reads it */
+static void ssh_loop_read_ios(ssh_channel channel, FILE* fp, const uint32_t count)
+{
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint8_t* packet;
+ uint32_t processed_packets = 0;
+ bool running = true;
+ uint32_t current_max = 0;
+ uint32_t new_max;
+
+ /* This is big enough to put on the heap */
+ packet = (uint8_t*)g_malloc(PACKET_MAX_SIZE);
+
+ do {
+ uint32_t len = 0;
+
+ /* Query count of available packets in buffer */
+ if (ssh_channel_printf(channel, "show monitor capture buffer %s parameters\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE) {
+ g_free(packet);
+ return;
+ }
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ g_free(packet);
+ return;
+ }
+ ws_debug("Read: %s", line);
+ packets_captured_count_ios(line, &new_max, &running);
+ ws_debug("Max counts %d %d", current_max, new_max);
+ if (new_max == current_max) {
+ /* There is no change in count of available packets, repeat the loop */
+ continue;
+ } else if (new_max < current_max) {
+ /* Buffer was cleared, stop */
+ g_free(packet);
+ return;
+ }
+ current_max = new_max;
+ ws_debug("New packet count %d\n", current_max);
+
+ /* Dump buffer */
+ if (ssh_channel_printf(channel, "show monitor capture buffer %s dump\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE) {
+ g_free(packet);
+ return;
+ }
+
+ /* Process buffer */
+ if (!process_buffer_response_ios(channel, packet, fp, count, &processed_packets)) {
+ g_free(packet);
+ return;
+ }
+ } while (!extcap_end_application && running && (processed_packets < count));
+
+ g_free(packet);
+
+ /* Discard any subsequent messages */
+ read_output_bytes_any(channel, -1, NULL);
+}
+
+/* IOS-XE 16: Reads response and parses buffer till prompt received */
+static int process_buffer_response_ios_xe_16(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets)
+{
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint32_t read_packets = 1;
+ int status = CISCODUMP_PARSER_STARTING;
+ int loop_end = 0;
+ unsigned packet_size = 0;
+ uint32_t len = 0;
+
+ /* Process response */
+ do {
+ loop_end = 0;
+ /* Read input till EOLN or prompt */
+ switch (ssh_channel_read_prompt(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
+ case READ_PROMPT_EOLN:
+ status = parse_line_ios_xe_16(packet, &packet_size, line);
+
+ if (status == CISCODUMP_PARSER_END_PACKET) {
+ ws_debug("Read packet %d\n", read_packets);
+ if (read_packets > *processed_packets) {
+ int err;
+ int64_t cur_time = g_get_real_time();
+ uint64_t bytes_written = 0;
+
+ ws_debug("Exporting packet %d\n", *processed_packets);
+ /* dump the packet to the pcap file */
+ if (!libpcap_write_packet(fp,
+ (uint32_t)(cur_time / G_USEC_PER_SEC), (uint32_t)(cur_time % G_USEC_PER_SEC),
+ packet_size, packet_size, packet, &bytes_written, &err)) {
+ ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
+ break;
+ }
+ fflush(fp);
+ ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
+ (*processed_packets)++;
+ }
+ packet_size = 0;
+ read_packets++;
+ }
+ break;
+ case READ_PROMPT_PROMPT:
+ ws_debug("Prompt found");
+ loop_end = 1;
+ break;
+ default:
+ /* We do not have better solution for that cases */
+ if (extcap_end_application) {
+ /* End by signal causes timeout so we decrease priority */
+ ws_debug("Timeout or response was too long\n");
+ } else {
+ ws_warning("Timeout or response was too long\n");
+ }
+ return false;
+ }
+ len = 0;
+ ws_debug("loop end detection %d %d %d %d", extcap_end_application, loop_end, *processed_packets, count);
+ } while ((!extcap_end_application) && (!loop_end) && (*processed_packets < count));
+
+ return true;
+}
+
+/* IOS-XE 17: Reads response and parses buffer till prompt received */
+static int process_buffer_response_ios_xe_17(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets)
+{
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint32_t read_packets = 1;
+ int status = CISCODUMP_PARSER_STARTING;
+ int loop_end = 0;
+ unsigned packet_size = 0;
+ uint32_t len = 0;
+
+ /* Process response */
+ do {
+ loop_end = 0;
+ /* Read input till EOLN */
+ switch (ssh_channel_read_line_timeout(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
+ case READ_PROMPT_EOLN:
+ /* Starting message? Ignore... */
+ if (NULL != g_strstr_len(line, -1, "Starting the packet display")) {
+ break;
+ }
+
+ /* End of dump message? End loop. */
+ if (NULL != g_strstr_len(line, -1, "Started capture point :")) {
+ loop_end = 1;
+ break;
+ }
+
+ status = parse_line_ios_xe_17(packet, &packet_size, line);
+ ws_debug("Packet offset %d\n", packet_size);
+
+ if ((status == CISCODUMP_PARSER_END_PACKET) && (packet_size > 0)) {
+ ws_debug("Read packet %d\n", read_packets);
+ if (read_packets > *processed_packets) {
+ int err;
+ int64_t cur_time = g_get_real_time();
+ uint64_t bytes_written;
+
+ ws_debug("Exporting packet %d\n", *processed_packets);
+ /* dump the packet to the pcap file */
+ if (!libpcap_write_packet(fp,
+ (uint32_t)(cur_time / G_USEC_PER_SEC), (uint32_t)(cur_time % G_USEC_PER_SEC),
+ packet_size, packet_size, packet, &bytes_written, &err)) {
+ ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
+ break;
+ }
+ fflush(fp);
+ ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
+ (*processed_packets)++;
+ }
+ packet_size = 0;
+ read_packets++;
+ }
+ break;
+ case READ_LINE_TIMEOUT:
+ /* Timeout is OK during reading of IOS XE 17 buffer */
+ break;
+ default:
+ /* We do not have better solution for that cases */
+ ws_warning("Error or response was too long\n");
+ return false;
+ }
+ len = 0;
+ ws_debug("loop end detection %d %d %d %d", extcap_end_application, loop_end, *processed_packets, count);
+ } while ((!extcap_end_application) && (!loop_end) && (*processed_packets < count));
+
+ return true;
+}
+
+/* IOS-XE 16: Queries buffer content and reads it */
+static void ssh_loop_read_ios_xe_16(ssh_channel channel, FILE* fp, const uint32_t count)
+{
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint8_t* packet;
+ uint32_t processed_packets = 0;
+ bool running = true;
+ uint32_t current_max = 0;
+ uint32_t new_max;
+
+ /* This is big enough to put on the heap */
+ packet = (uint8_t*)g_malloc(PACKET_MAX_SIZE);
+
+ do {
+ uint32_t len = 0;
+
+ /* Query count of available packets in buffer */
+ if (ssh_channel_printf(channel, "show monitor capture %s buffer | inc packets in buf\nshow monitor capture %s | inc Status :\n", WIRESHARK_CAPTURE, WIRESHARK_CAPTURE) == EXIT_FAILURE) {
+ g_free(packet);
+ return;
+ }
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ g_free(packet);
+ return;
+ }
+ ws_debug("Read: %s", line);
+ packets_captured_count_ios_xe_16(line, &new_max, &running);
+ ws_debug("Max counts %d %d", current_max, new_max);
+ if (new_max == current_max) {
+ /* There is no change in count of available packets, repeat the loop */
+ continue;
+ } else if (new_max < current_max) {
+ /* Buffer was cleared, stop */
+ g_free(packet);
+ return;
+ }
+ current_max = new_max;
+ ws_debug("New packet count %d\n", current_max);
+
+ /* Dump buffer */
+ if (ssh_channel_printf(channel, "show monitor capture %s buffer dump\n", WIRESHARK_CAPTURE) == EXIT_FAILURE) {
+ g_free(packet);
+ return;
+ }
+
+ /* Process buffer */
+ if (!process_buffer_response_ios_xe_16(channel, packet, fp, count, &processed_packets)) {
+ g_free(packet);
+ return;
+ }
+ } while (!extcap_end_application && running && (processed_packets < count));
+
+ g_free(packet);
+
+ /* Discard any subsequent messages */
+ read_output_bytes_any(channel, -1, NULL);
+}
+
+/* IOS-XE 17: Queries buffer content and reads it */
+static void ssh_loop_read_ios_xe_17(ssh_channel channel, FILE* fp, const uint32_t count)
+{
+ uint8_t* packet;
+ uint32_t processed_packets = 0;
+ bool running = true;
+
+ /* This is big enough to put on the heap */
+ packet = (uint8_t*)g_malloc(PACKET_MAX_SIZE);
+
+ do {
+ //uint32_t len = 0;
+
+ /* Process buffer */
+ if (!process_buffer_response_ios_xe_17(channel, packet, fp, count, &processed_packets)) {
+ g_free(packet);
+ return;
+ }
+ if (extcap_end_application && send_output_quit) {
+ ws_debug("Terminating the output\n");
+ ssh_channel_printf(channel, "\n\x1e\n");
+ }
+ } while (!extcap_end_application && running && (processed_packets < count));
+
+ g_free(packet);
+
+ /* Discard any subsequent messages */
+ read_output_bytes_any(channel, -1, NULL);
+}
+
+/* ASA: Reads response and parses buffer till prompt end of packet received */
+static int process_buffer_response_asa(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets, uint32_t *current_max)
+{
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint32_t read_packets = 1;
+ int status = CISCODUMP_PARSER_STARTING;
+ int loop_end = 0;
+ unsigned packet_size = 0;
+
+ do {
+ time_t pkt_time = 0;
+ uint32_t pkt_usec = 0;
+ uint32_t len = 0;
+
+ /* Dump buffer */
+ if (ssh_channel_printf(channel, "show cap %s packet-number %ld dump\n", WIRESHARK_CAPTURE, (*processed_packets)+1) == EXIT_FAILURE) {
+ return false;
+ }
+
+ /* Process response */
+ do {
+ loop_end = 0;
+ /* Read input till EOLN or prompt */
+ switch (ssh_channel_read_prompt(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
+ case READ_PROMPT_EOLN:
+ status = parse_line_asa(packet, &packet_size, line, current_max, &pkt_time, &pkt_usec);
+
+ if (status == CISCODUMP_PARSER_END_PACKET) {
+ ws_debug("Read packet %d\n", read_packets);
+ int err;
+ uint64_t bytes_written;
+
+ ws_debug("Exporting packet %d\n", *processed_packets);
+ /* dump the packet to the pcap file */
+ if (!libpcap_write_packet(fp,
+ pkt_time, pkt_usec,
+ packet_size, packet_size, packet, &bytes_written, &err)) {
+ ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
+ break;
+ }
+ fflush(fp);
+ ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
+ (*processed_packets)++;
+ packet_size = 0;
+ read_packets++;
+ loop_end = 1;
+ }
+ break;
+ case READ_PROMPT_PROMPT:
+ ws_debug("Prompt found");
+ loop_end = 1;
+ break;
+ default:
+ /* We do not have better solution for that cases */
+ if (extcap_end_application) {
+ /* End by signal causes timeout so we decrease priority */
+ ws_debug("Timeout or response was too long\n");
+ } else {
+ ws_warning("Timeout or response was too long\n");
+ }
+ return false;
+ }
+ len = 0;
+ ws_debug("loop end detection1 %d %d", *processed_packets, count);
+ } while (!extcap_end_application && !loop_end);
+ ws_debug("loop end detection2 %d %d %d", extcap_end_application, *processed_packets, count);
+ } while (!extcap_end_application && (*processed_packets < *current_max) && ((*processed_packets < count)));
+
+ return true;
+}
+
+/* ASA: Queries buffer content and reads it */
+static void ssh_loop_read_asa(ssh_channel channel, FILE* fp, const uint32_t count)
+{
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint8_t* packet;
+ uint32_t processed_packets = 0;
+ uint32_t current_max = 0;
+ bool running = true;
+ uint32_t new_max;
+
+ /* This is big enough to put on the heap */
+ packet = (uint8_t*)g_malloc(PACKET_MAX_SIZE);
+
+ do {
+ uint32_t len = 0;
+
+ /* Query count of available packets in buffer */
+ if (ssh_channel_printf(channel, "show cap %s packet-number 0 | inc packets captured\nshow cap | inc %s\n", WIRESHARK_CAPTURE, WIRESHARK_CAPTURE) == EXIT_FAILURE) {
+ g_free(packet);
+ return;
+ }
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ g_free(packet);
+ return;
+ }
+ ws_debug("Read: %s", line);
+ packets_captured_count_asa(line, &new_max, &running);
+ ws_debug("Max counts %d %d", current_max, new_max);
+ if (new_max == current_max) {
+ /* There is no change in count of available packets, repeat the loop */
+ continue;
+ } else if (new_max < current_max) {
+ /* Buffer was cleared, stop */
+ g_free(packet);
+ return;
+ }
+ current_max = new_max;
+ ws_debug("New packet count %d\n", current_max);
+
+ /* Process buffer */
+ if (!process_buffer_response_asa(channel, packet, fp, count, &processed_packets, &current_max)) {
+ g_free(packet);
+ return;
+ }
+ } while (!extcap_end_application && running && (processed_packets < count));
+
+ g_free(packet);
+
+ /* Discard any subsequent messages */
+ read_output_bytes_any(channel, -1, NULL);
+}
+
+
+static void ssh_loop_read(ssh_channel channel, FILE* fp, const uint32_t count _U_, CISCO_SW_TYPE sw_type)
+{
+ ws_debug("Starting reading loop");
+ switch (sw_type) {
+ case CISCO_IOS:
+ ssh_loop_read_ios(channel, fp, count);
+ break;
+ case CISCO_IOS_XE_16:
+ ssh_loop_read_ios_xe_16(channel, fp, count);
+ break;
+ case CISCO_IOS_XE_17:
+ ssh_loop_read_ios_xe_17(channel, fp, count);
+ break;
+ case CISCO_ASA:
+ ssh_loop_read_asa(channel, fp, count);
+ break;
+ case CISCO_UNKNOWN:
+ break;
+ }
+ ws_debug("Reading loop finished");
+}
+
+static int detect_host_prompt(ssh_channel channel)
+{
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ int len = 0;
+ char prompt_2[SSH_READ_BLOCK_SIZE + 1];
+
+ /* Discard any login message */
+ if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
+ return EXIT_FAILURE;
+
+ if (ssh_channel_printf(channel, "\n") == EXIT_FAILURE)
+ return EXIT_FAILURE;
+
+ /* Check if there is any response to empty line */
+ switch (ssh_channel_read_line_timeout(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
+ case READ_LINE_EOLN:
+ break;
+ default:
+ return EXIT_FAILURE;
+ }
+
+ if (ssh_channel_printf(channel, "\n") == EXIT_FAILURE)
+ return EXIT_FAILURE;
+
+ /* Read prompt_str and level char */
+ switch (ssh_channel_read_line_timeout(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
+ case READ_LINE_EOLN:
+ break;
+ default:
+ return EXIT_FAILURE;
+ }
+ if (len > 0) {
+ g_strlcpy(prompt_str, line, SSH_READ_BLOCK_SIZE + 1);
+
+ /* Is there hashtag at the end => enabled mode? */
+ if (prompt_str[strlen(prompt_str)-1] != '#') {
+ /* Is there hashtag and space (ASA) at the end => enabled mode? */
+ if ((prompt_str[strlen(prompt_str)-2] != '#') || (prompt_str[strlen(prompt_str)-1] != ' ')) {
+ return EXIT_FAILURE;
+ }
+ }
+ prompt_len = (int32_t)strlen(prompt_str);
+ } else {
+ return EXIT_FAILURE;
+ }
+
+ if (ssh_channel_printf(channel, "\n") == EXIT_FAILURE)
+ return EXIT_FAILURE;
+
+ /* Read prompt_str and level char again */
+ switch (ssh_channel_read_line_timeout(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
+ case READ_LINE_EOLN:
+ break;
+ default:
+ return EXIT_FAILURE;
+ }
+ if (len > 0) {
+ g_strlcpy(prompt_2, line, SSH_READ_BLOCK_SIZE + 1);
+ /* Does second prompt_str match first one? */
+ if (0 == g_strcmp0(prompt_str, prompt_2)) {
+ ws_debug("Detected prompt %s", prompt_str);
+ return true;
+ }
+ }
+
+ return EXIT_FAILURE;
+}
+
+static int check_ios_version(ssh_channel channel, CISCO_SW_TYPE *sw_type)
+{
+ char* cmdline_version = "show version | include Version\n";
+ const char* msg_ios = "Cisco IOS Software";
+ const char* msg_ios_xe = "Cisco IOS XE Software";
+ const char* msg_asa = "Cisco Adaptive Security Appliance Software";
+ const char* msg_version = "Version ";
+ char version[255];
+ int sw_major = 0;
+ int sw_minor = 0;
+ char* cur;
+
+ memset(version, 0x0, 255);
+
+ /* Discard any login message */
+ if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
+ return false;
+
+ if (ssh_channel_write(channel, cmdline_version, (uint32_t)strlen(cmdline_version)) == SSH_ERROR)
+ return false;
+ if (read_output_bytes(channel, 255, version) == EXIT_FAILURE)
+ return false;
+
+ /* Discard any subsequent text */
+ if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
+ return false;
+
+ /* We should check IOS XE first as its version contains IOS string too */
+ cur = g_strstr_len(version, strlen(version), msg_ios_xe);
+ if (cur) {
+ *sw_type = CISCO_IOS_XE_16;
+ cur += strlen(msg_ios_xe);
+ } else {
+ cur = g_strstr_len(version, strlen(version), msg_ios);
+ if (cur) {
+ *sw_type = CISCO_IOS;
+ cur += strlen(msg_ios);
+ } else {
+ cur = g_strstr_len(version, strlen(version), msg_asa);
+ if (cur) {
+ *sw_type = CISCO_ASA;
+ cur += strlen(msg_asa);
+ }
+ }
+ }
+
+ if (*sw_type != CISCO_UNKNOWN && cur) {
+ cur = g_strstr_len(cur, 255-strlen(cur), msg_version);
+ if (cur) {
+ cur += strlen(msg_version);
+ if (sscanf(cur, "%u.%u", &sw_major, &sw_minor) != 2)
+ return false;
+
+ switch (*sw_type) {
+ case CISCO_IOS:
+ ws_debug("Current IOS version: %u.%u", sw_major, sw_minor);
+ if ((sw_major > MINIMUM_IOS_MAJOR) || (sw_major == MINIMUM_IOS_MAJOR && sw_minor >= MINIMUM_IOS_MINOR)) {
+ return true;
+ }
+ break;
+ case CISCO_IOS_XE_16:
+ ws_debug("Current IOS XE version: %u.%u", sw_major, sw_minor);
+ if ((sw_major > MINIMUM_IOS_XE_MAJOR_17) || (sw_major == MINIMUM_IOS_XE_MAJOR_17 && sw_minor >= MINIMUM_IOS_XE_MINOR_17)) {
+ *sw_type = CISCO_IOS_XE_17;
+ return true;
+ }
+ if ((sw_major > MINIMUM_IOS_XE_MAJOR_16) || (sw_major == MINIMUM_IOS_XE_MAJOR_16 && sw_minor >= MINIMUM_IOS_XE_MINOR_16)) {
+ *sw_type = CISCO_IOS_XE_16;
+ return true;
+ }
+ break;
+ case CISCO_ASA:
+ ws_debug("Current ASA version: %u.%u", sw_major, sw_minor);
+ if ((sw_major > MINIMUM_ASA_MAJOR) || (sw_major == MINIMUM_ASA_MAJOR && sw_minor >= MINIMUM_ASA_MINOR)) {
+ return true;
+ }
+ break;
+ default:
+ return false;
+ }
+ ws_warning("Recognized software type, but minimal version requirements were not met\n");
+ return false;
+ } else {
+ ws_warning("Recognized software type %d, but unrecognized version\n", *sw_type);
+ }
+ } else {
+ ws_warning("Unrecognized type of control software.");
+ }
+
+ return false;
+}
+
+static bool run_capture_ios(ssh_channel channel, const char* iface, const char* cfilter, const uint32_t count)
+{
+ char* cmdline = NULL;
+ int ret = 0;
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint32_t len;
+ char* iface_copy = g_strdup(iface);
+ char* iface_one;
+ char* str = NULL;
+ int wscp_cnt = 1;
+ char* wscp_str = NULL;
+
+ if (ssh_channel_printf(channel, "terminal length 0\n") == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "monitor capture buffer %s max-size 9500\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (count > 0) {
+ if (ssh_channel_printf(channel, "monitor capture buffer %s limit packet-count %u\n", WIRESHARK_CAPTURE_BUFFER, count) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+ }
+
+ if (cfilter) {
+ char* multiline_filter;
+ char* chr;
+
+ if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
+ goto error;
+
+ if (ssh_channel_printf(channel, "ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
+ goto error;
+
+ multiline_filter = g_strdup(cfilter);
+ chr = multiline_filter;
+ while((chr = g_strstr_len(chr, strlen(chr), ",")) != NULL) {
+ chr[0] = '\n';
+ ws_debug("Splitting filter into multiline");
+ }
+ ret = ssh_channel_write(channel, multiline_filter, (uint32_t)strlen(multiline_filter));
+ g_free(multiline_filter);
+ if (ret == SSH_ERROR)
+ goto error;
+
+ if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "monitor capture buffer %s filter access-list %s\n",
+ WIRESHARK_CAPTURE_BUFFER, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+ }
+
+ wscp_cnt = 1;
+ for (str = iface_copy; ; str = NULL) {
+ iface_one = strtok(str, ",");
+ if (iface_one == NULL)
+ break;
+
+ wscp_str = g_strdup_printf("%s_%d", WIRESHARK_CAPTURE_POINT, wscp_cnt);
+ wscp_cnt++;
+
+ if (0 == g_strcmp0(iface_one, "process-switched")) {
+ cmdline = g_strdup_printf("monitor capture point ip process-switched %s both", wscp_str);
+ } else if (0 == g_strcmp0(iface_one, "from-us")) {
+ cmdline = g_strdup_printf("monitor capture point ip process-switched %s from-us", wscp_str);
+ } else {
+ cmdline = g_strdup_printf("monitor capture point ip cef %s %s both", wscp_str, iface_one);
+ }
+
+ if (ssh_channel_printf(channel, "%s\n", cmdline) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "monitor capture point associate %s %s \n", wscp_str,
+ WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ g_free(cmdline);
+ cmdline = NULL;
+ }
+
+ wscp_cnt = 1;
+ for (str = iface_copy; ; str = NULL) {
+ iface_one = strtok(str, ",");
+ if (iface_one == NULL)
+ break;
+
+ wscp_str = g_strdup_printf("%s_%d", WIRESHARK_CAPTURE_POINT, wscp_cnt);
+ wscp_cnt++;
+
+ if (ssh_channel_printf(channel, "monitor capture point start %s\n", wscp_str) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ g_free(wscp_str);
+ wscp_str = NULL;
+ }
+
+ g_free(iface_copy);
+ return true;
+error:
+ g_free(wscp_str);
+ g_free(iface_copy);
+ g_free(cmdline);
+ ws_warning("Error running ssh remote command");
+
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ return false;
+}
+
+static bool run_capture_ios_xe_16(ssh_channel channel, const char* iface, const char* cfilter, const uint32_t count)
+{
+ int ret = 0;
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint32_t len;
+ char* iface_copy = g_strdup(iface);
+ char* iface_one;
+ char* str = NULL;
+
+ if (ssh_channel_printf(channel, "terminal length 0\n") == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "monitor capture %s limit packet-len 9500\n", WIRESHARK_CAPTURE) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (count > 0) {
+ if (ssh_channel_printf(channel, "monitor capture %s limit packets %u\n", WIRESHARK_CAPTURE, count) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+ }
+
+ if (cfilter) {
+ char* multiline_filter;
+ char* chr;
+
+ if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
+ goto error;
+
+ if (ssh_channel_printf(channel, "ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
+ goto error;
+
+ multiline_filter = g_strdup(cfilter);
+ chr = multiline_filter;
+ while((chr = g_strstr_len(chr, strlen(chr), ",")) != NULL) {
+ chr[0] = '\n';
+ ws_debug("Splitting filter into multiline");
+ }
+ ret = ssh_channel_write(channel, multiline_filter, (uint32_t)strlen(multiline_filter));
+ g_free(multiline_filter);
+ if (ret == SSH_ERROR)
+ goto error;
+
+ if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "monitor capture %s access-list %s\n",
+ WIRESHARK_CAPTURE, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
+ goto error;
+ } else {
+ if (ssh_channel_printf(channel, "monitor capture %s match any\n",
+ WIRESHARK_CAPTURE) == EXIT_FAILURE)
+ goto error;
+ }
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ for (str = iface_copy; ; str = NULL) {
+ iface_one = strtok(str, ",");
+ if (iface_one == NULL)
+ break;
+
+ if (0 == g_strcmp0(iface_one, "control-plane")) {
+ if (ssh_channel_printf(channel, "monitor capture %s control-plane both\n", WIRESHARK_CAPTURE
+ ) == EXIT_FAILURE)
+ goto error;
+ } else {
+ if (ssh_channel_printf(channel, "monitor capture %s interface %s both\n", WIRESHARK_CAPTURE,
+ iface_one) == EXIT_FAILURE)
+ goto error;
+ }
+ }
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "monitor capture %s start\n", WIRESHARK_CAPTURE) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE,
+ "(Capture is not Supported|Unable to activate Capture)")
+ ) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ g_free(iface_copy);
+ return true;
+error:
+ g_free(iface_copy);
+ ws_warning("Error running ssh remote command");
+
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ return false;
+}
+
+static bool run_capture_ios_xe_17(ssh_channel channel, const char* iface, const char* cfilter, const uint32_t count)
+{
+ int ret = 0;
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint32_t len;
+ char* iface_copy = g_strdup(iface);
+ char* iface_one;
+ char* str = NULL;
+
+ if (ssh_channel_printf(channel, "terminal length 0\n") == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "monitor capture %s limit packet-len 9500\n", WIRESHARK_CAPTURE) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (count > 0) {
+ if (ssh_channel_printf(channel, "monitor capture %s limit packets %u\n", WIRESHARK_CAPTURE, count) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+ }
+
+ if (cfilter) {
+ char* multiline_filter;
+ char* chr;
+
+ if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
+ goto error;
+
+ if (ssh_channel_printf(channel, "ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
+ goto error;
+
+ multiline_filter = g_strdup(cfilter);
+ chr = multiline_filter;
+ while((chr = g_strstr_len(chr, strlen(chr), ",")) != NULL) {
+ chr[0] = '\n';
+ ws_debug("Splitting filter into multiline");
+ }
+ ret = ssh_channel_write(channel, multiline_filter, (uint32_t)strlen(multiline_filter));
+ g_free(multiline_filter);
+ if (ret == SSH_ERROR)
+ goto error;
+
+ if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "monitor capture %s access-list %s\n",
+ WIRESHARK_CAPTURE, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
+ goto error;
+ } else {
+ if (ssh_channel_printf(channel, "monitor capture %s match any\n",
+ WIRESHARK_CAPTURE) == EXIT_FAILURE)
+ goto error;
+ }
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ for (str = iface_copy; ; str = NULL) {
+ iface_one = strtok(str, ",");
+ if (iface_one == NULL)
+ break;
+
+ if (0 == g_strcmp0(iface_one, "control-plane")) {
+ if (ssh_channel_printf(channel, "monitor capture %s control-plane both\n", WIRESHARK_CAPTURE
+ ) == EXIT_FAILURE)
+ goto error;
+ } else {
+ if (ssh_channel_printf(channel, "monitor capture %s interface %s both\n", WIRESHARK_CAPTURE,
+ iface_one) == EXIT_FAILURE)
+ goto error;
+ }
+ }
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "monitor capture %s start display dump\n", WIRESHARK_CAPTURE) == EXIT_FAILURE)
+ goto error;
+
+/*
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE,
+ "(Capture is not Supported|Unable to activate Capture)")
+ ) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+*/
+
+ g_free(iface_copy);
+ return true;
+error:
+ g_free(iface_copy);
+ ws_warning("Error running ssh remote command");
+
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ return false;
+}
+
+static bool run_capture_asa(ssh_channel channel, const char* iface, const char* cfilter)
+{
+ char* cmdline = NULL;
+ char line[SSH_READ_BLOCK_SIZE + 1];
+ uint32_t len;
+ char *sep;
+ bool process_filter = true;
+ char* iface_copy = g_strdup(iface);
+ char* iface_one;
+ char* str = NULL;
+
+ if (ssh_channel_printf(channel, "terminal pager 0\n") == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ for (str = iface_copy; ; str = NULL) {
+ iface_one = strtok(str, ",");
+ if (iface_one == NULL)
+ break;
+
+ if (0 == g_strcmp0(iface_one, "asp-drop")) {
+ /* asp-drop: asp-drop: capture %s type asp-drop all packet-length 9216 !INCLUDE-DECRYPTED
+ */
+ cmdline = g_strdup_printf("capture %s type asp-drop all packet-length 9216", WIRESHARK_CAPTURE);
+ } else if (NULL != (sep = g_strstr_len(iface_one, -1, "---"))) {
+ /* Interface type separator found. We support:
+ * isakmp---ifname: capture %s type isakmp packet-length 32810 interface %s
+ * // webvpn---ifname: capture %s type webvpn user %s !NO FILTER !INCLUDE-DECRYPTED
+ * lacp---ifname: capture %s type lacp interface %s packet-length 9216 !NO FILTER !INCLUDE-DECRYPTED
+ * tls-proxy---ifname: capture %s type tls-proxy packet-length 9216 interface %s
+ * inline-tag---ifname: capture %s type inline-tag packet-length 9216 interface %s
+ * raw-data---ifname: capture %s type rawdata packet-length 9216 interface %s
+ *
+ * We support /decrypted for some of it:
+ * isakmp/decrypted---ifname
+ * tls-proxy/decrypted---ifname
+ * inline-tag/decrypted---ifname
+ * raw-data/decrypted---ifname
+ */
+ char* ifname = sep+3;
+
+ if (strstr(iface_one, "isakmp")) {
+ if (strstr(iface_one, "/decrypted")) {
+ cmdline = g_strdup_printf("capture %s type isakmp include-decrypted packet-length 32810 interface %s", WIRESHARK_CAPTURE, ifname);
+ } else {
+ cmdline = g_strdup_printf("capture %s type isakmp packet-length 32810 interface %s", WIRESHARK_CAPTURE, ifname);
+ }
+ /* Completelly different output
+ } else if (strstr(iface_one, "webvpn")) {
+ cmdline = g_strdup_printf("capture %s type webvpn user %s", WIRESHARK_CAPTURE, ifname);
+ process_filter = false;
+ */
+ } else if (strstr(iface_one, "lacp")) {
+ cmdline = g_strdup_printf("capture %s type lacp interface %s packet-length 9216", WIRESHARK_CAPTURE, ifname);
+ process_filter = false;
+ } else if (strstr(iface_one, "tls-proxy")) {
+ if (strstr(iface_one, "/decrypted")) {
+ cmdline = g_strdup_printf("capture %s type tls-proxy include-decrypted packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
+ } else {
+ cmdline = g_strdup_printf("capture %s type tls-proxy packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
+ }
+ } else if (strstr(iface_one, "inline-tag")) {
+ if (strstr(iface_one, "/decrypted")) {
+ cmdline = g_strdup_printf("capture %s type inline-tag include-decrypted packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
+ } else {
+ cmdline = g_strdup_printf("capture %s type inline-tag packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
+ }
+ } else if (strstr(iface_one, "raw-data")) {
+ if (strstr(iface_one, "/decrypted")) {
+ cmdline = g_strdup_printf("capture %s type raw-data include-decrypted packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
+ } else {
+ cmdline = g_strdup_printf("capture %s type raw-data packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
+ }
+ } else {
+ ws_warning("Unknown interface type : %s", iface_one);
+ goto error;
+ }
+ } else {
+ /* Just interface name */
+ cmdline = g_strdup_printf("capture %s type raw-data packet-length 9216 interface %s", WIRESHARK_CAPTURE, iface_one);
+ }
+
+ if (ssh_channel_printf(channel, "%s\n", cmdline) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ g_free(cmdline);
+ cmdline = NULL;
+ }
+
+ if (process_filter && cfilter) {
+ char* multiline_filter;
+ char* chr;
+ char* start;
+
+ if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
+ goto error;
+
+ multiline_filter = g_strdup(cfilter);
+ start = multiline_filter;
+ while((chr = g_strstr_len(start, strlen(start), ",")) != NULL) {
+ chr[0] = '\0';
+ ws_debug("Splitting filter into multiline");
+ if (ssh_channel_printf(channel, "access-list %s %s\n", WIRESHARK_CAPTURE_ACCESSLIST, start) == EXIT_FAILURE)
+ goto error;
+ start = chr+1;
+ }
+
+ if (ssh_channel_printf(channel, "access-list %s %s\n", WIRESHARK_CAPTURE_ACCESSLIST, start) == EXIT_FAILURE)
+ goto error;
+
+ if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+
+ if (ssh_channel_printf(channel, "capture %s access-list %s\n", WIRESHARK_CAPTURE, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
+ goto error;
+
+ if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
+ ws_warning("Received response: %s", crtoln(line));
+ goto error;
+ }
+ }
+
+ g_free(iface_copy);
+ return true;
+error:
+ g_free(iface_copy);
+ g_free(cmdline);
+ ws_warning("Error running ssh remote command");
+
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ return false;
+}
+
+static ssh_channel open_channel(ssh_session sshs)
+{
+ ssh_channel channel;
+
+ channel = ssh_channel_new(sshs);
+ if (!channel)
+ return NULL;
+
+ if (ssh_channel_open_session(channel) != SSH_OK)
+ goto error;
+
+ if (ssh_channel_request_pty(channel) != SSH_OK)
+ goto error;
+
+ if (ssh_channel_change_pty_size(channel, 80, 24) != SSH_OK)
+ goto error;
+
+ if (ssh_channel_request_shell(channel) != SSH_OK)
+ goto error;
+
+ return channel;
+
+error:
+ ws_warning("Error running ssh remote command");
+
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ return NULL;
+}
+
+static bool run_capture(ssh_channel channel, const char* iface, const char* cfilter, const uint32_t count, CISCO_SW_TYPE sw_type)
+{
+ switch (sw_type) {
+ case CISCO_IOS:
+ return run_capture_ios(channel, iface, cfilter, count);
+ case CISCO_IOS_XE_16:
+ return run_capture_ios_xe_16(channel, iface, cfilter, count);
+ case CISCO_IOS_XE_17:
+ return run_capture_ios_xe_17(channel, iface, cfilter, count);
+ case CISCO_ASA:
+ return run_capture_asa(channel, iface, cfilter);
+ case CISCO_UNKNOWN:
+ ws_warning("Unsupported cisco software. It will not collect any data most probably!");
+ return false;
+ }
+
+ return false;
+}
+
+static int ssh_open_remote_connection(const ssh_params_t* ssh_params, const char* iface, const char* cfilter,
+ const uint32_t count, const char* fifo)
+{
+ ssh_session sshs;
+ ssh_channel channel;
+ FILE* fp = stdout;
+ uint64_t bytes_written = 0;
+ int err;
+ int ret = EXIT_FAILURE;
+ char* err_info = NULL;
+
+ if (g_strcmp0(fifo, "-")) {
+ /* Open or create the output file */
+ fp = fopen(fifo, "wb");
+ if (!fp) {
+ ws_warning("Error creating output file: %s", g_strerror(errno));
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (!libpcap_write_file_header(fp, 1, PCAP_SNAPLEN, false, &bytes_written, &err)) {
+ ws_warning("Can't write pcap file header");
+ goto cleanup;
+ }
+
+ fflush(fp);
+
+ ws_debug("Create first ssh session");
+ sshs = create_ssh_connection(ssh_params, &err_info);
+ if (!sshs) {
+ ws_warning("Error creating connection: %s", err_info);
+ goto cleanup;
+ }
+
+ channel = open_channel(sshs);
+ if (!channel) {
+ ret = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ if (!detect_host_prompt(channel))
+ goto cleanup;
+
+ if (!check_ios_version(channel, &global_sw_type))
+ goto cleanup;
+
+ /* clean up and exit */
+ ciscodump_cleanup(channel, iface, cfilter, global_sw_type);
+
+ if (!run_capture(channel, iface, cfilter, count, global_sw_type)) {
+ ret = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ /* read from channel and write into fp */
+ ssh_loop_read(channel, fp, count, global_sw_type);
+
+ /* Read loop can be terminated by signal or QUIT command in
+ * mid of long "show" command and its reading can take really
+ * long time. So we terminate ssh session and then
+ * create new one to cleanup configuration
+ */
+ ws_debug("Disconnect first ssh session");
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ ssh_cleanup(&sshs, &channel);
+
+ ws_debug("Create second ssh session");
+ sshs = create_ssh_connection(ssh_params, &err_info);
+ if (!sshs) {
+ ws_warning("Error creating connection: %s", err_info);
+ goto cleanup;
+ }
+
+ channel = open_channel(sshs);
+ if (!channel) {
+ ret = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ /* clean up and exit */
+ ciscodump_cleanup(channel, iface, cfilter, global_sw_type);
+
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ ssh_cleanup(&sshs, &channel);
+
+ ret = EXIT_SUCCESS;
+cleanup:
+ if (fp != stdout)
+ fclose(fp);
+
+ return ret;
+}
+
+static int list_config(char *interface, unsigned int remote_port)
+{
+ unsigned inc = 0;
+ char* ipfilter;
+
+ if (!interface) {
+ ws_warning("No interface specified.");
+ return EXIT_FAILURE;
+ }
+
+ if (g_strcmp0(interface, CISCODUMP_EXTCAP_INTERFACE)) {
+ ws_warning("interface must be %s", CISCODUMP_EXTCAP_INTERFACE);
+ return EXIT_FAILURE;
+ }
+
+ ipfilter = local_interfaces_to_filter(remote_port);
+
+ printf("arg {number=%u}{call=--remote-host}{display=Remote SSH server address}"
+ "{type=string}{tooltip=The remote SSH host. It can be both "
+ "an IP address or a hostname}{required=true}{group=Server}\n", inc++);
+ printf("arg {number=%u}{call=--remote-port}{display=Remote SSH server port}"
+ "{type=unsigned}{default=22}{tooltip=The remote SSH host port (1-65535)}"
+ "{range=1,65535}{group=Server}\n", inc++);
+ printf("arg {number=%u}{call=--remote-username}{display=Remote SSH server username}"
+ "{type=string}{default=%s}{tooltip=The remote SSH username. If not provided, "
+ "the current user will be used}{group=Authentication}\n", inc++, g_get_user_name());
+ printf("arg {number=%u}{call=--remote-password}{display=Remote SSH server password}"
+ "{type=password}{tooltip=The SSH password, used when other methods (SSH agent "
+ "or key files) are unavailable.}{group=Authentication}\n", inc++);
+ printf("arg {number=%u}{call=--sshkey}{display=Path to SSH private key}"
+ "{type=fileselect}{tooltip=The path on the local filesystem of the private ssh key}"
+ "{group=Authentication}\n", inc++);
+ printf("arg {number=%u}{call=--proxycommand}{display=ProxyCommand}"
+ "{type=string}{tooltip=The command to use as proxy for the SSH connection}"
+ "{group=Authentication}\n", inc++);
+ printf("arg {number=%u}{call--sshkey-passphrase}{display=SSH key passphrase}"
+ "{type=password}{tooltip=Passphrase to unlock the SSH private key}"
+ "{group=Authentication\n", inc++);
+ printf("arg {number=%u}{call=--remote-interface}{display=Remote interface}"
+ "{type=string}{required=true}{tooltip=The remote network interface used for capture"
+ "}{group=Capture}\n", inc++);
+ printf("arg {number=%u}{call=--remote-filter}{display=Remote capture filter}"
+ "{type=string}{tooltip=The remote capture filter}", inc++);
+ if (ipfilter)
+ printf("{default=%s}", ipfilter);
+ printf("{group=Capture}\n");
+ printf("arg {number=%u}{call=--remote-count}{display=Packets to capture}"
+ "{type=unsigned}{required=true}{tooltip=The number of remote packets to capture.}"
+ "{group=Capture}\n", inc++);
+
+ extcap_config_debug(&inc);
+
+ g_free(ipfilter);
+
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[])
+{
+ char* err_msg;
+ int result;
+ int option_idx = 0;
+ ssh_params_t* ssh_params = ssh_params_new();
+ char* remote_interface = NULL;
+ char* remote_filter = NULL;
+ uint32_t count = 0;
+ int ret = EXIT_FAILURE;
+ extcap_parameters * extcap_conf = g_new0(extcap_parameters, 1);
+ char* help_url;
+ char* help_header = NULL;
+
+ /* Initialize log handler early so we can have proper logging during startup. */
+ extcap_log_init("ciscodump");
+
+ /*
+ * Get credential information for later use.
+ */
+ init_process_policies();
+
+ /*
+ * Attempt to get the pathname of the directory containing the
+ * executable file.
+ */
+ err_msg = configuration_init(argv[0], NULL);
+ if (err_msg != NULL) {
+ ws_warning("Can't get pathname of directory containing the extcap program: %s.",
+ err_msg);
+ g_free(err_msg);
+ }
+
+ help_url = data_file_url("ciscodump.html");
+ extcap_base_set_util_info(extcap_conf, argv[0], CISCODUMP_VERSION_MAJOR, CISCODUMP_VERSION_MINOR,
+ CISCODUMP_VERSION_RELEASE, help_url);
+ add_libssh_info(extcap_conf);
+ g_free(help_url);
+ extcap_base_register_interface(extcap_conf, CISCODUMP_EXTCAP_INTERFACE, "Cisco remote capture", 147, "Remote capture dependent DLT");
+ if (!extcap_base_register_graceful_shutdown_cb(extcap_conf, graceful_shutdown_cb)) {
+ ret = EXIT_FAILURE;
+ goto end;
+ }
+
+ help_header = ws_strdup_printf(
+ " %s --extcap-interfaces\n"
+ " %s --extcap-interface=%s --extcap-dlts\n"
+ " %s --extcap-interface=%s --extcap-config\n"
+ " %s --extcap-interface=%s --remote-host myhost --remote-port 22222 "
+ "--remote-username myuser --remote-interface gigabit0/0 "
+ "--fifo=FILENAME --capture\n", argv[0], argv[0], CISCODUMP_EXTCAP_INTERFACE, argv[0],
+ CISCODUMP_EXTCAP_INTERFACE, argv[0], CISCODUMP_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, "--remote-host <host>", "the remote SSH host");
+ extcap_help_add_option(extcap_conf, "--remote-port <port>", "the remote SSH port (default: 22)");
+ extcap_help_add_option(extcap_conf, "--remote-username <username>", "the remote SSH username (default: the current user)");
+ extcap_help_add_option(extcap_conf, "--remote-password <password>", "the remote SSH password. "
+ "If not specified, ssh-agent and ssh-key are used");
+ extcap_help_add_option(extcap_conf, "--sshkey <public key path>", "the path of the ssh key");
+ extcap_help_add_option(extcap_conf, "--sshkey-passphrase <public key passphrase>", "the passphrase to unlock public ssh");
+ extcap_help_add_option(extcap_conf, "--proxycommand <proxy command>", "the command to use as proxy for the ssh connection");
+ extcap_help_add_option(extcap_conf, "--remote-interface <iface>", "the remote capture interface");
+ extcap_help_add_option(extcap_conf, "--remote-filter <filter>", "a filter for remote capture "
+ "(default: don't capture data for all interfaces IPs)");
+
+ ws_opterr = 0;
+ ws_optind = 0;
+
+ if (argc == 1) {
+ extcap_help_print(extcap_conf);
+ goto end;
+ }
+
+ while ((result = ws_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:
+ extcap_version_print(extcap_conf);
+ goto end;
+
+ case OPT_REMOTE_HOST:
+ g_free(ssh_params->host);
+ ssh_params->host = g_strdup(ws_optarg);
+ break;
+
+ case OPT_REMOTE_PORT:
+ if (!ws_strtou16(ws_optarg, NULL, &ssh_params->port) || ssh_params->port == 0) {
+ ws_warning("Invalid port: %s", ws_optarg);
+ goto end;
+ }
+ break;
+
+ case OPT_REMOTE_USERNAME:
+ g_free(ssh_params->username);
+ ssh_params->username = g_strdup(ws_optarg);
+ break;
+
+ case OPT_REMOTE_PASSWORD:
+ g_free(ssh_params->password);
+ ssh_params->password = g_strdup(ws_optarg);
+ memset(ws_optarg, 'X', strlen(ws_optarg));
+ break;
+
+ case OPT_SSHKEY:
+ g_free(ssh_params->sshkey_path);
+ ssh_params->sshkey_path = g_strdup(ws_optarg);
+ break;
+
+ case OPT_SSHKEY_PASSPHRASE:
+ g_free(ssh_params->sshkey_passphrase);
+ ssh_params->sshkey_passphrase = g_strdup(ws_optarg);
+ memset(ws_optarg, 'X', strlen(ws_optarg));
+ break;
+
+ case OPT_PROXYCOMMAND:
+ g_free(ssh_params->proxycommand);
+ ssh_params->proxycommand = g_strdup(ws_optarg);
+ break;
+
+ case OPT_REMOTE_INTERFACE:
+ g_free(remote_interface);
+ remote_interface = g_strdup(ws_optarg);
+ break;
+
+ case OPT_REMOTE_FILTER:
+ g_free(remote_filter);
+ remote_filter = g_strdup(ws_optarg);
+ break;
+
+ case OPT_REMOTE_COUNT:
+ if (!ws_strtou32(ws_optarg, NULL, &count)) {
+ ws_warning("Invalid packet count: %s", ws_optarg);
+ goto end;
+ }
+ break;
+
+ case ':':
+ /* missing option argument */
+ ws_warning("Option '%s' requires an argument", argv[ws_optind - 1]);
+ break;
+
+ default:
+ if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, ws_optarg)) {
+ ws_warning("Invalid option: %s", argv[ws_optind - 1]);
+ goto end;
+ }
+ }
+ }
+
+ extcap_cmdline_debug(argv, argc);
+
+ if (ws_optind != argc) {
+ ws_warning("Unexpected extra option: %s", argv[ws_optind]);
+ goto end;
+ }
+
+ if (extcap_base_handle_interface(extcap_conf)) {
+ ret = EXIT_SUCCESS;
+ goto end;
+ }
+
+ if (extcap_conf->show_config) {
+ unsigned int port;
+ if (!ws_strtou16(ws_optarg, NULL, &ssh_params->port) || ssh_params->port == 0) {
+ port = 22;
+ } else {
+ port = ssh_params->port;
+ }
+ ret = list_config(extcap_conf->interface, port);
+ goto end;
+ }
+
+ err_msg = ws_init_sockets();
+ if (err_msg != NULL) {
+ ws_warning("ERROR: %s", err_msg);
+ g_free(err_msg);
+ ws_warning("%s", please_report_bug());
+ goto end;
+ }
+
+ if (extcap_conf->capture) {
+ if (!ssh_params->host) {
+ ws_warning("Missing parameter: --remote-host");
+ goto end;
+ }
+
+ if (!remote_interface) {
+ ws_warning("ERROR: No interface specified (--remote-interface)");
+ goto end;
+ }
+ if (count == 0) {
+ ws_warning("ERROR: count of packets must be specified (--remote-count)");
+ goto end;
+ }
+ ssh_params->debug = extcap_conf->debug;
+ ret = ssh_open_remote_connection(ssh_params, remote_interface,
+ remote_filter, count, extcap_conf->fifo);
+ } else {
+ ws_debug("You should not come here... maybe some parameter missing?");
+ ret = EXIT_FAILURE;
+ }
+
+end:
+ ssh_params_free(ssh_params);
+ g_free(remote_interface);
+ g_free(remote_filter);
+ extcap_base_cleanup(&extcap_conf);
+ return ret;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */