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
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>
30 #include <wsutil/time_util.h>
31 #include <wsutil/ws_strptime.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 */
84 /* Status of the parser */
86 CISCODUMP_PARSER_STARTING
,
87 CISCODUMP_PARSER_IN_PACKET
,
88 CISCODUMP_PARSER_IN_HEADER
,
89 CISCODUMP_PARSER_END_PACKET
,
90 CISCODUMP_PARSER_UNKNOWN
94 EXTCAP_BASE_OPTIONS_ENUM
,
101 OPT_REMOTE_INTERFACE
,
104 OPT_SSHKEY_PASSPHRASE
,
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
[] = {
117 { "help", ws_no_argument
, NULL
, OPT_HELP
},
118 { "version", ws_no_argument
, NULL
, OPT_VERSION
},
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
) {
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
);
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
);
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
);
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
)
185 total
= (bytes
> 0 ? bytes
: INT_MAX
);
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
);
191 outbuf
[bytes_read
] = chr
;
195 outbuf
[bytes_read
+1] = '\0';
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
)
208 total
= (bytes
> 0 ? bytes
: INT_MAX
);
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
);
216 outbuf
[bytes_read
] = chr
;
222 /* Reads input to buffer and parses EOL
223 * If line is NULL, just received count of characters in len is calculated
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
) {
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) {
251 /* Parse the current line */
255 return READ_LINE_EOLN
;
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
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
) {
275 int64_t start_time
= g_get_monotonic_time();
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) {
288 /* Parse the current line */
290 ws_noisy(" exiting: READ_PROMPT_EOLN (%d/%d)", *len
, max_len
);
291 return READ_PROMPT_EOLN
;
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
))) {
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
))) {
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
) {
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
);
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];
328 memset(line2
, 0x0, SSH_READ_BLOCK_SIZE
+ 1);
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
);
338 case READ_PROMPT_PROMPT
:
339 *len
= (uint32_t)g_strlcat(line
, line2
, max_len
);
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
) {
364 /* Is there ERROR: text in output? */
365 if (NULL
!= g_strstr_len(line
, -1, "ERROR:")) {
369 /* Is there ERROR: text in output? */
370 if (NULL
!= g_strstr_len(line
, -1, "% Invalid input detected at")) {
374 /* Is there error_re text in output? */
376 g_regex_match_simple(error_re
, line
, (GRegexCompileFlags
) (G_REGEX_CASELESS
| G_REGEX_RAW
), 0)
384 static void ciscodump_cleanup_ios(ssh_channel channel
, const char* iface
, const char* cfilter
)
386 char* iface_copy
= g_strdup(iface
);
390 char* wscp_str
= NULL
;
392 extcap_end_application
= false;
394 ws_debug("Removing configuration...");
395 read_output_bytes(channel
, -1, NULL
);
398 for (str
= iface_copy
; ; str
= NULL
) {
399 iface_one
= strtok(str
, ",");
400 if (iface_one
== NULL
)
403 wscp_str
= g_strdup_printf("%s_%d", WIRESHARK_CAPTURE_POINT
, 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
);
413 ssh_channel_printf(channel
, "no monitor capture buffer %s\n", WIRESHARK_CAPTURE_BUFFER
);
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");
426 static void ciscodump_cleanup_ios_xe_16(ssh_channel channel
, const char* cfilter
)
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
);
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
)
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
);
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
)
467 ws_debug("Removing configuration...");
468 read_output_bytes(channel
, -1, NULL
);
470 ssh_channel_printf(channel
, "no capture %s\n", WIRESHARK_CAPTURE
);
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");
487 ciscodump_cleanup_ios(channel
, iface
, cfilter
);
489 case CISCO_IOS_XE_16
:
490 ciscodump_cleanup_ios_xe_16(channel
, cfilter
);
492 case CISCO_IOS_XE_17
:
493 ciscodump_cleanup_ios_xe_17(channel
, cfilter
);
496 ciscodump_cleanup_asa(channel
, cfilter
);
501 ws_debug("Config cleanup finished");
504 static void packets_captured_count_ios(char *line
, uint32_t *max
, bool *running
) {
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)) {
517 if (strlen(*(part
+1)) > 0) {
518 ws_strtou32(*(part
+1), NULL
, max
);
524 if (g_regex_match_simple("Status : Active", line
, (GRegexCompileFlags
) (G_REGEX_CASELESS
| G_REGEX_RAW
), 0)) {
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
) {
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)) {
543 if (strlen(*(part
+1)) > 0) {
544 ws_strtou32(*(part
+1), NULL
, max
);
550 /* Check if capture is running */
551 if (g_regex_match_simple("Status : Active", line
, (GRegexCompileFlags
) (G_REGEX_CASELESS
| G_REGEX_RAW
), 0)) {
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
) {
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)) {
571 if (strlen(*(part
+1)) > 0) {
572 ws_strtou32(*(part
+1), NULL
, max
);
577 if (running
!= NULL
) {
579 /* Check if capture is running */
580 if (g_regex_match_simple("\\[Capturing -", line
, (GRegexCompileFlags
) (G_REGEX_CASELESS
| G_REGEX_RAW
), 0)) {
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
)
595 if (strlen(line
) <= 1) {
596 if (status
== CISCODUMP_PARSER_IN_PACKET
)
597 return CISCODUMP_PARSER_END_PACKET
;
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)) {
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 */
652 memcpy(&tm
, tm2
, sizeof(struct tm
));
655 ws_strtou32(*(parts
+2), NULL
, pkt_usec
);
657 *pkt_time
= mktime(&tm
);
660 return CISCODUMP_PARSER_IN_HEADER
;
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);
674 if (*part
&& *(part
+1)) {
675 /* There is at least one match. Skip first string */
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
;
690 return CISCODUMP_PARSER_IN_PACKET
;
693 static int parse_line_ios_xe_16(uint8_t* packet
, unsigned* offset
, char* line
)
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 ................
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 ................
724 /* we got the packet header */
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. */
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);
741 if (*part
&& *(part
+1)) {
742 /* There is at least one match. Skip first string */
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
;
757 return CISCODUMP_PARSER_IN_PACKET
;
760 static int parse_line_ios_xe_17(uint8_t* packet
, unsigned* offset
, char* line
)
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 */
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);
788 if (*part
&& *(part
+1)) {
789 /* There is at least one match. Skip first string */
792 if (strlen(*part
) > 1) {
793 ws_hexstrtou8(*part
, NULL
, &value
);
794 memcpy(packet
+ *offset
, &value
, 1);
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
)
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 ................
831 /* Update count of available packets */
832 /* 4599 packets captured */
833 packets_captured_count_asa(line
, &new_max
, NULL
);
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)) {
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
);
856 return CISCODUMP_PARSER_IN_HEADER
;
860 /* we got the packet tail */
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. */
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);
877 if (*part
&& *(part
+1)) {
878 /* There is at least one match. Skip first string */
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
;
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
;
903 unsigned packet_size
= 0;
905 uint32_t pkt_usec
= 0;
908 /* Process response */
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
) {
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
,
927 packet_size
, packet_size
, packet
, &bytes_written
, &err
)) {
928 ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err
));
932 ws_debug("Dumped packet %u size: %u\n", *processed_packets
, packet_size
);
933 (*processed_packets
)++;
939 case READ_PROMPT_PROMPT
:
940 ws_debug("Prompt found");
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");
949 ws_warning("Timeout or response was too long\n");
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
));
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];
965 uint32_t processed_packets
= 0;
967 uint32_t current_max
= 0;
970 /* This is big enough to put on the heap */
971 packet
= (uint8_t*)g_malloc(PACKET_MAX_SIZE
);
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
) {
981 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
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 */
991 } else if (new_max
< current_max
) {
992 /* Buffer was cleared, stop */
996 current_max
= new_max
;
997 ws_debug("New packet count %d\n", current_max
);
1000 if (ssh_channel_printf(channel
, "show monitor capture buffer %s dump\n", WIRESHARK_CAPTURE_BUFFER
) == EXIT_FAILURE
) {
1005 /* Process buffer */
1006 if (!process_buffer_response_ios(channel
, packet
, fp
, count
, &processed_packets
)) {
1010 } while (!extcap_end_application
&& running
&& (processed_packets
< count
));
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
;
1025 unsigned packet_size
= 0;
1028 /* Process response */
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
) {
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
));
1052 ws_debug("Dumped packet %u size: %u\n", *processed_packets
, packet_size
);
1053 (*processed_packets
)++;
1059 case READ_PROMPT_PROMPT
:
1060 ws_debug("Prompt found");
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");
1069 ws_warning("Timeout or response was too long\n");
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
));
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
;
1087 unsigned packet_size
= 0;
1090 /* Process response */
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")) {
1101 /* End of dump message? End loop. */
1102 if (NULL
!= g_strstr_len(line
, -1, "Started capture point :")) {
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
) {
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
));
1126 ws_debug("Dumped packet %u size: %u\n", *processed_packets
, packet_size
);
1127 (*processed_packets
)++;
1133 case READ_LINE_TIMEOUT
:
1134 /* Timeout is OK during reading of IOS XE 17 buffer */
1137 /* We do not have better solution for that cases */
1138 ws_warning("Error or response was too long\n");
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
));
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];
1153 uint32_t processed_packets
= 0;
1154 bool running
= true;
1155 uint32_t current_max
= 0;
1158 /* This is big enough to put on the heap */
1159 packet
= (uint8_t*)g_malloc(PACKET_MAX_SIZE
);
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
) {
1169 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
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 */
1179 } else if (new_max
< current_max
) {
1180 /* Buffer was cleared, stop */
1184 current_max
= new_max
;
1185 ws_debug("New packet count %d\n", current_max
);
1188 if (ssh_channel_printf(channel
, "show monitor capture %s buffer dump\n", WIRESHARK_CAPTURE
) == EXIT_FAILURE
) {
1193 /* Process buffer */
1194 if (!process_buffer_response_ios_xe_16(channel
, packet
, fp
, count
, &processed_packets
)) {
1198 } while (!extcap_end_application
&& running
&& (processed_packets
< count
));
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
)
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
);
1219 /* Process buffer */
1220 if (!process_buffer_response_ios_xe_17(channel
, packet
, fp
, count
, &processed_packets
)) {
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
));
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
;
1243 unsigned packet_size
= 0;
1246 time_t pkt_time
= 0;
1247 uint32_t pkt_usec
= 0;
1251 if (ssh_channel_printf(channel
, "show cap %s packet-number %ld dump\n", WIRESHARK_CAPTURE
, (*processed_packets
)+1) == EXIT_FAILURE
) {
1255 /* Process response */
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
);
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
,
1272 packet_size
, packet_size
, packet
, &bytes_written
, &err
)) {
1273 ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err
));
1277 ws_debug("Dumped packet %u size: %u\n", *processed_packets
, packet_size
);
1278 (*processed_packets
)++;
1284 case READ_PROMPT_PROMPT
:
1285 ws_debug("Prompt found");
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");
1294 ws_warning("Timeout or response was too long\n");
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
)));
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];
1312 uint32_t processed_packets
= 0;
1313 uint32_t current_max
= 0;
1314 bool running
= true;
1317 /* This is big enough to put on the heap */
1318 packet
= (uint8_t*)g_malloc(PACKET_MAX_SIZE
);
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
) {
1328 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
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 */
1338 } else if (new_max
< current_max
) {
1339 /* Buffer was cleared, stop */
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
, ¤t_max
)) {
1351 } while (!extcap_end_application
&& running
&& (processed_packets
< count
));
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");
1365 ssh_loop_read_ios(channel
, fp
, count
);
1367 case CISCO_IOS_XE_16
:
1368 ssh_loop_read_ios_xe_16(channel
, fp
, count
);
1370 case CISCO_IOS_XE_17
:
1371 ssh_loop_read_ios_xe_17(channel
, fp
, count
);
1374 ssh_loop_read_asa(channel
, fp
, count
);
1379 ws_debug("Reading loop finished");
1382 static int detect_host_prompt(ssh_channel channel
)
1384 char line
[SSH_READ_BLOCK_SIZE
+ 1];
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
:
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
:
1411 return EXIT_FAILURE
;
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
);
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
:
1436 return EXIT_FAILURE
;
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
);
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 ";
1462 memset(version
, 0x0, 255);
1464 /* Discard any login message */
1465 if (read_output_bytes(channel
, -1, NULL
) == EXIT_FAILURE
)
1468 if (ssh_channel_write(channel
, cmdline_version
, (uint32_t)strlen(cmdline_version
)) == SSH_ERROR
)
1470 if (read_output_bytes(channel
, 255, version
) == EXIT_FAILURE
)
1473 /* Discard any subsequent text */
1474 if (read_output_bytes(channel
, -1, NULL
) == EXIT_FAILURE
)
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
);
1480 *sw_type
= CISCO_IOS_XE_16
;
1481 cur
+= strlen(msg_ios_xe
);
1483 cur
= g_strstr_len(version
, strlen(version
), msg_ios
);
1485 *sw_type
= CISCO_IOS
;
1486 cur
+= strlen(msg_ios
);
1488 cur
= g_strstr_len(version
, strlen(version
), msg_asa
);
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
);
1499 cur
+= strlen(msg_version
);
1500 if (sscanf(cur
, "%u.%u", &sw_major
, &sw_minor
) != 2)
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
)) {
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
;
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
;
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
)) {
1530 ws_warning("Recognized software type, but minimal version requirements were not met\n");
1533 ws_warning("Recognized software type %d, but unrecognized version\n", *sw_type
);
1536 ws_warning("Unrecognized type of control software.");
1542 static bool run_capture_ios(ssh_channel channel
, const char* iface
, const char* cfilter
, const uint32_t count
)
1544 char* cmdline
= NULL
;
1546 char line
[SSH_READ_BLOCK_SIZE
+ 1];
1548 char* iface_copy
= g_strdup(iface
);
1552 char* wscp_str
= NULL
;
1554 if (ssh_channel_printf(channel
, "terminal length 0\n") == EXIT_FAILURE
)
1557 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1558 ws_warning("Received response: %s", crtoln(line
));
1562 if (ssh_channel_printf(channel
, "monitor capture buffer %s max-size 9500\n", WIRESHARK_CAPTURE_BUFFER
) == EXIT_FAILURE
)
1565 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1566 ws_warning("Received response: %s", crtoln(line
));
1571 if (ssh_channel_printf(channel
, "monitor capture buffer %s limit packet-count %u\n", WIRESHARK_CAPTURE_BUFFER
, count
) == EXIT_FAILURE
)
1574 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1575 ws_warning("Received response: %s", crtoln(line
));
1581 char* multiline_filter
;
1584 if (ssh_channel_printf(channel
, "configure terminal\n") == EXIT_FAILURE
)
1587 if (ssh_channel_printf(channel
, "ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST
) == EXIT_FAILURE
)
1590 multiline_filter
= g_strdup(cfilter
);
1591 chr
= multiline_filter
;
1592 while((chr
= g_strstr_len(chr
, strlen(chr
), ",")) != NULL
) {
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
)
1601 if (ssh_channel_printf(channel
, "\nend\n") == EXIT_FAILURE
)
1604 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1605 ws_warning("Received response: %s", crtoln(line
));
1609 if (ssh_channel_printf(channel
, "monitor capture buffer %s filter access-list %s\n",
1610 WIRESHARK_CAPTURE_BUFFER
, WIRESHARK_CAPTURE_ACCESSLIST
) == EXIT_FAILURE
)
1613 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1614 ws_warning("Received response: %s", crtoln(line
));
1620 for (str
= iface_copy
; ; str
= NULL
) {
1621 iface_one
= strtok(str
, ",");
1622 if (iface_one
== NULL
)
1625 wscp_str
= g_strdup_printf("%s_%d", WIRESHARK_CAPTURE_POINT
, 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
);
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
)
1639 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1640 ws_warning("Received response: %s", crtoln(line
));
1644 if (ssh_channel_printf(channel
, "monitor capture point associate %s %s \n", wscp_str
,
1645 WIRESHARK_CAPTURE_BUFFER
) == EXIT_FAILURE
)
1648 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1649 ws_warning("Received response: %s", crtoln(line
));
1658 for (str
= iface_copy
; ; str
= NULL
) {
1659 iface_one
= strtok(str
, ",");
1660 if (iface_one
== NULL
)
1663 wscp_str
= g_strdup_printf("%s_%d", WIRESHARK_CAPTURE_POINT
, wscp_cnt
);
1666 if (ssh_channel_printf(channel
, "monitor capture point start %s\n", wscp_str
) == EXIT_FAILURE
)
1669 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1670 ws_warning("Received response: %s", crtoln(line
));
1684 ws_warning("Error running ssh remote command");
1686 ssh_channel_close(channel
);
1687 ssh_channel_free(channel
);
1691 static bool run_capture_ios_xe_16(ssh_channel channel
, const char* iface
, const char* cfilter
, const uint32_t count
)
1694 char line
[SSH_READ_BLOCK_SIZE
+ 1];
1696 char* iface_copy
= g_strdup(iface
);
1700 if (ssh_channel_printf(channel
, "terminal length 0\n") == EXIT_FAILURE
)
1703 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1704 ws_warning("Received response: %s", crtoln(line
));
1708 if (ssh_channel_printf(channel
, "monitor capture %s limit packet-len 9500\n", WIRESHARK_CAPTURE
) == EXIT_FAILURE
)
1711 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1712 ws_warning("Received response: %s", crtoln(line
));
1717 if (ssh_channel_printf(channel
, "monitor capture %s limit packets %u\n", WIRESHARK_CAPTURE
, count
) == EXIT_FAILURE
)
1720 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1721 ws_warning("Received response: %s", crtoln(line
));
1727 char* multiline_filter
;
1730 if (ssh_channel_printf(channel
, "configure terminal\n") == EXIT_FAILURE
)
1733 if (ssh_channel_printf(channel
, "ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST
) == EXIT_FAILURE
)
1736 multiline_filter
= g_strdup(cfilter
);
1737 chr
= multiline_filter
;
1738 while((chr
= g_strstr_len(chr
, strlen(chr
), ",")) != NULL
) {
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
)
1747 if (ssh_channel_printf(channel
, "\nend\n") == EXIT_FAILURE
)
1750 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1751 ws_warning("Received response: %s", crtoln(line
));
1755 if (ssh_channel_printf(channel
, "monitor capture %s access-list %s\n",
1756 WIRESHARK_CAPTURE
, WIRESHARK_CAPTURE_ACCESSLIST
) == EXIT_FAILURE
)
1759 if (ssh_channel_printf(channel
, "monitor capture %s match any\n",
1760 WIRESHARK_CAPTURE
) == EXIT_FAILURE
)
1764 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1765 ws_warning("Received response: %s", crtoln(line
));
1769 for (str
= iface_copy
; ; str
= NULL
) {
1770 iface_one
= strtok(str
, ",");
1771 if (iface_one
== NULL
)
1774 if (0 == g_strcmp0(iface_one
, "control-plane")) {
1775 if (ssh_channel_printf(channel
, "monitor capture %s control-plane both\n", WIRESHARK_CAPTURE
1779 if (ssh_channel_printf(channel
, "monitor capture %s interface %s both\n", WIRESHARK_CAPTURE
,
1780 iface_one
) == EXIT_FAILURE
)
1785 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1786 ws_warning("Received response: %s", crtoln(line
));
1790 if (ssh_channel_printf(channel
, "monitor capture %s start\n", WIRESHARK_CAPTURE
) == EXIT_FAILURE
)
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
));
1804 ws_warning("Error running ssh remote command");
1806 ssh_channel_close(channel
);
1807 ssh_channel_free(channel
);
1811 static bool run_capture_ios_xe_17(ssh_channel channel
, const char* iface
, const char* cfilter
, const uint32_t count
)
1814 char line
[SSH_READ_BLOCK_SIZE
+ 1];
1816 char* iface_copy
= g_strdup(iface
);
1820 if (ssh_channel_printf(channel
, "terminal length 0\n") == EXIT_FAILURE
)
1823 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1824 ws_warning("Received response: %s", crtoln(line
));
1828 if (ssh_channel_printf(channel
, "monitor capture %s limit packet-len 9500\n", WIRESHARK_CAPTURE
) == EXIT_FAILURE
)
1831 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1832 ws_warning("Received response: %s", crtoln(line
));
1837 if (ssh_channel_printf(channel
, "monitor capture %s limit packets %u\n", WIRESHARK_CAPTURE
, count
) == EXIT_FAILURE
)
1840 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1841 ws_warning("Received response: %s", crtoln(line
));
1847 char* multiline_filter
;
1850 if (ssh_channel_printf(channel
, "configure terminal\n") == EXIT_FAILURE
)
1853 if (ssh_channel_printf(channel
, "ip access-list extended %s\n", WIRESHARK_CAPTURE_ACCESSLIST
) == EXIT_FAILURE
)
1856 multiline_filter
= g_strdup(cfilter
);
1857 chr
= multiline_filter
;
1858 while((chr
= g_strstr_len(chr
, strlen(chr
), ",")) != NULL
) {
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
)
1867 if (ssh_channel_printf(channel
, "\nend\n") == EXIT_FAILURE
)
1870 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1871 ws_warning("Received response: %s", crtoln(line
));
1875 if (ssh_channel_printf(channel
, "monitor capture %s access-list %s\n",
1876 WIRESHARK_CAPTURE
, WIRESHARK_CAPTURE_ACCESSLIST
) == EXIT_FAILURE
)
1879 if (ssh_channel_printf(channel
, "monitor capture %s match any\n",
1880 WIRESHARK_CAPTURE
) == EXIT_FAILURE
)
1884 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1885 ws_warning("Received response: %s", crtoln(line
));
1889 for (str
= iface_copy
; ; str
= NULL
) {
1890 iface_one
= strtok(str
, ",");
1891 if (iface_one
== NULL
)
1894 if (0 == g_strcmp0(iface_one
, "control-plane")) {
1895 if (ssh_channel_printf(channel
, "monitor capture %s control-plane both\n", WIRESHARK_CAPTURE
1899 if (ssh_channel_printf(channel
, "monitor capture %s interface %s both\n", WIRESHARK_CAPTURE
,
1900 iface_one
) == EXIT_FAILURE
)
1905 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1906 ws_warning("Received response: %s", crtoln(line
));
1910 if (ssh_channel_printf(channel
, "monitor capture %s start display dump\n", WIRESHARK_CAPTURE
) == EXIT_FAILURE
)
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));
1926 ws_warning("Error running ssh remote command");
1928 ssh_channel_close(channel
);
1929 ssh_channel_free(channel
);
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];
1939 bool process_filter
= true;
1940 char* iface_copy
= g_strdup(iface
);
1944 if (ssh_channel_printf(channel
, "terminal pager 0\n") == EXIT_FAILURE
)
1947 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
1948 ws_warning("Received response: %s", crtoln(line
));
1952 for (str
= iface_copy
; ; str
= NULL
) {
1953 iface_one
= strtok(str
, ",");
1954 if (iface_one
== NULL
)
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
);
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
);
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
);
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
);
2008 cmdline
= g_strdup_printf("capture %s type raw-data packet-length 9216 interface %s", WIRESHARK_CAPTURE
, ifname
);
2011 ws_warning("Unknown interface type : %s", iface_one
);
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
)
2022 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
2023 ws_warning("Received response: %s", crtoln(line
));
2031 if (process_filter
&& cfilter
) {
2032 char* multiline_filter
;
2036 if (ssh_channel_printf(channel
, "configure terminal\n") == EXIT_FAILURE
)
2039 multiline_filter
= g_strdup(cfilter
);
2040 start
= multiline_filter
;
2041 while((chr
= g_strstr_len(start
, strlen(start
), ",")) != NULL
) {
2043 ws_debug("Splitting filter into multiline");
2044 if (ssh_channel_printf(channel
, "access-list %s %s\n", WIRESHARK_CAPTURE_ACCESSLIST
, start
) == EXIT_FAILURE
)
2049 if (ssh_channel_printf(channel
, "access-list %s %s\n", WIRESHARK_CAPTURE_ACCESSLIST
, start
) == EXIT_FAILURE
)
2052 if (ssh_channel_printf(channel
, "\nend\n") == EXIT_FAILURE
)
2055 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
2056 ws_warning("Received response: %s", crtoln(line
));
2060 if (ssh_channel_printf(channel
, "capture %s access-list %s\n", WIRESHARK_CAPTURE
, WIRESHARK_CAPTURE_ACCESSLIST
) == EXIT_FAILURE
)
2063 if (!ssh_channel_wait_prompt_check_error(channel
, line
, &len
, SSH_READ_BLOCK_SIZE
, NULL
)) {
2064 ws_warning("Received response: %s", crtoln(line
));
2074 ws_warning("Error running ssh remote command");
2076 ssh_channel_close(channel
);
2077 ssh_channel_free(channel
);
2081 static ssh_channel
open_channel(ssh_session sshs
)
2083 ssh_channel channel
;
2085 channel
= ssh_channel_new(sshs
);
2089 if (ssh_channel_open_session(channel
) != SSH_OK
)
2092 if (ssh_channel_request_pty(channel
) != SSH_OK
)
2095 if (ssh_channel_change_pty_size(channel
, 80, 24) != SSH_OK
)
2098 if (ssh_channel_request_shell(channel
) != SSH_OK
)
2104 ws_warning("Error running ssh remote command");
2106 ssh_channel_close(channel
);
2107 ssh_channel_free(channel
);
2111 static bool run_capture(ssh_channel channel
, const char* iface
, const char* cfilter
, const uint32_t count
, CISCO_SW_TYPE sw_type
)
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
);
2121 return run_capture_asa(channel
, iface
, cfilter
);
2123 ws_warning("Unsupported cisco software. It will not collect any data most probably!");
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
)
2134 ssh_channel channel
;
2136 uint64_t bytes_written
= 0;
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");
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");
2157 ws_debug("Create first ssh session");
2158 sshs
= create_ssh_connection(ssh_params
, &err_info
);
2160 ws_warning("Error creating connection: %s", err_info
);
2164 channel
= open_channel(sshs
);
2170 if (!detect_host_prompt(channel
))
2173 if (!check_ios_version(channel
, &global_sw_type
))
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
)) {
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
);
2200 ws_warning("Error creating connection: %s", err_info
);
2204 channel
= open_channel(sshs
);
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
);
2225 static int list_config(char *interface
, unsigned int remote_port
)
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}"
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
++);
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
);
2282 return EXIT_SUCCESS
;
2285 int main(int argc
, char *argv
[])
2290 ssh_params_t
* ssh_params
= ssh_params_new();
2291 char* remote_interface
= NULL
;
2292 char* remote_filter
= NULL
;
2294 int ret
= EXIT_FAILURE
;
2295 extcap_parameters
* extcap_conf
= g_new0(extcap_parameters
, 1);
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
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.",
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
);
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
)) {
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)");
2359 extcap_help_print(extcap_conf
);
2363 while ((result
= ws_getopt_long(argc
, argv
, ":", longopts
, &option_idx
)) != -1) {
2368 extcap_help_print(extcap_conf
);
2373 extcap_version_print(extcap_conf
);
2376 case OPT_REMOTE_HOST
:
2377 g_free(ssh_params
->host
);
2378 ssh_params
->host
= g_strdup(ws_optarg
);
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
);
2388 case OPT_REMOTE_USERNAME
:
2389 g_free(ssh_params
->username
);
2390 ssh_params
->username
= g_strdup(ws_optarg
);
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
));
2400 g_free(ssh_params
->sshkey_path
);
2401 ssh_params
->sshkey_path
= g_strdup(ws_optarg
);
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
));
2410 case OPT_PROXYCOMMAND
:
2411 g_free(ssh_params
->proxycommand
);
2412 ssh_params
->proxycommand
= g_strdup(ws_optarg
);
2416 ssh_params
->ssh_sha1
= true;
2419 case OPT_REMOTE_INTERFACE
:
2420 g_free(remote_interface
);
2421 remote_interface
= g_strdup(ws_optarg
);
2424 case OPT_REMOTE_FILTER
:
2425 g_free(remote_filter
);
2426 remote_filter
= g_strdup(ws_optarg
);
2429 case OPT_REMOTE_COUNT
:
2430 if (!ws_strtou32(ws_optarg
, NULL
, &count
)) {
2431 ws_warning("Invalid packet count: %s", ws_optarg
);
2437 /* missing option argument */
2438 ws_warning("Option '%s' requires an argument", argv
[ws_optind
- 1]);
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]);
2449 extcap_cmdline_debug(argv
, argc
);
2451 if (ws_optind
!= argc
) {
2452 ws_warning("Unexpected extra option: %s", argv
[ws_optind
]);
2456 if (extcap_base_handle_interface(extcap_conf
)) {
2461 if (extcap_conf
->show_config
) {
2463 if (!ws_strtou16(ws_optarg
, NULL
, &ssh_params
->port
) || ssh_params
->port
== 0) {
2466 port
= ssh_params
->port
;
2468 ret
= list_config(extcap_conf
->interface
, port
);
2472 err_msg
= ws_init_sockets();
2473 if (err_msg
!= NULL
) {
2474 ws_warning("ERROR: %s", err_msg
);
2476 ws_warning("%s", please_report_bug());
2480 if (extcap_conf
->capture
) {
2481 if (!ssh_params
->host
) {
2482 ws_warning("Missing parameter: --remote-host");
2486 if (!remote_interface
) {
2487 ws_warning("ERROR: No interface specified (--remote-interface)");
2491 ws_warning("ERROR: count of packets must be specified (--remote-count)");
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
);
2498 ws_debug("You should not come here... maybe some parameter missing?");
2503 ssh_params_free(ssh_params
);
2504 g_free(remote_interface
);
2505 g_free(remote_filter
);
2506 extcap_base_cleanup(&extcap_conf
);
2511 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2516 * indent-tabs-mode: t
2519 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2520 * :indentSize=8:tabSize=8:noTabs=false: