1# tc(8) completion -*- shell-script -*- 2# Copyright 2016 6WIND S.A. 3# Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com> 4 5QDISC_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 ' 8FILTER_KIND=' basic bpf cgroup flow flower fw route rsvp tcindex u32 matchall ' 9ACTION_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} && 798complete -F _tc tc 799 800# ex: ts=4 sw=4 et filetype=sh 801