1 # tc(8) completion -*- shell-script -*-
2 # Copyright 2016 6WIND S.A.
3 # Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com>
5 QDISC_KIND=' choke codel bfifo pfifo pfifo_head_drop fq fq_codel gred hhf \
6 mqprio multiq netem pfifo_fast pie red rr sfb sfq tbf atm cbq drr \
7 dsmark hfsc htb prio qfq '
8 FILTER_KIND=' basic bpf cgroup flow flower fw route rsvp tcindex u32 matchall '
9 ACTION_KIND=' gact mirred bpf sample '
11 # Takes a list of words in argument; each one of them is added to COMPREPLY if
12 # it is not already present on the command line. Returns no value.
15 local w subcword found
18 for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
19 if [[ $w == ${words[subcword]} ]]; then
24 [[ $found -eq 0 ]] && \
25 COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
29 # Takes a list of words in argument; each one of them is added to COMPREPLY if
30 # it is not already present on the command line from the provided index. Returns
34 local w subcword found from=$1
38 for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
39 if [[ $w == ${words[subcword]} ]]; then
44 [[ $found -eq 0 ]] && \
45 COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
49 # Takes a list of words in argument; adds them all to COMPREPLY if none of them
50 # is already present on the command line. Returns no value.
55 for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
56 [[ $w == ${words[subcword]} ]] && return 1
59 COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
62 # Takes a list of words in argument; adds them all to COMPREPLY if none of them
63 # is already present on the command line from the provided index. Returns no
65 _tc_one_of_list_from()
67 local w subcword from=$1
70 for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
71 [[ $w == ${words[subcword]} ]] && return 1
74 COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
77 # Returns "$cur ${cur}arg1 ${cur}arg2 ..."
80 [[ $cur =~ ^[0-9]+ ]] || return 1
81 local value=${cur%%[^0-9]*}
82 [[ $cur == $value ]] && echo $cur
86 # Complete based on given word, usually $prev (or possibly the word before),
87 # for when an argument or an option name has but a few possible arguments (so
88 # tc does not take particular commands into account here).
89 # Returns 0 is completion should stop after running this function, 1 otherwise.
102 local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
103 COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
110 local i iface ids cmd
111 for (( i=3; i < ${#words[@]}-2; i++ )); do
112 [[ ${words[i]} == dev ]] && iface=${words[i+1]}
115 for cmd in qdisc class; do
116 if [[ -n $iface ]]; then
117 ids+=$( tc $cmd show dev $iface 2>/dev/null | \
120 ids+=$( tc $cmd show 2>/dev/null | cut -d\ -f 3 )
123 [[ $ids != " " ]] && \
124 COMPREPLY+=( $( compgen -W "$ids" -- "$cur" ) )
127 protocol) # list comes from lib/ll_proto.c
128 COMPREPLY+=( $( compgen -W ' 802.1Q 802.1ad 802_2 802_3 LLDP aarp \
129 all aoe arp atalk atmfate atmmpoa ax25 bpq can control cust \
130 ddcmp dec diag dna_dl dna_rc dna_rt econet ieeepup ieeepupat \
131 ip ipv4 ipv6 ipx irda lat localtalk loop mobitex ppp_disc \
132 ppp_mp ppp_ses ppptalk pup pupat rarp sca snap tipc tr_802_2 \
133 wan_ppp x25' -- "$cur" ) )
140 COMPREPLY+=( $( compgen -W 'mtu tsize mpu overhead
141 linklayer' -- "$cur" ) )
144 # Qdiscs and classes options
145 alpha|bands|beta|buckets|corrupt|debug|decrement|default|\
146 default_index|depth|direct_qlen|divisor|duplicate|ewma|flow_limit|\
147 flows|hh_limit|increment|indices|linklayer|non_hh_weight|num_tc|\
148 penalty_burst|penalty_rate|prio|priomap|probability|queues|r2q|\
153 COMPREPLY+=( $( compgen -W 'vqs' -- "$cur" ) )
157 COMPREPLY+=( $( compgen -W '1 0' -- "$cur" ) )
161 COMPREPLY+=( $( compgen -W 'uniform normal pareto
162 paretonormal' -- "$cur" ) )
166 COMPREPLY+=( $( compgen -W 'random state gmodel' -- "$cur" ) )
170 # Qdiscs and classes options options
177 COMPREPLY+=( $( compgen -W 'key' -- "$cur" ) )
181 COMPREPLY+=( $( compgen -W 'keys' -- "$cur" ) )
185 _available_interfaces
189 COMPREPLY+=( $( compgen -W 'ipv4 ipv6' -- "$cur" ) )
193 COMPREPLY+=( $( compgen -W 'tcp udp' -- "$cur" ) )
197 # Filters options options
199 [[ ${words[@]} =~ graft ]] && return 1
200 COMPREPLY+=( $( compgen -W 'src dst proto proto-src proto-dst iif \
201 priority mark nfct nfct-src nfct-dst nfct-proto-src \
202 nfct-proto-dst rt-classid sk-uid sk-gid vlan-tag rxhash' -- \
207 # BPF options - used for filters, actions, and exec
208 export|bytecode|bytecode-file|object-file)
212 object-pinned|graft) # Pinned object is probably under /sys/fs/bpf/
213 [[ -n "$cur" ]] && _filedir && return 0
214 COMPREPLY=( $( compgen -G "/sys/fs/bpf/*" -- "$cur" ) ) || _filedir
219 if (type objdump > /dev/null 2>&1) ; then
220 local fword objfile section_list
221 for (( fword=3; fword < ${#words[@]}-3; fword++ )); do
222 if [[ ${words[fword]} == object-file ]]; then
223 objfile=${words[fword+1]}
227 section_list=$( objdump -h $objfile 2>/dev/null | \
228 sed -n 's/^ *[0-9]\+ \([^ ]*\) *.*/\1/p' )
229 COMPREPLY+=( $( compgen -W "$section_list" -- "$cur" ) )
238 COMPREPLY+=( $( compgen -W 'cls act' -- "$cur" ) )
244 _tc_one_of_list 'netrand determ'
248 # Units for option arguments
249 bandwidth|maxrate|peakrate|rate)
250 local list=$( _tc_expand_units 'bit' \
251 'kbit' 'kibit' 'kbps' 'kibps' \
252 'mbit' 'mibit' 'mbps' 'mibps' \
253 'gbit' 'gibit' 'gbps' 'gibps' \
254 'tbit' 'tibit' 'tbps' 'tibps' )
255 COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
257 admit_bytes|avpkt|burst|cell|initial_quantum|limit|max|min|mtu|mpu|\
258 overhead|quantum|redflowlist)
259 local list=$( _tc_expand_units \
260 'b' 'kbit' 'k' 'mbit' 'm' 'gbit' 'g' )
261 COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
263 db|delay|evict_timeout|interval|latency|perturb|rehash|reset_timeout|\
265 local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
266 COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
272 # Complete with options names for qdiscs. Each qdisc has its own set of options
273 # and it seems we cannot really parse it from anywhere, so we add it manually
275 # Returns 0 is completion should stop after running this function, 1 otherwise.
280 _tc_once_attr 'limit bandwidth ecn min max burst'
284 _tc_once_attr 'limit target interval'
285 _tc_one_of_list 'ecn noecn'
288 bfifo|pfifo|pfifo_head_drop)
289 _tc_once_attr 'limit'
293 _tc_once_attr 'limit flow_limit quantum initial_quantum maxrate \
295 _tc_one_of_list 'pacing nopacing'
299 _tc_once_attr 'limit flows target interval quantum'
300 _tc_one_of_list 'ecn noecn'
304 _tc_once_attr 'setup vqs default grio vq prio limit min max avpkt \
305 burst probability bandwidth'
309 _tc_once_attr 'limit quantum hh_limit reset_timeout admit_bytes \
310 evict_timeout non_hh_weight'
314 _tc_once_attr 'num_tc map queues hw'
318 _tc_once_attr 'delay distribution corrupt duplicate loss ecn \
323 _tc_once_attr 'limit target tupdate alpha beta'
324 _tc_one_of_list 'bytemode nobytemode'
325 _tc_one_of_list 'ecn noecn'
329 _tc_once_attr 'limit min max avpkt burst adaptive probability \
330 bandwidth ecn harddrop'
334 _tc_once_attr 'bands priomap multiqueue'
338 _tc_once_attr 'rehash db limit max target increment decrement \
339 penalty_rate penalty_burst'
343 _tc_once_attr 'limit perturb quantum divisor flows depth headdrop \
344 redflowlimit min max avpkt burst probability ecn harddrop'
348 _tc_once_attr 'limit burst rate mtu peakrate latency overhead \
353 _tc_once_attr 'bandwidth avpkt mpu cell ewma'
357 _tc_once_attr 'indices default_index set_tc_index'
361 _tc_once_attr 'default'
365 _tc_once_attr 'default r2q direct_qlen debug'
368 multiq|pfifo_fast|atm|drr|qfq)
375 # Complete with options names for BPF filters or actions.
376 # Returns 0 is completion should stop after running this function, 1 otherwise.
379 [[ ${words[${#words[@]}-3]} == object-file ]] && \
380 _tc_once_attr 'section export'
381 [[ ${words[${#words[@]}-5]} == object-file ]] && \
382 [[ ${words[${#words[@]}-3]} =~ (section|export) ]] && \
383 _tc_once_attr 'section export'
384 _tc_one_of_list 'bytecode bytecode-file object-file object-pinned'
385 _tc_once_attr 'verbose index direct-action action classid'
389 # Complete with options names for filter actions.
390 # This function is recursive, thus allowing multiple actions statement to be
392 # Returns 0 is completion should stop after running this function, 1 otherwise.
393 _tc_filter_action_options()
395 for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
397 if [[ action == ${words[acwd]} ]]; then
398 _tc_filter_action_options $((acwd+1)) && return 0
403 for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); do
404 if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then
405 _tc_one_of_list_from $acwd action
406 _tc_action_options $acwd && return 0
409 _tc_one_of_list_from $acwd $ACTION_KIND
413 # Complete with options names for filters.
414 # Returns 0 is completion should stop after running this function, 1 otherwise.
418 for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
420 if [[ action == ${words[acwd]} ]]; then
421 _tc_filter_action_options $((acwd+1)) && return 0
428 _tc_once_attr 'match action classid'
436 _tc_once_attr 'match action'
441 for (( i=5; i < ${#words[@]}-1; i++ )); do
442 if [[ ${words[i]} =~ ^keys?$ ]]; then
443 _tc_direct_complete 'key'
444 COMPREPLY+=( $( compgen -W 'or and xor rshift addend' -- \
449 _tc_once_attr 'map hash divisor baseclass match action'
453 _tc_once_attr 'action skip_sw skip_hw'
457 _tc_once_attr 'action classid indev dst_mac src_mac eth_type \
458 ip_proto dst_ip src_ip dst_port src_port'
462 _tc_once_attr 'action classid'
466 _tc_one_of_list 'from fromif'
467 _tc_once_attr 'to classid action'
471 _tc_once_attr 'ipproto session sender classid action tunnelid \
472 tunnel flowlabel spi/ah spi/esp u8 u16 u32'
473 [[ ${words[${#words[@]}-3]} == tunnel ]] && \
474 COMPREPLY+=( $( compgen -W 'skip' -- "$cur" ) )
475 [[ ${words[${#words[@]}-3]} =~ u(8|16|32) ]] && \
476 COMPREPLY+=( $( compgen -W 'mask' -- "$cur" ) )
477 [[ ${words[${#words[@]}-3]} == mask ]] && \
478 COMPREPLY+=( $( compgen -W 'at' -- "$cur" ) )
482 _tc_once_attr 'hash mask shift classid action'
483 _tc_one_of_list 'pass_on fall_through'
487 _tc_once_attr 'match link classid action offset ht hashkey sample'
488 COMPREPLY+=( $( compgen -W 'ip ip6 udp tcp icmp u8 u16 u32 mark \
489 divisor' -- "$cur" ) )
496 # Complete with options names for actions.
497 # Returns 0 is completion should stop after running this function, 1 otherwise.
501 local action=${words[from]}
508 _tc_one_of_list_from $from 'ingress egress'
509 _tc_one_of_list_from $from 'mirror redirect'
510 _tc_once_attr_from $from 'index dev'
514 _tc_once_attr_from $from 'rate'
515 _tc_once_attr_from $from 'trunc'
516 _tc_once_attr_from $from 'group'
520 _tc_one_of_list_from $from 'reclassify drop continue pass'
521 _tc_once_attr_from $from 'random'
528 # Complete with options names for exec.
529 # Returns 0 is completion should stop after running this function, 1 otherwise.
534 [[ ${words[${#words[@]}-3]} == import ]] && \
539 COMPREPLY+=( $( compgen -W 'key type' -- "$cur" ) )
540 [[ ${words[${#words[@]}-3]} == object-file ]] && \
549 # Main completion function
550 # Logic is as follows:
551 # 1. Check if previous word is a global option; if so, propose arguments.
552 # 2. Check if current word is a global option; if so, propose completion.
553 # 3. Check for the presence of a main command (qdisc|class|filter|...). If
554 # there is one, first call _tc_direct_complete to see if previous word is
555 # waiting for a particular completion. If so, propose completion and exit.
556 # 4. Extract main command and -- if available -- its subcommand
557 # (add|delete|show|...).
558 # 5. Propose completion based on main and sub- command in use. Additional
559 # functions may be called for qdiscs, classes or filter options.
562 local cur prev words cword
563 _init_completion || return
574 COMPREPLY=( $( compgen -W '-batch' -- "$cur" ) )
578 [[ -r /etc/iproute2/tc_cls ]] || \
579 COMPREPLY=( $( compgen -W '-conf' -- "$cur" ) )
583 local nslist=$( ip netns list 2>/dev/null )
584 COMPREPLY+=( $( compgen -W "$nslist" -- "$cur" ) )
588 _tc_once_attr '-statistics'
589 COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
593 _tc_once_attr '-statistics -tshort'
594 COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
599 # Search for main commands
600 local subcword cmd subcmd
601 for (( subcword=1; subcword < ${#words[@]}-1; subcword++ )); do
602 [[ ${words[subcword]} == -b?(atch) ]] && return 0
603 [[ -n $cmd ]] && subcmd=${words[subcword]} && break
604 [[ ${words[subcword]} != -* && \
605 ${words[subcword-1]} != -@(n?(et?(ns))|c?(on)f) ]] && \
606 cmd=${words[subcword]}
609 if [[ -z $cmd ]]; then
612 local c='-Version -statistics -details -raw -pretty \
613 -iec -graphe -batch -name -netns -timestamp'
614 [[ $cword -eq 1 ]] && c+=' -force'
615 COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
619 COMPREPLY=( $( compgen -W "help $( tc help 2>&1 | \
621 -e '/OBJECT := /!d' \
624 -e \ 's/|//g' )" -- "$cur" ) )
630 [[ $subcmd == help ]] && return 0
632 # For this set of commands we may create COMPREPLY just by analysing the
633 # previous word, if it expects for a specific list of options or values.
634 if [[ $cmd =~ (qdisc|class|filter|action|exec) ]]; then
635 _tc_direct_complete $prev && return 0
636 if [[ ${words[${#words[@]}-3]} == estimator ]]; then
637 local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
638 COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) && return 0
642 # Completion depends on main command and subcommand in use.
646 add|change|replace|link|del|delete)
647 if [[ $(($cword-$subcword)) -eq 1 ]]; then
648 COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
652 for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
653 if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
655 _tc_qdisc_options $qdisc && return 0
658 _tc_one_of_list $QDISC_KIND
659 _tc_one_of_list 'root ingress parent clsact'
660 _tc_once_attr 'handle estimator stab'
664 _tc_one_of_list 'ingress clsact'
665 _tc_once_attr '-statistics -details -raw -pretty -iec \
672 [[ $cword -eq $subcword ]] && \
673 COMPREPLY=( $( compgen -W 'help add delete change \
674 replace link show' -- "$cur" ) )
681 add|change|replace|del|delete)
682 if [[ $(($cword-$subcword)) -eq 1 ]]; then
683 COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
687 for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
688 if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
690 _tc_qdisc_options $qdisc && return 0
693 _tc_one_of_list $QDISC_KIND
694 _tc_one_of_list 'root parent'
695 _tc_once_attr 'classid'
699 _tc_one_of_list 'root parent'
700 _tc_once_attr '-statistics -details -raw -pretty -iec \
707 [[ $cword -eq $subcword ]] && \
708 COMPREPLY=( $( compgen -W 'help add delete change \
709 replace show' -- "$cur" ) )
716 add|change|replace|del|delete)
717 if [[ $(($cword-$subcword)) -eq 1 ]]; then
718 COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
722 for ((fltwd=$subcword; fltwd < ${#words[@]}-1; fltwd++));
724 if [[ $FILTER_KIND =~ ' '${words[fltwd]}' ' ]]; then
725 _tc_filter_options $fltwd && return 0
728 _tc_one_of_list $FILTER_KIND
729 _tc_one_of_list 'root ingress egress parent'
730 _tc_once_attr 'handle estimator pref protocol'
734 _tc_one_of_list 'root ingress egress parent'
735 _tc_once_attr '-statistics -details -raw -pretty -iec \
742 [[ $cword -eq $subcword ]] && \
743 COMPREPLY=( $( compgen -W 'help add delete change \
744 replace show' -- "$cur" ) )
753 for ((acwd=$subcword; acwd < ${#words[@]}-1; acwd++)); do
754 if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then
755 _tc_action_options $acwd && return 0
758 _tc_one_of_list $ACTION_KIND
761 _tc_once_attr 'index'
764 _tc_one_of_list $ACTION_KIND
767 [[ $cword -eq $subcword ]] && \
768 COMPREPLY=( $( compgen -W 'help add delete change \
769 replace show list flush action' -- "$cur" ) )
775 COMPREPLY=( $( compgen -W 'help' -- "$cur" ) )
781 local excmd exwd EXEC_KIND=' import debug graft '
782 for ((exwd=$subcword; exwd < ${#words[@]}-1; exwd++)); do
783 if [[ $EXEC_KIND =~ ' '${words[exwd]}' ' ]]; then
785 _tc_exec_options $excmd && return 0
788 _tc_one_of_list $EXEC_KIND
791 [[ $cword -eq $subcword ]] && \
792 COMPREPLY=( $( compgen -W 'bpf' -- "$cur" ) )
800 # ex: ts=4 sw=4 et filetype=sh