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
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
|
# tc(8) completion -*- shell-script -*-
# Copyright 2016 6WIND S.A.
# Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com>
QDISC_KIND=' choke codel bfifo pfifo pfifo_head_drop fq fq_codel gred hhf \
mqprio multiq netem pfifo_fast pie fq_pie red rr sfb sfq tbf atm \
cbq drr dsmark hfsc htb prio qfq '
FILTER_KIND=' basic bpf cgroup flow flower fw route rsvp tcindex u32 matchall '
ACTION_KIND=' gact mirred bpf sample '
# Takes a list of words in argument; each one of them is added to COMPREPLY if
# it is not already present on the command line. Returns no value.
_tc_once_attr()
{
local w subcword found
for w in $*; do
found=0
for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
if [[ $w == ${words[subcword]} ]]; then
found=1
break
fi
done
[[ $found -eq 0 ]] && \
COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
done
}
# Takes a list of words in argument; each one of them is added to COMPREPLY if
# it is not already present on the command line from the provided index. Returns
# no value.
_tc_once_attr_from()
{
local w subcword found from=$1
shift
for w in $*; do
found=0
for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
if [[ $w == ${words[subcword]} ]]; then
found=1
break
fi
done
[[ $found -eq 0 ]] && \
COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
done
}
# Takes a list of words in argument; adds them all to COMPREPLY if none of them
# is already present on the command line. Returns no value.
_tc_one_of_list()
{
local w subcword
for w in $*; do
for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
[[ $w == ${words[subcword]} ]] && return 1
done
done
COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
}
# Takes a list of words in argument; adds them all to COMPREPLY if none of them
# is already present on the command line from the provided index. Returns no
# value.
_tc_one_of_list_from()
{
local w subcword from=$1
shift
for w in $*; do
for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
[[ $w == ${words[subcword]} ]] && return 1
done
done
COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
}
# Returns "$cur ${cur}arg1 ${cur}arg2 ..."
_tc_expand_units()
{
[[ $cur =~ ^[0-9]+ ]] || return 1
local value=${cur%%[^0-9]*}
[[ $cur == $value ]] && echo $cur
echo ${@/#/$value}
}
# Complete based on given word, usually $prev (or possibly the word before),
# for when an argument or an option name has but a few possible arguments (so
# tc does not take particular commands into account here).
# Returns 0 is completion should stop after running this function, 1 otherwise.
_tc_direct_complete()
{
case $1 in
# Command options
dev)
_available_interfaces
return 0
;;
classid)
return 0
;;
estimator)
local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
return 0
;;
handle)
return 0
;;
parent|flowid)
local i iface ids cmd
for (( i=3; i < ${#words[@]}-2; i++ )); do
[[ ${words[i]} == dev ]] && iface=${words[i+1]}
break
done
for cmd in qdisc class; do
if [[ -n $iface ]]; then
ids+=$( tc $cmd show dev $iface 2>/dev/null | \
cut -d\ -f 3 )" "
else
ids+=$( tc $cmd show 2>/dev/null | cut -d\ -f 3 )
fi
done
[[ $ids != " " ]] && \
COMPREPLY+=( $( compgen -W "$ids" -- "$cur" ) )
return 0
;;
protocol) # list comes from lib/ll_proto.c
COMPREPLY+=( $( compgen -W ' 802.1Q 802.1ad 802_2 802_3 LLDP aarp \
all aoe arp atalk atmfate atmmpoa ax25 bpq can control cust \
ddcmp dec diag dna_dl dna_rc dna_rt econet ethercat ieeepup \
ieeepupat ip ipv4 ipv6 ipx irda lat localtalk loop mobitex \
ppp_disc ppp_mp ppp_ses ppptalk profinet pup pupat rarp sca \
snap tipc tr_802_2 wan_ppp x25' -- "$cur" ) )
return 0
;;
prio)
return 0
;;
stab)
COMPREPLY+=( $( compgen -W 'mtu tsize mpu overhead
linklayer' -- "$cur" ) )
;;
# Qdiscs and classes options
alpha|bands|beta|buckets|corrupt|debug|decrement|default|\
default_index|depth|direct_qlen|divisor|duplicate|ewma|flow_limit|\
flows|hh_limit|increment|indices|linklayer|non_hh_weight|num_tc|\
penalty_burst|penalty_rate|prio|priomap|probability|queues|r2q|\
reorder|vq|vqs)
return 0
;;
setup)
COMPREPLY+=( $( compgen -W 'vqs' -- "$cur" ) )
return 0
;;
hw)
COMPREPLY+=( $( compgen -W '1 0' -- "$cur" ) )
return 0
;;
distribution)
COMPREPLY+=( $( compgen -W 'uniform normal pareto
paretonormal' -- "$cur" ) )
return 0
;;
loss)
COMPREPLY+=( $( compgen -W 'random state gmodel' -- "$cur" ) )
return 0
;;
# Qdiscs and classes options options
gap|gmodel|state)
return 0
;;
# Filters options
map)
COMPREPLY+=( $( compgen -W 'key' -- "$cur" ) )
return 0
;;
hash)
COMPREPLY+=( $( compgen -W 'keys' -- "$cur" ) )
return 0
;;
indev)
_available_interfaces
return 0
;;
eth_type)
COMPREPLY+=( $( compgen -W 'ipv4 ipv6' -- "$cur" ) )
return 0
;;
ip_proto)
COMPREPLY+=( $( compgen -W 'tcp udp' -- "$cur" ) )
return 0
;;
# Filters options options
key|keys)
[[ ${words[@]} =~ graft ]] && return 1
COMPREPLY+=( $( compgen -W 'src dst proto proto-src proto-dst iif \
priority mark nfct nfct-src nfct-dst nfct-proto-src \
nfct-proto-dst rt-classid sk-uid sk-gid vlan-tag rxhash' -- \
"$cur" ) )
return 0
;;
# BPF options - used for filters, actions, and exec
export|bytecode|bytecode-file|object-file)
_filedir
return 0
;;
object-pinned|graft) # Pinned object is probably under /sys/fs/bpf/
[[ -n "$cur" ]] && _filedir && return 0
COMPREPLY=( $( compgen -G "/sys/fs/bpf/*" -- "$cur" ) ) || _filedir
compopt -o nospace
return 0
;;
section)
if (type objdump > /dev/null 2>&1) ; then
local fword objfile section_list
for (( fword=3; fword < ${#words[@]}-3; fword++ )); do
if [[ ${words[fword]} == object-file ]]; then
objfile=${words[fword+1]}
break
fi
done
section_list=$( objdump -h $objfile 2>/dev/null | \
sed -n 's/^ *[0-9]\+ \([^ ]*\) *.*/\1/p' )
COMPREPLY+=( $( compgen -W "$section_list" -- "$cur" ) )
fi
return 0
;;
import|run)
_filedir
return 0
;;
type)
COMPREPLY+=( $( compgen -W 'cls act' -- "$cur" ) )
return 0
;;
# Actions options
random)
_tc_one_of_list 'netrand determ'
return 0
;;
# Units for option arguments
bandwidth|maxrate|peakrate|rate)
local list=$( _tc_expand_units 'bit' \
'kbit' 'kibit' 'kbps' 'kibps' \
'mbit' 'mibit' 'mbps' 'mibps' \
'gbit' 'gibit' 'gbps' 'gibps' \
'tbit' 'tibit' 'tbps' 'tibps' )
COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
;;
admit_bytes|avpkt|burst|cell|initial_quantum|limit|max|min|mtu|mpu|\
overhead|quantum|redflowlist)
local list=$( _tc_expand_units \
'b' 'kbit' 'k' 'mbit' 'm' 'gbit' 'g' )
COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
;;
db|delay|evict_timeout|interval|latency|perturb|rehash|reset_timeout|\
target|tupdate)
local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
;;
esac
return 1
}
# Complete with options names for qdiscs. Each qdisc has its own set of options
# and it seems we cannot really parse it from anywhere, so we add it manually
# in this function.
# Returns 0 is completion should stop after running this function, 1 otherwise.
_tc_qdisc_options()
{
case $1 in
choke)
_tc_once_attr 'limit bandwidth ecn min max burst'
return 0
;;
codel)
_tc_once_attr 'limit target interval'
_tc_one_of_list 'ecn noecn'
return 0
;;
bfifo|pfifo|pfifo_head_drop)
_tc_once_attr 'limit'
return 0
;;
fq)
_tc_once_attr 'limit flow_limit quantum initial_quantum maxrate \
buckets'
_tc_one_of_list 'pacing nopacing'
return 0
;;
fq_codel)
_tc_once_attr 'limit flows target interval quantum'
_tc_one_of_list 'ecn noecn'
return 0
;;
gred)
_tc_once_attr 'setup vqs default grio vq prio limit min max avpkt \
burst probability bandwidth ecn harddrop'
return 0
;;
hhf)
_tc_once_attr 'limit quantum hh_limit reset_timeout admit_bytes \
evict_timeout non_hh_weight'
return 0
;;
mqprio)
_tc_once_attr 'num_tc map queues hw'
return 0
;;
netem)
_tc_once_attr 'delay distribution corrupt duplicate loss ecn \
reorder rate'
return 0
;;
pie)
_tc_once_attr 'limit target tupdate alpha beta'
_tc_one_of_list 'bytemode nobytemode'
_tc_one_of_list 'ecn noecn'
_tc_one_of_list 'dq_rate_estimator no_dq_rate_estimator'
return 0
;;
fq_pie)
_tc_once_attr 'limit flows target tupdate \
alpha beta quantum memory_limit ecn_prob'
_tc_one_of_list 'ecn noecn'
_tc_one_of_list 'bytemode nobytemode'
_tc_one_of_list 'dq_rate_estimator no_dq_rate_estimator'
return 0
;;
red)
_tc_once_attr 'limit min max avpkt burst adaptive probability \
bandwidth ecn harddrop'
return 0
;;
rr|prio)
_tc_once_attr 'bands priomap multiqueue'
return 0
;;
sfb)
_tc_once_attr 'rehash db limit max target increment decrement \
penalty_rate penalty_burst'
return 0
;;
sfq)
_tc_once_attr 'limit perturb quantum divisor flows depth headdrop \
redflowlimit min max avpkt burst probability ecn harddrop'
return 0
;;
tbf)
_tc_once_attr 'limit burst rate mtu peakrate latency overhead \
linklayer'
return 0
;;
cbq)
_tc_once_attr 'bandwidth avpkt mpu cell ewma'
return 0
;;
dsmark)
_tc_once_attr 'indices default_index set_tc_index'
return 0
;;
hfsc)
_tc_once_attr 'default'
return 0
;;
htb)
_tc_once_attr 'default r2q direct_qlen debug'
return 0
;;
multiq|pfifo_fast|atm|drr|qfq)
return 0
;;
esac
return 1
}
# Complete with options names for BPF filters or actions.
# Returns 0 is completion should stop after running this function, 1 otherwise.
_tc_bpf_options()
{
[[ ${words[${#words[@]}-3]} == object-file ]] && \
_tc_once_attr 'section export'
[[ ${words[${#words[@]}-5]} == object-file ]] && \
[[ ${words[${#words[@]}-3]} =~ (section|export) ]] && \
_tc_once_attr 'section export'
_tc_one_of_list 'bytecode bytecode-file object-file object-pinned'
_tc_once_attr 'verbose index direct-action action classid'
return 0
}
# Complete with options names for filter actions.
# This function is recursive, thus allowing multiple actions statement to be
# parsed.
# Returns 0 is completion should stop after running this function, 1 otherwise.
_tc_filter_action_options()
{
for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
do
if [[ action == ${words[acwd]} ]]; then
_tc_filter_action_options $((acwd+1)) && return 0
fi
done
local action acwd
for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); do
if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then
_tc_one_of_list_from $acwd action
_tc_action_options $acwd && return 0
fi
done
_tc_one_of_list_from $acwd $ACTION_KIND
return 0
}
# Complete with options names for filters.
# Returns 0 is completion should stop after running this function, 1 otherwise.
_tc_filter_options()
{
for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
do
if [[ action == ${words[acwd]} ]]; then
_tc_filter_action_options $((acwd+1)) && return 0
fi
done
filter=${words[$1]}
case $filter in
basic)
_tc_once_attr 'match action classid'
return 0
;;
bpf)
_tc_bpf_options
return 0
;;
cgroup)
_tc_once_attr 'match action'
return 0
;;
flow)
local i
for (( i=5; i < ${#words[@]}-1; i++ )); do
if [[ ${words[i]} =~ ^keys?$ ]]; then
_tc_direct_complete 'key'
COMPREPLY+=( $( compgen -W 'or and xor rshift addend' -- \
"$cur" ) )
break
fi
done
_tc_once_attr 'map hash divisor baseclass match action'
return 0
;;
matchall)
_tc_once_attr 'action classid skip_sw skip_hw'
return 0
;;
flower)
_tc_once_attr 'action classid indev dst_mac src_mac eth_type \
ip_proto dst_ip src_ip dst_port src_port'
return 0
;;
fw)
_tc_once_attr 'action classid'
return 0
;;
route)
_tc_one_of_list 'from fromif'
_tc_once_attr 'to classid action'
return 0
;;
rsvp)
_tc_once_attr 'ipproto session sender classid action tunnelid \
tunnel flowlabel spi/ah spi/esp u8 u16 u32'
[[ ${words[${#words[@]}-3]} == tunnel ]] && \
COMPREPLY+=( $( compgen -W 'skip' -- "$cur" ) )
[[ ${words[${#words[@]}-3]} =~ u(8|16|32) ]] && \
COMPREPLY+=( $( compgen -W 'mask' -- "$cur" ) )
[[ ${words[${#words[@]}-3]} == mask ]] && \
COMPREPLY+=( $( compgen -W 'at' -- "$cur" ) )
return 0
;;
tcindex)
_tc_once_attr 'hash mask shift classid action'
_tc_one_of_list 'pass_on fall_through'
return 0
;;
u32)
_tc_once_attr 'match link classid action offset ht hashkey sample'
COMPREPLY+=( $( compgen -W 'ip ip6 udp tcp icmp u8 u16 u32 mark \
divisor' -- "$cur" ) )
return 0
;;
esac
return 1
}
# Complete with options names for actions.
# Returns 0 is completion should stop after running this function, 1 otherwise.
_tc_action_options()
{
local from=$1
local action=${words[from]}
case $action in
bpf)
_tc_bpf_options
return 0
;;
mirred)
_tc_one_of_list_from $from 'ingress egress'
_tc_one_of_list_from $from 'mirror redirect'
_tc_once_attr_from $from 'index dev'
return 0
;;
sample)
_tc_once_attr_from $from 'rate'
_tc_once_attr_from $from 'trunc'
_tc_once_attr_from $from 'group'
return 0
;;
gact)
_tc_one_of_list_from $from 'reclassify drop continue pass'
_tc_once_attr_from $from 'random'
return 0
;;
esac
return 1
}
# Complete with options names for exec.
# Returns 0 is completion should stop after running this function, 1 otherwise.
_tc_exec_options()
{
case $1 in
import)
[[ ${words[${#words[@]}-3]} == import ]] && \
_tc_once_attr 'run'
return 0
;;
graft)
COMPREPLY+=( $( compgen -W 'key type' -- "$cur" ) )
[[ ${words[${#words[@]}-3]} == object-file ]] && \
_tc_once_attr 'type'
_tc_bpf_options
return 0
;;
esac
return 1
}
# Main completion function
# Logic is as follows:
# 1. Check if previous word is a global option; if so, propose arguments.
# 2. Check if current word is a global option; if so, propose completion.
# 3. Check for the presence of a main command (qdisc|class|filter|...). If
# there is one, first call _tc_direct_complete to see if previous word is
# waiting for a particular completion. If so, propose completion and exit.
# 4. Extract main command and -- if available -- its subcommand
# (add|delete|show|...).
# 5. Propose completion based on main and sub- command in use. Additional
# functions may be called for qdiscs, classes or filter options.
_tc()
{
local cur prev words cword
_init_completion || return
case $prev in
-V|-Version)
return 0
;;
-b|-batch|-cf|-conf)
_filedir
return 0
;;
-force)
COMPREPLY=( $( compgen -W '-batch' -- "$cur" ) )
return 0
;;
-nm|name)
[[ -r /etc/iproute2/tc_cls ]] || \
COMPREPLY=( $( compgen -W '-conf' -- "$cur" ) )
return 0
;;
-n|-net|-netns)
local nslist=$( ip netns list 2>/dev/null )
COMPREPLY+=( $( compgen -W "$nslist" -- "$cur" ) )
return 0
;;
-tshort)
_tc_once_attr '-statistics'
COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
return 0
;;
-timestamp)
_tc_once_attr '-statistics -tshort'
COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
return 0
;;
esac
# Search for main commands
local subcword cmd subcmd
for (( subcword=1; subcword < ${#words[@]}-1; subcword++ )); do
[[ ${words[subcword]} == -b?(atch) ]] && return 0
[[ -n $cmd ]] && subcmd=${words[subcword]} && break
[[ ${words[subcword]} != -* && \
${words[subcword-1]} != -@(n?(et?(ns))|c?(on)f) ]] && \
cmd=${words[subcword]}
done
if [[ -z $cmd ]]; then
case $cur in
-*)
local c='-Version -statistics -details -raw -pretty \
-iec -graphe -batch -name -netns -timestamp'
[[ $cword -eq 1 ]] && c+=' -force'
COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
return 0
;;
*)
COMPREPLY=( $( compgen -W "help $( tc help 2>&1 | \
command sed \
-e '/OBJECT := /!d' \
-e 's/.*{//' \
-e 's/}.*//' \
-e \ 's/|//g' )" -- "$cur" ) )
return 0
;;
esac
fi
[[ $subcmd == help ]] && return 0
# For this set of commands we may create COMPREPLY just by analysing the
# previous word, if it expects for a specific list of options or values.
if [[ $cmd =~ (qdisc|class|filter|action|exec) ]]; then
_tc_direct_complete $prev && return 0
if [[ ${words[${#words[@]}-3]} == estimator ]]; then
local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) && return 0
fi
fi
# Completion depends on main command and subcommand in use.
case $cmd in
qdisc)
case $subcmd in
add|change|replace|link|del|delete)
if [[ $(($cword-$subcword)) -eq 1 ]]; then
COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
return 0
fi
local qdisc qdwd
for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
qdisc=${words[qdwd]}
_tc_qdisc_options $qdisc && return 0
fi
done
_tc_one_of_list $QDISC_KIND
_tc_one_of_list 'root ingress parent clsact'
_tc_once_attr 'handle estimator stab'
;;
show)
_tc_once_attr 'dev'
_tc_one_of_list 'ingress clsact'
_tc_once_attr '-statistics -details -raw -pretty -iec \
-graph -name'
;;
help)
return 0
;;
*)
[[ $cword -eq $subcword ]] && \
COMPREPLY=( $( compgen -W 'help add delete change \
replace link show' -- "$cur" ) )
;;
esac
;;
class)
case $subcmd in
add|change|replace|del|delete)
if [[ $(($cword-$subcword)) -eq 1 ]]; then
COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
return 0
fi
local qdisc qdwd
for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
qdisc=${words[qdwd]}
_tc_qdisc_options $qdisc && return 0
fi
done
_tc_one_of_list $QDISC_KIND
_tc_one_of_list 'root parent'
_tc_once_attr 'classid'
;;
show)
_tc_once_attr 'dev'
_tc_one_of_list 'root parent'
_tc_once_attr '-statistics -details -raw -pretty -iec \
-graph -name'
;;
help)
return 0
;;
*)
[[ $cword -eq $subcword ]] && \
COMPREPLY=( $( compgen -W 'help add delete change \
replace show' -- "$cur" ) )
;;
esac
;;
filter)
case $subcmd in
add|change|replace|del|delete)
if [[ $(($cword-$subcword)) -eq 1 ]]; then
COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
return 0
fi
local filter fltwd
for ((fltwd=$subcword; fltwd < ${#words[@]}-1; fltwd++));
do
if [[ $FILTER_KIND =~ ' '${words[fltwd]}' ' ]]; then
_tc_filter_options $fltwd && return 0
fi
done
_tc_one_of_list $FILTER_KIND
_tc_one_of_list 'root ingress egress parent'
_tc_once_attr 'handle estimator pref protocol'
;;
show)
_tc_once_attr 'dev'
_tc_one_of_list 'root ingress egress parent'
_tc_once_attr '-statistics -details -raw -pretty -iec \
-graph -name'
;;
help)
return 0
;;
*)
[[ $cword -eq $subcword ]] && \
COMPREPLY=( $( compgen -W 'help add delete change \
replace show' -- "$cur" ) )
;;
esac
;;
action)
case $subcmd in
add|change|replace)
local action acwd
for ((acwd=$subcword; acwd < ${#words[@]}-1; acwd++)); do
if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then
_tc_action_options $acwd && return 0
fi
done
_tc_one_of_list $ACTION_KIND
;;
get|del|delete)
_tc_once_attr 'index'
;;
lst|list|flush|show)
_tc_one_of_list $ACTION_KIND
;;
*)
[[ $cword -eq $subcword ]] && \
COMPREPLY=( $( compgen -W 'help add delete change \
replace show list flush action' -- "$cur" ) )
;;
esac
;;
monitor)
COMPREPLY=( $( compgen -W 'help' -- "$cur" ) )
;;
exec)
case $subcmd in
bpf)
local excmd exwd EXEC_KIND=' import debug graft '
for ((exwd=$subcword; exwd < ${#words[@]}-1; exwd++)); do
if [[ $EXEC_KIND =~ ' '${words[exwd]}' ' ]]; then
excmd=${words[exwd]}
_tc_exec_options $excmd && return 0
fi
done
_tc_one_of_list $EXEC_KIND
;;
*)
[[ $cword -eq $subcword ]] && \
COMPREPLY=( $( compgen -W 'bpf' -- "$cur" ) )
;;
esac
;;
esac
} &&
complete -F _tc tc
# ex: ts=4 sw=4 et filetype=sh
|