decrypt drsuapi attributes
[wireshark-sm.git] / extcap / ciscodump.c
blobe01669ccb9a05e025e4331436edc94f2abfd4cc0
1 /* ciscodump.c
2 * ciscodump is extcap tool used to capture data using a ssh on a remote cisco router
4 * Copyright 2015, Dario Lombardo
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
13 #include "config.h"
14 #define WS_LOG_DOMAIN "ciscodump"
16 #include <extcap/extcap-base.h>
17 #include <wsutil/interface.h>
18 #include <wsutil/strtoi.h>
19 #include <wsutil/filesystem.h>
20 #include <wsutil/privileges.h>
21 #include <wsutil/please_report_bug.h>
22 #include <wsutil/wslog.h>
23 #include <extcap/ssh-base.h>
24 #include <writecap/pcapio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <fcntl.h>
30 #include <wsutil/time_util.h>
31 #include <wsutil/ws_strptime.h>
33 #include <cli_main.h>
35 #define CISCODUMP_VERSION_MAJOR "1"
36 #define CISCODUMP_VERSION_MINOR "0"
37 #define CISCODUMP_VERSION_RELEASE "0"
39 /* The read timeout in msec */
40 #define CISCODUMP_READ_TIMEOUT_MSEC 300
42 #define CISCODUMP_EXTCAP_INTERFACE "ciscodump"
43 #define SSH_READ_BLOCK_SIZE 1024
44 #define SSH_READ_TIMEOUT_MSES 10000
45 #define SSH_READ_TIMEOUT_USEC (SSH_READ_TIMEOUT_MSES*1000)
47 #define WIRESHARK_CAPTURE "WSC"
48 #define WIRESHARK_CAPTURE_POINT "WSC_P"
49 #define WIRESHARK_CAPTURE_BUFFER "WSC_B"
50 #define WIRESHARK_CAPTURE_ACCESSLIST "WSC_ACL"
52 #define PCAP_SNAPLEN 0xffff
54 #define PACKET_MAX_SIZE 65535
56 #define MINIMUM_IOS_MAJOR 12
57 #define MINIMUM_IOS_MINOR 4
58 #define MINIMUM_IOS_XE_MAJOR_16 16
59 #define MINIMUM_IOS_XE_MINOR_16 1
60 #define MINIMUM_IOS_XE_MAJOR_17 17
61 #define MINIMUM_IOS_XE_MINOR_17 1
62 #define MINIMUM_ASA_MAJOR 8
63 #define MINIMUM_ASA_MINOR 4
65 #define READ_PROMPT_ERROR -1
66 #define READ_PROMPT_EOLN 0
67 #define READ_PROMPT_PROMPT 1
68 #define READ_PROMPT_TOO_LONG 2
70 #define READ_LINE_ERROR -1
71 #define READ_LINE_EOLN 0
72 #define READ_LINE_TIMEOUT 1
73 #define READ_LINE_TOO_LONG 2
75 /* Type of Cisco device */
76 typedef enum {
77 CISCO_UNKNOWN,
78 CISCO_IOS,
79 CISCO_IOS_XE_16,
80 CISCO_IOS_XE_17,
81 CISCO_ASA
82 } CISCO_SW_TYPE;
84 /* Status of the parser */
85 enum {
86 CISCODUMP_PARSER_STARTING,
87 CISCODUMP_PARSER_IN_PACKET,
88 CISCODUMP_PARSER_IN_HEADER,
89 CISCODUMP_PARSER_END_PACKET,
90 CISCODUMP_PARSER_UNKNOWN
93 enum {
94 EXTCAP_BASE_OPTIONS_ENUM,
95 OPT_HELP,
96 OPT_VERSION,
97 OPT_REMOTE_HOST,
98 OPT_REMOTE_PORT,
99 OPT_REMOTE_USERNAME,
100 OPT_REMOTE_PASSWORD,
101 OPT_REMOTE_INTERFACE,
102 OPT_REMOTE_FILTER,
103 OPT_SSHKEY,
104 OPT_SSHKEY_PASSPHRASE,
105 OPT_PROXYCOMMAND,
106 OPT_SSH_SHA1,
107 OPT_REMOTE_COUNT
110 static char prompt_str[SSH_READ_BLOCK_SIZE + 1];
111 static int32_t prompt_len = -1;
112 CISCO_SW_TYPE global_sw_type = CISCO_UNKNOWN;
113 static bool send_output_quit; /* IOS XE 17: send quit during output */
115 static const struct ws_option longopts[] = {
116 EXTCAP_BASE_OPTIONS,
117 { "help", ws_no_argument, NULL, OPT_HELP},
118 { "version", ws_no_argument, NULL, OPT_VERSION},
119 SSH_BASE_OPTIONS,
120 { 0, 0, 0, 0}
123 static void graceful_shutdown_cb(void)
125 if (global_sw_type == CISCO_IOS_XE_17) {
126 send_output_quit = true;
130 /* Replaces needle with rep in line */
131 static char* str_replace_char(char *line, char needle, char rep)
133 for(int i = 0; line[i] != '\0'; i++) {
134 if (line[i] == needle) {
135 line[i] = rep;
139 return line;
142 /* Replaces CR with LN */
143 static char* crtoln(char *line)
145 return str_replace_char(line, '\r', '\n');
148 static char* interfaces_list_to_filter(GSList* interfaces, unsigned int remote_port)
150 GString* filter = g_string_new(NULL);
151 GSList* cur;
153 if (interfaces) {
154 g_string_append_printf(filter, "deny tcp host %s any eq %u, deny tcp any eq %u host %s",
155 (char*)interfaces->data, remote_port, remote_port, (char*)interfaces->data);
156 cur = g_slist_next(interfaces);
157 while (cur) {
158 g_string_append_printf(filter, ", deny tcp host %s any eq %u, deny tcp any eq %u host %s",
159 (char*)cur->data, remote_port, remote_port, (char*)cur->data);
160 cur = g_slist_next(cur);
162 g_string_append_printf(filter, ", permit ip any any");
165 return g_string_free(filter, FALSE);
168 static char* local_interfaces_to_filter(const unsigned int remote_port)
170 GSList* interfaces = local_interfaces_to_list();
171 char* filter = interfaces_list_to_filter(interfaces, remote_port);
172 g_slist_free_full(interfaces, g_free);
173 return filter;
176 /* Read bytes from the channel with no escape character.
177 * If bytes == -1, read all data (until timeout). If outbuf != NULL, data are stored there
179 static int read_output_bytes_any(ssh_channel channel, int bytes, char* outbuf)
181 char chr;
182 int total;
183 int bytes_read;
185 total = (bytes > 0 ? bytes : INT_MAX);
186 bytes_read = 0;
188 while(ssh_channel_read_timeout(channel, &chr, 1, 0, CISCODUMP_READ_TIMEOUT_MSEC) > 0 && bytes_read < total) {
189 ws_noisy("%c %02x", chr, chr);
190 if (outbuf)
191 outbuf[bytes_read] = chr;
192 bytes_read++;
194 if (outbuf)
195 outbuf[bytes_read+1] = '\0';
196 return EXIT_SUCCESS;
199 /* Read bytes from the channel. Recognize escape char '^'.
200 * If bytes == -1, read all data (until timeout). If outbuf != NULL, data are stored there
202 static int read_output_bytes(ssh_channel channel, int bytes, char* outbuf)
204 char chr;
205 int total;
206 int bytes_read;
208 total = (bytes > 0 ? bytes : INT_MAX);
209 bytes_read = 0;
211 while(ssh_channel_read_timeout(channel, &chr, 1, 0, CISCODUMP_READ_TIMEOUT_MSEC) > 0 && bytes_read < total) {
212 ws_noisy("%c %02x", chr, chr);
213 if (chr == '^')
214 return EXIT_FAILURE;
215 if (outbuf)
216 outbuf[bytes_read] = chr;
217 bytes_read++;
219 return EXIT_SUCCESS;
222 /* Reads input to buffer and parses EOL
223 * If line is NULL, just received count of characters in len is calculated
224 * It returns:
225 * READ_LINE_ERROR - any ssh error occurred
226 * READ_LINE_EOLN - EOLN found, line/len contains \0 terminated string
227 * READ_LINE_TIMEOUT - reading ended with timeout, line/len contains \0 terminate prompt
228 * READ_LINE_TOO_LONG - buffer is full with no EOLN nor PROMPT found, line is filled with NOT \0 terminated data
230 static int ssh_channel_read_line_timeout(ssh_channel channel, char *line, int *len, int max_len) {
231 char chr;
232 int rlen = 0;
234 *len = 0;
235 do {
236 rlen = ssh_channel_read_timeout(channel, &chr, 1, false, CISCODUMP_READ_TIMEOUT_MSEC);
237 ws_noisy("%c %02x %d", chr, chr, rlen);
238 if (rlen == SSH_ERROR) {
239 ws_warning("Error reading from channel");
240 return READ_LINE_ERROR;
241 } else if (rlen > 0) {
242 if (chr != '\n') {
243 /* Ignore \r */
244 if (chr != '\r') {
245 if (line) {
246 line[*len] = chr;
248 (*len)++;
250 } else {
251 /* Parse the current line */
252 if (line) {
253 line[*len] = '\0';
255 return READ_LINE_EOLN;
257 } else {
258 return READ_LINE_TIMEOUT;
260 } while (*len < max_len);
262 return READ_LINE_TOO_LONG;
265 /* Reads input to buffer and parses EOL or prompt_str PROMPT
266 * It returns:
267 * READ_PROMPT_ERROR - any ssh error occurred
268 * READ_PROMPT_EOLN - EOLN found, line/len contains \0 terminated string
269 * READ_PROMPT_PROMPT - reading ended and it ends with PROMPT, line/len contains \0 terminate prompt
270 * READ_PROMPT_TOO_LONG - buffer is full with no EOLN nor PROMPT found, line is filled with NOT \0 terminated data
272 static int ssh_channel_read_prompt(ssh_channel channel, char *line, uint32_t *len, uint32_t max_len) {
273 char chr;
274 int rlen = 0;
275 int64_t start_time = g_get_monotonic_time();
277 do {
278 rlen = ssh_channel_read_timeout(channel, &chr, 1, false, CISCODUMP_READ_TIMEOUT_MSEC);
279 ws_noisy("%c %02x %d", chr, chr, rlen);
280 if (rlen == SSH_ERROR) {
281 ws_warning("Error reading from channel");
282 return READ_PROMPT_ERROR;
283 } else if (rlen > 0) {
284 if (chr != '\n') {
285 line[*len] = chr;
286 (*len)++;
287 } else {
288 /* Parse the current line */
289 line[*len] = '\0';
290 ws_noisy(" exiting: READ_PROMPT_EOLN (%d/%d)", *len, max_len);
291 return READ_PROMPT_EOLN;
293 } else {
294 int64_t cur_time = g_get_monotonic_time();
296 /* ssh timeout, we might be on prompt */
297 /* IOS, IOS-XE: check if line has same length as prompt and if it match prompt */
298 if ((*len == (uint32_t)prompt_len) && (0 == strncmp(line, prompt_str, prompt_len))) {
299 line[*len] = '\0';
300 ws_noisy(" exiting: READ_PROMPT_PROMPT (%d/%d)", *len, max_len);
301 return READ_PROMPT_PROMPT;
303 /* ASA: check if line begins with \r and has same length as prompt and if it match prompt */
304 if ((line[0] == '\r') && (*len == (uint32_t)prompt_len+1) && (0 == strncmp(line+1, prompt_str, prompt_len))) {
305 line[*len] = '\0';
306 ws_noisy(" exiting: READ_PROMPT_PROMPT (%d/%d)", *len, max_len);
307 return READ_PROMPT_PROMPT;
309 /* no prompt found, so we continue in waiting for data, but we should check global timeout */
310 if ((cur_time-start_time) > SSH_READ_TIMEOUT_USEC) {
311 line[*len] = '\0';
312 ws_noisy(" exiting: READ_PROMPT_ERROR");
313 return READ_PROMPT_ERROR;
316 } while (!extcap_end_application && (*len < max_len));
318 ws_noisy(" exiting: READ_PROMPT_TOO_LONG (%d/%d/%d)", *len, max_len, extcap_end_application);
319 line[*len] = '\0';
320 return READ_PROMPT_TOO_LONG;
323 static int ssh_channel_wait_prompt(ssh_channel channel, char *line, uint32_t *len, uint32_t max_len) {
324 char line2[SSH_READ_BLOCK_SIZE + 1];
325 uint32_t len2;
326 int status;
328 memset(line2, 0x0, SSH_READ_BLOCK_SIZE + 1);
329 line[0] = '\0';
330 *len = 0;
331 do {
332 len2 = 0;
333 switch (status = ssh_channel_read_prompt(channel, line2, &len2, SSH_READ_BLOCK_SIZE)) {
334 case READ_PROMPT_EOLN:
335 *len = (uint32_t)g_strlcat(line, line2, max_len);
336 len2 = 0;
337 break;
338 case READ_PROMPT_PROMPT:
339 *len = (uint32_t)g_strlcat(line, line2, max_len);
340 len2 = 0;
341 break;
342 default:
343 /* We do not have better solution for that cases */
344 /* Just terminate the line and return error */
345 *len = (uint32_t)g_strlcat(line, line2, max_len);
346 line[max_len] = '\0';
347 ws_noisy("Returning READ_PROMPT_ERROR (%d/%d)", *len, max_len);
348 return READ_PROMPT_ERROR;
350 } while (status == READ_PROMPT_EOLN);
352 ws_noisy("Returning READ_PROMPT_PROMPT (%d/%d)", *len, max_len);
353 return READ_PROMPT_PROMPT;
356 /* true if prompt and no error text in response. false otherwise */
357 /* Note: It do not catch all CISCO CLI errors, but many of them */
358 static bool ssh_channel_wait_prompt_check_error(ssh_channel channel, char *line, uint32_t *len, uint32_t max_len, char *error_re) {
359 /* Did we received prompt? */
360 if (ssh_channel_wait_prompt(channel, line, len, max_len) != READ_PROMPT_PROMPT) {
361 return false;
364 /* Is there ERROR: text in output? */
365 if (NULL != g_strstr_len(line, -1, "ERROR:")) {
366 return false;
369 /* Is there ERROR: text in output? */
370 if (NULL != g_strstr_len(line, -1, "% Invalid input detected at")) {
371 return false;
374 /* Is there error_re text in output? */
375 if (error_re &&
376 g_regex_match_simple(error_re, line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)
378 return false;
381 return true;
384 static void ciscodump_cleanup_ios(ssh_channel channel, const char* iface, const char* cfilter)
386 char* iface_copy = g_strdup(iface);
387 char* iface_one;
388 char* str = NULL;
389 int wscp_cnt = 1;
390 char* wscp_str = NULL;
392 extcap_end_application = false;
393 if (channel) {
394 ws_debug("Removing configuration...");
395 read_output_bytes(channel, -1, NULL);
397 wscp_cnt = 1;
398 for (str = iface_copy; ; str = NULL) {
399 iface_one = strtok(str, ",");
400 if (iface_one == NULL)
401 break;
403 wscp_str = g_strdup_printf("%s_%d", WIRESHARK_CAPTURE_POINT, wscp_cnt);
404 wscp_cnt++;
406 ssh_channel_printf(channel, "monitor capture point stop %s\n", wscp_str);
407 ssh_channel_printf(channel, "no monitor capture point ip cef %s %s\n", wscp_str, iface_one);
409 g_free(wscp_str);
410 wscp_str = NULL;
413 ssh_channel_printf(channel, "no monitor capture buffer %s\n", WIRESHARK_CAPTURE_BUFFER);
414 if (cfilter) {
415 ssh_channel_printf(channel, "configure terminal\n");
416 ssh_channel_printf(channel, "no ip access-list ex %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
419 read_output_bytes(channel, -1, NULL);
420 ws_debug("Configuration removed");
423 g_free(iface_copy);
426 static void ciscodump_cleanup_ios_xe_16(ssh_channel channel, const char* cfilter)
428 if (channel) {
429 ws_debug("Removing configuration...");
430 read_output_bytes(channel, -1, NULL);
432 ssh_channel_printf(channel, "monitor capture %s stop\n", WIRESHARK_CAPTURE);
433 ssh_channel_printf(channel, "no monitor capture %s\n", WIRESHARK_CAPTURE);
434 if (cfilter) {
435 ssh_channel_printf(channel, "configure terminal\n");
436 ssh_channel_printf(channel, "no ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
437 ssh_channel_printf(channel, "\nend\n");
440 read_output_bytes(channel, -1, NULL);
441 ws_debug("Configuration removed");
445 static void ciscodump_cleanup_ios_xe_17(ssh_channel channel, const char* cfilter)
447 if (channel) {
448 ws_debug("Removing configuration...");
449 read_output_bytes(channel, -1, NULL);
451 ssh_channel_printf(channel, "monitor capture %s stop\n", WIRESHARK_CAPTURE);
452 ssh_channel_printf(channel, "no monitor capture %s\n", WIRESHARK_CAPTURE);
453 if (cfilter) {
454 ssh_channel_printf(channel, "configure terminal\n");
455 ssh_channel_printf(channel, "no ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
456 ssh_channel_printf(channel, "\nend\n");
459 read_output_bytes(channel, -1, NULL);
460 ws_debug("Configuration removed");
464 static void ciscodump_cleanup_asa(ssh_channel channel, const char* cfilter)
466 if (channel) {
467 ws_debug("Removing configuration...");
468 read_output_bytes(channel, -1, NULL);
470 ssh_channel_printf(channel, "no capture %s\n", WIRESHARK_CAPTURE);
471 if (cfilter) {
472 ssh_channel_printf(channel, "configure terminal\n");
473 ssh_channel_printf(channel, "clear configure access-list %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
474 ssh_channel_printf(channel, "\nend\n", WIRESHARK_CAPTURE_ACCESSLIST);
477 read_output_bytes(channel, -1, NULL);
478 ws_debug("Configuration removed");
482 static void ciscodump_cleanup(ssh_channel channel, const char* iface, const char* cfilter, CISCO_SW_TYPE sw_type)
484 ws_debug("Starting config cleanup");
485 switch (sw_type) {
486 case CISCO_IOS:
487 ciscodump_cleanup_ios(channel, iface, cfilter);
488 break;
489 case CISCO_IOS_XE_16:
490 ciscodump_cleanup_ios_xe_16(channel, cfilter);
491 break;
492 case CISCO_IOS_XE_17:
493 ciscodump_cleanup_ios_xe_17(channel, cfilter);
494 break;
495 case CISCO_ASA:
496 ciscodump_cleanup_asa(channel, cfilter);
497 break;
498 case CISCO_UNKNOWN:
499 break;
501 ws_debug("Config cleanup finished");
504 static void packets_captured_count_ios(char *line, uint32_t *max, bool *running) {
505 char** part;
507 *max = 0;
509 ws_debug("Analyzing response: %s", line);
511 /* Read count of packets */
512 part = g_regex_split_simple(
513 "Packets :\\s*(\\d+)",
514 line, G_REGEX_CASELESS, 0);
515 if (*part && *(part+1)) {
516 /* RE matched */
517 if (strlen(*(part+1)) > 0) {
518 ws_strtou32(*(part+1), NULL, max);
521 g_strfreev(part);
523 *running = false;
524 if (g_regex_match_simple("Status : Active", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
525 *running = true;
527 ws_debug("Count of packets: %d", *max);
528 ws_debug("Capture is running: %d", *running);
531 static void packets_captured_count_ios_xe_16(char *line, uint32_t *max, bool *running) {
532 char** part;
534 *max = 0;
536 ws_debug("Analyzing response: %s", line);
538 part = g_regex_split_simple(
539 "packets in buf\\s+:\\s+(\\d+)",
540 line, G_REGEX_CASELESS, 0);
541 if (*part && *(part+1)) {
542 /* RE matched */
543 if (strlen(*(part+1)) > 0) {
544 ws_strtou32(*(part+1), NULL, max);
547 g_strfreev(part);
549 *running = false;
550 /* Check if capture is running */
551 if (g_regex_match_simple("Status : Active", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
552 *running = true;
554 ws_debug("Count of packets: %d", *max);
555 ws_debug("Capture is running: %d", *running);
558 static void packets_captured_count_asa(char *line, uint32_t *max, bool *running) {
559 char** part;
561 *max = 0;
563 ws_debug("Analyzing response: %s", line);
565 /* Read count of packets */
566 part = g_regex_split_simple(
567 "(\\d+) packets captured",
568 line, G_REGEX_CASELESS, 0);
569 if (*part && *(part+1)) {
570 /* RE matched */
571 if (strlen(*(part+1)) > 0) {
572 ws_strtou32(*(part+1), NULL, max);
575 g_strfreev(part);
577 if (running != NULL) {
578 *running = false;
579 /* Check if capture is running */
580 if (g_regex_match_simple("\\[Capturing -", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
581 *running = true;
583 ws_debug("Capture is running: %d", *running);
585 ws_debug("Count of packets: %d", *max);
588 static int parse_line_ios(uint8_t* packet, unsigned* offset, char* line, int status, time_t *pkt_time, uint32_t *pkt_usec)
590 char** parts;
591 char** part;
592 uint32_t value;
593 size_t size;
595 if (strlen(line) <= 1) {
596 if (status == CISCODUMP_PARSER_IN_PACKET)
597 return CISCODUMP_PARSER_END_PACKET;
598 else
599 return status;
603 22:45:44.700 UTC Nov 27 2021 : IPv4 LES CEF : Fa4.320 None
605 0F1A2C00: B0FAEBC7 A8620050 0zkG(b.P
606 0F1A2C10: 568494FB 08004588 004EF201 40003F11 V..{..E..Nr.@.?.
607 0F1A2C20: 5263AC10 1F60AC10 7F31D0DF 00A1003A Rc,..`,..1P_.!.:
608 0F1A2C30: D1753030 02010104 07707269 76617465 Qu00.....private
609 0F1A2C40: A0220204 1A00A2E8 02010002 01003014 "...."h......0.
610 0F1A2C50: 3012060E 2B060104 0182CB21 01040103 0...+.....K!....
611 0F1A2C60: 06000500 00 .....
613 22:45:44.700 UTC Nov 27 2021 : IPv4 LES CEF : Fa4.320 None
615 0F1A2C00: B0FAEBC7 A8620050 0zkG(b.P
616 0F1A2C10: 568494FB 08004588 004E7FFB 40003F11 V..{..E..N.{@.?.
617 0F1A2C20: EB72AC10 1F60AC10 5828E872 00A1003A kr,..`,.X(hr.!.:
618 0F1A2C30: 2B393030 02010104 07707269 76617465 +900.....private
619 0F1A2C40: A0220204 07836B18 02010002 01003014 "....k.......0.
620 0F1A2C50: 3012060E 2B060104 0182CB21 01040103 0...+.....K!....
621 0F1A2C60: 06000500 00 .....
624 /* we got the packet header */
625 /* The packet header is a line like: */
626 /* 16:09:37.171 ITA Mar 18 2016 : IPv4 LES CEF : Gi0/1 None */
627 parts = g_regex_split_simple(
628 "^(\\d{2}:\\d{2}:\\d{2}).(\\d+) (\\w+) (\\w+ \\d+ \\d+) :",
629 line, G_REGEX_CASELESS, 0);
630 if (parts && *(parts+1)) {
631 /* RE matched */
632 char* cp;
633 struct tm tm;
634 /* Date without msec, with timezone */
635 char* d1 = g_strdup_printf("%s %s %s", *(parts+1), *(parts+3), *(parts+4));
636 /* Date without msec, without timezone */
637 char* d2 = g_strdup_printf("%s %s", *(parts+1), *(parts+4));
639 memset(&tm, 0x0, sizeof(struct tm));
641 cp = ws_strptime_p(d1, "%H:%M:%S %Z %b %d %Y", &tm);
642 if (!cp || (*cp != '\0')) {
643 /* Time zone parse failed */
644 cp = ws_strptime_p(d2, "%H:%M:%S %b %d %Y", &tm);
645 if (!cp || (*cp != '\0')) {
646 /* Time parse failed, use now */
647 time_t t;
648 struct tm *tm2;
650 t = time(0);
651 tm2 = localtime(&t);
652 memcpy(&tm, tm2, sizeof(struct tm));
655 ws_strtou32(*(parts+2), NULL, pkt_usec);
656 *pkt_usec *= 1000;
657 *pkt_time = mktime(&tm);
659 g_strfreev(parts);
660 return CISCODUMP_PARSER_IN_HEADER;
662 g_strfreev(parts);
664 /* we got a line of the packet */
665 /* A line looks like */
666 /* <address>: <1st group> <2nd group> <3rd group> <4th group> <ascii representation> */
667 /* ABCDEF01: 01020304 05060708 090A0B0C 0D0E0F10 ................ */
668 /* Note that any of the 4 groups are optional and that a group can be 1 to 4 bytes long */
669 parts = g_regex_split_simple(
670 "^[\\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}.*",
671 line, G_REGEX_CASELESS, 0);
673 part = parts;
674 if (*part && *(part+1)) {
675 /* There is at least one match. Skip first string */
676 part++;
677 while(*part) {
678 /* RE matched */
679 if (strlen(*part) > 1) {
680 ws_hexstrtou32(*part, NULL, &value);
681 value = g_ntohl(value);
682 size = strlen(*part) / 2;
683 memcpy(packet + *offset, &value, size);
684 *offset += (uint32_t)size;
686 part++;
689 g_strfreev(parts);
690 return CISCODUMP_PARSER_IN_PACKET;
693 static int parse_line_ios_xe_16(uint8_t* packet, unsigned* offset, char* line)
695 char** parts;
696 char** part;
697 uint32_t value;
698 size_t size;
700 if (strlen(line) <= 1) {
701 return CISCODUMP_PARSER_END_PACKET;
706 0000: 00000C07 AC154C5D 3C259068 08004500 ......L]<%.h..E.
707 0010: 00547549 40003F01 B582C0A8 4983C0A8 .TuI@.?.....I...
708 0020: 46090800 B28E456E 00030000 00000000 F.....En........
709 0030: 00000000 00000000 00000000 00000000 ................
710 0040: 00000000 00000000 00000000 00000000 ................
711 0050: 00000000 00000000 00000000 00000000 ................
712 0060: 0000 ..
715 0000: 4C5D3C25 9068A49B CD904C74 08004500 L]<%.h....Lt..E.
716 0010: 00547549 4000FF01 F581C0A8 4609C0A8 .TuI@.......F...
717 0020: 49830000 BA8E456E 00030000 00000000 I.....En........
718 0030: 00000000 00000000 00000000 00000000 ................
719 0040: 00000000 00000000 00000000 00000000 ................
720 0050: 00000000 00000000 00000000 00000000 ................
721 0060: 0000 ..
724 /* we got the packet header */
725 /*0*/
726 if (g_regex_match_simple("^\\d+$", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
727 return CISCODUMP_PARSER_IN_HEADER;
730 /* we got a line of the packet */
731 /* A line looks like */
732 /* 0000: 00000C07 AC154C5D 3C259068 08004500 ......L]<%.h..E. */
733 /* ... */
734 /* 0060: 0000 .. */
735 /* Note that any of the 4 groups are optional and that a group can be 1 to 8 bytes long */
736 parts = g_regex_split_simple(
737 "^\\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+.*",
738 line, G_REGEX_CASELESS, 0);
740 part = parts;
741 if (*part && *(part+1)) {
742 /* There is at least one match. Skip first string */
743 part++;
744 while(*part) {
745 if (strlen(*part) > 1) {
746 ws_hexstrtou32(*part, NULL, &value);
747 value = g_ntohl(value);
748 size = strlen(*part) / 2;
749 memcpy(packet + *offset, &value, size);
750 *offset += (uint32_t)size;
752 part++;
755 g_strfreev(parts);
757 return CISCODUMP_PARSER_IN_PACKET;
760 static int parse_line_ios_xe_17(uint8_t* packet, unsigned* offset, char* line)
762 char** parts;
763 char** part;
764 uint8_t value;
766 if (strlen(line) <= 1) {
767 return CISCODUMP_PARSER_END_PACKET;
771 0000 6c 5e 3b 88 5e 80 6c 5e 3b 88 5e 80 08 00 45 00 l^;.^.l^;.^...E.
772 0010 00 28 9b 00 00 00 ff 06 0f 02 c0 a8 c8 01 c0 a8 .(..............
773 0020 c8 7a 7e 77 00 31 4c ba ba a5 92 d3 0a eb 50 10 .z~w.1L.......P.
774 0030 10 20 6a 20 00 00 00 00 00 00 00 00 . j ........
777 /* we got a line of the packet */
778 /* A line looks like */
779 /* 0000 6c 5e 3b 88 5e 80 6c 5e 3b 88 5e 80 08 00 45 00 l^;.^.l^;.^...E */
780 /* ... */
781 /* 0030 10 20 6a 20 00 00 00 00 00 00 00 00 . j ........ */
782 /* Note that any of the 4 groups are optional and that a group can be 1 to 8 bytes long */
783 parts = g_regex_split_simple(
784 "^\\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+.*",
785 line, G_REGEX_CASELESS, 0);
787 part = parts;
788 if (*part && *(part+1)) {
789 /* There is at least one match. Skip first string */
790 part++;
791 while(*part) {
792 if (strlen(*part) > 1) {
793 ws_hexstrtou8(*part, NULL, &value);
794 memcpy(packet + *offset, &value, 1);
795 *offset += 1;
797 part++;
800 g_strfreev(parts);
802 return CISCODUMP_PARSER_IN_PACKET;
805 static int parse_line_asa(uint8_t* packet, unsigned* offset, char* line, uint32_t *current_max, time_t *pkt_time, uint32_t *pkt_usec)
807 char** parts;
808 char** part;
809 uint16_t value;
810 size_t size;
811 uint32_t new_max;
813 if (strlen(line) <= 1) {
814 return CISCODUMP_PARSER_UNKNOWN;
818 4599 packets captured
820 1: 20:40:01.108469 10.124.255.212 > 10.124.255.5 icmp: echo request
821 0x0000 a453 0ef3 7fc0 74ad 98e5 0004 0800 4500 .S....t.......E.
822 0x0010 0054 dc73 4000 4001 4a63 0a7c ffd4 0a7c .T.s@.@.Jc.|...|
823 0x0020 ff05 0800 275b d0a4 0000 0000 0000 0000 ....'[..........
824 0x0030 0000 0000 0000 0000 0000 0000 0000 0000 ................
825 0x0040 0000 0000 0000 0000 0000 0000 0000 0000 ................
826 0x0050 0000 0000 0000 0000 0000 0000 0000 0000 ................
827 0x0060 0000 ..
828 1 packet shown
831 /* Update count of available packets */
832 /* 4599 packets captured */
833 packets_captured_count_asa(line, &new_max, NULL);
834 if (new_max > 0) {
835 *current_max = new_max;
836 return CISCODUMP_PARSER_IN_HEADER;
839 /* we got the packet header */
840 /* 1: 20:40:01.108469 10.124.255.212 > 10.124.255.5 icmp: echo request */
841 parts = g_regex_split_simple("^\\s*\\d+:\\s+(\\d+):(\\d+):(\\d+)\\.(\\d+)\\s+", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0);
842 if (parts && *(parts+1)) {
843 /* RE matched */
844 struct tm *tm;
845 time_t t;
847 t = time(0);
848 tm = localtime(&t);
849 ws_strtoi32(*(parts+1), NULL, &(tm->tm_hour));
850 ws_strtoi32(*(parts+2), NULL, &(tm->tm_min));
851 ws_strtoi32(*(parts+3), NULL, &(tm->tm_sec));
852 ws_strtou32(*(parts+4), NULL, pkt_usec);
853 *pkt_time = mktime(tm);
855 g_strfreev(parts);
856 return CISCODUMP_PARSER_IN_HEADER;
858 g_strfreev(parts);
860 /* we got the packet tail */
861 /* 1 packet shown */
862 if (g_regex_match_simple("^\\s*1 packet shown.*$", line, (GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_RAW), 0)) {
863 return CISCODUMP_PARSER_END_PACKET;
866 /* we got a line of the packet */
867 /* A line looks like */
868 /* 0x<address>: <1st group> <...> <8th group> <5th group> <ascii representation> */
869 /* 0x0000 a453 0ef3 7fc0 74ad 98e5 0004 0800 4500 -.S....t.......E. */
870 /* 0x0060 0000 .. */
871 /* Note that any of the 8 groups are optional and that a group can be 1 to 8 bytes long */
872 parts = g_regex_split_simple(
873 "^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+.*",
874 line, G_REGEX_CASELESS, 0);
876 part = parts;
877 if (*part && *(part+1)) {
878 /* There is at least one match. Skip first string */
879 part++;
880 while(*part) {
881 if (strlen(*part) > 1) {
882 ws_hexstrtou16(*part, NULL, &value);
883 value = g_ntohs(value);
884 size = strlen(*part) / 2;
885 memcpy(packet + *offset, &value, size);
886 *offset += (uint32_t)size;
888 part++;
891 g_strfreev(parts);
893 return CISCODUMP_PARSER_IN_PACKET;
896 /* IOS: Reads response and parses buffer till prompt received */
897 static int process_buffer_response_ios(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets)
899 char line[SSH_READ_BLOCK_SIZE + 1];
900 uint32_t read_packets = 1;
901 int status = CISCODUMP_PARSER_STARTING;
902 int loop_end = 0;
903 unsigned packet_size = 0;
904 time_t pkt_time = 0;
905 uint32_t pkt_usec = 0;
906 uint32_t len = 0;
908 /* Process response */
909 do {
911 loop_end = 0;
912 /* Read input till EOLN or prompt */
913 switch (ssh_channel_read_prompt(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
914 case READ_PROMPT_EOLN:
915 status = parse_line_ios(packet, &packet_size, line, status, &pkt_time, &pkt_usec);
917 if (status == CISCODUMP_PARSER_END_PACKET) {
918 ws_debug("Read packet %d\n", read_packets);
919 if (read_packets > *processed_packets) {
920 int err;
921 uint64_t bytes_written;
923 ws_debug("Exporting packet %d\n", *processed_packets);
924 /* dump the packet to the pcap file */
925 if (!libpcap_write_packet(fp,
926 pkt_time, pkt_usec,
927 packet_size, packet_size, packet, &bytes_written, &err)) {
928 ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
929 break;
931 fflush(fp);
932 ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
933 (*processed_packets)++;
935 packet_size = 0;
936 read_packets++;
938 break;
939 case READ_PROMPT_PROMPT:
940 ws_debug("Prompt found");
941 loop_end = 1;
942 break;
943 default:
944 /* We do not have better solution for that cases */
945 if (extcap_end_application) {
946 /* End by signal causes timeout so we decrease priority */
947 ws_debug("Timeout or response was too long\n");
948 } else {
949 ws_warning("Timeout or response was too long\n");
951 return false;
953 len = 0;
954 ws_debug("loop end detection %d %d %d %d", extcap_end_application, loop_end, *processed_packets, count);
955 } while ((!extcap_end_application) && (!loop_end) && (*processed_packets < count));
957 return true;
960 /* IOS: Queries buffer content and reads it */
961 static void ssh_loop_read_ios(ssh_channel channel, FILE* fp, const uint32_t count)
963 char line[SSH_READ_BLOCK_SIZE + 1];
964 uint8_t* packet;
965 uint32_t processed_packets = 0;
966 bool running = true;
967 uint32_t current_max = 0;
968 uint32_t new_max;
970 /* This is big enough to put on the heap */
971 packet = (uint8_t*)g_malloc(PACKET_MAX_SIZE);
973 do {
974 uint32_t len = 0;
976 /* Query count of available packets in buffer */
977 if (ssh_channel_printf(channel, "show monitor capture buffer %s parameters\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE) {
978 g_free(packet);
979 return;
981 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
982 g_free(packet);
983 return;
985 ws_debug("Read: %s", line);
986 packets_captured_count_ios(line, &new_max, &running);
987 ws_debug("Max counts %d %d", current_max, new_max);
988 if (new_max == current_max) {
989 /* There is no change in count of available packets, repeat the loop */
990 continue;
991 } else if (new_max < current_max) {
992 /* Buffer was cleared, stop */
993 g_free(packet);
994 return;
996 current_max = new_max;
997 ws_debug("New packet count %d\n", current_max);
999 /* Dump buffer */
1000 if (ssh_channel_printf(channel, "show monitor capture buffer %s dump\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE) {
1001 g_free(packet);
1002 return;
1005 /* Process buffer */
1006 if (!process_buffer_response_ios(channel, packet, fp, count, &processed_packets)) {
1007 g_free(packet);
1008 return;
1010 } while (!extcap_end_application && running && (processed_packets < count));
1012 g_free(packet);
1014 /* Discard any subsequent messages */
1015 read_output_bytes_any(channel, -1, NULL);
1018 /* IOS-XE 16: Reads response and parses buffer till prompt received */
1019 static int process_buffer_response_ios_xe_16(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets)
1021 char line[SSH_READ_BLOCK_SIZE + 1];
1022 uint32_t read_packets = 1;
1023 int status = CISCODUMP_PARSER_STARTING;
1024 int loop_end = 0;
1025 unsigned packet_size = 0;
1026 uint32_t len = 0;
1028 /* Process response */
1029 do {
1030 loop_end = 0;
1031 /* Read input till EOLN or prompt */
1032 switch (ssh_channel_read_prompt(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
1033 case READ_PROMPT_EOLN:
1034 status = parse_line_ios_xe_16(packet, &packet_size, line);
1036 if (status == CISCODUMP_PARSER_END_PACKET) {
1037 ws_debug("Read packet %d\n", read_packets);
1038 if (read_packets > *processed_packets) {
1039 int err;
1040 int64_t cur_time = g_get_real_time();
1041 uint64_t bytes_written = 0;
1043 ws_debug("Exporting packet %d\n", *processed_packets);
1044 /* dump the packet to the pcap file */
1045 if (!libpcap_write_packet(fp,
1046 (uint32_t)(cur_time / G_USEC_PER_SEC), (uint32_t)(cur_time % G_USEC_PER_SEC),
1047 packet_size, packet_size, packet, &bytes_written, &err)) {
1048 ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
1049 break;
1051 fflush(fp);
1052 ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
1053 (*processed_packets)++;
1055 packet_size = 0;
1056 read_packets++;
1058 break;
1059 case READ_PROMPT_PROMPT:
1060 ws_debug("Prompt found");
1061 loop_end = 1;
1062 break;
1063 default:
1064 /* We do not have better solution for that cases */
1065 if (extcap_end_application) {
1066 /* End by signal causes timeout so we decrease priority */
1067 ws_debug("Timeout or response was too long\n");
1068 } else {
1069 ws_warning("Timeout or response was too long\n");
1071 return false;
1073 len = 0;
1074 ws_debug("loop end detection %d %d %d %d", extcap_end_application, loop_end, *processed_packets, count);
1075 } while ((!extcap_end_application) && (!loop_end) && (*processed_packets < count));
1077 return true;
1080 /* IOS-XE 17: Reads response and parses buffer till prompt received */
1081 static int process_buffer_response_ios_xe_17(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets)
1083 char line[SSH_READ_BLOCK_SIZE + 1];
1084 uint32_t read_packets = 1;
1085 int status = CISCODUMP_PARSER_STARTING;
1086 int loop_end = 0;
1087 unsigned packet_size = 0;
1088 uint32_t len = 0;
1090 /* Process response */
1091 do {
1092 loop_end = 0;
1093 /* Read input till EOLN */
1094 switch (ssh_channel_read_line_timeout(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
1095 case READ_PROMPT_EOLN:
1096 /* Starting message? Ignore... */
1097 if (NULL != g_strstr_len(line, -1, "Starting the packet display")) {
1098 break;
1101 /* End of dump message? End loop. */
1102 if (NULL != g_strstr_len(line, -1, "Started capture point :")) {
1103 loop_end = 1;
1104 break;
1107 status = parse_line_ios_xe_17(packet, &packet_size, line);
1108 ws_debug("Packet offset %d\n", packet_size);
1110 if ((status == CISCODUMP_PARSER_END_PACKET) && (packet_size > 0)) {
1111 ws_debug("Read packet %d\n", read_packets);
1112 if (read_packets > *processed_packets) {
1113 int err;
1114 int64_t cur_time = g_get_real_time();
1115 uint64_t bytes_written;
1117 ws_debug("Exporting packet %d\n", *processed_packets);
1118 /* dump the packet to the pcap file */
1119 if (!libpcap_write_packet(fp,
1120 (uint32_t)(cur_time / G_USEC_PER_SEC), (uint32_t)(cur_time % G_USEC_PER_SEC),
1121 packet_size, packet_size, packet, &bytes_written, &err)) {
1122 ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
1123 break;
1125 fflush(fp);
1126 ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
1127 (*processed_packets)++;
1129 packet_size = 0;
1130 read_packets++;
1132 break;
1133 case READ_LINE_TIMEOUT:
1134 /* Timeout is OK during reading of IOS XE 17 buffer */
1135 break;
1136 default:
1137 /* We do not have better solution for that cases */
1138 ws_warning("Error or response was too long\n");
1139 return false;
1141 len = 0;
1142 ws_debug("loop end detection %d %d %d %d", extcap_end_application, loop_end, *processed_packets, count);
1143 } while ((!extcap_end_application) && (!loop_end) && (*processed_packets < count));
1145 return true;
1148 /* IOS-XE 16: Queries buffer content and reads it */
1149 static void ssh_loop_read_ios_xe_16(ssh_channel channel, FILE* fp, const uint32_t count)
1151 char line[SSH_READ_BLOCK_SIZE + 1];
1152 uint8_t* packet;
1153 uint32_t processed_packets = 0;
1154 bool running = true;
1155 uint32_t current_max = 0;
1156 uint32_t new_max;
1158 /* This is big enough to put on the heap */
1159 packet = (uint8_t*)g_malloc(PACKET_MAX_SIZE);
1161 do {
1162 uint32_t len = 0;
1164 /* Query count of available packets in buffer */
1165 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) {
1166 g_free(packet);
1167 return;
1169 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1170 g_free(packet);
1171 return;
1173 ws_debug("Read: %s", line);
1174 packets_captured_count_ios_xe_16(line, &new_max, &running);
1175 ws_debug("Max counts %d %d", current_max, new_max);
1176 if (new_max == current_max) {
1177 /* There is no change in count of available packets, repeat the loop */
1178 continue;
1179 } else if (new_max < current_max) {
1180 /* Buffer was cleared, stop */
1181 g_free(packet);
1182 return;
1184 current_max = new_max;
1185 ws_debug("New packet count %d\n", current_max);
1187 /* Dump buffer */
1188 if (ssh_channel_printf(channel, "show monitor capture %s buffer dump\n", WIRESHARK_CAPTURE) == EXIT_FAILURE) {
1189 g_free(packet);
1190 return;
1193 /* Process buffer */
1194 if (!process_buffer_response_ios_xe_16(channel, packet, fp, count, &processed_packets)) {
1195 g_free(packet);
1196 return;
1198 } while (!extcap_end_application && running && (processed_packets < count));
1200 g_free(packet);
1202 /* Discard any subsequent messages */
1203 read_output_bytes_any(channel, -1, NULL);
1206 /* IOS-XE 17: Queries buffer content and reads it */
1207 static void ssh_loop_read_ios_xe_17(ssh_channel channel, FILE* fp, const uint32_t count)
1209 uint8_t* packet;
1210 uint32_t processed_packets = 0;
1211 bool running = true;
1213 /* This is big enough to put on the heap */
1214 packet = (uint8_t*)g_malloc(PACKET_MAX_SIZE);
1216 do {
1217 //uint32_t len = 0;
1219 /* Process buffer */
1220 if (!process_buffer_response_ios_xe_17(channel, packet, fp, count, &processed_packets)) {
1221 g_free(packet);
1222 return;
1224 if (extcap_end_application && send_output_quit) {
1225 ws_debug("Terminating the output\n");
1226 ssh_channel_printf(channel, "\n\x1e\n");
1228 } while (!extcap_end_application && running && (processed_packets < count));
1230 g_free(packet);
1232 /* Discard any subsequent messages */
1233 read_output_bytes_any(channel, -1, NULL);
1236 /* ASA: Reads response and parses buffer till prompt end of packet received */
1237 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)
1239 char line[SSH_READ_BLOCK_SIZE + 1];
1240 uint32_t read_packets = 1;
1241 int status = CISCODUMP_PARSER_STARTING;
1242 int loop_end = 0;
1243 unsigned packet_size = 0;
1245 do {
1246 time_t pkt_time = 0;
1247 uint32_t pkt_usec = 0;
1248 uint32_t len = 0;
1250 /* Dump buffer */
1251 if (ssh_channel_printf(channel, "show cap %s packet-number %ld dump\n", WIRESHARK_CAPTURE, (*processed_packets)+1) == EXIT_FAILURE) {
1252 return false;
1255 /* Process response */
1256 do {
1257 loop_end = 0;
1258 /* Read input till EOLN or prompt */
1259 switch (ssh_channel_read_prompt(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
1260 case READ_PROMPT_EOLN:
1261 status = parse_line_asa(packet, &packet_size, line, current_max, &pkt_time, &pkt_usec);
1263 if (status == CISCODUMP_PARSER_END_PACKET) {
1264 ws_debug("Read packet %d\n", read_packets);
1265 int err;
1266 uint64_t bytes_written;
1268 ws_debug("Exporting packet %d\n", *processed_packets);
1269 /* dump the packet to the pcap file */
1270 if (!libpcap_write_packet(fp,
1271 pkt_time, pkt_usec,
1272 packet_size, packet_size, packet, &bytes_written, &err)) {
1273 ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
1274 break;
1276 fflush(fp);
1277 ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
1278 (*processed_packets)++;
1279 packet_size = 0;
1280 read_packets++;
1281 loop_end = 1;
1283 break;
1284 case READ_PROMPT_PROMPT:
1285 ws_debug("Prompt found");
1286 loop_end = 1;
1287 break;
1288 default:
1289 /* We do not have better solution for that cases */
1290 if (extcap_end_application) {
1291 /* End by signal causes timeout so we decrease priority */
1292 ws_debug("Timeout or response was too long\n");
1293 } else {
1294 ws_warning("Timeout or response was too long\n");
1296 return false;
1298 len = 0;
1299 ws_debug("loop end detection1 %d %d", *processed_packets, count);
1300 } while (!extcap_end_application && !loop_end);
1301 ws_debug("loop end detection2 %d %d %d", extcap_end_application, *processed_packets, count);
1302 } while (!extcap_end_application && (*processed_packets < *current_max) && ((*processed_packets < count)));
1304 return true;
1307 /* ASA: Queries buffer content and reads it */
1308 static void ssh_loop_read_asa(ssh_channel channel, FILE* fp, const uint32_t count)
1310 char line[SSH_READ_BLOCK_SIZE + 1];
1311 uint8_t* packet;
1312 uint32_t processed_packets = 0;
1313 uint32_t current_max = 0;
1314 bool running = true;
1315 uint32_t new_max;
1317 /* This is big enough to put on the heap */
1318 packet = (uint8_t*)g_malloc(PACKET_MAX_SIZE);
1320 do {
1321 uint32_t len = 0;
1323 /* Query count of available packets in buffer */
1324 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) {
1325 g_free(packet);
1326 return;
1328 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1329 g_free(packet);
1330 return;
1332 ws_debug("Read: %s", line);
1333 packets_captured_count_asa(line, &new_max, &running);
1334 ws_debug("Max counts %d %d", current_max, new_max);
1335 if (new_max == current_max) {
1336 /* There is no change in count of available packets, repeat the loop */
1337 continue;
1338 } else if (new_max < current_max) {
1339 /* Buffer was cleared, stop */
1340 g_free(packet);
1341 return;
1343 current_max = new_max;
1344 ws_debug("New packet count %d\n", current_max);
1346 /* Process buffer */
1347 if (!process_buffer_response_asa(channel, packet, fp, count, &processed_packets, &current_max)) {
1348 g_free(packet);
1349 return;
1351 } while (!extcap_end_application && running && (processed_packets < count));
1353 g_free(packet);
1355 /* Discard any subsequent messages */
1356 read_output_bytes_any(channel, -1, NULL);
1360 static void ssh_loop_read(ssh_channel channel, FILE* fp, const uint32_t count _U_, CISCO_SW_TYPE sw_type)
1362 ws_debug("Starting reading loop");
1363 switch (sw_type) {
1364 case CISCO_IOS:
1365 ssh_loop_read_ios(channel, fp, count);
1366 break;
1367 case CISCO_IOS_XE_16:
1368 ssh_loop_read_ios_xe_16(channel, fp, count);
1369 break;
1370 case CISCO_IOS_XE_17:
1371 ssh_loop_read_ios_xe_17(channel, fp, count);
1372 break;
1373 case CISCO_ASA:
1374 ssh_loop_read_asa(channel, fp, count);
1375 break;
1376 case CISCO_UNKNOWN:
1377 break;
1379 ws_debug("Reading loop finished");
1382 static int detect_host_prompt(ssh_channel channel)
1384 char line[SSH_READ_BLOCK_SIZE + 1];
1385 int len = 0;
1386 char prompt_2[SSH_READ_BLOCK_SIZE + 1];
1388 /* Discard any login message */
1389 if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
1390 return EXIT_FAILURE;
1392 if (ssh_channel_printf(channel, "\n") == EXIT_FAILURE)
1393 return EXIT_FAILURE;
1395 /* Check if there is any response to empty line */
1396 switch (ssh_channel_read_line_timeout(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
1397 case READ_LINE_EOLN:
1398 break;
1399 default:
1400 return EXIT_FAILURE;
1403 if (ssh_channel_printf(channel, "\n") == EXIT_FAILURE)
1404 return EXIT_FAILURE;
1406 /* Read prompt_str and level char */
1407 switch (ssh_channel_read_line_timeout(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
1408 case READ_LINE_EOLN:
1409 break;
1410 default:
1411 return EXIT_FAILURE;
1413 if (len > 0) {
1414 g_strlcpy(prompt_str, line, SSH_READ_BLOCK_SIZE + 1);
1416 /* Is there hashtag at the end => enabled mode? */
1417 if (prompt_str[strlen(prompt_str)-1] != '#') {
1418 /* Is there hashtag and space (ASA) at the end => enabled mode? */
1419 if ((prompt_str[strlen(prompt_str)-2] != '#') || (prompt_str[strlen(prompt_str)-1] != ' ')) {
1420 return EXIT_FAILURE;
1423 prompt_len = (int32_t)strlen(prompt_str);
1424 } else {
1425 return EXIT_FAILURE;
1428 if (ssh_channel_printf(channel, "\n") == EXIT_FAILURE)
1429 return EXIT_FAILURE;
1431 /* Read prompt_str and level char again */
1432 switch (ssh_channel_read_line_timeout(channel, line, &len, SSH_READ_BLOCK_SIZE)) {
1433 case READ_LINE_EOLN:
1434 break;
1435 default:
1436 return EXIT_FAILURE;
1438 if (len > 0) {
1439 g_strlcpy(prompt_2, line, SSH_READ_BLOCK_SIZE + 1);
1440 /* Does second prompt_str match first one? */
1441 if (0 == g_strcmp0(prompt_str, prompt_2)) {
1442 ws_debug("Detected prompt %s", prompt_str);
1443 return true;
1447 return EXIT_FAILURE;
1450 static int check_ios_version(ssh_channel channel, CISCO_SW_TYPE *sw_type)
1452 char* cmdline_version = "show version | include Version\n";
1453 const char* msg_ios = "Cisco IOS Software";
1454 const char* msg_ios_xe = "Cisco IOS XE Software";
1455 const char* msg_asa = "Cisco Adaptive Security Appliance Software";
1456 const char* msg_version = "Version ";
1457 char version[255];
1458 int sw_major = 0;
1459 int sw_minor = 0;
1460 char* cur;
1462 memset(version, 0x0, 255);
1464 /* Discard any login message */
1465 if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
1466 return false;
1468 if (ssh_channel_write(channel, cmdline_version, (uint32_t)strlen(cmdline_version)) == SSH_ERROR)
1469 return false;
1470 if (read_output_bytes(channel, 255, version) == EXIT_FAILURE)
1471 return false;
1473 /* Discard any subsequent text */
1474 if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
1475 return false;
1477 /* We should check IOS XE first as its version contains IOS string too */
1478 cur = g_strstr_len(version, strlen(version), msg_ios_xe);
1479 if (cur) {
1480 *sw_type = CISCO_IOS_XE_16;
1481 cur += strlen(msg_ios_xe);
1482 } else {
1483 cur = g_strstr_len(version, strlen(version), msg_ios);
1484 if (cur) {
1485 *sw_type = CISCO_IOS;
1486 cur += strlen(msg_ios);
1487 } else {
1488 cur = g_strstr_len(version, strlen(version), msg_asa);
1489 if (cur) {
1490 *sw_type = CISCO_ASA;
1491 cur += strlen(msg_asa);
1496 if (*sw_type != CISCO_UNKNOWN && cur) {
1497 cur = g_strstr_len(cur, 255-strlen(cur), msg_version);
1498 if (cur) {
1499 cur += strlen(msg_version);
1500 if (sscanf(cur, "%u.%u", &sw_major, &sw_minor) != 2)
1501 return false;
1503 switch (*sw_type) {
1504 case CISCO_IOS:
1505 ws_debug("Current IOS version: %u.%u", sw_major, sw_minor);
1506 if ((sw_major > MINIMUM_IOS_MAJOR) || (sw_major == MINIMUM_IOS_MAJOR && sw_minor >= MINIMUM_IOS_MINOR)) {
1507 return true;
1509 break;
1510 case CISCO_IOS_XE_16:
1511 ws_debug("Current IOS XE version: %u.%u", sw_major, sw_minor);
1512 if ((sw_major > MINIMUM_IOS_XE_MAJOR_17) || (sw_major == MINIMUM_IOS_XE_MAJOR_17 && sw_minor >= MINIMUM_IOS_XE_MINOR_17)) {
1513 *sw_type = CISCO_IOS_XE_17;
1514 return true;
1516 if ((sw_major > MINIMUM_IOS_XE_MAJOR_16) || (sw_major == MINIMUM_IOS_XE_MAJOR_16 && sw_minor >= MINIMUM_IOS_XE_MINOR_16)) {
1517 *sw_type = CISCO_IOS_XE_16;
1518 return true;
1520 break;
1521 case CISCO_ASA:
1522 ws_debug("Current ASA version: %u.%u", sw_major, sw_minor);
1523 if ((sw_major > MINIMUM_ASA_MAJOR) || (sw_major == MINIMUM_ASA_MAJOR && sw_minor >= MINIMUM_ASA_MINOR)) {
1524 return true;
1526 break;
1527 default:
1528 return false;
1530 ws_warning("Recognized software type, but minimal version requirements were not met\n");
1531 return false;
1532 } else {
1533 ws_warning("Recognized software type %d, but unrecognized version\n", *sw_type);
1535 } else {
1536 ws_warning("Unrecognized type of control software.");
1539 return false;
1542 static bool run_capture_ios(ssh_channel channel, const char* iface, const char* cfilter, const uint32_t count)
1544 char* cmdline = NULL;
1545 int ret = 0;
1546 char line[SSH_READ_BLOCK_SIZE + 1];
1547 uint32_t len;
1548 char* iface_copy = g_strdup(iface);
1549 char* iface_one;
1550 char* str = NULL;
1551 int wscp_cnt = 1;
1552 char* wscp_str = NULL;
1554 if (ssh_channel_printf(channel, "terminal length 0\n") == EXIT_FAILURE)
1555 goto error;
1557 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1558 ws_warning("Received response: %s", crtoln(line));
1559 goto error;
1562 if (ssh_channel_printf(channel, "monitor capture buffer %s max-size 9500\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE)
1563 goto error;
1565 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1566 ws_warning("Received response: %s", crtoln(line));
1567 goto error;
1570 if (count > 0) {
1571 if (ssh_channel_printf(channel, "monitor capture buffer %s limit packet-count %u\n", WIRESHARK_CAPTURE_BUFFER, count) == EXIT_FAILURE)
1572 goto error;
1574 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1575 ws_warning("Received response: %s", crtoln(line));
1576 goto error;
1580 if (cfilter) {
1581 char* multiline_filter;
1582 char* chr;
1584 if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
1585 goto error;
1587 if (ssh_channel_printf(channel, "ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
1588 goto error;
1590 multiline_filter = g_strdup(cfilter);
1591 chr = multiline_filter;
1592 while((chr = g_strstr_len(chr, strlen(chr), ",")) != NULL) {
1593 chr[0] = '\n';
1594 ws_debug("Splitting filter into multiline");
1596 ret = ssh_channel_write(channel, multiline_filter, (uint32_t)strlen(multiline_filter));
1597 g_free(multiline_filter);
1598 if (ret == SSH_ERROR)
1599 goto error;
1601 if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
1602 goto error;
1604 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1605 ws_warning("Received response: %s", crtoln(line));
1606 goto error;
1609 if (ssh_channel_printf(channel, "monitor capture buffer %s filter access-list %s\n",
1610 WIRESHARK_CAPTURE_BUFFER, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
1611 goto error;
1613 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1614 ws_warning("Received response: %s", crtoln(line));
1615 goto error;
1619 wscp_cnt = 1;
1620 for (str = iface_copy; ; str = NULL) {
1621 iface_one = strtok(str, ",");
1622 if (iface_one == NULL)
1623 break;
1625 wscp_str = g_strdup_printf("%s_%d", WIRESHARK_CAPTURE_POINT, wscp_cnt);
1626 wscp_cnt++;
1628 if (0 == g_strcmp0(iface_one, "process-switched")) {
1629 cmdline = g_strdup_printf("monitor capture point ip process-switched %s both", wscp_str);
1630 } else if (0 == g_strcmp0(iface_one, "from-us")) {
1631 cmdline = g_strdup_printf("monitor capture point ip process-switched %s from-us", wscp_str);
1632 } else {
1633 cmdline = g_strdup_printf("monitor capture point ip cef %s %s both", wscp_str, iface_one);
1636 if (ssh_channel_printf(channel, "%s\n", cmdline) == EXIT_FAILURE)
1637 goto error;
1639 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1640 ws_warning("Received response: %s", crtoln(line));
1641 goto error;
1644 if (ssh_channel_printf(channel, "monitor capture point associate %s %s \n", wscp_str,
1645 WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE)
1646 goto error;
1648 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1649 ws_warning("Received response: %s", crtoln(line));
1650 goto error;
1653 g_free(cmdline);
1654 cmdline = NULL;
1657 wscp_cnt = 1;
1658 for (str = iface_copy; ; str = NULL) {
1659 iface_one = strtok(str, ",");
1660 if (iface_one == NULL)
1661 break;
1663 wscp_str = g_strdup_printf("%s_%d", WIRESHARK_CAPTURE_POINT, wscp_cnt);
1664 wscp_cnt++;
1666 if (ssh_channel_printf(channel, "monitor capture point start %s\n", wscp_str) == EXIT_FAILURE)
1667 goto error;
1669 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1670 ws_warning("Received response: %s", crtoln(line));
1671 goto error;
1674 g_free(wscp_str);
1675 wscp_str = NULL;
1678 g_free(iface_copy);
1679 return true;
1680 error:
1681 g_free(wscp_str);
1682 g_free(iface_copy);
1683 g_free(cmdline);
1684 ws_warning("Error running ssh remote command");
1686 ssh_channel_close(channel);
1687 ssh_channel_free(channel);
1688 return false;
1691 static bool run_capture_ios_xe_16(ssh_channel channel, const char* iface, const char* cfilter, const uint32_t count)
1693 int ret = 0;
1694 char line[SSH_READ_BLOCK_SIZE + 1];
1695 uint32_t len;
1696 char* iface_copy = g_strdup(iface);
1697 char* iface_one;
1698 char* str = NULL;
1700 if (ssh_channel_printf(channel, "terminal length 0\n") == EXIT_FAILURE)
1701 goto error;
1703 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1704 ws_warning("Received response: %s", crtoln(line));
1705 goto error;
1708 if (ssh_channel_printf(channel, "monitor capture %s limit packet-len 9500\n", WIRESHARK_CAPTURE) == EXIT_FAILURE)
1709 goto error;
1711 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1712 ws_warning("Received response: %s", crtoln(line));
1713 goto error;
1716 if (count > 0) {
1717 if (ssh_channel_printf(channel, "monitor capture %s limit packets %u\n", WIRESHARK_CAPTURE, count) == EXIT_FAILURE)
1718 goto error;
1720 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1721 ws_warning("Received response: %s", crtoln(line));
1722 goto error;
1726 if (cfilter) {
1727 char* multiline_filter;
1728 char* chr;
1730 if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
1731 goto error;
1733 if (ssh_channel_printf(channel, "ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
1734 goto error;
1736 multiline_filter = g_strdup(cfilter);
1737 chr = multiline_filter;
1738 while((chr = g_strstr_len(chr, strlen(chr), ",")) != NULL) {
1739 chr[0] = '\n';
1740 ws_debug("Splitting filter into multiline");
1742 ret = ssh_channel_write(channel, multiline_filter, (uint32_t)strlen(multiline_filter));
1743 g_free(multiline_filter);
1744 if (ret == SSH_ERROR)
1745 goto error;
1747 if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
1748 goto error;
1750 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1751 ws_warning("Received response: %s", crtoln(line));
1752 goto error;
1755 if (ssh_channel_printf(channel, "monitor capture %s access-list %s\n",
1756 WIRESHARK_CAPTURE, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
1757 goto error;
1758 } else {
1759 if (ssh_channel_printf(channel, "monitor capture %s match any\n",
1760 WIRESHARK_CAPTURE) == EXIT_FAILURE)
1761 goto error;
1764 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1765 ws_warning("Received response: %s", crtoln(line));
1766 goto error;
1769 for (str = iface_copy; ; str = NULL) {
1770 iface_one = strtok(str, ",");
1771 if (iface_one == NULL)
1772 break;
1774 if (0 == g_strcmp0(iface_one, "control-plane")) {
1775 if (ssh_channel_printf(channel, "monitor capture %s control-plane both\n", WIRESHARK_CAPTURE
1776 ) == EXIT_FAILURE)
1777 goto error;
1778 } else {
1779 if (ssh_channel_printf(channel, "monitor capture %s interface %s both\n", WIRESHARK_CAPTURE,
1780 iface_one) == EXIT_FAILURE)
1781 goto error;
1785 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1786 ws_warning("Received response: %s", crtoln(line));
1787 goto error;
1790 if (ssh_channel_printf(channel, "monitor capture %s start\n", WIRESHARK_CAPTURE) == EXIT_FAILURE)
1791 goto error;
1793 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE,
1794 "(Capture is not Supported|Unable to activate Capture)")
1796 ws_warning("Received response: %s", crtoln(line));
1797 goto error;
1800 g_free(iface_copy);
1801 return true;
1802 error:
1803 g_free(iface_copy);
1804 ws_warning("Error running ssh remote command");
1806 ssh_channel_close(channel);
1807 ssh_channel_free(channel);
1808 return false;
1811 static bool run_capture_ios_xe_17(ssh_channel channel, const char* iface, const char* cfilter, const uint32_t count)
1813 int ret = 0;
1814 char line[SSH_READ_BLOCK_SIZE + 1];
1815 uint32_t len;
1816 char* iface_copy = g_strdup(iface);
1817 char* iface_one;
1818 char* str = NULL;
1820 if (ssh_channel_printf(channel, "terminal length 0\n") == EXIT_FAILURE)
1821 goto error;
1823 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1824 ws_warning("Received response: %s", crtoln(line));
1825 goto error;
1828 if (ssh_channel_printf(channel, "monitor capture %s limit packet-len 9500\n", WIRESHARK_CAPTURE) == EXIT_FAILURE)
1829 goto error;
1831 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1832 ws_warning("Received response: %s", crtoln(line));
1833 goto error;
1836 if (count > 0) {
1837 if (ssh_channel_printf(channel, "monitor capture %s limit packets %u\n", WIRESHARK_CAPTURE, count) == EXIT_FAILURE)
1838 goto error;
1840 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1841 ws_warning("Received response: %s", crtoln(line));
1842 goto error;
1846 if (cfilter) {
1847 char* multiline_filter;
1848 char* chr;
1850 if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
1851 goto error;
1853 if (ssh_channel_printf(channel, "ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
1854 goto error;
1856 multiline_filter = g_strdup(cfilter);
1857 chr = multiline_filter;
1858 while((chr = g_strstr_len(chr, strlen(chr), ",")) != NULL) {
1859 chr[0] = '\n';
1860 ws_debug("Splitting filter into multiline");
1862 ret = ssh_channel_write(channel, multiline_filter, (uint32_t)strlen(multiline_filter));
1863 g_free(multiline_filter);
1864 if (ret == SSH_ERROR)
1865 goto error;
1867 if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
1868 goto error;
1870 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1871 ws_warning("Received response: %s", crtoln(line));
1872 goto error;
1875 if (ssh_channel_printf(channel, "monitor capture %s access-list %s\n",
1876 WIRESHARK_CAPTURE, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
1877 goto error;
1878 } else {
1879 if (ssh_channel_printf(channel, "monitor capture %s match any\n",
1880 WIRESHARK_CAPTURE) == EXIT_FAILURE)
1881 goto error;
1884 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1885 ws_warning("Received response: %s", crtoln(line));
1886 goto error;
1889 for (str = iface_copy; ; str = NULL) {
1890 iface_one = strtok(str, ",");
1891 if (iface_one == NULL)
1892 break;
1894 if (0 == g_strcmp0(iface_one, "control-plane")) {
1895 if (ssh_channel_printf(channel, "monitor capture %s control-plane both\n", WIRESHARK_CAPTURE
1896 ) == EXIT_FAILURE)
1897 goto error;
1898 } else {
1899 if (ssh_channel_printf(channel, "monitor capture %s interface %s both\n", WIRESHARK_CAPTURE,
1900 iface_one) == EXIT_FAILURE)
1901 goto error;
1905 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1906 ws_warning("Received response: %s", crtoln(line));
1907 goto error;
1910 if (ssh_channel_printf(channel, "monitor capture %s start display dump\n", WIRESHARK_CAPTURE) == EXIT_FAILURE)
1911 goto error;
1914 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE,
1915 "(Capture is not Supported|Unable to activate Capture)")
1917 ws_warning("Received response: %s", crtoln(line));
1918 goto error;
1922 g_free(iface_copy);
1923 return true;
1924 error:
1925 g_free(iface_copy);
1926 ws_warning("Error running ssh remote command");
1928 ssh_channel_close(channel);
1929 ssh_channel_free(channel);
1930 return false;
1933 static bool run_capture_asa(ssh_channel channel, const char* iface, const char* cfilter)
1935 char* cmdline = NULL;
1936 char line[SSH_READ_BLOCK_SIZE + 1];
1937 uint32_t len;
1938 char *sep;
1939 bool process_filter = true;
1940 char* iface_copy = g_strdup(iface);
1941 char* iface_one;
1942 char* str = NULL;
1944 if (ssh_channel_printf(channel, "terminal pager 0\n") == EXIT_FAILURE)
1945 goto error;
1947 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
1948 ws_warning("Received response: %s", crtoln(line));
1949 goto error;
1952 for (str = iface_copy; ; str = NULL) {
1953 iface_one = strtok(str, ",");
1954 if (iface_one == NULL)
1955 break;
1957 if (0 == g_strcmp0(iface_one, "asp-drop")) {
1958 /* asp-drop: asp-drop: capture %s type asp-drop all packet-length 9216 !INCLUDE-DECRYPTED
1960 cmdline = g_strdup_printf("capture %s type asp-drop all packet-length 9216", WIRESHARK_CAPTURE);
1961 } else if (NULL != (sep = g_strstr_len(iface_one, -1, "---"))) {
1962 /* Interface type separator found. We support:
1963 * isakmp---ifname: capture %s type isakmp packet-length 32810 interface %s
1964 * // webvpn---ifname: capture %s type webvpn user %s !NO FILTER !INCLUDE-DECRYPTED
1965 * lacp---ifname: capture %s type lacp interface %s packet-length 9216 !NO FILTER !INCLUDE-DECRYPTED
1966 * tls-proxy---ifname: capture %s type tls-proxy packet-length 9216 interface %s
1967 * inline-tag---ifname: capture %s type inline-tag packet-length 9216 interface %s
1968 * raw-data---ifname: capture %s type rawdata packet-length 9216 interface %s
1970 * We support /decrypted for some of it:
1971 * isakmp/decrypted---ifname
1972 * tls-proxy/decrypted---ifname
1973 * inline-tag/decrypted---ifname
1974 * raw-data/decrypted---ifname
1976 char* ifname = sep+3;
1978 if (strstr(iface_one, "isakmp")) {
1979 if (strstr(iface_one, "/decrypted")) {
1980 cmdline = g_strdup_printf("capture %s type isakmp include-decrypted packet-length 32810 interface %s", WIRESHARK_CAPTURE, ifname);
1981 } else {
1982 cmdline = g_strdup_printf("capture %s type isakmp packet-length 32810 interface %s", WIRESHARK_CAPTURE, ifname);
1984 /* Completelly different output
1985 } else if (strstr(iface_one, "webvpn")) {
1986 cmdline = g_strdup_printf("capture %s type webvpn user %s", WIRESHARK_CAPTURE, ifname);
1987 process_filter = false;
1989 } else if (strstr(iface_one, "lacp")) {
1990 cmdline = g_strdup_printf("capture %s type lacp interface %s packet-length 9216", WIRESHARK_CAPTURE, ifname);
1991 process_filter = false;
1992 } else if (strstr(iface_one, "tls-proxy")) {
1993 if (strstr(iface_one, "/decrypted")) {
1994 cmdline = g_strdup_printf("capture %s type tls-proxy include-decrypted packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
1995 } else {
1996 cmdline = g_strdup_printf("capture %s type tls-proxy packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
1998 } else if (strstr(iface_one, "inline-tag")) {
1999 if (strstr(iface_one, "/decrypted")) {
2000 cmdline = g_strdup_printf("capture %s type inline-tag include-decrypted packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
2001 } else {
2002 cmdline = g_strdup_printf("capture %s type inline-tag packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
2004 } else if (strstr(iface_one, "raw-data")) {
2005 if (strstr(iface_one, "/decrypted")) {
2006 cmdline = g_strdup_printf("capture %s type raw-data include-decrypted packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
2007 } else {
2008 cmdline = g_strdup_printf("capture %s type raw-data packet-length 9216 interface %s", WIRESHARK_CAPTURE, ifname);
2010 } else {
2011 ws_warning("Unknown interface type : %s", iface_one);
2012 goto error;
2014 } else {
2015 /* Just interface name */
2016 cmdline = g_strdup_printf("capture %s type raw-data packet-length 9216 interface %s", WIRESHARK_CAPTURE, iface_one);
2019 if (ssh_channel_printf(channel, "%s\n", cmdline) == EXIT_FAILURE)
2020 goto error;
2022 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
2023 ws_warning("Received response: %s", crtoln(line));
2024 goto error;
2027 g_free(cmdline);
2028 cmdline = NULL;
2031 if (process_filter && cfilter) {
2032 char* multiline_filter;
2033 char* chr;
2034 char* start;
2036 if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
2037 goto error;
2039 multiline_filter = g_strdup(cfilter);
2040 start = multiline_filter;
2041 while((chr = g_strstr_len(start, strlen(start), ",")) != NULL) {
2042 chr[0] = '\0';
2043 ws_debug("Splitting filter into multiline");
2044 if (ssh_channel_printf(channel, "access-list %s %s\n", WIRESHARK_CAPTURE_ACCESSLIST, start) == EXIT_FAILURE)
2045 goto error;
2046 start = chr+1;
2049 if (ssh_channel_printf(channel, "access-list %s %s\n", WIRESHARK_CAPTURE_ACCESSLIST, start) == EXIT_FAILURE)
2050 goto error;
2052 if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
2053 goto error;
2055 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
2056 ws_warning("Received response: %s", crtoln(line));
2057 goto error;
2060 if (ssh_channel_printf(channel, "capture %s access-list %s\n", WIRESHARK_CAPTURE, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
2061 goto error;
2063 if (!ssh_channel_wait_prompt_check_error(channel, line, &len, SSH_READ_BLOCK_SIZE, NULL)) {
2064 ws_warning("Received response: %s", crtoln(line));
2065 goto error;
2069 g_free(iface_copy);
2070 return true;
2071 error:
2072 g_free(iface_copy);
2073 g_free(cmdline);
2074 ws_warning("Error running ssh remote command");
2076 ssh_channel_close(channel);
2077 ssh_channel_free(channel);
2078 return false;
2081 static ssh_channel open_channel(ssh_session sshs)
2083 ssh_channel channel;
2085 channel = ssh_channel_new(sshs);
2086 if (!channel)
2087 return NULL;
2089 if (ssh_channel_open_session(channel) != SSH_OK)
2090 goto error;
2092 if (ssh_channel_request_pty(channel) != SSH_OK)
2093 goto error;
2095 if (ssh_channel_change_pty_size(channel, 80, 24) != SSH_OK)
2096 goto error;
2098 if (ssh_channel_request_shell(channel) != SSH_OK)
2099 goto error;
2101 return channel;
2103 error:
2104 ws_warning("Error running ssh remote command");
2106 ssh_channel_close(channel);
2107 ssh_channel_free(channel);
2108 return NULL;
2111 static bool run_capture(ssh_channel channel, const char* iface, const char* cfilter, const uint32_t count, CISCO_SW_TYPE sw_type)
2113 switch (sw_type) {
2114 case CISCO_IOS:
2115 return run_capture_ios(channel, iface, cfilter, count);
2116 case CISCO_IOS_XE_16:
2117 return run_capture_ios_xe_16(channel, iface, cfilter, count);
2118 case CISCO_IOS_XE_17:
2119 return run_capture_ios_xe_17(channel, iface, cfilter, count);
2120 case CISCO_ASA:
2121 return run_capture_asa(channel, iface, cfilter);
2122 case CISCO_UNKNOWN:
2123 ws_warning("Unsupported cisco software. It will not collect any data most probably!");
2124 return false;
2127 return false;
2130 static int ssh_open_remote_connection(const ssh_params_t* ssh_params, const char* iface, const char* cfilter,
2131 const uint32_t count, const char* fifo)
2133 ssh_session sshs;
2134 ssh_channel channel;
2135 FILE* fp = stdout;
2136 uint64_t bytes_written = 0;
2137 int err;
2138 int ret = EXIT_FAILURE;
2139 char* err_info = NULL;
2141 if (g_strcmp0(fifo, "-")) {
2142 /* Open or create the output file */
2143 fp = fopen(fifo, "wb");
2144 if (!fp) {
2145 ws_warning("Error creating output file: %s", g_strerror(errno));
2146 return EXIT_FAILURE;
2150 if (!libpcap_write_file_header(fp, 1, PCAP_SNAPLEN, false, &bytes_written, &err)) {
2151 ws_warning("Can't write pcap file header");
2152 goto cleanup;
2155 fflush(fp);
2157 ws_debug("Create first ssh session");
2158 sshs = create_ssh_connection(ssh_params, &err_info);
2159 if (!sshs) {
2160 ws_warning("Error creating connection: %s", err_info);
2161 goto cleanup;
2164 channel = open_channel(sshs);
2165 if (!channel) {
2166 ret = EXIT_FAILURE;
2167 goto cleanup;
2170 if (!detect_host_prompt(channel))
2171 goto cleanup;
2173 if (!check_ios_version(channel, &global_sw_type))
2174 goto cleanup;
2176 /* clean up and exit */
2177 ciscodump_cleanup(channel, iface, cfilter, global_sw_type);
2179 if (!run_capture(channel, iface, cfilter, count, global_sw_type)) {
2180 ret = EXIT_FAILURE;
2181 goto cleanup;
2184 /* read from channel and write into fp */
2185 ssh_loop_read(channel, fp, count, global_sw_type);
2187 /* Read loop can be terminated by signal or QUIT command in
2188 * mid of long "show" command and its reading can take really
2189 * long time. So we terminate ssh session and then
2190 * create new one to cleanup configuration
2192 ws_debug("Disconnect first ssh session");
2193 ssh_channel_close(channel);
2194 ssh_channel_free(channel);
2195 ssh_cleanup(&sshs, &channel);
2197 ws_debug("Create second ssh session");
2198 sshs = create_ssh_connection(ssh_params, &err_info);
2199 if (!sshs) {
2200 ws_warning("Error creating connection: %s", err_info);
2201 goto cleanup;
2204 channel = open_channel(sshs);
2205 if (!channel) {
2206 ret = EXIT_FAILURE;
2207 goto cleanup;
2210 /* clean up and exit */
2211 ciscodump_cleanup(channel, iface, cfilter, global_sw_type);
2213 ssh_channel_close(channel);
2214 ssh_channel_free(channel);
2215 ssh_cleanup(&sshs, &channel);
2217 ret = EXIT_SUCCESS;
2218 cleanup:
2219 if (fp != stdout)
2220 fclose(fp);
2222 return ret;
2225 static int list_config(char *interface, unsigned int remote_port)
2227 unsigned inc = 0;
2228 char* ipfilter;
2230 if (!interface) {
2231 ws_warning("No interface specified.");
2232 return EXIT_FAILURE;
2235 if (g_strcmp0(interface, CISCODUMP_EXTCAP_INTERFACE)) {
2236 ws_warning("interface must be %s", CISCODUMP_EXTCAP_INTERFACE);
2237 return EXIT_FAILURE;
2240 ipfilter = local_interfaces_to_filter(remote_port);
2242 printf("arg {number=%u}{call=--remote-host}{display=Remote SSH server address}"
2243 "{type=string}{tooltip=The remote SSH host. It can be both "
2244 "an IP address or a hostname}{required=true}{group=Server}\n", inc++);
2245 printf("arg {number=%u}{call=--remote-port}{display=Remote SSH server port}"
2246 "{type=unsigned}{default=22}{tooltip=The remote SSH host port (1-65535)}"
2247 "{range=1,65535}{group=Server}\n", inc++);
2248 printf("arg {number=%u}{call=--remote-username}{display=Remote SSH server username}"
2249 "{type=string}{default=%s}{tooltip=The remote SSH username. If not provided, "
2250 "the current user will be used}{group=Authentication}\n", inc++, g_get_user_name());
2251 printf("arg {number=%u}{call=--remote-password}{display=Remote SSH server password}"
2252 "{type=password}{tooltip=The SSH password, used when other methods (SSH agent "
2253 "or key files) are unavailable.}{group=Authentication}\n", inc++);
2254 printf("arg {number=%u}{call=--sshkey}{display=Path to SSH private key}"
2255 "{type=fileselect}{tooltip=The path on the local filesystem of the private ssh key}"
2256 "{group=Authentication}\n", inc++);
2257 printf("arg {number=%u}{call=--proxycommand}{display=ProxyCommand}"
2258 "{type=string}{tooltip=The command to use as proxy for the SSH connection}"
2259 "{group=Authentication}\n", inc++);
2260 printf("arg {number=%u}{call--sshkey-passphrase}{display=SSH key passphrase}"
2261 "{type=password}{tooltip=Passphrase to unlock the SSH private key}"
2262 "{group=Authentication\n", inc++);
2263 printf("arg {number=%u}{call=--ssh-sha1}{display=Support SHA-1 keys (deprecated)}"
2264 "{type=boolflag}{tooltip=Support keys and key exchange algorithms using SHA-1 (deprecated)}{group=Authentication}"
2265 "\n", inc++);
2266 printf("arg {number=%u}{call=--remote-interface}{display=Remote interface}"
2267 "{type=string}{required=true}{tooltip=The remote network interface used for capture"
2268 "}{group=Capture}\n", inc++);
2269 printf("arg {number=%u}{call=--remote-filter}{display=Remote capture filter}"
2270 "{type=string}{tooltip=The remote capture filter}", inc++);
2271 if (ipfilter)
2272 printf("{default=%s}", ipfilter);
2273 printf("{group=Capture}\n");
2274 printf("arg {number=%u}{call=--remote-count}{display=Packets to capture}"
2275 "{type=unsigned}{required=true}{tooltip=The number of remote packets to capture.}"
2276 "{group=Capture}\n", inc++);
2278 extcap_config_debug(&inc);
2280 g_free(ipfilter);
2282 return EXIT_SUCCESS;
2285 int main(int argc, char *argv[])
2287 char* err_msg;
2288 int result;
2289 int option_idx = 0;
2290 ssh_params_t* ssh_params = ssh_params_new();
2291 char* remote_interface = NULL;
2292 char* remote_filter = NULL;
2293 uint32_t count = 0;
2294 int ret = EXIT_FAILURE;
2295 extcap_parameters * extcap_conf = g_new0(extcap_parameters, 1);
2296 char* help_url;
2297 char* help_header = NULL;
2299 /* Initialize log handler early so we can have proper logging during startup. */
2300 extcap_log_init("ciscodump");
2303 * Get credential information for later use.
2305 init_process_policies();
2308 * Attempt to get the pathname of the directory containing the
2309 * executable file.
2311 err_msg = configuration_init(argv[0], NULL);
2312 if (err_msg != NULL) {
2313 ws_warning("Can't get pathname of directory containing the extcap program: %s.",
2314 err_msg);
2315 g_free(err_msg);
2318 help_url = data_file_url("ciscodump.html");
2319 extcap_base_set_util_info(extcap_conf, argv[0], CISCODUMP_VERSION_MAJOR, CISCODUMP_VERSION_MINOR,
2320 CISCODUMP_VERSION_RELEASE, help_url);
2321 add_libssh_info(extcap_conf);
2322 g_free(help_url);
2323 extcap_base_register_interface(extcap_conf, CISCODUMP_EXTCAP_INTERFACE, "Cisco remote capture", 147, "Remote capture dependent DLT");
2324 if (!extcap_base_register_graceful_shutdown_cb(extcap_conf, graceful_shutdown_cb)) {
2325 ret = EXIT_FAILURE;
2326 goto end;
2329 help_header = ws_strdup_printf(
2330 " %s --extcap-interfaces\n"
2331 " %s --extcap-interface=%s --extcap-dlts\n"
2332 " %s --extcap-interface=%s --extcap-config\n"
2333 " %s --extcap-interface=%s --remote-host myhost --remote-port 22222 "
2334 "--remote-username myuser --remote-interface gigabit0/0 "
2335 "--fifo=FILENAME --capture\n", argv[0], argv[0], CISCODUMP_EXTCAP_INTERFACE, argv[0],
2336 CISCODUMP_EXTCAP_INTERFACE, argv[0], CISCODUMP_EXTCAP_INTERFACE);
2337 extcap_help_add_header(extcap_conf, help_header);
2338 g_free(help_header);
2340 extcap_help_add_option(extcap_conf, "--help", "print this help");
2341 extcap_help_add_option(extcap_conf, "--version", "print the version");
2342 extcap_help_add_option(extcap_conf, "--remote-host <host>", "the remote SSH host");
2343 extcap_help_add_option(extcap_conf, "--remote-port <port>", "the remote SSH port (default: 22)");
2344 extcap_help_add_option(extcap_conf, "--remote-username <username>", "the remote SSH username (default: the current user)");
2345 extcap_help_add_option(extcap_conf, "--remote-password <password>", "the remote SSH password. "
2346 "If not specified, ssh-agent and ssh-key are used");
2347 extcap_help_add_option(extcap_conf, "--sshkey <public key path>", "the path of the ssh key");
2348 extcap_help_add_option(extcap_conf, "--sshkey-passphrase <public key passphrase>", "the passphrase to unlock public ssh");
2349 extcap_help_add_option(extcap_conf, "--proxycommand <proxy command>", "the command to use as proxy for the ssh connection");
2350 extcap_help_add_option(extcap_conf, "--ssh-sha1", "support keys and key exchange using SHA-1 (deprecated)");
2351 extcap_help_add_option(extcap_conf, "--remote-interface <iface>", "the remote capture interface");
2352 extcap_help_add_option(extcap_conf, "--remote-filter <filter>", "a filter for remote capture "
2353 "(default: don't capture data for all interfaces IPs)");
2355 ws_opterr = 0;
2356 ws_optind = 0;
2358 if (argc == 1) {
2359 extcap_help_print(extcap_conf);
2360 goto end;
2363 while ((result = ws_getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) {
2365 switch (result) {
2367 case OPT_HELP:
2368 extcap_help_print(extcap_conf);
2369 ret = EXIT_SUCCESS;
2370 goto end;
2372 case OPT_VERSION:
2373 extcap_version_print(extcap_conf);
2374 goto end;
2376 case OPT_REMOTE_HOST:
2377 g_free(ssh_params->host);
2378 ssh_params->host = g_strdup(ws_optarg);
2379 break;
2381 case OPT_REMOTE_PORT:
2382 if (!ws_strtou16(ws_optarg, NULL, &ssh_params->port) || ssh_params->port == 0) {
2383 ws_warning("Invalid port: %s", ws_optarg);
2384 goto end;
2386 break;
2388 case OPT_REMOTE_USERNAME:
2389 g_free(ssh_params->username);
2390 ssh_params->username = g_strdup(ws_optarg);
2391 break;
2393 case OPT_REMOTE_PASSWORD:
2394 g_free(ssh_params->password);
2395 ssh_params->password = g_strdup(ws_optarg);
2396 memset(ws_optarg, 'X', strlen(ws_optarg));
2397 break;
2399 case OPT_SSHKEY:
2400 g_free(ssh_params->sshkey_path);
2401 ssh_params->sshkey_path = g_strdup(ws_optarg);
2402 break;
2404 case OPT_SSHKEY_PASSPHRASE:
2405 g_free(ssh_params->sshkey_passphrase);
2406 ssh_params->sshkey_passphrase = g_strdup(ws_optarg);
2407 memset(ws_optarg, 'X', strlen(ws_optarg));
2408 break;
2410 case OPT_PROXYCOMMAND:
2411 g_free(ssh_params->proxycommand);
2412 ssh_params->proxycommand = g_strdup(ws_optarg);
2413 break;
2415 case OPT_SSH_SHA1:
2416 ssh_params->ssh_sha1 = true;
2417 break;
2419 case OPT_REMOTE_INTERFACE:
2420 g_free(remote_interface);
2421 remote_interface = g_strdup(ws_optarg);
2422 break;
2424 case OPT_REMOTE_FILTER:
2425 g_free(remote_filter);
2426 remote_filter = g_strdup(ws_optarg);
2427 break;
2429 case OPT_REMOTE_COUNT:
2430 if (!ws_strtou32(ws_optarg, NULL, &count)) {
2431 ws_warning("Invalid packet count: %s", ws_optarg);
2432 goto end;
2434 break;
2436 case ':':
2437 /* missing option argument */
2438 ws_warning("Option '%s' requires an argument", argv[ws_optind - 1]);
2439 break;
2441 default:
2442 if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, ws_optarg)) {
2443 ws_warning("Invalid option: %s", argv[ws_optind - 1]);
2444 goto end;
2449 extcap_cmdline_debug(argv, argc);
2451 if (ws_optind != argc) {
2452 ws_warning("Unexpected extra option: %s", argv[ws_optind]);
2453 goto end;
2456 if (extcap_base_handle_interface(extcap_conf)) {
2457 ret = EXIT_SUCCESS;
2458 goto end;
2461 if (extcap_conf->show_config) {
2462 unsigned int port;
2463 if (!ws_strtou16(ws_optarg, NULL, &ssh_params->port) || ssh_params->port == 0) {
2464 port = 22;
2465 } else {
2466 port = ssh_params->port;
2468 ret = list_config(extcap_conf->interface, port);
2469 goto end;
2472 err_msg = ws_init_sockets();
2473 if (err_msg != NULL) {
2474 ws_warning("ERROR: %s", err_msg);
2475 g_free(err_msg);
2476 ws_warning("%s", please_report_bug());
2477 goto end;
2480 if (extcap_conf->capture) {
2481 if (!ssh_params->host) {
2482 ws_warning("Missing parameter: --remote-host");
2483 goto end;
2486 if (!remote_interface) {
2487 ws_warning("ERROR: No interface specified (--remote-interface)");
2488 goto end;
2490 if (count == 0) {
2491 ws_warning("ERROR: count of packets must be specified (--remote-count)");
2492 goto end;
2494 ssh_params_set_log_level(ssh_params, extcap_conf->debug);
2495 ret = ssh_open_remote_connection(ssh_params, remote_interface,
2496 remote_filter, count, extcap_conf->fifo);
2497 } else {
2498 ws_debug("You should not come here... maybe some parameter missing?");
2499 ret = EXIT_FAILURE;
2502 end:
2503 ssh_params_free(ssh_params);
2504 g_free(remote_interface);
2505 g_free(remote_filter);
2506 extcap_base_cleanup(&extcap_conf);
2507 return ret;
2511 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2513 * Local variables:
2514 * c-basic-offset: 8
2515 * tab-width: 8
2516 * indent-tabs-mode: t
2517 * End:
2519 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2520 * :indentSize=8:tabSize=8:noTabs=false: