Home | History | Annotate | Download | only in bash-completion
      1 # tc(8) completion                                         -*- shell-script -*-
      2 # Copyright 2016 6WIND S.A.
      3 # Copyright 2016 Quentin Monnet <quentin.monnet (a] 6wind.com>
      4 
      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 '
     10 
     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.
     13 _tc_once_attr()
     14 {
     15     local w subcword found
     16     for w in $*; do
     17         found=0
     18         for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
     19             if [[ $w == ${words[subcword]} ]]; then
     20                 found=1
     21                 break
     22             fi
     23         done
     24         [[ $found -eq 0 ]] && \
     25             COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
     26     done
     27 }
     28 
     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
     31 # no value.
     32 _tc_once_attr_from()
     33 {
     34     local w subcword found from=$1
     35     shift
     36     for w in $*; do
     37         found=0
     38         for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
     39             if [[ $w == ${words[subcword]} ]]; then
     40                 found=1
     41                 break
     42             fi
     43         done
     44         [[ $found -eq 0 ]] && \
     45             COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
     46     done
     47 }
     48 
     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.
     51 _tc_one_of_list()
     52 {
     53     local w subcword
     54     for w in $*; do
     55         for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
     56             [[ $w == ${words[subcword]} ]] && return 1
     57         done
     58     done
     59     COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
     60 }
     61 
     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
     64 # value.
     65 _tc_one_of_list_from()
     66 {
     67     local w subcword from=$1
     68     shift
     69     for w in $*; do
     70         for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
     71             [[ $w == ${words[subcword]} ]] && return 1
     72         done
     73     done
     74     COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
     75 }
     76 
     77 # Returns "$cur ${cur}arg1 ${cur}arg2 ..."
     78 _tc_expand_units()
     79 {
     80     [[ $cur =~ ^[0-9]+ ]] || return 1
     81     local value=${cur%%[^0-9]*}
     82     [[ $cur == $value ]] && echo $cur
     83     echo ${@/#/$value}
     84 }
     85 
     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.
     90 _tc_direct_complete()
     91 {
     92     case $1 in
     93         # Command options
     94         dev)
     95             _available_interfaces
     96             return 0
     97             ;;
     98         classid)
     99             return 0
    100             ;;
    101         estimator)
    102             local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
    103             COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
    104             return 0
    105             ;;
    106         handle)
    107             return 0
    108             ;;
    109         parent|flowid)
    110             local i iface ids cmd
    111             for (( i=3; i < ${#words[@]}-2; i++ )); do
    112                 [[ ${words[i]} == dev ]] && iface=${words[i+1]}
    113                 break
    114             done
    115             for cmd in qdisc class; do
    116                 if [[ -n $iface ]]; then
    117                     ids+=$( tc $cmd show dev $iface 2>/dev/null | \
    118                         cut -d\  -f 3 )" "
    119                 else
    120                     ids+=$( tc $cmd show 2>/dev/null | cut -d\  -f 3 )
    121                 fi
    122             done
    123             [[ $ids != " " ]] && \
    124                 COMPREPLY+=( $( compgen -W "$ids" -- "$cur" ) )
    125             return 0
    126             ;;
    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" ) )
    134             return 0
    135             ;;
    136         prio)
    137             return 0
    138             ;;
    139         stab)
    140             COMPREPLY+=( $( compgen -W 'mtu tsize mpu overhead
    141                 linklayer' -- "$cur" ) )
    142             ;;
    143 
    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|\
    149         reorder|vq|vqs)
    150             return 0
    151             ;;
    152         setup)
    153             COMPREPLY+=( $( compgen -W 'vqs' -- "$cur" ) )
    154             return 0
    155             ;;
    156         hw)
    157             COMPREPLY+=( $( compgen -W '1 0' -- "$cur" ) )
    158             return 0
    159             ;;
    160         distribution)
    161             COMPREPLY+=( $( compgen -W 'uniform normal pareto
    162                 paretonormal' -- "$cur" ) )
    163             return 0
    164             ;;
    165         loss)
    166             COMPREPLY+=( $( compgen -W 'random state gmodel' -- "$cur" ) )
    167             return 0
    168             ;;
    169 
    170         # Qdiscs and classes options options
    171         gap|gmodel|state)
    172             return 0
    173             ;;
    174 
    175         # Filters options
    176         map)
    177             COMPREPLY+=( $( compgen -W 'key' -- "$cur" ) )
    178             return 0
    179             ;;
    180         hash)
    181             COMPREPLY+=( $( compgen -W 'keys' -- "$cur" ) )
    182             return 0
    183             ;;
    184         indev)
    185             _available_interfaces
    186             return 0
    187             ;;
    188         eth_type)
    189             COMPREPLY+=( $( compgen -W 'ipv4 ipv6' -- "$cur" ) )
    190             return 0
    191             ;;
    192         ip_proto)
    193             COMPREPLY+=( $( compgen -W 'tcp udp' -- "$cur" ) )
    194             return 0
    195             ;;
    196 
    197         # Filters options options
    198         key|keys)
    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' -- \
    203                 "$cur" ) )
    204             return 0
    205             ;;
    206 
    207         # BPF options - used for filters, actions, and exec
    208         export|bytecode|bytecode-file|object-file)
    209             _filedir
    210             return 0
    211             ;;
    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
    215             compopt -o nospace
    216             return 0
    217             ;;
    218         section)
    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]}
    224                         break
    225                     fi
    226                 done
    227                 section_list=$( objdump -h $objfile 2>/dev/null | \
    228                     sed -n 's/^ *[0-9]\+ \([^ ]*\) *.*/\1/p' )
    229                 COMPREPLY+=( $( compgen -W "$section_list" -- "$cur" ) )
    230             fi
    231             return 0
    232             ;;
    233         import|run)
    234             _filedir
    235             return 0
    236             ;;
    237         type)
    238             COMPREPLY+=( $( compgen -W 'cls act' -- "$cur" ) )
    239             return 0
    240             ;;
    241 
    242         # Actions options
    243         random)
    244             _tc_one_of_list 'netrand determ'
    245             return 0
    246             ;;
    247 
    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" ) )
    256             ;;
    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" ) )
    262             ;;
    263         db|delay|evict_timeout|interval|latency|perturb|rehash|reset_timeout|\
    264         target|tupdate)
    265             local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
    266             COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
    267             ;;
    268     esac
    269     return 1
    270 }
    271 
    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
    274 # in this function.
    275 # Returns 0 is completion should stop after running this function, 1 otherwise.
    276 _tc_qdisc_options()
    277 {
    278     case $1 in
    279         choke)
    280             _tc_once_attr 'limit bandwidth ecn min max burst'
    281             return 0
    282             ;;
    283         codel)
    284             _tc_once_attr 'limit target interval'
    285             _tc_one_of_list 'ecn noecn'
    286             return 0
    287             ;;
    288         bfifo|pfifo|pfifo_head_drop)
    289             _tc_once_attr 'limit'
    290             return 0
    291             ;;
    292         fq)
    293             _tc_once_attr 'limit flow_limit quantum initial_quantum maxrate \
    294                 buckets'
    295             _tc_one_of_list 'pacing nopacing'
    296             return 0
    297             ;;
    298         fq_codel)
    299             _tc_once_attr 'limit flows target interval quantum'
    300             _tc_one_of_list 'ecn noecn'
    301             return 0
    302             ;;
    303         gred)
    304             _tc_once_attr 'setup vqs default grio vq prio limit min max avpkt \
    305                 burst probability bandwidth'
    306             return 0
    307             ;;
    308         hhf)
    309             _tc_once_attr 'limit quantum hh_limit reset_timeout admit_bytes \
    310                 evict_timeout non_hh_weight'
    311             return 0
    312             ;;
    313         mqprio)
    314             _tc_once_attr 'num_tc map queues hw'
    315             return 0
    316             ;;
    317         netem)
    318             _tc_once_attr 'delay distribution corrupt duplicate loss ecn \
    319                 reorder rate'
    320             return 0
    321             ;;
    322         pie)
    323             _tc_once_attr 'limit target tupdate alpha beta'
    324             _tc_one_of_list 'bytemode nobytemode'
    325             _tc_one_of_list 'ecn noecn'
    326             return 0
    327             ;;
    328         red)
    329             _tc_once_attr 'limit min max avpkt burst adaptive probability \
    330                 bandwidth ecn harddrop'
    331             return 0
    332             ;;
    333         rr|prio)
    334             _tc_once_attr 'bands priomap multiqueue'
    335             return 0
    336             ;;
    337         sfb)
    338             _tc_once_attr 'rehash db limit max target increment decrement \
    339                 penalty_rate penalty_burst'
    340             return 0
    341             ;;
    342         sfq)
    343             _tc_once_attr 'limit perturb quantum divisor flows depth headdrop \
    344                 redflowlimit min max avpkt burst probability ecn harddrop'
    345             return 0
    346             ;;
    347         tbf)
    348             _tc_once_attr 'limit burst rate mtu peakrate latency overhead \
    349                 linklayer'
    350             return 0
    351             ;;
    352         cbq)
    353             _tc_once_attr 'bandwidth avpkt mpu cell ewma'
    354             return 0
    355             ;;
    356         dsmark)
    357             _tc_once_attr 'indices default_index set_tc_index'
    358             return 0
    359             ;;
    360         hfsc)
    361             _tc_once_attr 'default'
    362             return 0
    363             ;;
    364         htb)
    365             _tc_once_attr 'default r2q direct_qlen debug'
    366             return 0
    367             ;;
    368         multiq|pfifo_fast|atm|drr|qfq)
    369             return 0
    370             ;;
    371     esac
    372     return 1
    373 }
    374 
    375 # Complete with options names for BPF filters or actions.
    376 # Returns 0 is completion should stop after running this function, 1 otherwise.
    377 _tc_bpf_options()
    378 {
    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'
    386     return 0
    387 }
    388 
    389 # Complete with options names for filter actions.
    390 # This function is recursive, thus allowing multiple actions statement to be
    391 # parsed.
    392 # Returns 0 is completion should stop after running this function, 1 otherwise.
    393 _tc_filter_action_options()
    394 {
    395     for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
    396     do
    397         if [[ action == ${words[acwd]} ]]; then
    398             _tc_filter_action_options $((acwd+1)) && return 0
    399         fi
    400     done
    401 
    402     local action acwd
    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
    407         fi
    408     done
    409     _tc_one_of_list_from $acwd $ACTION_KIND
    410     return 0
    411 }
    412 
    413 # Complete with options names for filters.
    414 # Returns 0 is completion should stop after running this function, 1 otherwise.
    415 _tc_filter_options()
    416 {
    417 
    418     for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
    419     do
    420         if [[ action == ${words[acwd]} ]]; then
    421             _tc_filter_action_options $((acwd+1)) && return 0
    422         fi
    423     done
    424 
    425     filter=${words[$1]}
    426     case $filter in
    427         basic)
    428             _tc_once_attr 'match action classid'
    429             return 0
    430             ;;
    431         bpf)
    432             _tc_bpf_options
    433             return 0
    434             ;;
    435         cgroup)
    436             _tc_once_attr 'match action'
    437             return 0
    438             ;;
    439         flow)
    440             local i
    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' -- \
    445                         "$cur" ) )
    446                     break
    447                 fi
    448             done
    449             _tc_once_attr 'map hash divisor baseclass match action'
    450             return 0
    451             ;;
    452         matchall)
    453             _tc_once_attr 'action skip_sw skip_hw'
    454             return 0
    455             ;;
    456         flower)
    457             _tc_once_attr 'action classid indev dst_mac src_mac eth_type \
    458                 ip_proto dst_ip src_ip dst_port src_port'
    459             return 0
    460             ;;
    461         fw)
    462             _tc_once_attr 'action classid'
    463             return 0
    464             ;;
    465         route)
    466             _tc_one_of_list 'from fromif'
    467             _tc_once_attr 'to classid action'
    468             return 0
    469             ;;
    470         rsvp)
    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" ) )
    479             return 0
    480             ;;
    481         tcindex)
    482             _tc_once_attr 'hash mask shift classid action'
    483             _tc_one_of_list 'pass_on fall_through'
    484             return 0
    485             ;;
    486         u32)
    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" ) )
    490             return 0
    491             ;;
    492     esac
    493     return 1
    494 }
    495 
    496 # Complete with options names for actions.
    497 # Returns 0 is completion should stop after running this function, 1 otherwise.
    498 _tc_action_options()
    499 {
    500     local from=$1
    501     local action=${words[from]}
    502     case $action in
    503         bpf)
    504             _tc_bpf_options
    505             return 0
    506             ;;
    507         mirred)
    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'
    511             return 0
    512             ;;
    513         sample)
    514             _tc_once_attr_from $from 'rate'
    515             _tc_once_attr_from $from 'trunc'
    516             _tc_once_attr_from $from 'group'
    517             return 0
    518             ;;
    519         gact)
    520             _tc_one_of_list_from $from 'reclassify drop continue pass'
    521             _tc_once_attr_from $from 'random'
    522             return 0
    523             ;;
    524     esac
    525     return 1
    526 }
    527 
    528 # Complete with options names for exec.
    529 # Returns 0 is completion should stop after running this function, 1 otherwise.
    530 _tc_exec_options()
    531 {
    532     case $1 in
    533         import)
    534             [[ ${words[${#words[@]}-3]} == import ]] && \
    535                 _tc_once_attr 'run'
    536             return 0
    537             ;;
    538         graft)
    539             COMPREPLY+=( $( compgen -W 'key type' -- "$cur" ) )
    540             [[ ${words[${#words[@]}-3]} == object-file ]] && \
    541                 _tc_once_attr 'type'
    542             _tc_bpf_options
    543             return 0
    544             ;;
    545     esac
    546     return 1
    547 }
    548 
    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.
    560 _tc()
    561 {
    562     local cur prev words cword
    563     _init_completion || return
    564 
    565     case $prev in
    566         -V|-Version)
    567             return 0
    568             ;;
    569         -b|-batch|-cf|-conf)
    570             _filedir
    571             return 0
    572             ;;
    573         -force)
    574             COMPREPLY=( $( compgen -W '-batch' -- "$cur" ) )
    575             return 0
    576             ;;
    577         -nm|name)
    578             [[ -r /etc/iproute2/tc_cls ]] || \
    579                 COMPREPLY=( $( compgen -W '-conf' -- "$cur" ) )
    580             return 0
    581             ;;
    582         -n|-net|-netns)
    583             local nslist=$( ip netns list 2>/dev/null )
    584             COMPREPLY+=( $( compgen -W "$nslist" -- "$cur" ) )
    585             return 0
    586             ;;
    587         -tshort)
    588             _tc_once_attr '-statistics'
    589             COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
    590             return 0
    591             ;;
    592         -timestamp)
    593             _tc_once_attr '-statistics -tshort'
    594             COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
    595             return 0
    596             ;;
    597     esac
    598 
    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]}
    607     done
    608 
    609     if [[ -z $cmd ]]; then
    610         case $cur in
    611             -*)
    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" ) )
    616                 return 0
    617                 ;;
    618             *)
    619                 COMPREPLY=( $( compgen -W "help $( tc help 2>&1 | \
    620                     command sed \
    621                     -e '/OBJECT := /!d' \
    622                     -e 's/.*{//' \
    623                     -e 's/}.*//' \
    624                     -e \ 's/|//g' )" -- "$cur" ) )
    625                 return 0
    626                 ;;
    627         esac
    628     fi
    629 
    630     [[ $subcmd == help ]] && return 0
    631 
    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
    639         fi
    640     fi
    641 
    642     # Completion depends on main command and subcommand in use.
    643     case $cmd in
    644         qdisc)
    645             case $subcmd in
    646                 add|change|replace|link|del|delete)
    647                     if [[ $(($cword-$subcword)) -eq 1 ]]; then
    648                         COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
    649                         return 0
    650                     fi
    651                     local qdisc qdwd
    652                     for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
    653                         if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
    654                             qdisc=${words[qdwd]}
    655                             _tc_qdisc_options $qdisc && return 0
    656                         fi
    657                     done
    658                     _tc_one_of_list $QDISC_KIND
    659                     _tc_one_of_list 'root ingress parent clsact'
    660                     _tc_once_attr 'handle estimator stab'
    661                     ;;
    662                 show)
    663                     _tc_once_attr 'dev'
    664                     _tc_one_of_list 'ingress clsact'
    665                     _tc_once_attr '-statistics -details -raw -pretty -iec \
    666                         -graph -name'
    667                     ;;
    668                 help)
    669                     return 0
    670                     ;;
    671                 *)
    672                     [[ $cword -eq $subcword ]] && \
    673                         COMPREPLY=( $( compgen -W 'help add delete change \
    674                             replace link show' -- "$cur" ) )
    675                     ;;
    676             esac
    677             ;;
    678 
    679         class)
    680             case $subcmd in
    681                 add|change|replace|del|delete)
    682                     if [[ $(($cword-$subcword)) -eq 1 ]]; then
    683                         COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
    684                         return 0
    685                     fi
    686                     local qdisc qdwd
    687                     for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
    688                         if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
    689                             qdisc=${words[qdwd]}
    690                             _tc_qdisc_options $qdisc && return 0
    691                         fi
    692                     done
    693                     _tc_one_of_list $QDISC_KIND
    694                     _tc_one_of_list 'root parent'
    695                     _tc_once_attr 'classid'
    696                     ;;
    697                 show)
    698                     _tc_once_attr 'dev'
    699                     _tc_one_of_list 'root parent'
    700                     _tc_once_attr '-statistics -details -raw -pretty -iec \
    701                         -graph -name'
    702                     ;;
    703                 help)
    704                     return 0
    705                     ;;
    706                 *)
    707                     [[ $cword -eq $subcword ]] && \
    708                         COMPREPLY=( $( compgen -W 'help add delete change \
    709                             replace show' -- "$cur" ) )
    710                     ;;
    711             esac
    712             ;;
    713 
    714         filter)
    715             case $subcmd in
    716                 add|change|replace|del|delete)
    717                     if [[ $(($cword-$subcword)) -eq 1 ]]; then
    718                         COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
    719                         return 0
    720                     fi
    721                     local filter fltwd
    722                     for ((fltwd=$subcword; fltwd < ${#words[@]}-1; fltwd++));
    723                     do
    724                         if [[ $FILTER_KIND =~ ' '${words[fltwd]}' ' ]]; then
    725                             _tc_filter_options $fltwd && return 0
    726                         fi
    727                     done
    728                     _tc_one_of_list $FILTER_KIND
    729                     _tc_one_of_list 'root ingress egress parent'
    730                     _tc_once_attr 'handle estimator pref protocol'
    731                     ;;
    732                 show)
    733                     _tc_once_attr 'dev'
    734                     _tc_one_of_list 'root ingress egress parent'
    735                     _tc_once_attr '-statistics -details -raw -pretty -iec \
    736                         -graph -name'
    737                     ;;
    738                 help)
    739                     return 0
    740                     ;;
    741                 *)
    742                     [[ $cword -eq $subcword ]] && \
    743                         COMPREPLY=( $( compgen -W 'help add delete change \
    744                             replace show' -- "$cur" ) )
    745                     ;;
    746             esac
    747             ;;
    748 
    749         action)
    750             case $subcmd in
    751                 add|change|replace)
    752                     local action acwd
    753                     for ((acwd=$subcword; acwd < ${#words[@]}-1; acwd++)); do
    754                         if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then
    755                             _tc_action_options $acwd && return 0
    756                         fi
    757                     done
    758                     _tc_one_of_list $ACTION_KIND
    759                     ;;
    760                 get|del|delete)
    761                     _tc_once_attr 'index'
    762                     ;;
    763                 lst|list|flush|show)
    764                     _tc_one_of_list $ACTION_KIND
    765                     ;;
    766                 *)
    767                     [[ $cword -eq $subcword ]] && \
    768                         COMPREPLY=( $( compgen -W 'help add delete change \
    769                             replace show list flush action' -- "$cur" ) )
    770                     ;;
    771             esac
    772             ;;
    773 
    774         monitor)
    775             COMPREPLY=( $( compgen -W 'help' -- "$cur" ) )
    776             ;;
    777 
    778         exec)
    779             case $subcmd in
    780                 bpf)
    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
    784                             excmd=${words[exwd]}
    785                             _tc_exec_options $excmd && return 0
    786                         fi
    787                     done
    788                     _tc_one_of_list $EXEC_KIND
    789                     ;;
    790                 *)
    791                     [[ $cword -eq $subcword ]] && \
    792                         COMPREPLY=( $( compgen -W 'bpf' -- "$cur" ) )
    793                     ;;
    794             esac
    795             ;;
    796     esac
    797 } &&
    798 complete -F _tc tc
    799 
    800 # ex: ts=4 sw=4 et filetype=sh
    801