-
Notifications
You must be signed in to change notification settings - Fork 3
/
macco.sh
executable file
·414 lines (354 loc) · 9.13 KB
/
macco.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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#!/bin/bash
################################# Set defaults ################################
FUNCT=to_linux
AUTO_FUNCT=to_cisco
AUTO_MODE=1
ONLY_MATCHING=0
case_fnct() { tr 'A-Z' 'a-z' ; }
############################### Create Help text ##############################
HELP="
Usage: [STDIN] | $0 [OPTIONS]... [MAC-ADDRESSES]...
-a,-A 'Automatic' mode (depends on defaults defined in the script)
-b,-B Binary style
-c Cisco style ('maca.ddre.sses') - for newer Cisco IOS
-C Cisco style ('maca.ddre.sses') - for older Cisco IOS
-h Help - display brief help text and quit.
-H Help - display full help text and quit.
-i,-I Interface Lookup
-l Linux style - lowercase ('ma:ca:dd:re:ss:es')
-L Linux style - UPPERCASE ('MA:CA:DD:RE:SS:ES')
-m HP/H3C ComWare style - lowercase ('maca-ddre-sses')
-M HP/H3C ComWare style - UPPERCASE ('MACA-DDRE-SSES')
-n Naked style - lowercase ('macaddresses')
-N Naked style - UPPERCASE ('MACADDRESSES')
-O,-o Only print MAC addresses; similar to the -o flag in grep(1)
-p H(P) style - lowercase ('macadd-resses')
-P H(P) style - UPPERCASE ('MACADD-RESSES')
-r,-R ARP Lookup
-s Solaris style - lowercase ('50:1A:12:14:a:b')
-S Solaris style - UPPERCASE ('50:1A:12:15:A:B')
-w Windows style - lowercase ('ma-ca-dd-re-ss-es')
-W Windows style - UPPERCASE ('MA-CA-DD-RE-SS-ES')
-x,-X 'Exclude' mode (filters out things like global broadcast)
"
# HELP2 - 'extended' help text
HELP2="
Notes:
- Automatic Mode converts to the 'default' format (as defined by 'FUNCT').
If the supplied MAC is already in that format, it is converted to the
'automatic' format (defined by 'AUTO_FUNCT')
- MAC address(es) can be supplied by STDIN, and/or script arguments.
If both STDIN and arguments are supplied, STDIN is processed first.
- Input from STDIN will be parsed for MAC addresses (that is, the script will
make an effort to only convert tokens which look like MAC addresses whilst
passing through all other input).
- If no MAC addresses are supplied, all system MAC addresses
(excluding loopback) are displayed.
- Interface Lookup (INT_LOOKUP) and ARP Lookup (ARP_LOOKUP) embeds the
converted addresses in various lookup commands (per the style used).
NB: Both Lookup modes imply ONLY_MATCHING (-o).
- The New/Old divide in Cisco/IOS affects whether 'show mac-address-table'
(old) or 'show mac address-table' (new) is used. It only matters if
INT_LOOKUP (-m|-M) is used.
- Exclude mode current filters 00:00:00:00:00:00 and FF:FF:FF:FF:FF:FF.
More (or custom) filters might be added in future versions.
"
######################### Define Conversion Functions #########################
function to_cisco
{
# Cisco-style: maca.ddre.sses (always lowercase)
sed 's/..../&\./g;s/\.$//' | tr 'A-F' 'a-f'
}
function to_linux
{
# Linux-style: ma:ca:dd:re:ss:es
sed 's/../&:/g;s/\:$//' | case_fnct
}
function to_hp
{
# HP-style: macadd-resses
sed 's/....../&-/g;s/\-$//' | case_fnct
}
function to_h3c
{
# HP/H3C ComWare style: maca-ddre-sses
sed 's/..../&-/g;s/\-$//' | case_fnct
}
function to_windows
{
# Windows-style: MA-CA-DD-RE-SS-ES
sed 's/../&-/g;s/\-$//' | case_fnct
}
function to_naked
{
# Naked-style: macaddresses / MACADDRESSES
tr -d '\:\-\.' | case_fnct
}
function to_solaris
{
sed 's/../:&/g;s/:0/:/g;s/^://' | case_fnct
}
function to_binary
{
bc <<< "obase=2; ibase=16; $(cat - | to_naked | tr [a-z] [A-Z])" | zfill 48
}
######################### Define ARP_LOOKUP Functions #########################
function arp_cisco
{
# Cisco ARP lookup:` show ip arp maca.ddre.sses`
to_cisco | sed 's/^/show ip arp /'
}
function arp_linux
{
# Linux ARP lookup: `arp -n | grep ma:ca:dd:re:ss:es`
to_linux | sed 's/^/arp -n | grep /'
}
function arp_windows
{
# Windows ARP lookup: `arp -a | findstr MA-CA-DD-RE-SS-ES`
to_windows | sed 's/^/arp -a | findstr /'
}
######################### Define INT_LOOKUP Functions #########################
function int_cisco
{
# Cisco INT lookup: 'show mac[- ]address-table maca.ddre.sses | include /'
if (( NEW_CISCO ))
then
to_cisco | sed 's/.*/show mac address-table address & | include \//'
else
to_cisco | sed 's/.*/show mac-address-table address & | include \//'
fi
}
function int_linux
{
# Linux INT lookup: `arp -n | grep ma:ca:dd:re:ss:es`
to_linux | sed 's/^/arp -n | grep /'
}
function int_windows
{
# Windows INT lookup: `arp -a | findstr MA-CA-DD-RE-SS-ES`
to_windows | sed 's/^/arp -a | findstr /'
}
########################### Define Helper Functions ###########################
function zfill
{
cat - | sed ':a;s/^.\{1,'"$[$1-1]"'\}$/0&/g;ta'
}
function is_equiv
{
[[ -z "$2" ]] && return 0
function normalise
{
echo "$@" | tr '[a-f]' '[A-F]'
}
ref="$(normalise $1)"
shift
for x in "$@"
do
[[ "$ref" != "$(normalise $x)" ]] && return 1
done
return 0
}
function is_excludeable
{
naked=$(echo $1 | to_naked)
[[ "$naked" == 000000000000 ]] && return 0
[[ "$naked" == ffffffffffff ]] && return 0
[[ "$naked" == FFFFFFFFFFFF ]] && return 0
return 1
}
function parse
{
# Attempt to detect MAC addresses (only), and convert them.
#
# No arguments. Reads STDIN
#
# The following assumes hexadecimal digits (optionally) separated by
# *regular* arbitrary separators.
delim_chr=''
delim_cnt=0
cnt=0
token=''
function is_complete_mac
{
# Check if the supplied token constitutes a complete MAC address
#
# $1 - token
# $2 - delim_cnt
if [[ $2 -eq 0 ]] && [[ ${#1} -eq 12 ]]
then
return
elif [[ ${#1} -eq $[ 11 + 12 / $[ $2 - 1 ] ] ]]
then
return
else
return 1
fi
}
while IFS='' read -d '\n' -n1 chr # get one character from STDIN and assign to $chr
do
if [[ ! "$chr" =~ [0-9a-fA-F] ]] && is_complete_mac "$token" "$delim_cnt"
then
convtoken=$(echo "$token" | to_naked | $FUNCT)
unset delim_chr
delim_cnt=0
cnt=0
# Exclude mode logic
(( $EXCLUDE )) && $(is_excludeable "$convtoken") && unset convtoken token && continue
# Auto function logic
if (( $AUTO_MODE )) && $(is_equiv "$convtoken" "$token")
then
convtoken=$(echo "$token" | to_naked | $AUTO_FUNCT)
fi
(( $ONLY_MATCHING )) && echo "$convtoken" || echo -n "$convtoken$chr"
unset convtoken token
continue
elif [[ "$chr" =~ [a-fA-F0-9] ]] # if chr is a hexadecimal digit
then
let cnt+=1
token="${token}${chr}"
elif [[ -z "$delim_chr" && $cnt -gt 0 && $cnt -le 6 ]]
then
# set the delim_chr
let cnt+=1
delim_chr=$chr
delim_cnt=$cnt
token="${token}${chr}"
continue
elif [[ $delim_cnt -gt 0 ]] && [[ $[ ($cnt + 1) % $delim_cnt ] -eq 0 ]] && [[ "$chr" == "$delim_chr" ]]
then
# delim_chr regular repeat detected
token="${token}${chr}"
let cnt+=1
continue
else
# the delim is not regular or consistent - not a MAC address
(( $ONLY_MATCHING )) || printf -- "${token}${chr}"
token=''
delim_chr=''
delim_cnt=0
cnt=0
fi
done
}
################################# Get Options #################################
SHIFT=0
while getopts "aAbBcChHiIlLmMnNoOpPrRsSwWxX" OPTION
do
let SHIFT+=1
case "$OPTION" in
#### 'Style' options ####
b|B)
FUNCT=to_binary
AUTO_MODE=0
;;
c|C)
FUNCT=to_cisco
[[ $OPTION == c ]] && NEW_CISCO=1 || NEW_CISCO=0
# old cisco is `mac-address-table`, new cisco is `mac address-table`
AUTO_MODE=0
;;
l|L)
FUNCT=to_linux
AUTO_MODE=0
;;
m|M)
FUNCT=to_h3c
AUTO_MODE=0
;;
n|N)
FUNCT=to_naked
AUTO_MODE=0
;;
p|P)
FUNCT=to_hp
AUTO_MODE=0
;;
s|S)
FUNCT=to_solaris
AUTO_MODE=0
;;
w|W)
FUNCT=to_windows
AUTO_MODE=0
;;
i|I)
INT_LOOKUP=1
;;
r|R)
ARP_LOOKUP=1
;;
## 'Normal' options
# Please use continue/exit/break in the following when adding more options
a|A)
AUTO_MODE=1
continue
;;
h)
echo -e "$HELP"
exit 0
;;
H)
echo -e "$HELP$HELP2"
exit 0
;;
o|O)
ONLY_MATCHING=1
continue
;;
x|X)
EXCLUDE=1
continue
;;
--)
break
;;
?)
echo "$HELP" >&2
exit 1
;;
esac
if [[ "$OPTION" =~ [A-Z] ]]
then
case_fnct() { tr 'a-z' 'A-Z' ; }
else
case_fnct() { tr 'A-Z' 'a-z' ; }
fi
done
shift $SHIFT # Must be done outside of `while getopt` loop (or things break).
####################### Bind lookup functions per style #######################
if (( ARP_LOOKUP ))
then
ONLY_MATCHING=1
ORIG_FUNCT="$FUNCT"
(( INT_LOOKUP )) && { echo "ARP_LOOKUP (-r|-R) and INT_LOOKUP (-i|-I) are mutually exclusive!" >&2 ; exit 1 ; }
[[ $FUNCT == to_cisco ]] && FUNCT='arp_cisco'
[[ $FUNCT == to_linux ]] && FUNCT='arp_linux'
[[ $FUNCT == to_windows ]] && FUNCT='arp_windows'
fi
if (( INT_LOOKUP ))
then
ONLY_MATCHING=1
ORIG_FUNCT="$FUNCT"
[[ $FUNCT == to_cisco ]] && FUNCT='int_cisco'
[[ $FUNCT == to_linux ]] && FUNCT='int_linux'
[[ $FUNCT == to_windows ]] && FUNCT='int_windows'
fi
################################# Run Program #################################
if [[ ! -t 0 ]]
# STDIN not empty
then
parse
fi
if [[ $# -gt 0 ]]
then
echo $@ | parse
elif [[ -t 0 ]]
then
[[ -n $ORIG_FUNCT ]] && FUNCT="$ORIG_FUNCT"
AUTO_MODE=0
ip link | awk '/LOOPBACK/ {getline;next} {printf $2 "\t";getline;print $2}' | while read iface mac
do
[[ -n "$mac" ]] && echo -e "$iface\t$(echo -e "$mac" | parse)"
done
fi