Add _nft (iptables successor) completion
Some things to note here: 1.: There are bugs in the code as I'm no expert on zsh completion *at all*. I just trial-and-error'd a big part of it. 2.: Some things are not done here (marked with a `TODO`), namely the statement syntax of some `nft add` commands. 3.: Some completions try to complete existing chain names/rule handles/table names. For this you either have to be root or use sudo AND enable the gain-privileges zstyle (otherwise this will not work) 4.: I also found the nft man page to be somewhat lacking so there might be some edge case completions which are plain wrong.
This commit is contained in:
parent
c614accc90
commit
d4d55235da
|
@ -0,0 +1,465 @@
|
|||
#compdef nft
|
||||
|
||||
_nft(){
|
||||
local -a rules states prev args families options descriptors
|
||||
local state="start" line nextstate cmd_obj cmd_subcmd cmd_fam cmd_tab cmd_chain #curcontext="$curcontext"
|
||||
|
||||
options=(
|
||||
'(-)'{-h,--help}'[show help]' \
|
||||
'(-)'{-v,--version}'[print version information]' \
|
||||
"(-i --interactive)"{-i,--interactive}'[read input from interactive CLI]: :->end' \
|
||||
"(-f --file)"{-f,--file}'[Read input from <filename>]:nftables rule file:_files' \
|
||||
'(-c --check -n --numeric -N)'{-c,--check}'[check commands validity without actually applying the changes]' \
|
||||
'(-j --json)'{-j,--json}'[format output in json]' \
|
||||
'(-c --check -N)*'{-n,--numeric}'[can be specified up to 3 times, Shows 1:network addresses(default behaviour), 2:Internet services (port numbers) and 3:protocols, user IDs, and group IDs numerically]' \
|
||||
'(-s --stateless)'{-s,--stateless}'[omit stateful information of ruleset]' \
|
||||
'(-N -n --numeric -c --check)'-N'[translate IP addresses to names]' \
|
||||
'(-a --handle)'{-a,--handle}'[output rule handle]' \
|
||||
'(-e --echo)'{-e,--echo}'[echo what has been added, inserted or replaced]' \
|
||||
{-I,--includepath}'[add <directory> to the paths searched for include files. Default is /usr/share]:include directory:_path_files -/' \
|
||||
)
|
||||
|
||||
# start a state machine. The state is modified by _arguments if the
|
||||
# current argument (descriptors) cannot be completed. Each state has to define is successive state and the
|
||||
# 'descriptors' for _arguments, which essentially tells _arguments how to complete
|
||||
_i=0
|
||||
while true;do
|
||||
(( _i+=1 ))
|
||||
#Guard for endless loops
|
||||
[[ $_i -gt 100 ]] && return 1
|
||||
|
||||
descriptors=()
|
||||
nextstate="end"
|
||||
case $state in
|
||||
(start)
|
||||
##if line is empty (at the start) or ends with semicolon, autocomplete subcommands,
|
||||
# else if we are after a space,complete a semicolon (end the current nft command) and start anew
|
||||
if [[ $line[1] = "" || $line[1] =~ ';$' ]] ; then
|
||||
descriptors=( ":: :_nft_subcommands" )
|
||||
nextstate="category"
|
||||
else
|
||||
if [[ $words =~ ' $' ]]; then
|
||||
descriptors=(':: :(\;)')
|
||||
else
|
||||
descriptors=(':argument: ')
|
||||
fi
|
||||
nextstate="start"
|
||||
fi
|
||||
;;
|
||||
(category)
|
||||
case $line[1] in
|
||||
(add | list | flush | delete | create | rename | insert | replace | reset)
|
||||
descriptors=( ":: :_nft_${line[1]}" )
|
||||
nextstate=$line[1]
|
||||
;;
|
||||
(monitor)
|
||||
descriptors=( ":: : _nft_mon_filter" )
|
||||
nextstate="mon1"
|
||||
;;
|
||||
(export)
|
||||
descriptors=( ":: :(ruleset)" ":: :_nft_out_format" )
|
||||
nextstate="preend"
|
||||
;;
|
||||
(describe)
|
||||
descriptors=( ":expression: ")
|
||||
nextstate="start" #x restart
|
||||
;;
|
||||
(*)
|
||||
return 1;
|
||||
;;
|
||||
esac
|
||||
|
||||
#descriptors=( "(ruleset)" )
|
||||
#nextstate="end"
|
||||
;;
|
||||
(mon1)
|
||||
case $line[1] in
|
||||
(new | destroy)
|
||||
# descriptors=( ":: :_nft_mon_keywords" ":: :_nft_out_format")
|
||||
descriptors=( ":: : _nft_mon_keywords")
|
||||
nextstate="mon1"
|
||||
;;
|
||||
(tables | chains | sets | rules | elements | ruleset)
|
||||
descriptors=( ":: : _nft_out_format")
|
||||
nextstate="preend"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
#all completions for create and insert match with the completions of add
|
||||
(create | insert)
|
||||
state="add"
|
||||
;|
|
||||
#all completions for reset and flush match with the completions of list
|
||||
(reset | flush)
|
||||
state="list"
|
||||
;|
|
||||
#(add(^table)/create(^table)/delete/flush(^ruleset)/insert/list(^ruleset)/rename/replace)[family]table
|
||||
(reset | delete | insert | rename | replace | add | create | flush | list)
|
||||
if [[ $state = "add" && $line[1] = "table" ]]; then
|
||||
descriptors=( ":: :_nft_families" ":table name:")
|
||||
nextstate="start" #x restart
|
||||
elif [[ $state = "list" && ( $line[1] = "ruleset" || $line[1] = "tables" ) ]]; then
|
||||
descriptors=( ":: :_nft_families")
|
||||
nextstate="start" #x restart
|
||||
elif [[ $state = "delete" && $line[1] = "table" ]]; then
|
||||
descriptors=(": : _nft_table all-handle")
|
||||
nextstate="tcomplete-delete-table"
|
||||
else
|
||||
cmd_obj=$line[1]
|
||||
cmd_subcmd=$state
|
||||
descriptors=(": : _nft_table all")
|
||||
nextstate="tcomplete"
|
||||
fi
|
||||
;;
|
||||
(tcomplete-delete-table)
|
||||
# if only a family was completed, complete the table name.
|
||||
case $line[1] in
|
||||
(arp | bridge | inet | ip | ip6 | netdev)
|
||||
descriptors=(": : _nft_table ${line[1]}-handle")
|
||||
cmd_fam=$line[1]
|
||||
;;
|
||||
# else, complete nothing and go to the next state. default family is 'ip'
|
||||
(*)
|
||||
descriptors=()
|
||||
cmd_fam="ip"
|
||||
;;
|
||||
esac
|
||||
nextstate="delete-table"
|
||||
;;
|
||||
(tcomplete)
|
||||
# if only a family was completed, complete the table name.
|
||||
case $line[1] in
|
||||
(arp | bridge | inet | ip | ip6 | netdev)
|
||||
descriptors=(": : _nft_table ${line[1]}")
|
||||
cmd_fam=$line[1]
|
||||
;;
|
||||
# else, complete nothing and go to the next state. default family is 'ip'
|
||||
(*)
|
||||
descriptors=()
|
||||
cmd_fam="ip"
|
||||
;;
|
||||
esac
|
||||
nextstate="$cmd_subcmd-$cmd_obj"
|
||||
;;
|
||||
(list-table)
|
||||
descriptors=(":: :(\;)")
|
||||
nextstate="start"
|
||||
;;
|
||||
(delete-table)
|
||||
if [[ $line[1] == "handle" ]]; then
|
||||
descriptors=(":table handle: _nft_table_handle $cmd_fam" )
|
||||
else
|
||||
descriptors=()
|
||||
fi
|
||||
nextstate="start"
|
||||
;;
|
||||
(delete-chain | delete-set | delete-quota | delete-counter | delete-ct\\ helper)
|
||||
cmd_tab=$line[1]
|
||||
descriptors=(": : _nft_obj $cmd_fam $cmd_tab $cmd_obj true")
|
||||
nextstate="delete-obj-handle"
|
||||
;;
|
||||
(delete-obj-handle)
|
||||
if [[ $line[1] == "handle" ]]; then
|
||||
descriptors=(": : _nft_obj_handle $cmd_fam $cmd_tab $cmd_obj")
|
||||
else
|
||||
descriptors=(": :(\;)")
|
||||
fi
|
||||
nextstate="start"
|
||||
;;
|
||||
(add-chain)
|
||||
descriptors=(":chain name:")
|
||||
nextstate="start"
|
||||
;;
|
||||
(rename-chain)
|
||||
cmd_tab=$line[1]
|
||||
descriptors=(": : _nft_chain $cmd_fam $cmd_tab false")
|
||||
nextstate="add-chain"
|
||||
;;
|
||||
(replace-rule | delete-rule)
|
||||
cmd_tab=$line[1]
|
||||
descriptors=(": : _nft_chain $cmd_fam $cmd_tab false")
|
||||
nextstate="repdel-rule"
|
||||
;;
|
||||
(repdel-rule)
|
||||
cmd_chain=$line[1]
|
||||
descriptors=(": :(handle)" ": : _nft_rule_handle $cmd_fam $cmd_tab ${line[1]}")
|
||||
if [[ $cmd_subcmd = "replace" ]];then
|
||||
nextstate="rule-stmt"
|
||||
else
|
||||
nextstate="start"
|
||||
fi
|
||||
;;
|
||||
(add-rule)
|
||||
cmd_tab=$line[1]
|
||||
descriptors=(": : _nft_chain $cmd_fam $cmd_tab false")
|
||||
nextstate="add-rule-2"
|
||||
;;
|
||||
(add-rule-2)
|
||||
cmd_chain=$line[1]
|
||||
descriptors=(": :(handle index position)")
|
||||
nextstate="add-rule-3"
|
||||
;;
|
||||
(add-rule-3)
|
||||
case $line[1] in
|
||||
(index | position)
|
||||
descriptors=(":${line[1]}:")
|
||||
;;
|
||||
(handle)
|
||||
descriptors=(": : _nft_rule_handle $cmd_fam $cmd_tab $cmd_chain")
|
||||
;;
|
||||
(*)
|
||||
descriptors=()
|
||||
;;
|
||||
esac
|
||||
nextstate="rule-stmt"
|
||||
;;
|
||||
(rule-stmt)
|
||||
#TODO
|
||||
# _nft_rule $cmd_fam $cmd_tab $cmd_chain\
|
||||
# && return 0;
|
||||
descriptors=":expression: "
|
||||
nextstate="start"
|
||||
;;
|
||||
(list-set | list-map | delete-map | list-chain | list-flowtable | delete-flowtable | list-ct\\ helper | list-counter | list-quota | list-meter)
|
||||
cmd_tab=$line[1]
|
||||
descriptors=(": : _nft_obj $cmd_fam $cmd_tab $cmd_obj false")
|
||||
nextstate="start"
|
||||
;;
|
||||
#TODO:
|
||||
#(add-element | delete-element)
|
||||
#(add-set | add-map)
|
||||
#(add-flowtable)
|
||||
#("add-ct\ helper")
|
||||
#(add-counter)
|
||||
#(add-quota)
|
||||
|
||||
(*)
|
||||
return 1;
|
||||
;;
|
||||
esac
|
||||
_arguments -C -s \
|
||||
"${options[@]}" \
|
||||
"${descriptors[@]}" \
|
||||
"*:: :->$nextstate" \
|
||||
&& return 0;
|
||||
|
||||
done
|
||||
} # end _nft
|
||||
|
||||
_nft_subcommands(){
|
||||
local commands=(
|
||||
'add:add a table, chain, rule, set, map, or object'
|
||||
'list:list a ruleset, table, chain, set, map, or object'
|
||||
'flush:flush (delete everything from) a ruleset, table, chain, set, or map'
|
||||
'export:print the ruleset in a machine readable format (json or xml)'
|
||||
'delete:delete a table, chain, rule, set, element, map, or object'
|
||||
'create:similar to add but returns an error for existing chain'
|
||||
'rename:rename the specified chain'
|
||||
'insert:similar to the add command, but the rule is prepended to the beginning of the chain or before the rule at the given position'
|
||||
'replace:similar to the add command, but replaces the specified rule'
|
||||
'reset:list-and-reset stateful object'
|
||||
'monitor:listen to Netlink events'
|
||||
'describe:show information about the type of an expression and its data type'
|
||||
)
|
||||
_describe -t commands 'nft subcommands' commands "${expl[@]}"
|
||||
}
|
||||
_nft_mon_filter(){
|
||||
local filter=(
|
||||
'new:show only events of created objects'
|
||||
'destroy:show only events of deleted objects'
|
||||
)
|
||||
_describe -t filter 'nft monitor' filter -J "action filter" "${expl[@]}"
|
||||
_nft_mon_keywords
|
||||
}
|
||||
|
||||
_nft_mon_keywords(){
|
||||
local objs=(
|
||||
'tables:show table events'
|
||||
'chains:show chain events'
|
||||
'sets:show set events'
|
||||
'rules:show rule events'
|
||||
'elements:show only events of element objects'
|
||||
'ruleset:show ruleset events, such as table, chain, rule, set, counters and quotas'
|
||||
)
|
||||
_describe -t objs 'nft monitor' objs -J "object filter" "${expl[@]}"
|
||||
_nft_out_format
|
||||
}
|
||||
|
||||
_nft_out_format(){
|
||||
local commands=(
|
||||
'json:format output to JSON'
|
||||
'xml:format output to XML'
|
||||
)
|
||||
_describe -t commands "output format" commands -J "output format options" "${expl[@]}"
|
||||
}
|
||||
|
||||
_nft_add(){
|
||||
local commands=(
|
||||
'table:add a new table'
|
||||
'flowtable:add a new flowtable'
|
||||
'chain:add a chain to a table'
|
||||
'rule:add a rule to an existing chain'
|
||||
'set:add a set to a table'
|
||||
'map:add a map to a table'
|
||||
'element:add one or multiple element(s) to a set or map'
|
||||
'ct\ helper:add a ct helper to a table'
|
||||
'counter:add a named counter to a table'
|
||||
'quota:add a named quota helper to a table'
|
||||
)
|
||||
_describe -t commands 'nft add' commands "$@"
|
||||
}
|
||||
|
||||
_nft_create(){
|
||||
local commands=(
|
||||
"table:add a table, but return an error if it already exists"
|
||||
"chain:add a chain to a table, but return an error if it already exists"
|
||||
"flowtable:add a flowtable, but return an error if it already exists"
|
||||
)
|
||||
_describe -t commands 'nft create' commands "$@"
|
||||
}
|
||||
|
||||
_nft_delete(){
|
||||
local commands=(
|
||||
"table:delete the specified table"
|
||||
"chain:delete the specified chain, chain must be empty and mustn't be used as jump target"
|
||||
"rule:delete the specified rule, rule must be referrable to by a handle"
|
||||
"set:delete the specified set"
|
||||
"map:delete the specified map"
|
||||
"element:delete element(s) from the specified set/map"
|
||||
"flowtable:delete the specified flowtable"
|
||||
"ct\ helper:delete the specified ct helper"
|
||||
"counter:delete the specified counter"
|
||||
"quota:delete the specified quota"
|
||||
)
|
||||
_describe -t commands 'nft delete' commands "$@"
|
||||
}
|
||||
|
||||
_nft_flush(){
|
||||
local commands=(
|
||||
"ruleset:clear the whole ruleset, including removing all tables and containing objects"
|
||||
"table:flush all chains and rules of the specified table"
|
||||
"chain:flush all rules of the specified chain"
|
||||
"set:remove all elements from the specified set"
|
||||
"map:remove all elements from the specified map"
|
||||
)
|
||||
_describe -t commands 'nft flush' commands "$@"
|
||||
}
|
||||
|
||||
_nft_insert(){
|
||||
local commands=(
|
||||
"rule:prepend a rule to the beginning of the chain or before the rule with the given handle"
|
||||
)
|
||||
_describe -t commands 'nft insert' commands "$@"
|
||||
}
|
||||
|
||||
_nft_list(){
|
||||
local commands=(
|
||||
"ruleset:print the ruleset in human-readable format"
|
||||
"tables:list all tables (undocumented)"
|
||||
"table:list all chains and rules of the specified table"
|
||||
"chain:list all rules of the specified chain"
|
||||
"set:display the elements in the specified set"
|
||||
"map:display the elements in the specified map"
|
||||
"flowtable:list all flowtables"
|
||||
"ct\ helper:display stateful information the ct helper holds"
|
||||
"counter:display stateful information the counter holds"
|
||||
"quota:display stateful information the quota holds"
|
||||
)
|
||||
_describe -t commands 'nft list' commands "$@"
|
||||
}
|
||||
|
||||
_nft_rename(){
|
||||
local commands=(
|
||||
"chain:replace a chain"
|
||||
)
|
||||
_describe -t commands 'nft rename' commands "$@"
|
||||
}
|
||||
|
||||
_nft_replace(){
|
||||
local commands=(
|
||||
"rule:replace a rule"
|
||||
)
|
||||
_describe -t commands 'nft replace' commands "$@"
|
||||
}
|
||||
|
||||
_nft_reset(){
|
||||
local commands=(
|
||||
'ct\ helper:reset and list a ct helper to a table'
|
||||
'counter:reset and list a counter from a table'
|
||||
'quota:reset and list a quota object a table'
|
||||
)
|
||||
_describe -t commands 'nft reset' commands "$@"
|
||||
}
|
||||
_nft_families(){
|
||||
local commands=(
|
||||
"ip:IPv4 address family"
|
||||
"ip6:IPv6 address family"
|
||||
"inet:internet (IPv4+IPv6) address family"
|
||||
"arp:ARP address family, handling IPv4 ARP packets"
|
||||
"bridge:Bridge address family, handling packets which traverse a bridge device"
|
||||
"netdev:Netdev address family, handling packets from ingress"
|
||||
)
|
||||
_describe -t commands 'nft families' commands "$@"
|
||||
}
|
||||
|
||||
_nft_table(){
|
||||
#$1 can be: all all-handle <family> <family>-handle
|
||||
local tables=()
|
||||
if [[ "$1" =~ "^all" ]]; then
|
||||
tables+=( ${(f)"$(_call_program -p nft-tables nft list tables 2>/dev/null \
|
||||
| cut -d\ -f2 |sort|uniq -u )"} )
|
||||
1="${1/all/ip}"
|
||||
fi
|
||||
if [[ "$1" =~ "-handle$" ]]; then
|
||||
tables+=("handle")
|
||||
#remove -handle from $1 to be able to complete table names
|
||||
1="${1/-handle/}"
|
||||
fi
|
||||
case $1 in
|
||||
(arp | bridge | inet | ip | ip6 | netdev)
|
||||
tables+=( ${(f)"$(_call_program -p nft-tables nft list tables $1 2>/dev/null \
|
||||
| cut -d\ -f3 )"} )
|
||||
;;
|
||||
esac
|
||||
_describe -V -t tables tables tables "${expl[@]}"
|
||||
}
|
||||
|
||||
_nft_table_handle(){
|
||||
local tables=( ${(f)"$(_call_program -p nft-table-handles nft list ruleset -a 2>/dev/null \
|
||||
| grep '^table' | sed 's/table // ;s/{ # handle // ;s/\(\S*\) \(\S*\) \(\S*\)/\3:\2(type \1)/' )"} )
|
||||
_describe -t tables tables tables "${expl[@]}"
|
||||
}
|
||||
|
||||
_nft_obj(){
|
||||
#$1:protocol family
|
||||
#$2:table
|
||||
#$3:obj type (chain/set/map/flowtable/ct helper/counter/quota/meter)
|
||||
#$4:include 'handle'?
|
||||
local objs=( ${(f)"$(_call_program -p nft-$3s nft list table $1 $2 -a 2>/dev/null\
|
||||
| grep ""\\s\*$3"" | sed 's/\s*'"$3"' // ;s/ { # \(.*\)/:(\1)/' )"} )
|
||||
if $4 ;then
|
||||
objs+=( "handle:adress chain by handle")
|
||||
fi
|
||||
_describe -J -t objs objs objs "${expl[@]}"
|
||||
}
|
||||
|
||||
_nft_obj_handle(){
|
||||
#$1:protocol family
|
||||
#$2:table
|
||||
#$3:obj type (chain/set/ct helper/counter/quota)
|
||||
local handles=( ${(f)"$(_call_program -p nft-$3-handles nft list table $1 $2 -a 2>/dev/null\
|
||||
| grep ""\\s\*$3"" | sed 's/\s*'"$3"' // ;s/ { # handle// ;s/\(\S*\) \(\S*\)/\2:\1/' )"} )
|
||||
_describe -t handles handles handles "${expl[@]}"
|
||||
}
|
||||
|
||||
_nft_rule_handle(){
|
||||
#$1:protocol family
|
||||
#$2:table
|
||||
#$3:chain name
|
||||
local rules=( ${(f)"$(_call_program -p nft-rule-handles nft list chain $1 $2 $3 -a 2>/dev/null \
|
||||
|grep -v '^\s*\(table\|chain\|type\|\}\)'|sed 's/^\s*\(.*\) # handle \(\S*\)$/\2:\1/' )"} )
|
||||
_describe -t rules rules rules "${expl[@]}"
|
||||
}
|
||||
|
||||
|
||||
_nft "$@"
|
Loading…
Reference in New Issue