-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathrtcpdump.sh
executable file
·314 lines (273 loc) · 9.14 KB
/
rtcpdump.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#!/bin/bash
usage(){
echo "
'rtcpdump' starts 'tcpdump' on a remote host (Linux or Arista switch),
opens 'wireshark' locally and pipes them together to display the distant
network packets in a GUI, in realtime.
This solution makes the troubleshoot of (lightweigth) network protocols
faster and more confortable, without being physically connected to the
spotted network segment.
Usage:
$0 -r <user>@<remote_ip> -p <password|password_file> -i <remote_interface>
[-P user@proxy] [-c <packet_count>] [-v] [-d rx|tx|both] [-a] ['filter_expression']
-r ssh path; can be an alias in your local ssh config
-P ssh proxy proxy; can also be an alias
-p password used if you have sshpass installed, can be a password file
-i remote interface name. On a switch, it can either be simple '10'
or a part of a quad-port '10/2'
-c limit of captured packets (default $pkt_count as safety for network)
-C capture cpu-targeted protocols (PTP, IGMP, ARP...) directly on a CPU iface, no port mirroring
-v verbose
-d direction 'rx|tx': keep ingress traffic only or egress. Default is both
-a Arista ACL filter mode <filter_expression> must be followed by an ACL rule (rx only)
filter_expression: tcpdump-like expression (ACL-like if '-a ...') with additional aliases
supported see example below.
Examples:
- PTP on Arista switch port, directly on the CPU:
$0 -r user@server -p pass -C -i Et10/1 ptp
- IGMP using ACL mode, with password file provided and verbose mode:
$0 -r user@server -p ~/passwordfile.txt -i Et10/1 -v -a 'permit igmp any any'
- LLDP:
$0 -r user@server -p pass -i Et10/1 'ether proto 0x88CC'
- HTTP between a Arista sw (on the management interface) and a specific host:
$0 -r user@server -p pass -i Ma1 'port 80 and host XXX.XXX.XXX.XXX'
- DHCP/bootp on a Linux host for a given MAC:
$0 -r user@server -p pass -i ens192 'dhcp and ether host XX:XX:XX:XX:XX:XX'
- VLAN-tagged http packets
$0 -r user@server -p pass -i ens192 '\-e \(vlan 1434 and port 80\)'
Script execution steps:
- login to remote through ssh
- detect if remote is normal linux host or Arista switch
- if Arista, init a monitor session that mirrors targeted port to
cpu interface
- launch tcpdump in remote bash and output to stdout (raw)
- launch local wireshark and read from stdin
- clean up monitor session on wireshark exit
Tested:
- Localhost: Linux, Windows (WSL2 installed)
- Arista switches (EOS-4.29.3M): DCS-7060SX2-48YC6, DCS-7280CR2A-30 DCS-7280SR2-48YC6,
DCS-7280TR-48C6, DCS-7280CR3K-32D4, DCS-7020TR-48
- Not supported: Arista sw like CCS-720XP-48ZC2, CCS-720XP-48Y6, DCS-7050SX-64 are not
supported since 'Monitor session' is limited. Traffic-recirculation feature may reorder
packets when combining rx and tx which results in an unrielable capture.
Limitations:
- capturing a high bitrate port isn't a good idea given the additional load transfer over the
network. This is why the capture is limited to 10000 pkts by default. Additionally, a monitor
session on an Arista switch consists in mirroring the traffic to the Cpu through a 10Mbps link.
As a result, some packets may be lost, even when a filter is given to tcpdump. Consider using
the '-C' or '-a' flags
- note that 'StrictHostKeyChecking=no' option is used for ssh, at you own risks
" 1>&2
}
function title() {
printf "\e[1;34m====================================================\e[m\n" "$1"
printf "\e[1;34m%s\e[m\n" "$1"
}
function warning() {
printf "\e[1;33m%s\e[m\n" "$1"
}
function error() {
printf "\e[1;31m%s\e[m\n" "$1"
}
##################################################################
# CONST
pkt_count=10000
session=RTCPDUMP
filter_mode=tcpdump
direction="both"
proxy=''
port_mirroring=true
##################################################################
# PARSE ARGS
while getopts ":r:p:P:i:c:Cd:va" o; do
case "${o}" in
r)
remote=${OPTARG}
;;
i)
iface=${OPTARG}
;;
p)
if [ -f ${OPTARG} ]; then
passfile=${OPTARG}
else
password=${OPTARG}
fi
;;
P)
proxy="-o ProxyJump=${OPTARG}"
;;
c)
pkt_count=${OPTARG}
;;
C)
port_mirroring=false
;;
d)
direction=${OPTARG}
;;
a)
filter_mode=acl
;;
v)
set -x
;;
*)
error "unsupported option ${o}"
usage
exit 1
;;
esac
done
shift $((OPTIND-1))
if [ -z "$remote" -o -z "$iface" ]; then
error "Missing argument -r or -i"
usage
exit 1
fi
if [ ! "$direction" = "rx" -a ! "$direction" = "tx" -a ! "$direction" = "both" ]; then
error "Wrong value for -d param"
usage
exit 1
fi
# filter: convert aliases to tcpdump-compatible expression
filter=$@
filter=$(echo $filter | sed "s/ptp/\\\(dst port 319 or dst port 320\\\)/")
filter=$(echo $filter | sed "s/dhcp/\\\(port 67 or port 68\\\)/")
filter=$(echo $filter | sed "s/http/\\\(port 80 or port 443\\\)/")
ssh_cmd="ssh -T -o StrictHostKeyChecking=no $proxy $remote "
##################################################################
# CHECKS
title "RTCPDUMP"
if mount | grep -q "^C:\\\ on"; then
echo "Host: WSL"
wireshark="/mnt/c/Progra~1/Wireshark/Wireshark.exe"
# wireshark may complain about IOR.txt wrong permission but the capture works fine
else
echo "Host: Linux"
wireshark=$(which wireshark)
fi
if [ ! -f "$wireshark" ]; then
error "$wireshark not found"
exit 1
fi
if ! which ssh >/dev/null; then
error "ssh client not found"
exit 1
fi
echo "ssh cmd:"
echo $ssh_cmd
if which sshpass >/dev/null; then
if [ ! -z "$passfile" ]; then
pass="-f $passfile"
elif [ ! -z "$password" ]; then
pass="-p $password"
else #from stdin
pass=""
fi
ssh_cmd="sshpass $pass $ssh_cmd"
else
warning "
sshpass not installed. It is going to be painful to enter the ssh
password at multiple times. Do you still want to proceed? [y/n]"
read no
if [ $no = "n" ]; then
exit 0
fi
fi
##################################################################
# Remote detection = regular Linux (easy)
if $ssh_cmd "ls" > /dev/null; then
echo "Remote: Linux host"
echo "Interfaces."
ifaces=$($ssh_cmd "ls /sys/class/net")
if echo $ifaces | grep -v -q $iface; then
echo $iface not found
exit 1
fi
title "Capture $cpu_iface."
warning ">>>>>>>>>>> Press CTRL+C to interrupt. <<<<<<<<<<<<<"
$ssh_cmd "tcpdump -i $iface -c $pkt_count -U -s0 -w - $filter" | "$wireshark" -k -i -
exit 0
fi
##################################################################
# Remote detection = Arista
echo "Remote: Arista switch"
title "Connection: ssh "
if ! $ssh_cmd "enable"; then
echo "Can't connect to target with this cmd."
echo "\"$ssh_cmd\""
echo "Exit."
exit 1
fi
echo OK
title "Interface: $iface"
echo "lldp:"
if ! $ssh_cmd "show lldp neighbors $iface"; then
echo "Wrong interface ($iface)? Exit."
exit 1
fi
echo "stats:"
port_stat=$($ssh_cmd "show interfaces $iface")
echo "$port_stat"
if echo $port_stat | grep -q -v "is up"; then
error "Port $iface is wrong or down, exit."
exit 1
fi
acl_monitor_option=""
if [ $filter_mode = "acl" ]; then
title "Create a IP access list: $filter"
$ssh_cmd "enable
conf
ip access-list $session
$filter
"
# need a short break for Cpu iface allocation
$ssh_cmd "enable
show ip access-list $session
"
filter=""
direction="rx"
acl_monitor_option="ip access-group $session"
fi
if $port_mirroring; then
title "Create a monitor session:"
$ssh_cmd "enable
conf
monitor session $session source $iface $direction $acl_monitor_option
monitor session $session destination Cpu"
sleep 1 # need a short break for Cpu iface allocation
sessions=$($ssh_cmd "enable
show monitor session")
echo "$sessions" | grep -v -e '^$' | grep -v "\-\-\-\-\-\-\-"
cpu_iface=$(echo "$sessions" | grep $session -A 14 | grep Cpu | sed 's/.*(\(.*\))/\1/')
if [ -z $cpu_iface ]; then
echo "Couldn't find cpu interface. Exit."
exit -1
fi
else
# convert iface name from EOS to Linux (Et...10/1 => et10_1)
cpu_iface=$(echo $iface | tr '[:upper:]' '[:lower:]' | sed 's/et[a-z]*\([0-9].*\)/et\1/;s/\//_/')
fi
title "Capture on CPU interface: $cpu_iface"
tcpdump_cmd="tcpdump -i $cpu_iface -c $pkt_count -U -s0 -w - $filter"
echo $tcpdump_cmd
warning ">>>>>>>>>>> Press CTRL+C to interrupt. <<<<<<<<<<<<<"
trap 'echo Interrupted.' SIGINT # catch Ctrl-C to exit Arista bash properly
$ssh_cmd "enable
conf
bash $tcpdump_cmd" | "$wireshark" -k -i -
title "Cleanup."
if $port_mirroring; then
$ssh_cmd "enable
conf
no ip access-list $session
no monitor session $session
show monitor session
bash pidof tcpdump > /dev/null && killall tcpdump
" | grep -v -e '^$'
else
$ssh_cmd "bash pidof tcpdump > /dev/null && killall tcpdump
" | grep -v -e '^$'
fi
echo "Exit."