diff -Nru iproute2-4.3.0/bash-completion/tc iproute2-4.9.0/bash-completion/tc --- iproute2-4.3.0/bash-completion/tc 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/bash-completion/tc 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,723 @@ +# tc(8) completion -*- shell-script -*- +# Copyright 2016 6WIND S.A. +# Copyright 2016 Quentin Monnet + +# 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; 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" ) ) +} + +# 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 ieeepup ieeepupat \ + ip ipv4 ipv6 ipx irda lat localtalk loop mobitex ppp_disc \ + ppp_mp ppp_ses ppptalk 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' + 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' + 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 filters. +# Returns 0 is completion should stop after running this function, 1 otherwise. +_tc_filter_options() +{ + case $1 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 + ;; + 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() +{ + case $1 in + bpf) + _tc_bpf_options + return 0 + ;; + mirred) + _tc_one_of_list 'ingress egress' + _tc_one_of_list 'mirror redirect' + _tc_once_attr 'index dev' + return 0 + ;; + gact) + _tc_one_of_list 'reclassify drop continue pass' + _tc_once_attr '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 QDISC_KIND=' choke codel bfifo pfifo \ + pfifo_head_drop fq fq_codel gred hhf mqprio multiq \ + netem pfifo_fast pie red rr sfb sfq tbf atm cbq drr \ + dsmark hfsc htb prio qfq ' + 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 QDISC_KIND=' choke codel bfifo pfifo \ + pfifo_head_drop fq fq_codel gred hhf mqprio multiq \ + netem pfifo_fast pie red rr sfb sfq tbf atm cbq drr \ + dsmark hfsc htb prio qfq ' + 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 FILTER_KIND=' basic bpf cgroup flow \ + flower fw route rsvp tcindex u32 ' + for ((fltwd=$subcword; fltwd < ${#words[@]}-1; fltwd++)); + do + if [[ $FILTER_KIND =~ ' '${words[fltwd]}' ' ]]; then + filter=${words[fltwd]} + _tc_filter_options $filter && 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 ACTION_KIND=' gact mirred bpf ' + for ((acwd=$subcword; acwd < ${#words[@]}-1; acwd++)); do + if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then + action=${words[acwd]} + _tc_action_options $action && 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 diff -Nru iproute2-4.3.0/bridge/br_common.h iproute2-4.9.0/bridge/br_common.h --- iproute2-4.3.0/bridge/br_common.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/bridge/br_common.h 2016-12-12 23:07:42.000000000 +0000 @@ -1,3 +1,9 @@ +#define MDB_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + RTA_ALIGN(sizeof(struct br_mdb_entry)))) + +#define MDB_RTR_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + RTA_ALIGN(sizeof(__u32)))) + extern int print_linkinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); @@ -17,4 +23,5 @@ extern int show_details; extern int timestamp; extern int compress_vlans; +extern int json_output; extern struct rtnl_handle rth; diff -Nru iproute2-4.3.0/bridge/bridge.c iproute2-4.9.0/bridge/bridge.c --- iproute2-4.3.0/bridge/bridge.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/bridge/bridge.c 2016-12-12 23:07:42.000000000 +0000 @@ -23,6 +23,7 @@ int show_stats; int show_details; int compress_vlans; +int json_output; int timestamp; char *batch_file; int force; @@ -38,7 +39,7 @@ "where OBJECT := { link | fdb | mdb | vlan | monitor }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] |\n" " -o[neline] | -t[imestamp] | -n[etns] name |\n" -" -c[ompressvlans] }\n"); +" -c[ompressvlans] -j{son} }\n"); exit(-1); } @@ -173,6 +174,8 @@ ++compress_vlans; } else if (matches(opt, "-force") == 0) { ++force; + } else if (matches(opt, "-json") == 0) { + ++json_output; } else if (matches(opt, "-batch") == 0) { argc--; argv++; diff -Nru iproute2-4.3.0/bridge/fdb.c iproute2-4.9.0/bridge/fdb.c --- iproute2-4.3.0/bridge/fdb.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/bridge/fdb.c 2016-12-12 23:07:42.000000000 +0000 @@ -21,25 +21,29 @@ #include #include #include +#include +#include #include "libnetlink.h" #include "br_common.h" #include "rt_names.h" #include "utils.h" -static unsigned int filter_index; +static unsigned int filter_index, filter_vlan; + +json_writer_t *jw_global; static void usage(void) { fprintf(stderr, "Usage: bridge fdb { add | append | del | replace } ADDR dev DEV\n" " [ self ] [ master ] [ use ] [ router ]\n" - " [ local | temp ] [ dst IPADDR ] [ vlan VID ]\n" - " [ port PORT] [ vni VNI ] [ via DEV ]\n"); - fprintf(stderr, " bridge fdb [ show [ br BRDEV ] [ brport DEV ] ]\n"); + " [ local | static | dynamic ] [ dst IPADDR ] [ vlan VID ]\n" + " [ port PORT] [ vni VNI ] [ via DEV ]\n"); + fprintf(stderr, " bridge fdb [ show [ br BRDEV ] [ brport DEV ] [ vlan VID ] ]\n"); exit(-1); } -static const char *state_n2a(unsigned s) +static const char *state_n2a(unsigned int s) { static char buf[32]; @@ -59,17 +63,28 @@ return buf; } +static void start_json_fdb_flags_array(bool *fdb_flags) +{ + if (*fdb_flags) + return; + jsonw_name(jw_global, "flags"); + jsonw_start_array(jw_global); + *fdb_flags = true; +} + int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { FILE *fp = arg; struct ndmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[NDA_MAX+1]; + struct rtattr *tb[NDA_MAX+1]; + __u16 vid = 0; + bool fdb_flags = false; + const char *state_s; if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) { fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); - return 0; } @@ -88,44 +103,86 @@ parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); - if (n->nlmsg_type == RTM_DELNEIGH) - fprintf(fp, "Deleted "); + if (tb[NDA_VLAN]) + vid = rta_getattr_u16(tb[NDA_VLAN]); + + if (filter_vlan && filter_vlan != vid) + return 0; + + if (jw_global) { + jsonw_pretty(jw_global, 1); + jsonw_start_object(jw_global); + } + + if (n->nlmsg_type == RTM_DELNEIGH) { + if (jw_global) + jsonw_string_field(jw_global, "opCode", "deleted"); + else + fprintf(fp, "Deleted "); + } if (tb[NDA_LLADDR]) { SPRINT_BUF(b1); - fprintf(fp, "%s ", - ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), - RTA_PAYLOAD(tb[NDA_LLADDR]), - ll_index_to_type(r->ndm_ifindex), - b1, sizeof(b1))); + ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), + RTA_PAYLOAD(tb[NDA_LLADDR]), + ll_index_to_type(r->ndm_ifindex), + b1, sizeof(b1)); + if (jw_global) + jsonw_string_field(jw_global, "mac", b1); + else + fprintf(fp, "%s ", b1); + } + + if (!filter_index && r->ndm_ifindex) { + if (jw_global) + jsonw_string_field(jw_global, "dev", + ll_index_to_name(r->ndm_ifindex)); + else + fprintf(fp, "dev %s ", + ll_index_to_name(r->ndm_ifindex)); } - if (!filter_index && r->ndm_ifindex) - fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex)); - if (tb[NDA_DST]) { - SPRINT_BUF(abuf); int family = AF_INET; + const char *abuf_s; if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr)) family = AF_INET6; - fprintf(fp, "dst %s ", - format_host(family, - RTA_PAYLOAD(tb[NDA_DST]), - RTA_DATA(tb[NDA_DST]), - abuf, sizeof(abuf))); - } - - if (tb[NDA_VLAN]) { - __u16 vid = rta_getattr_u16(tb[NDA_VLAN]); - fprintf(fp, "vlan %hu ", vid); - } - - if (tb[NDA_PORT]) - fprintf(fp, "port %d ", ntohs(rta_getattr_u16(tb[NDA_PORT]))); - if (tb[NDA_VNI]) - fprintf(fp, "vni %d ", rta_getattr_u32(tb[NDA_VNI])); + abuf_s = format_host(family, + RTA_PAYLOAD(tb[NDA_DST]), + RTA_DATA(tb[NDA_DST])); + if (jw_global) + jsonw_string_field(jw_global, "dst", abuf_s); + else + fprintf(fp, "dst %s ", abuf_s); + } + + if (vid) { + if (jw_global) + jsonw_uint_field(jw_global, "vlan", vid); + else + fprintf(fp, "vlan %hu ", vid); + } + + if (tb[NDA_PORT]) { + if (jw_global) + jsonw_uint_field(jw_global, "port", + ntohs(rta_getattr_u16(tb[NDA_PORT]))); + else + fprintf(fp, "port %d ", + ntohs(rta_getattr_u16(tb[NDA_PORT]))); + } + + if (tb[NDA_VNI]) { + if (jw_global) + jsonw_uint_field(jw_global, "vni", + rta_getattr_u32(tb[NDA_VNI])); + else + fprintf(fp, "vni %d ", + rta_getattr_u32(tb[NDA_VNI])); + } + if (tb[NDA_IFINDEX]) { unsigned int ifindex = rta_getattr_u32(tb[NDA_IFINDEX]); @@ -133,37 +190,95 @@ char ifname[IF_NAMESIZE]; if (!tb[NDA_LINK_NETNSID] && - if_indextoname(ifindex, ifname)) - fprintf(fp, "via %s ", ifname); - else - fprintf(fp, "via ifindex %u ", ifindex); + if_indextoname(ifindex, ifname)) { + if (jw_global) + jsonw_string_field(jw_global, "viaIf", + ifname); + else + fprintf(fp, "via %s ", ifname); + } else { + if (jw_global) + jsonw_uint_field(jw_global, "viaIfIndex", + ifindex); + else + fprintf(fp, "via ifindex %u ", ifindex); + } } } - if (tb[NDA_LINK_NETNSID]) - fprintf(fp, "link-netnsid %d ", - rta_getattr_u32(tb[NDA_LINK_NETNSID])); + + if (tb[NDA_LINK_NETNSID]) { + if (jw_global) + jsonw_uint_field(jw_global, "linkNetNsId", + rta_getattr_u32(tb[NDA_LINK_NETNSID])); + else + fprintf(fp, "link-netnsid %d ", + rta_getattr_u32(tb[NDA_LINK_NETNSID])); + } if (show_stats && tb[NDA_CACHEINFO]) { struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); int hz = get_user_hz(); - fprintf(fp, "used %d/%d ", ci->ndm_used/hz, - ci->ndm_updated/hz); + if (jw_global) { + jsonw_uint_field(jw_global, "used", + ci->ndm_used/hz); + jsonw_uint_field(jw_global, "updated", + ci->ndm_updated/hz); + } else { + fprintf(fp, "used %d/%d ", ci->ndm_used/hz, + ci->ndm_updated/hz); + } } - if (r->ndm_flags & NTF_SELF) - fprintf(fp, "self "); - if (tb[NDA_MASTER]) - fprintf(fp, "master %s ", - ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER]))); - else if (r->ndm_flags & NTF_MASTER) - fprintf(fp, "master "); - if (r->ndm_flags & NTF_ROUTER) - fprintf(fp, "router "); - if (r->ndm_flags & NTF_EXT_LEARNED) - fprintf(fp, "offload "); - fprintf(fp, "%s\n", state_n2a(r->ndm_state)); - fflush(fp); + if (jw_global) { + if (r->ndm_flags & NTF_SELF) { + start_json_fdb_flags_array(&fdb_flags); + jsonw_string(jw_global, "self"); + } + if (r->ndm_flags & NTF_ROUTER) { + start_json_fdb_flags_array(&fdb_flags); + jsonw_string(jw_global, "router"); + } + if (r->ndm_flags & NTF_EXT_LEARNED) { + start_json_fdb_flags_array(&fdb_flags); + jsonw_string(jw_global, "offload"); + } + if (r->ndm_flags & NTF_MASTER) + jsonw_string(jw_global, "master"); + if (fdb_flags) + jsonw_end_array(jw_global); + + if (tb[NDA_MASTER]) + jsonw_string_field(jw_global, + "master", + ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER]))); + + } else { + if (r->ndm_flags & NTF_SELF) + fprintf(fp, "self "); + if (r->ndm_flags & NTF_ROUTER) + fprintf(fp, "router "); + if (r->ndm_flags & NTF_EXT_LEARNED) + fprintf(fp, "offload "); + if (tb[NDA_MASTER]) { + fprintf(fp, "master %s ", + ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER]))); + } else if (r->ndm_flags & NTF_MASTER) { + fprintf(fp, "master "); + } + } + + state_s = state_n2a(r->ndm_state); + if (jw_global) { + if (state_s[0]) + jsonw_string_field(jw_global, "state", state_s); + + jsonw_end_object(jw_global); + } else { + fprintf(fp, "%s\n", state_s); + + fflush(fp); + } return 0; } @@ -171,19 +286,18 @@ static int fdb_show(int argc, char **argv) { struct { - struct nlmsghdr n; + struct nlmsghdr n; struct ifinfomsg ifm; - char buf[256]; - } req; + char buf[256]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .ifm.ifi_family = PF_BRIDGE, + }; char *filter_dev = NULL; char *br = NULL; int msg_size = sizeof(struct ifinfomsg); - memset(&req, 0, sizeof(req)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.ifm.ifi_family = PF_BRIDGE; - while (argc > 0) { if ((strcmp(*argv, "brport") == 0) || strcmp(*argv, "dev") == 0) { NEXT_ARG(); @@ -191,6 +305,11 @@ } else if (strcmp(*argv, "br") == 0) { NEXT_ARG(); br = *argv; + } else if (strcmp(*argv, "vlan") == 0) { + NEXT_ARG(); + if (filter_vlan) + duparg("vlan", *argv); + filter_vlan = atoi(*argv); } else { if (matches(*argv, "help") == 0) usage(); @@ -200,6 +319,7 @@ if (br) { int br_ifindex = ll_name_to_index(br); + if (br_ifindex == 0) { fprintf(stderr, "Cannot find bridge device \"%s\"\n", br); return -1; @@ -224,10 +344,22 @@ exit(1); } + if (json_output) { + jw_global = jsonw_new(stdout); + if (!jw_global) { + fprintf(stderr, "Error allocation json object\n"); + exit(1); + } + jsonw_start_array(jw_global); + } if (rtnl_dump_filter(&rth, print_fdb, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } + if (jw_global) { + jsonw_end_array(jw_global); + jsonw_destroy(&jw_global); + } return 0; } @@ -235,10 +367,16 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv) { struct { - struct nlmsghdr n; - struct ndmsg ndm; - char buf[256]; - } req; + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .ndm.ndm_family = PF_BRIDGE, + .ndm.ndm_state = NUD_NOARP, + }; char *addr = NULL; char *d = NULL; char abuf[ETH_ALEN]; @@ -250,14 +388,6 @@ char *endptr; short vid = -1; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.ndm.ndm_family = PF_BRIDGE; - req.ndm.ndm_state = NUD_NOARP; - while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); @@ -298,11 +428,15 @@ req.ndm.ndm_flags |= NTF_MASTER; } else if (matches(*argv, "router") == 0) { req.ndm.ndm_flags |= NTF_ROUTER; - } else if (matches(*argv, "local") == 0|| + } else if (matches(*argv, "local") == 0 || matches(*argv, "permanent") == 0) { req.ndm.ndm_state |= NUD_PERMANENT; - } else if (matches(*argv, "temp") == 0) { + } else if (matches(*argv, "temp") == 0 || + matches(*argv, "static") == 0) { + req.ndm.ndm_state |= NUD_REACHABLE; + } else if (matches(*argv, "dynamic") == 0) { req.ndm.ndm_state |= NUD_REACHABLE; + req.ndm.ndm_state &= ~NUD_NOARP; } else if (matches(*argv, "vlan") == 0) { if (vid >= 0) duparg2("vlan", *argv); diff -Nru iproute2-4.3.0/bridge/link.c iproute2-4.9.0/bridge/link.c --- iproute2-4.3.0/bridge/link.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/bridge/link.c 2016-12-12 23:07:42.000000000 +0000 @@ -25,17 +25,17 @@ [BR_STATE_BLOCKING] = "blocking", }; -extern char *if_indextoname (unsigned int __ifindex, char *__ifname); +extern char *if_indextoname(unsigned int __ifindex, char *__ifname); -static void print_link_flags(FILE *fp, unsigned flags) +static void print_link_flags(FILE *fp, unsigned int flags) { fprintf(fp, "<"); if (flags & IFF_UP && !(flags & IFF_RUNNING)) fprintf(fp, "NO-CARRIER%s", flags ? "," : ""); flags &= ~IFF_RUNNING; #define _PF(f) if (flags&IFF_##f) { \ - flags &= ~IFF_##f ; \ - fprintf(fp, #f "%s", flags ? "," : ""); } + flags &= ~IFF_##f ; \ + fprintf(fp, #f "%s", flags ? "," : ""); } _PF(LOOPBACK); _PF(BROADCAST); _PF(POINTOPOINT); @@ -55,7 +55,7 @@ _PF(DORMANT); _PF(ECHO); #undef _PF - if (flags) + if (flags) fprintf(fp, "%x", flags); fprintf(fp, "> "); } @@ -69,7 +69,7 @@ static void print_operstate(FILE *f, __u8 state) { - if (state >= sizeof(oper_states)/sizeof(oper_states[0])) + if (state >= ARRAY_SIZE(oper_states)) fprintf(f, "state %#x ", state); else fprintf(f, "state %s ", oper_states[state]); @@ -90,7 +90,7 @@ static void print_hwmode(FILE *f, __u16 mode) { - if (mode >= sizeof(hw_mode)/sizeof(hw_mode[0])) + if (mode >= ARRAY_SIZE(hw_mode)) fprintf(f, "hwmode %#hx ", mode); else fprintf(f, "hwmode %s ", hw_mode[mode]); @@ -102,14 +102,14 @@ FILE *fp = arg; int len = n->nlmsg_len; struct ifinfomsg *ifi = NLMSG_DATA(n); - struct rtattr * tb[IFLA_MAX+1]; + struct rtattr *tb[IFLA_MAX+1]; char b1[IFNAMSIZ]; len -= NLMSG_LENGTH(sizeof(*ifi)); if (len < 0) { fprintf(stderr, "Message too short!\n"); return -1; - } + } if (!(ifi->ifi_family == AF_BRIDGE || ifi->ifi_family == AF_UNSPEC)) return 0; @@ -136,6 +136,7 @@ if (tb[IFLA_LINK]) { SPRINT_BUF(b1); int iflink = rta_getattr_u32(tb[IFLA_LINK]); + if (iflink == 0) fprintf(fp, "@NONE: "); else @@ -194,6 +195,9 @@ if (prtb[IFLA_BRPORT_UNICAST_FLOOD]) print_onoff(fp, "flood", rta_getattr_u8(prtb[IFLA_BRPORT_UNICAST_FLOOD])); + if (prtb[IFLA_BRPORT_MCAST_FLOOD]) + print_onoff(fp, "mcast_flood", + rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_FLOOD])); } } else print_portstate(fp, rta_getattr_u8(tb[IFLA_PROTINFO])); @@ -220,12 +224,13 @@ { fprintf(stderr, "Usage: bridge link set dev DEV [ cost COST ] [ priority PRIO ] [ state STATE ]\n"); fprintf(stderr, " [ guard {on | off} ]\n"); - fprintf(stderr, " [ hairpin {on | off} ] \n"); + fprintf(stderr, " [ hairpin {on | off} ]\n"); fprintf(stderr, " [ fastleave {on | off} ]\n"); fprintf(stderr, " [ root_block {on | off} ]\n"); fprintf(stderr, " [ learning {on | off} ]\n"); fprintf(stderr, " [ learning_sync {on | off} ]\n"); fprintf(stderr, " [ flood {on | off} ]\n"); + fprintf(stderr, " [ mcast_flood {on | off} ]\n"); fprintf(stderr, " [ hwmode {vepa | veb} ]\n"); fprintf(stderr, " [ self ] [ master ]\n"); fprintf(stderr, " bridge link show [dev DEV]\n"); @@ -254,11 +259,17 @@ struct nlmsghdr n; struct ifinfomsg ifm; char buf[512]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_SETLINK, + .ifm.ifi_family = PF_BRIDGE, + }; char *d = NULL; __s8 learning = -1; __s8 learning_sync = -1; __s8 flood = -1; + __s8 mcast_flood = -1; __s8 hairpin = -1; __s8 bpdu_guard = -1; __s8 fast_leave = -1; @@ -270,13 +281,6 @@ __u16 flags = 0; struct rtattr *nest; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_SETLINK; - req.ifm.ifi_family = PF_BRIDGE; - while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); @@ -309,6 +313,10 @@ NEXT_ARG(); if (!on_off("flood", &flood, *argv)) return -1; + } else if (strcmp(*argv, "mcast_flood") == 0) { + NEXT_ARG(); + if (!on_off("mcast_flood", &mcast_flood, *argv)) + return -1; } else if (strcmp(*argv, "cost") == 0) { NEXT_ARG(); cost = atoi(*argv); @@ -318,7 +326,8 @@ } else if (strcmp(*argv, "state") == 0) { NEXT_ARG(); char *endptr; - size_t nstates = sizeof(port_states) / sizeof(*port_states); + size_t nstates = ARRAY_SIZE(port_states); + state = strtol(*argv, &endptr, 10); if (!(**argv != '\0' && *endptr == '\0')) { for (state = 0; state < nstates; state++) @@ -339,8 +348,7 @@ mode = BRIDGE_MODE_VEB; else { fprintf(stderr, - "Mode argument must be \"vepa\" or " - "\"veb\".\n"); + "Mode argument must be \"vepa\" or \"veb\".\n"); return -1; } } else if (strcmp(*argv, "self") == 0) { @@ -381,6 +389,9 @@ addattr8(&req.n, sizeof(req), IFLA_BRPORT_PROTECT, root_block); if (flood >= 0) addattr8(&req.n, sizeof(req), IFLA_BRPORT_UNICAST_FLOOD, flood); + if (mcast_flood >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_MCAST_FLOOD, + mcast_flood); if (learning >= 0) addattr8(&req.n, sizeof(req), IFLA_BRPORT_LEARNING, learning); if (learning_sync >= 0) diff -Nru iproute2-4.3.0/bridge/Makefile iproute2-4.9.0/bridge/Makefile --- iproute2-4.3.0/bridge/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/bridge/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -9,6 +9,7 @@ all: bridge bridge: $(BROBJ) $(LIBNETLINK) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ install: all install -m 0755 bridge $(DESTDIR)$(SBINDIR) diff -Nru iproute2-4.3.0/bridge/mdb.c iproute2-4.9.0/bridge/mdb.c --- iproute2-4.3.0/bridge/mdb.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/bridge/mdb.c 2016-12-12 23:07:42.000000000 +0000 @@ -21,65 +21,114 @@ #ifndef MDBA_RTA #define MDBA_RTA(r) \ - ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg)))) + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg)))) #endif -static unsigned int filter_index; +static unsigned int filter_index, filter_vlan; static void usage(void) { fprintf(stderr, "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n"); - fprintf(stderr, " bridge mdb {show} [ dev DEV ]\n"); + fprintf(stderr, " bridge mdb {show} [ dev DEV ] [ vid VID ]\n"); exit(-1); } -static void br_print_router_ports(FILE *f, struct rtattr *attr) +static bool is_temp_mcast_rtr(__u8 type) +{ + return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP; +} + +static void __print_router_port_stats(FILE *f, struct rtattr *pattr) +{ + struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1]; + struct timeval tv; + __u8 type; + + parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)), + RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t))); + if (tb[MDBA_ROUTER_PATTR_TIMER]) { + __jiffies_to_tv(&tv, + rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER])); + fprintf(f, " %4i.%.2i", + (int)tv.tv_sec, (int)tv.tv_usec/10000); + } + if (tb[MDBA_ROUTER_PATTR_TYPE]) { + type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]); + fprintf(f, " %s", + is_temp_mcast_rtr(type) ? "temp" : "permanent"); + } +} + +static void br_print_router_ports(FILE *f, struct rtattr *attr, __u32 brifidx) { uint32_t *port_ifindex; struct rtattr *i; int rem; + if (!show_stats) + fprintf(f, "router ports on %s: ", ll_index_to_name(brifidx)); + rem = RTA_PAYLOAD(attr); for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { port_ifindex = RTA_DATA(i); - fprintf(f, "%s ", ll_index_to_name(*port_ifindex)); + if (show_stats) { + fprintf(f, "router ports on %s: %s", + ll_index_to_name(brifidx), + ll_index_to_name(*port_ifindex)); + __print_router_port_stats(f, i); + fprintf(f, "\n"); + } else { + fprintf(f, "%s ", ll_index_to_name(*port_ifindex)); + } } - - fprintf(f, "\n"); + if (!show_stats) + fprintf(f, "\n"); } static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e, - struct nlmsghdr *n) + struct nlmsghdr *n, struct rtattr **tb) { SPRINT_BUF(abuf); const void *src; int af; + if (filter_vlan && e->vid != filter_vlan) + return; af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6; src = af == AF_INET ? (const void *)&e->addr.u.ip4 : (const void *)&e->addr.u.ip6; if (n->nlmsg_type == RTM_DELMDB) fprintf(f, "Deleted "); - fprintf(f, "dev %s port %s grp %s %s", ll_index_to_name(ifindex), + fprintf(f, "dev %s port %s grp %s %s %s", ll_index_to_name(ifindex), ll_index_to_name(e->ifindex), inet_ntop(af, src, abuf, sizeof(abuf)), - (e->state & MDB_PERMANENT) ? "permanent" : "temp"); + (e->state & MDB_PERMANENT) ? "permanent" : "temp", + (e->flags & MDB_FLAGS_OFFLOAD) ? "offload" : ""); if (e->vid) fprintf(f, " vid %hu", e->vid); + if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) { + struct timeval tv; + + __jiffies_to_tv(&tv, rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER])); + fprintf(f, "%4i.%.2i", (int)tv.tv_sec, (int)tv.tv_usec/10000); + } fprintf(f, "\n"); } static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr, struct nlmsghdr *n) { + struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1]; + struct br_mdb_entry *e; struct rtattr *i; int rem; - struct br_mdb_entry *e; rem = RTA_PAYLOAD(attr); for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { e = RTA_DATA(i); - print_mdb_entry(f, ifindex, e, n); + parse_rtattr(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)), + RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e))); + print_mdb_entry(f, ifindex, e, n, etb); } } @@ -117,11 +166,9 @@ if (tb[MDBA_ROUTER]) { if (n->nlmsg_type == RTM_GETMDB) { - if (show_details) { - fprintf(fp, "router ports on %s: ", - ll_index_to_name(r->ifindex)); - br_print_router_ports(fp, tb[MDBA_ROUTER]); - } + if (show_details) + br_print_router_ports(fp, tb[MDBA_ROUTER], + r->ifindex); } else { uint32_t *port_ifindex; @@ -150,6 +197,11 @@ if (filter_dev) duparg("dev", *argv); filter_dev = *argv; + } else if (strcmp(*argv, "vid") == 0) { + NEXT_ARG(); + if (filter_vlan) + duparg("vid", *argv); + filter_vlan = atoi(*argv); } argc--; argv++; } @@ -179,22 +231,19 @@ static int mdb_modify(int cmd, int flags, int argc, char **argv) { struct { - struct nlmsghdr n; + struct nlmsghdr n; struct br_port_msg bpm; - char buf[1024]; - } req; - struct br_mdb_entry entry; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .bpm.family = PF_BRIDGE, + }; + struct br_mdb_entry entry = {}; char *d = NULL, *p = NULL, *grp = NULL; short vid = 0; - memset(&req, 0, sizeof(req)); - memset(&entry, 0, sizeof(entry)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.bpm.family = PF_BRIDGE; - while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); diff -Nru iproute2-4.3.0/bridge/monitor.c iproute2-4.9.0/bridge/monitor.c --- iproute2-4.3.0/bridge/monitor.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/bridge/monitor.c 2016-12-12 23:07:42.000000000 +0000 @@ -76,10 +76,10 @@ int do_monitor(int argc, char **argv) { char *file = NULL; - unsigned groups = ~RTMGRP_TC; - int llink=0; - int lneigh=0; - int lmdb=0; + unsigned int groups = ~RTMGRP_TC; + int llink = 0; + int lneigh = 0; + int lmdb = 0; rtnl_close(&rth); @@ -88,7 +88,7 @@ NEXT_ARG(); file = *argv; } else if (matches(*argv, "link") == 0) { - llink=1; + llink = 1; groups = 0; } else if (matches(*argv, "fdb") == 0) { lneigh = 1; @@ -98,7 +98,7 @@ groups = 0; } else if (strcmp(*argv, "all") == 0) { groups = ~RTMGRP_TC; - prefix_banner=1; + prefix_banner = 1; } else if (matches(*argv, "help") == 0) { usage(); } else { @@ -122,6 +122,7 @@ if (file) { FILE *fp; int err; + fp = fopen(file, "r"); if (fp == NULL) { perror("Cannot fopen"); @@ -141,4 +142,3 @@ return 0; } - diff -Nru iproute2-4.3.0/bridge/vlan.c iproute2-4.9.0/bridge/vlan.c --- iproute2-4.3.0/bridge/vlan.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/bridge/vlan.c 2016-12-12 23:07:42.000000000 +0000 @@ -7,50 +7,52 @@ #include #include #include +#include #include #include "libnetlink.h" #include "br_common.h" #include "utils.h" -static unsigned int filter_index; +static unsigned int filter_index, filter_vlan; +static int last_ifidx = -1; + +json_writer_t *jw_global = NULL; static void usage(void) { - fprintf(stderr, "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ pvid] [ untagged ]\n"); + fprintf(stderr, "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ pvid ] [ untagged ]\n"); fprintf(stderr, " [ self ] [ master ]\n"); - fprintf(stderr, " bridge vlan { show } [ dev DEV ]\n"); + fprintf(stderr, " bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n"); exit(-1); } static int vlan_modify(int cmd, int argc, char **argv) { struct { - struct nlmsghdr n; - struct ifinfomsg ifm; - char buf[1024]; - } req; + struct nlmsghdr n; + struct ifinfomsg ifm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = cmd, + .ifm.ifi_family = PF_BRIDGE, + }; char *d = NULL; short vid = -1; short vid_end = -1; struct rtattr *afspec; - struct bridge_vlan_info vinfo; + struct bridge_vlan_info vinfo = {}; unsigned short flags = 0; - memset(&vinfo, 0, sizeof(vinfo)); - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = cmd; - req.ifm.ifi_family = PF_BRIDGE; - while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); d = *argv; } else if (strcmp(*argv, "vid") == 0) { char *p; + NEXT_ARG(); p = strchr(*argv, '-'); if (p) { @@ -137,6 +139,48 @@ return 0; } +/* In order to use this function for both filtering and non-filtering cases + * we need to make it a tristate: + * return -1 - if filtering we've gone over so don't continue + * return 0 - skip entry and continue (applies to range start or to entries + * which are less than filter_vlan) + * return 1 - print the entry and continue + */ +static int filter_vlan_check(struct bridge_vlan_info *vinfo) +{ + /* if we're filtering we should stop on the first greater entry */ + if (filter_vlan && vinfo->vid > filter_vlan && + !(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) + return -1; + if ((vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) || + vinfo->vid < filter_vlan) + return 0; + + return 1; +} + +static void print_vlan_port(FILE *fp, int ifi_index) +{ + if (jw_global) { + jsonw_pretty(jw_global, 1); + jsonw_name(jw_global, + ll_index_to_name(ifi_index)); + jsonw_start_array(jw_global); + } else { + fprintf(fp, "%s", + ll_index_to_name(ifi_index)); + } +} + +static void start_json_vlan_flags_array(bool *vlan_flags) +{ + if (*vlan_flags) + return; + jsonw_name(jw_global, "flags"); + jsonw_start_array(jw_global); + *vlan_flags = true; +} + static int print_vlan(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) @@ -144,7 +188,8 @@ FILE *fp = arg; struct ifinfomsg *ifm = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[IFLA_MAX+1]; + struct rtattr *tb[IFLA_MAX+1]; + bool vlan_flags = false; if (n->nlmsg_type != RTM_NEWLINK) { fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n", @@ -168,34 +213,170 @@ /* if AF_SPEC isn't there, vlan table is not preset for this port */ if (!tb[IFLA_AF_SPEC]) { - fprintf(fp, "%s\tNone\n", ll_index_to_name(ifm->ifi_index)); + if (!filter_vlan && !jw_global) + fprintf(fp, "%s\tNone\n", + ll_index_to_name(ifm->ifi_index)); return 0; } else { struct rtattr *i, *list = tb[IFLA_AF_SPEC]; int rem = RTA_PAYLOAD(list); + __u16 last_vid_start = 0; + + if (!filter_vlan) + print_vlan_port(fp, ifm->ifi_index); - fprintf(fp, "%s", ll_index_to_name(ifm->ifi_index)); for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { struct bridge_vlan_info *vinfo; + int vcheck_ret; if (i->rta_type != IFLA_BRIDGE_VLAN_INFO) continue; vinfo = RTA_DATA(i); - if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) - fprintf(fp, "-%hu", vinfo->vid); - else - fprintf(fp, "\t %hu", vinfo->vid); - if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) + + if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) + last_vid_start = vinfo->vid; + vcheck_ret = filter_vlan_check(vinfo); + if (vcheck_ret == -1) + break; + else if (vcheck_ret == 0) continue; - if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) - fprintf(fp, " PVID"); - if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) - fprintf(fp, " Egress Untagged"); - fprintf(fp, "\n"); + + if (filter_vlan) + print_vlan_port(fp, ifm->ifi_index); + if (jw_global) { + jsonw_start_object(jw_global); + jsonw_uint_field(jw_global, "vlan", + last_vid_start); + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) + continue; + } else { + fprintf(fp, "\t %hu", last_vid_start); + } + if (last_vid_start != vinfo->vid) { + if (jw_global) + jsonw_uint_field(jw_global, "vlanEnd", + vinfo->vid); + else + fprintf(fp, "-%hu", vinfo->vid); + } + if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) { + if (jw_global) { + start_json_vlan_flags_array(&vlan_flags); + jsonw_string(jw_global, "PVID"); + } else { + fprintf(fp, " PVID"); + } + } + if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) { + if (jw_global) { + start_json_vlan_flags_array(&vlan_flags); + jsonw_string(jw_global, + "Egress Untagged"); + } else { + fprintf(fp, " Egress Untagged"); + } + } + if (jw_global && vlan_flags) { + jsonw_end_array(jw_global); + vlan_flags = false; + } + + if (jw_global) + jsonw_end_object(jw_global); + else + fprintf(fp, "\n"); } } + if (!filter_vlan) { + if (jw_global) + jsonw_end_array(jw_global); + else + fprintf(fp, "\n"); + + } + fflush(fp); + return 0; +} + +static void print_one_vlan_stats(FILE *fp, + const struct bridge_vlan_xstats *vstats, + int ifindex) +{ + const char *ifname = ""; + + if (filter_vlan && filter_vlan != vstats->vid) + return; + /* skip pure port entries, they'll be dumped via the slave stats call */ + if ((vstats->flags & BRIDGE_VLAN_INFO_MASTER) && + !(vstats->flags & BRIDGE_VLAN_INFO_BRENTRY)) + return; + + if (last_ifidx != ifindex) { + ifname = ll_index_to_name(ifindex); + last_ifidx = ifindex; + } + fprintf(fp, "%-16s %hu", ifname, vstats->vid); + if (vstats->flags & BRIDGE_VLAN_INFO_PVID) + fprintf(fp, " PVID"); + if (vstats->flags & BRIDGE_VLAN_INFO_UNTAGGED) + fprintf(fp, " Egress Untagged"); fprintf(fp, "\n"); + fprintf(fp, "%-16s RX: %llu bytes %llu packets\n", + "", vstats->rx_bytes, vstats->rx_packets); + fprintf(fp, "%-16s TX: %llu bytes %llu packets\n", + "", vstats->tx_bytes, vstats->tx_packets); +} + +static void print_vlan_stats_attr(FILE *fp, struct rtattr *attr, int ifindex) +{ + struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1]; + struct rtattr *i, *list; + int rem; + + parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr), + RTA_PAYLOAD(attr)); + if (!brtb[LINK_XSTATS_TYPE_BRIDGE]) + return; + + list = brtb[LINK_XSTATS_TYPE_BRIDGE]; + rem = RTA_PAYLOAD(list); + for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + if (i->rta_type != BRIDGE_XSTATS_VLAN) + continue; + print_one_vlan_stats(fp, RTA_DATA(i), ifindex); + } +} + +static int print_vlan_stats(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + struct if_stats_msg *ifsm = NLMSG_DATA(n); + struct rtattr *tb[IFLA_STATS_MAX+1]; + int len = n->nlmsg_len; + FILE *fp = arg; + + len -= NLMSG_LENGTH(sizeof(*ifsm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter_index && filter_index != ifsm->ifindex) + return 0; + + parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len); + + /* We have to check if any of the two attrs are usable */ + if (tb[IFLA_STATS_LINK_XSTATS]) + print_vlan_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS], + ifsm->ifindex); + + if (tb[IFLA_STATS_LINK_XSTATS_SLAVE]) + print_vlan_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS_SLAVE], + ifsm->ifindex); + fflush(fp); return 0; } @@ -210,6 +391,11 @@ if (filter_dev) duparg("dev", *argv); filter_dev = *argv; + } else if (strcmp(*argv, "vid") == 0) { + NEXT_ARG(); + if (filter_vlan) + duparg("vid", *argv); + filter_vlan = atoi(*argv); } argc--; argv++; } @@ -222,24 +408,68 @@ } } - if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK, - (compress_vlans ? - RTEXT_FILTER_BRVLAN_COMPRESSED : - RTEXT_FILTER_BRVLAN)) < 0) { - perror("Cannont send dump request"); - exit(1); + if (!show_stats) { + if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK, + (compress_vlans ? + RTEXT_FILTER_BRVLAN_COMPRESSED : + RTEXT_FILTER_BRVLAN)) < 0) { + perror("Cannont send dump request"); + exit(1); + } + if (json_output) { + jw_global = jsonw_new(stdout); + if (!jw_global) { + fprintf(stderr, "Error allocation json object\n"); + exit(1); + } + jsonw_start_object(jw_global); + } else { + printf("port\tvlan ids\n"); + } + + if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) { + fprintf(stderr, "Dump ternminated\n"); + exit(1); + } + } else { + __u32 filt_mask; + + filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS); + if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC, + RTM_GETSTATS, + filt_mask) < 0) { + perror("Cannont send dump request"); + exit(1); + } + + printf("%-16s vlan id\n", "port"); + if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE); + if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC, + RTM_GETSTATS, + filt_mask) < 0) { + perror("Cannont send slave dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } } - printf("port\tvlan ids\n"); - if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) { - fprintf(stderr, "Dump ternminated\n"); - exit(1); + if (jw_global) { + jsonw_end_object(jw_global); + jsonw_destroy(&jw_global); } return 0; } - int do_vlan(int argc, char **argv) { ll_init_map(&rth); @@ -255,9 +485,10 @@ return vlan_show(argc-1, argv+1); if (matches(*argv, "help") == 0) usage(); - } else + } else { return vlan_show(0, NULL); + } - fprintf(stderr, "Command \"%s\" is unknown, try \"bridge fdb help\".\n", *argv); + fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv); exit(-1); } diff -Nru iproute2-4.3.0/configure iproute2-4.9.0/configure --- iproute2-4.3.0/configure 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/configure 2016-12-12 23:07:42.000000000 +0000 @@ -57,6 +57,14 @@ rm -f $TMPDIR/atmtest.c $TMPDIR/atmtest } +check_xtables() +{ + if ! ${PKG_CONFIG} xtables --exists + then + echo "TC_CONFIG_NO_XT:=y" >>Config + fi +} + check_xt() { #check if we have xtables from iptables >= 1.4.5. @@ -317,7 +325,35 @@ rm -f $TMPDIR/dbtest.c $TMPDIR/dbtest } +quiet_config() +{ + cat <Config +quiet_config >> Config + check_toolchain echo "TC schedulers" @@ -325,18 +361,25 @@ echo -n " ATM " check_atm -echo -n " IPT " -check_xt -check_xt_old -check_xt_old_internal_h -check_ipt - -echo -n " IPSET " -check_ipset +check_xtables +if ! grep -q TC_CONFIG_NO_XT Config +then + echo -n " IPT " + check_xt + check_xt_old + check_xt_old_internal_h + check_ipt + + echo -n " IPSET " + check_ipset +fi echo -echo -n "iptables modules directory: " -check_ipt_lib_dir +if ! grep -q TC_CONFIG_NO_XT Config +then + echo -n "iptables modules directory: " + check_ipt_lib_dir +fi echo -n "libc has setns: " check_setns @@ -357,3 +400,7 @@ echo -n "docs:" check_docs echo + +echo >> Config +echo "%.o: %.c" >> Config +echo ' $(QUIET_CC)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<' >> Config diff -Nru iproute2-4.3.0/debian/changelog iproute2-4.9.0/debian/changelog --- iproute2-4.3.0/debian/changelog 2016-04-05 11:38:17.000000000 +0000 +++ iproute2-4.9.0/debian/changelog 2017-01-30 21:01:21.000000000 +0000 @@ -1,30 +1,72 @@ -iproute2 (4.3.0-1ubuntu3) xenial; urgency=medium +iproute2 (4.9.0-1ppa1) zesty; urgency=medium - * Add d/p/1003-fix-variable-in-libnetlink.patch (LP: #1522371), fix a variable name - collision issuing a segfault; from upstream. - * Add TIPC support (LP: #1508225): - - d/control: add libmnl-dev to BDs, to include TIPC support. - - d/iproute2.install: add tipc binary. + * testing - -- Pierre-André MOREY Tue, 05 Apr 2016 09:43:44 +0200 + -- Jon Grimm Mon, 30 Jan 2017 15:01:21 -0600 -iproute2 (4.3.0-1ubuntu2) xenial; urgency=medium +iproute2 (4.9.0-1) unstable; urgency=medium - * No-change rebuild for new iptables. + * New upstream release, tested by Julian Wollrath. - -- Matthias Klose Sun, 21 Feb 2016 20:00:55 +0100 + -- Andreas Henriksson Tue, 13 Dec 2016 16:57:50 +0100 -iproute2 (4.3.0-1ubuntu1) xenial; urgency=low +iproute2 (4.8.0-1) unstable; urgency=medium - * Merge from Debian unstable. Remaining changes: - - debian/patches/1000-ubuntu-poc-fan-driver.patch - Ubuntu FAN driver support V2 (LP: #1439706) - - debian/patches/1001-ubuntu-poc-fan-driver-v3.patch - Ubuntu FAN driver support V3 (LP: #1470091) - - debian/patches/1002-ubuntu-poc-fan-driver-vxlan.patch - Ubuntu FAN driver support VXLAN V4. + * Fix debian/gbp.conf to import upstream tag on import-orig + * New upstream release. + * Ship upstream iproute2 docs in iproute2-doc package + + -- Andreas Henriksson Wed, 26 Oct 2016 16:07:30 +0200 + +iproute2 (4.6.0-4) unstable; urgency=medium + + * Fix conflict resolution, really remove ifstat binary (Closes: #832573) + + -- Andreas Henriksson Tue, 16 Aug 2016 09:48:05 +0200 + +iproute2 (4.6.0-3) unstable; urgency=medium + + * Team upload. + + [ Laurent Bigonville ] + * debian/control: Downgrade transitional packages to Priority extra + + [ Andreas Henriksson ] + * Revert "Install ifstat executable and manpage" (Closes: #832573) + + -- Andreas Henriksson Mon, 15 Aug 2016 17:36:13 +0200 + +iproute2 (4.6.0-2) unstable; urgency=medium + + * Team upload. + [ Julian Wollrath ] + * Bump standards version to 3.9.8 (no changes needed) + * Use at least debhelper version 9 (Closes: #672828) + * Add build-dep on libelf-dev (Closes: #812774) + * Add build-dep on libmnl-dev + * Install tipc and devlink executables (Closes: #803922) + + [ Laurent Bigonville ] + * debian/iproute2.install, debian/iproute2.manpages: Rely on upstream build + system to install the files and do not pick them from the source directory + * debian/iproute2.install: Install the bash completion file + * debian/iproute2.manpages: Install the devlink and genl manpages not + installed by the upstream build system + * Install ifstat executable and manpage + * Run wrap-and-sort script + * debian/control: Update the Homepage URL + + -- Laurent Bigonville Tue, 26 Jul 2016 18:43:10 +0200 + +iproute2 (4.6.0-1) unstable; urgency=medium + + [ Julian Wollrath ] + * Imported upstream version 4.6.0: + - Add build-dep on texlive-latex-extra for fullpage + * Remove debian/patches/f_u32 + * Change VCS URLs to the canonical ones - -- Andy Whitcroft Tue, 22 Dec 2015 09:32:32 +0000 + -- Andreas Henriksson Sat, 09 Jul 2016 13:17:22 +0200 iproute2 (4.3.0-1) unstable; urgency=medium @@ -37,23 +79,6 @@ -- Andreas Henriksson Thu, 26 Nov 2015 18:34:59 +0100 -iproute2 (4.1.1-1ubuntu2) xenial; urgency=low - - * debian/patches/1002-ubuntu-poc-fan-driver-vxlan.patch - Ubuntu FAN drvier support VXLAN V4. - - -- Andy Whitcroft Tue, 17 Nov 2015 13:29:03 +0000 - -iproute2 (4.1.1-1ubuntu1) wily; urgency=medium - - * Merge with Debian, remaining changes (LP: #1448800): - - debian/patches/1000-ubuntu-poc-fan-driver.patch - Ubuntu FAN driver support V2 (LP: #1439706) - - debian/patches/1001-ubuntu-poc-fan-driver-v3.patch - Ubuntu FAN driver support V3 (LP: #1470091) - - -- Andy Whitcroft Wed, 19 Aug 2015 09:59:35 +0100 - iproute2 (4.1.1-1) unstable; urgency=medium * [be9f298] Imported Upstream version 4.1.1 @@ -70,18 +95,6 @@ -- Alexander Wirt Wed, 24 Jun 2015 10:01:45 +0200 -iproute2 (3.16.0-2ubuntu2) wily; urgency=low - - * Update Ubuntu FAN support to V3. (LP: #1470091) - - -- Andy Whitcroft Mon, 29 Jun 2015 16:59:24 +0100 - -iproute2 (3.16.0-2ubuntu1) vivid; urgency=low - - * Add Ubuntu FAN support. (LP: #1439706) - - -- Andy Whitcroft Tue, 24 Feb 2015 14:45:19 +0000 - iproute2 (3.16.0-2) unstable; urgency=medium * Cherry-pick upstream commit f1b66ff8 @@ -1204,4 +1217,3 @@ Local variables: mode: debian-changelog End: - diff -Nru iproute2-4.3.0/debian/compat iproute2-4.9.0/debian/compat --- iproute2-4.3.0/debian/compat 2015-11-26 22:10:05.000000000 +0000 +++ iproute2-4.9.0/debian/compat 2017-01-30 21:00:57.000000000 +0000 @@ -1 +1 @@ -8 +9 diff -Nru iproute2-4.3.0/debian/control iproute2-4.9.0/debian/control --- iproute2-4.3.0/debian/control 2016-04-05 11:35:52.000000000 +0000 +++ iproute2-4.9.0/debian/control 2017-01-30 21:01:21.000000000 +0000 @@ -5,17 +5,18 @@ XSBC-Original-Maintainer: Debian iproute2 Maintainers Uploaders: Andreas Henriksson , Alexander Wirt -Homepage: http://www.linux-foundation.org/en/Net:Iproute2 -Vcs-Browser: http://git.debian.org/?p=collab-maint/pkg-iproute.git -Vcs-Git: git://git.debian.org/git/collab-maint/pkg-iproute.git -Standards-Version: 3.9.6 +Homepage: https://wiki.linuxfoundation.org/networking/iproute2 +Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/pkg-iproute.git +Vcs-Git: https://anonscm.debian.org/git/collab-maint/pkg-iproute.git +Standards-Version: 3.9.8 Build-Depends: bison, cm-super-minimal, - debhelper (>= 8), + debhelper (>= 9), flex, iptables-dev, libatm1-dev, libdb-dev, + libelf-dev, libmnl-dev, libselinux1-dev, linux-libc-dev, @@ -24,6 +25,7 @@ pkg-config, texlive-fonts-recommended, texlive-latex-base, + texlive-latex-extra, texlive-latex-recommended Package: iproute2 @@ -60,6 +62,7 @@ Depends: iproute2, ${misc:Depends} Architecture: all Section: oldlibs +Priority: extra Description: transitional dummy package for iproute2 This is a transitional dummy package to get upgrading systems to install the iproute2 package. It can safely be removed once no other package @@ -69,6 +72,7 @@ Depends: iproute2-doc, ${misc:Depends} Architecture: all Section: oldlibs +Priority: extra Description: transitional dummy package for iproute2-doc This is a transitional dummy package to get upgrading systems to install the iproute2-doc package. It can safely be removed once no other package diff -Nru iproute2-4.3.0/debian/gbp.conf iproute2-4.9.0/debian/gbp.conf --- iproute2-4.3.0/debian/gbp.conf 2015-11-26 16:44:01.000000000 +0000 +++ iproute2-4.9.0/debian/gbp.conf 2017-01-30 21:00:57.000000000 +0000 @@ -8,4 +8,6 @@ [buildpackage] #tarball-dir = ../tarballs/ #export-dir = ../build-area/ -upstream-tag = v%(version)s + +[import-orig] +upstream-vcs-tag = v%(version)s diff -Nru iproute2-4.3.0/debian/iproute2-doc.docs iproute2-4.9.0/debian/iproute2-doc.docs --- iproute2-4.3.0/debian/iproute2-doc.docs 2015-11-26 22:10:05.000000000 +0000 +++ iproute2-4.9.0/debian/iproute2-doc.docs 2017-01-30 21:00:57.000000000 +0000 @@ -1,4 +1,4 @@ README* doc/Plan +debian/htb/* doc/*.tex doc/*.dvi doc/*.ps doc/*.sty doc/*.txt doc/*.html -debian/htb/* diff -Nru iproute2-4.3.0/debian/iproute2-doc.install iproute2-4.9.0/debian/iproute2-doc.install --- iproute2-4.3.0/debian/iproute2-doc.install 2015-11-26 22:10:05.000000000 +0000 +++ iproute2-4.9.0/debian/iproute2-doc.install 2017-01-30 21:00:57.000000000 +0000 @@ -1 +1,2 @@ debian/doc/htb/* /usr/share/doc/iproute2-doc/htb +usr/share/doc/iproute2/* /usr/share/doc/iproute2-doc diff -Nru iproute2-4.3.0/debian/iproute2.install iproute2-4.9.0/debian/iproute2.install --- iproute2-4.3.0/debian/iproute2.install 2016-04-05 11:35:52.000000000 +0000 +++ iproute2-4.9.0/debian/iproute2.install 2017-01-30 21:00:57.000000000 +0000 @@ -1,10 +1,18 @@ -etc/* /etc -ip/routef ip/routel /usr/bin -ip/rtmon tc/tc misc/rtacct bridge/bridge /sbin -misc/arpd /usr/sbin -misc/lnstat misc/nstat /usr/bin/ -misc/ss ip/ip /bin -netem/*.dist /usr/lib/tc -tc/*.so /usr/lib/tc -genl/genl /usr/sbin -tipc/tipc /sbin +etc/ +sbin/arpd /usr/sbin +sbin/bridge +sbin/devlink +sbin/genl /usr/sbin +sbin/ip /bin +sbin/lnstat /usr/bin/ +sbin/nstat /usr/bin/ +sbin/routef /usr/bin +sbin/routel /usr/bin +sbin/rtacct +sbin/rtmon +sbin/ss /bin +sbin/tc +sbin/tipc +usr/lib/tc/ +usr/share/bash-completion/ +usr/share/man/ diff -Nru iproute2-4.3.0/debian/iproute2.links iproute2-4.9.0/debian/iproute2.links --- iproute2-4.3.0/debian/iproute2.links 2015-11-26 22:10:05.000000000 +0000 +++ iproute2-4.9.0/debian/iproute2.links 2017-01-30 21:00:57.000000000 +0000 @@ -1,4 +1,4 @@ -/usr/bin/lnstat usr/bin/rtstat /usr/bin/lnstat usr/bin/ctstat -bin/ip sbin/ip +/usr/bin/lnstat usr/bin/rtstat /usr/lib/tc/m_xt.so usr/lib/tc/m_ipt.so +bin/ip sbin/ip diff -Nru iproute2-4.3.0/debian/iproute2.manpages iproute2-4.9.0/debian/iproute2.manpages --- iproute2-4.3.0/debian/iproute2.manpages 2015-11-26 22:10:05.000000000 +0000 +++ iproute2-4.9.0/debian/iproute2.manpages 2017-01-30 21:00:57.000000000 +0000 @@ -1 +1,2 @@ -debian/tmp/usr/share/man/*/* +man/man8/devlink*.8 +man/man8/genl.8 diff -Nru iproute2-4.3.0/debian/patches/1000-ubuntu-poc-fan-driver.patch iproute2-4.9.0/debian/patches/1000-ubuntu-poc-fan-driver.patch --- iproute2-4.3.0/debian/patches/1000-ubuntu-poc-fan-driver.patch 2015-08-19 08:57:29.000000000 +0000 +++ iproute2-4.9.0/debian/patches/1000-ubuntu-poc-fan-driver.patch 2017-01-30 21:00:57.000000000 +0000 @@ -2,25 +2,22 @@ POC Fan driver support Author: Jay Vosburgh -Index: iproute2-4.1.1/include/linux/if_tunnel.h -=================================================================== ---- iproute2-4.1.1.orig/include/linux/if_tunnel.h -+++ iproute2-4.1.1/include/linux/if_tunnel.h -@@ -57,6 +57,9 @@ enum { - IFLA_IPTUN_ENCAP_FLAGS, +--- a/include/linux/if_tunnel.h ++++ b/include/linux/if_tunnel.h +@@ -75,6 +75,10 @@ enum { IFLA_IPTUN_ENCAP_SPORT, IFLA_IPTUN_ENCAP_DPORT, + IFLA_IPTUN_COLLECT_METADATA, + + IFLA_IPTUN_FAN_UNDERLAY = 32, + ++ __IFLA_IPTUN_MAX, }; #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) -Index: iproute2-4.1.1/ip/link_iptnl.c -=================================================================== ---- iproute2-4.1.1.orig/ip/link_iptnl.c -+++ iproute2-4.1.1/ip/link_iptnl.c -@@ -66,6 +66,7 @@ static int iptunnel_parse_opt(struct lin +--- a/ip/link_iptnl.c ++++ b/ip/link_iptnl.c +@@ -73,6 +73,7 @@ static int iptunnel_parse_opt(struct lin __u32 link = 0; __u32 laddr = 0; __u32 raddr = 0; @@ -38,7 +35,7 @@ } else if (strcmp(*argv, "local") == 0) { NEXT_ARG(); if (strcmp(*argv, "any")) -@@ -318,6 +322,9 @@ get_failed: +@@ -329,6 +333,9 @@ get_failed: } } @@ -48,7 +45,7 @@ return 0; } -@@ -349,6 +356,14 @@ static void iptunnel_print_opt(struct li +@@ -360,6 +367,14 @@ static void iptunnel_print_opt(struct li fprintf(f, "local %s ", local); @@ -57,9 +54,9 @@ + + if (addr) + fprintf(f, "underlay %s ", -+ format_host(AF_INET, 4, &addr, s1, sizeof(s1))); ++ format_host(AF_INET, 4, &addr)); + } + if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) { - unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); + unsigned int link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); const char *n = if_indextoname(link, s2); diff -Nru iproute2-4.3.0/debian/patches/1001-ubuntu-poc-fan-driver-v3.patch iproute2-4.9.0/debian/patches/1001-ubuntu-poc-fan-driver-v3.patch --- iproute2-4.3.0/debian/patches/1001-ubuntu-poc-fan-driver-v3.patch 2015-08-19 09:20:24.000000000 +0000 +++ iproute2-4.9.0/debian/patches/1001-ubuntu-poc-fan-driver-v3.patch 2017-01-30 21:00:57.000000000 +0000 @@ -1,19 +1,17 @@ Description: Fan driver support v3 Fan driver support v3 Author: Jay Vosburgh -Index: iproute2-4.1.1/include/linux/if_tunnel.h -=================================================================== ---- iproute2-4.1.1.orig/include/linux/if_tunnel.h -+++ iproute2-4.1.1/include/linux/if_tunnel.h -@@ -59,6 +59,7 @@ enum { - IFLA_IPTUN_ENCAP_DPORT, +--- a/include/linux/if_tunnel.h ++++ b/include/linux/if_tunnel.h +@@ -77,6 +77,7 @@ enum { + IFLA_IPTUN_COLLECT_METADATA, IFLA_IPTUN_FAN_UNDERLAY = 32, + IFLA_IPTUN_FAN_MAP = 33, + __IFLA_IPTUN_MAX, - }; -@@ -134,4 +135,20 @@ enum { +@@ -155,4 +156,20 @@ enum { }; #define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1) @@ -34,11 +32,9 @@ +}; + #endif /* _IF_TUNNEL_H_ */ -Index: iproute2-4.1.1/ip/link_iptnl.c -=================================================================== ---- iproute2-4.1.1.orig/ip/link_iptnl.c -+++ iproute2-4.1.1/ip/link_iptnl.c -@@ -49,6 +49,42 @@ static void usage(int sit) +--- a/ip/link_iptnl.c ++++ b/ip/link_iptnl.c +@@ -50,6 +50,42 @@ static void usage(int sit) print_usage(stderr, sit); exit(-1); } @@ -62,7 +58,7 @@ + invarg("invalid fan-map overlay", *argv); + if (get_prefix(&underlay, colon + 1, AF_INET)) + invarg("invalid fan-map underlay", colon + 1); -+ ++ + memcpy(&map.underlay, underlay.data, 4); + map.underlay_prefix = underlay.bitlen; + memcpy(&map.overlay, overlay.data, 4); @@ -92,7 +88,7 @@ } else if (strcmp(*argv, "local") == 0) { NEXT_ARG(); if (strcmp(*argv, "any")) -@@ -328,6 +368,28 @@ get_failed: +@@ -339,6 +379,28 @@ get_failed: return 0; } @@ -111,9 +107,9 @@ + p = RTA_PAYLOAD(i); + m = RTA_DATA(i); + fprintf(f, "%s/%d:%s/%d ", -+ rt_addr_n2a(AF_INET, p, &m->overlay, b1, INET_ADDRSTRLEN), ++ rt_addr_n2a_r(AF_INET, p, &m->overlay, b1, INET_ADDRSTRLEN), + m->overlay_prefix, -+ rt_addr_n2a(AF_INET, p, &m->underlay, b2, INET_ADDRSTRLEN), ++ rt_addr_n2a_r(AF_INET, p, &m->underlay, b2, INET_ADDRSTRLEN), + m->underlay_prefix); + } +} @@ -121,13 +117,13 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { char s1[1024]; -@@ -364,6 +426,9 @@ static void iptunnel_print_opt(struct li - format_host(AF_INET, 4, &addr, s1, sizeof(s1))); +@@ -375,6 +437,9 @@ static void iptunnel_print_opt(struct li + format_host(AF_INET, 4, &addr)); } + if (tb[IFLA_IPTUN_FAN_MAP]) + fan_print_map(f, tb[IFLA_IPTUN_FAN_MAP]); + if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) { - unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); + unsigned int link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); const char *n = if_indextoname(link, s2); diff -Nru iproute2-4.3.0/debian/patches/1002-ubuntu-poc-fan-driver-vxlan.patch iproute2-4.9.0/debian/patches/1002-ubuntu-poc-fan-driver-vxlan.patch --- iproute2-4.3.0/debian/patches/1002-ubuntu-poc-fan-driver-vxlan.patch 2015-12-07 11:22:22.000000000 +0000 +++ iproute2-4.9.0/debian/patches/1002-ubuntu-poc-fan-driver-vxlan.patch 2017-01-30 21:00:57.000000000 +0000 @@ -1,23 +1,19 @@ Description: Fan driver support VXLAN (p4) Fan driver setup support for vxlan interfaces. -Index: iproute2-4.3.0/include/linux/if_link.h -=================================================================== ---- iproute2-4.3.0.orig/include/linux/if_link.h -+++ iproute2-4.3.0/include/linux/if_link.h -@@ -392,6 +392,7 @@ enum { - IFLA_VXLAN_GBP, - IFLA_VXLAN_REMCSUM_NOPARTIAL, +--- a/include/linux/if_link.h ++++ b/include/linux/if_link.h +@@ -496,6 +496,7 @@ enum { IFLA_VXLAN_COLLECT_METADATA, + IFLA_VXLAN_LABEL, + IFLA_VXLAN_GPE, + IFLA_VXLAN_FAN_MAP = 33, __IFLA_VXLAN_MAX }; #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) -Index: iproute2-4.3.0/include/linux/if_tunnel.h -=================================================================== ---- iproute2-4.3.0.orig/include/linux/if_tunnel.h -+++ iproute2-4.3.0/include/linux/if_tunnel.h -@@ -145,7 +145,7 @@ enum { +--- a/include/linux/if_tunnel.h ++++ b/include/linux/if_tunnel.h +@@ -165,7 +165,7 @@ enum { #define IFLA_FAN_MAX (__IFLA_FAN_MAX - 1) @@ -26,10 +22,8 @@ __be32 underlay; __be32 overlay; __u16 underlay_prefix; -Index: iproute2-4.3.0/ip/iplink_vxlan.c -=================================================================== ---- iproute2-4.3.0.orig/ip/iplink_vxlan.c -+++ iproute2-4.3.0/ip/iplink_vxlan.c +--- a/ip/iplink_vxlan.c ++++ b/ip/iplink_vxlan.c @@ -15,7 +15,10 @@ #include #include @@ -41,7 +35,7 @@ #include "rt_names.h" #include "utils.h" -@@ -43,6 +46,45 @@ static void explain(void) +@@ -45,6 +48,45 @@ static void explain(void) print_explain(stderr); } @@ -87,18 +81,18 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { -@@ -201,6 +243,10 @@ static int vxlan_parse_opt(struct link_u - udp6zerocsumrx = 0; +@@ -236,6 +278,10 @@ static int vxlan_parse_opt(struct link_u + metadata = 0; } else if (!matches(*argv, "gbp")) { gbp = 1; + } else if (!matches(*argv, "fan-map")) { + NEXT_ARG(); + if (fan_parse_map(&argc, &argv, n)) + invarg("invalid fan-map", *argv); + } else if (!matches(*argv, "gpe")) { + gpe = 1; } else if (matches(*argv, "help") == 0) { - explain(); - return -1; -@@ -279,6 +325,28 @@ static int vxlan_parse_opt(struct link_u +@@ -337,6 +383,28 @@ static int vxlan_parse_opt(struct link_u return 0; } @@ -117,9 +111,9 @@ + p = RTA_PAYLOAD(i); + m = RTA_DATA(i); + fprintf(f, "%s/%d:%s/%d ", -+ rt_addr_n2a(AF_INET, p, &m->overlay, b1, INET_ADDRSTRLEN), ++ rt_addr_n2a_r(AF_INET, p, &m->overlay, b1, INET_ADDRSTRLEN), + m->overlay_prefix, -+ rt_addr_n2a(AF_INET, p, &m->underlay, b2, INET_ADDRSTRLEN), ++ rt_addr_n2a_r(AF_INET, p, &m->underlay, b2, INET_ADDRSTRLEN), + m->underlay_prefix); + } +} @@ -127,7 +121,7 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { __u32 vni; -@@ -321,6 +389,9 @@ static void vxlan_print_opt(struct link_ +@@ -380,6 +448,9 @@ static void vxlan_print_opt(struct link_ } } @@ -136,12 +130,10 @@ + if (tb[IFLA_VXLAN_LOCAL]) { __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]); - if (addr) -Index: iproute2-4.3.0/ip/link_iptnl.c -=================================================================== ---- iproute2-4.3.0.orig/ip/link_iptnl.c -+++ iproute2-4.3.0/ip/link_iptnl.c -@@ -49,10 +49,11 @@ static void usage(int sit) + +--- a/ip/link_iptnl.c ++++ b/ip/link_iptnl.c +@@ -50,10 +50,11 @@ static void usage(int sit) print_usage(stderr, sit); exit(-1); } @@ -154,7 +146,7 @@ struct rtattr *nest; char **argv = *argvp; int argc = *argcp; -@@ -61,8 +62,10 @@ static int fan_parse_map(int *argcp, cha +@@ -62,8 +63,10 @@ static int fan_parse_map(int *argcp, cha while (argc > 0) { char *colon = strchr(*argv, ':'); @@ -166,7 +158,7 @@ *colon = '\0'; if (get_prefix(&overlay, *argv, AF_INET)) -@@ -371,7 +374,7 @@ get_failed: +@@ -382,7 +385,7 @@ get_failed: static void fan_print_map(FILE *f, struct rtattr *attr) { char b1[INET_ADDRSTRLEN], b2[INET_ADDRSTRLEN]; diff -Nru iproute2-4.3.0/debian/patches/1003-fix-variable-in-libnetlink.patch iproute2-4.9.0/debian/patches/1003-fix-variable-in-libnetlink.patch --- iproute2-4.3.0/debian/patches/1003-fix-variable-in-libnetlink.patch 2016-04-05 11:35:52.000000000 +0000 +++ iproute2-4.9.0/debian/patches/1003-fix-variable-in-libnetlink.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -From ed108cfc0260b6b751647982b77d6363b1defb15 Mon Sep 17 00:00:00 2001 -From: Nicolas Dichtel -Date: Thu, 3 Dec 2015 17:13:48 +0100 -Ubuntu-Bug: https://bugs.launchpad.net/ubuntu/+source/iproute2/+bug/1522371 -Debian-Bug: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=812102 -Source: Upstream, https://git.kernel.org/cgit/linux/kernel/git/shemminger/iproute2.git/commit/?id=ed108cfc0260b6b751647982b77d6363b1defb15 -Subject: libnetlink: don't confuse variables in rtnl_talk() - -There is two variables named 'len' in rtnl_talk. In fact, commit -c079e121a73a didn't work. For example, it was possible to trigger -a seg fault with this command: -$ ip link set gre2 type ip6gre hoplimit 32 - -Let's rename the argument len to maxlen. - -Fixes: c079e121a73a ("libnetlink: add size argument to rtnl_talk") -Reported-by: Thomas Faivre -Signed-off-by: Nicolas Dichtel ---- - lib/libnetlink.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/lib/libnetlink.c b/lib/libnetlink.c -index 922ec2d..1658214 100644 ---- a/lib/libnetlink.c -+++ b/lib/libnetlink.c -@@ -332,7 +332,7 @@ int rtnl_dump_filter_nc(struct rtnl_handle *rth, - } - - int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, -- struct nlmsghdr *answer, size_t len) -+ struct nlmsghdr *answer, size_t maxlen) - { - int status; - unsigned seq; -@@ -415,7 +415,7 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, - } else if (!err->error) { - if (answer) - memcpy(answer, h, -- MIN(len, h->nlmsg_len)); -+ MIN(maxlen, h->nlmsg_len)); - return 0; - } - -@@ -427,7 +427,7 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, - - if (answer) { - memcpy(answer, h, -- MIN(len, h->nlmsg_len)); -+ MIN(maxlen, h->nlmsg_len)); - return 0; - } - diff -Nru iproute2-4.3.0/debian/patches/f_u32 iproute2-4.9.0/debian/patches/f_u32 --- iproute2-4.3.0/debian/patches/f_u32 2015-11-26 22:10:05.000000000 +0000 +++ iproute2-4.9.0/debian/patches/f_u32 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -#! /bin/sh -e -## -## All lines beginning with `## DP:' are a description of the patch. -## DP: Fixes the u32 calculation for 2.6 kernel - by Russell Stuart - -[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts -patch_opts="${patch_opts:--f --no-backup-if-mismatch}" - -if [ $# -ne 1 ]; then - echo >&2 "`basename $0`: script expects -patch|-unpatch as argument" - exit 1 -fi -case "$1" in - -patch) patch $patch_opts -p1 < $0;; - -unpatch) patch $patch_opts -p1 -R < $0;; - *) - echo >&2 "`basename $0`: script expects -patch|-unpatch as argument" - exit 1;; -esac - -exit 0 -@DPATCH@ -diff -Nur iproute-20051007.keep/tc/f_u32.c iproute-20051007/tc/f_u32.c ---- iproute-20051007.keep/tc/f_u32.c 2006-01-12 17:34:37.000000000 +1000 -+++ iproute-20051007/tc/f_u32.c 2006-02-07 17:10:29.000000000 +1000 -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -874,6 +875,7 @@ - htid = (handle&0xFFFFF000); - } else if (strcmp(*argv, "sample") == 0) { - __u32 hash; -+ struct utsname utsname; - struct { - struct tc_u32_sel sel; - struct tc_u32_key keys[4]; -@@ -889,8 +891,19 @@ - return -1; - } - hash = sel2.sel.keys[0].val&sel2.sel.keys[0].mask; -- hash ^= hash>>16; -- hash ^= hash>>8; -+ uname(&utsname); -+ if (strncmp(utsname.release, "2.4.", 4) == 0) { -+ hash ^= hash>>16; -+ hash ^= hash>>8; -+ } -+ else { -+ __u32 mask = sel2.sel.keys[0].mask; -+ while (mask && !(mask & 1)) { -+ mask >>= 1; -+ hash >>= 1; -+ } -+ hash &= 0xFF; -+ } - htid = ((hash<<12)&0xFF000)|(htid&0xFFF00000); - sample_ok = 1; - continue; diff -Nru iproute2-4.3.0/debian/patches/series iproute2-4.9.0/debian/patches/series --- iproute2-4.3.0/debian/patches/series 2016-04-05 11:35:52.000000000 +0000 +++ iproute2-4.9.0/debian/patches/series 2017-01-30 21:00:57.000000000 +0000 @@ -3,4 +3,3 @@ 1000-ubuntu-poc-fan-driver.patch 1001-ubuntu-poc-fan-driver-v3.patch 1002-ubuntu-poc-fan-driver-vxlan.patch -1003-fix-variable-in-libnetlink.patch diff -Nru iproute2-4.3.0/devlink/devlink.c iproute2-4.9.0/devlink/devlink.c --- iproute2-4.3.0/devlink/devlink.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/devlink/devlink.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,2537 @@ +/* + * devlink.c Devlink tool + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Jiri Pirko + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SNAPSHOT.h" +#include "list.h" +#include "mnlg.h" +#include "json_writer.h" + +#define ESWITCH_MODE_LEGACY "legacy" +#define ESWITCH_MODE_SWITCHDEV "switchdev" + +#define pr_err(args...) fprintf(stderr, ##args) +#define pr_out(args...) fprintf(stdout, ##args) +#define pr_out_sp(num, args...) \ + do { \ + int ret = fprintf(stdout, ##args); \ + if (ret < num) \ + fprintf(stdout, "%*s", num - ret, ""); \ + } while (0) + +static int _mnlg_socket_recv_run(struct mnlg_socket *nlg, + mnl_cb_t data_cb, void *data) +{ + int err; + + err = mnlg_socket_recv_run(nlg, data_cb, data); + if (err < 0) { + pr_err("devlink answers: %s\n", strerror(errno)); + return -errno; + } + return 0; +} + +static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg, + const struct nlmsghdr *nlh, + mnl_cb_t data_cb, void *data) +{ + int err; + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) { + pr_err("Failed to call mnlg_socket_send\n"); + return -errno; + } + return _mnlg_socket_recv_run(nlg, data_cb, data); +} + +static int _mnlg_socket_group_add(struct mnlg_socket *nlg, + const char *group_name) +{ + int err; + + err = mnlg_socket_group_add(nlg, group_name); + if (err < 0) { + pr_err("Failed to call mnlg_socket_group_add\n"); + return -errno; + } + return 0; +} + +struct ifname_map { + struct list_head list; + char *bus_name; + char *dev_name; + uint32_t port_index; + char *ifname; +}; + +static struct ifname_map *ifname_map_alloc(const char *bus_name, + const char *dev_name, + uint32_t port_index, + const char *ifname) +{ + struct ifname_map *ifname_map; + + ifname_map = calloc(1, sizeof(*ifname_map)); + if (!ifname_map) + return NULL; + ifname_map->bus_name = strdup(bus_name); + ifname_map->dev_name = strdup(dev_name); + ifname_map->port_index = port_index; + ifname_map->ifname = strdup(ifname); + if (!ifname_map->bus_name || !ifname_map->dev_name || + !ifname_map->ifname) { + free(ifname_map->ifname); + free(ifname_map->dev_name); + free(ifname_map->bus_name); + free(ifname_map); + return NULL; + } + return ifname_map; +} + +static void ifname_map_free(struct ifname_map *ifname_map) +{ + free(ifname_map->ifname); + free(ifname_map->dev_name); + free(ifname_map->bus_name); + free(ifname_map); +} + +#define BIT(nr) (1UL << (nr)) +#define DL_OPT_HANDLE BIT(0) +#define DL_OPT_HANDLEP BIT(1) +#define DL_OPT_PORT_TYPE BIT(2) +#define DL_OPT_PORT_COUNT BIT(3) +#define DL_OPT_SB BIT(4) +#define DL_OPT_SB_POOL BIT(5) +#define DL_OPT_SB_SIZE BIT(6) +#define DL_OPT_SB_TYPE BIT(7) +#define DL_OPT_SB_THTYPE BIT(8) +#define DL_OPT_SB_TH BIT(9) +#define DL_OPT_SB_TC BIT(10) +#define DL_OPT_ESWITCH_MODE BIT(11) + +struct dl_opts { + uint32_t present; /* flags of present items */ + char *bus_name; + char *dev_name; + uint32_t port_index; + enum devlink_port_type port_type; + uint32_t port_count; + uint32_t sb_index; + uint16_t sb_pool_index; + uint32_t sb_pool_size; + enum devlink_sb_pool_type sb_pool_type; + enum devlink_sb_threshold_type sb_pool_thtype; + uint32_t sb_threshold; + uint16_t sb_tc_index; + enum devlink_eswitch_mode eswitch_mode; +}; + +struct dl { + struct mnlg_socket *nlg; + struct list_head ifname_map_list; + int argc; + char **argv; + bool no_nice_names; + struct dl_opts opts; + json_writer_t *jw; + bool json_output; + bool pretty_output; + struct { + bool present; + char *bus_name; + char *dev_name; + uint32_t port_index; + } arr_last; +}; + +static int dl_argc(struct dl *dl) +{ + return dl->argc; +} + +static char *dl_argv(struct dl *dl) +{ + if (dl_argc(dl) == 0) + return NULL; + return *dl->argv; +} + +static void dl_arg_inc(struct dl *dl) +{ + if (dl_argc(dl) == 0) + return; + dl->argc--; + dl->argv++; +} + +static char *dl_argv_next(struct dl *dl) +{ + char *ret; + + if (dl_argc(dl) == 0) + return NULL; + + ret = *dl->argv; + dl_arg_inc(dl); + return ret; +} + +static char *dl_argv_index(struct dl *dl, unsigned int index) +{ + if (index >= dl_argc(dl)) + return NULL; + return dl->argv[index]; +} + +static int strcmpx(const char *str1, const char *str2) +{ + if (strlen(str1) > strlen(str2)) + return -1; + return strncmp(str1, str2, strlen(str1)); +} + +static bool dl_argv_match(struct dl *dl, const char *pattern) +{ + if (dl_argc(dl) == 0) + return false; + return strcmpx(dl_argv(dl), pattern) == 0; +} + +static bool dl_no_arg(struct dl *dl) +{ + return dl_argc(dl) == 0; +} + +static int attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type; + + type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, DEVLINK_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + if (type == DEVLINK_ATTR_BUS_NAME && + mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_DEV_NAME && + mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_PORT_INDEX && + mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_PORT_TYPE && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_PORT_DESIRED_TYPE && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_PORT_NETDEV_IFINDEX && + mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_PORT_NETDEV_NAME && + mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_PORT_IBDEV_NAME && + mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_INDEX && + mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_SIZE && + mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_INGRESS_POOL_COUNT && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_EGRESS_POOL_COUNT && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_INGRESS_TC_COUNT && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_EGRESS_TC_COUNT && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_POOL_INDEX && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_POOL_TYPE && + mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_POOL_SIZE && + mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE && + mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_THRESHOLD && + mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_TC_INDEX && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_OCC_CUR && + mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_SB_OCC_MAX && + mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_ESWITCH_MODE && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + tb[type] = attr; + return MNL_CB_OK; +} + +static int ifname_map_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct dl *dl = data; + struct ifname_map *ifname_map; + const char *bus_name; + const char *dev_name; + uint32_t port_ifindex; + const char *port_ifname; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX]) + return MNL_CB_ERROR; + + if (!tb[DEVLINK_ATTR_PORT_NETDEV_NAME]) + return MNL_CB_OK; + + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + port_ifindex = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); + port_ifname = mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME]); + ifname_map = ifname_map_alloc(bus_name, dev_name, + port_ifindex, port_ifname); + if (!ifname_map) + return MNL_CB_ERROR; + list_add(&ifname_map->list, &dl->ifname_map_list); + + return MNL_CB_OK; +} + +static void ifname_map_fini(struct dl *dl) +{ + struct ifname_map *ifname_map, *tmp; + + list_for_each_entry_safe(ifname_map, tmp, + &dl->ifname_map_list, list) { + list_del(&ifname_map->list); + ifname_map_free(ifname_map); + } +} + +static int ifname_map_init(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + INIT_LIST_HEAD(&dl->ifname_map_list); + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_GET, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP); + + err = _mnlg_socket_sndrcv(dl->nlg, nlh, ifname_map_cb, dl); + if (err) { + ifname_map_fini(dl); + return err; + } + return 0; +} + +static int ifname_map_lookup(struct dl *dl, const char *ifname, + char **p_bus_name, char **p_dev_name, + uint32_t *p_port_index) +{ + struct ifname_map *ifname_map; + + list_for_each_entry(ifname_map, &dl->ifname_map_list, list) { + if (strcmp(ifname, ifname_map->ifname) == 0) { + *p_bus_name = ifname_map->bus_name; + *p_dev_name = ifname_map->dev_name; + *p_port_index = ifname_map->port_index; + return 0; + } + } + return -ENOENT; +} + +static int ifname_map_rev_lookup(struct dl *dl, const char *bus_name, + const char *dev_name, uint32_t port_index, + char **p_ifname) +{ + struct ifname_map *ifname_map; + + list_for_each_entry(ifname_map, &dl->ifname_map_list, list) { + if (strcmp(bus_name, ifname_map->bus_name) == 0 && + strcmp(dev_name, ifname_map->dev_name) == 0 && + port_index == ifname_map->port_index) { + *p_ifname = ifname_map->ifname; + return 0; + } + } + return -ENOENT; +} + +static unsigned int strslashcount(char *str) +{ + unsigned int count = 0; + char *pos = str; + + while ((pos = strchr(pos, '/'))) { + count++; + pos++; + } + return count; +} + +static int strslashrsplit(char *str, char **before, char **after) +{ + char *slash; + + slash = strrchr(str, '/'); + if (!slash) + return -EINVAL; + *slash = '\0'; + *before = str; + *after = slash + 1; + return 0; +} + +static int strtouint32_t(const char *str, uint32_t *p_val) +{ + char *endptr; + unsigned long int val; + + val = strtoul(str, &endptr, 10); + if (endptr == str || *endptr != '\0') + return -EINVAL; + if (val > UINT_MAX) + return -ERANGE; + *p_val = val; + return 0; +} + +static int strtouint16_t(const char *str, uint16_t *p_val) +{ + char *endptr; + unsigned long int val; + + val = strtoul(str, &endptr, 10); + if (endptr == str || *endptr != '\0') + return -EINVAL; + if (val > USHRT_MAX) + return -ERANGE; + *p_val = val; + return 0; +} + +static int __dl_argv_handle(char *str, char **p_bus_name, char **p_dev_name) +{ + strslashrsplit(str, p_bus_name, p_dev_name); + return 0; +} + +static int dl_argv_handle(struct dl *dl, char **p_bus_name, char **p_dev_name) +{ + char *str = dl_argv_next(dl); + + if (!str) { + pr_err("Devlink identification (\"bus_name/dev_name\") expected\n"); + return -EINVAL; + } + if (strslashcount(str) != 1) { + pr_err("Wrong devlink identification string format.\n"); + pr_err("Expected \"bus_name/dev_name\".\n"); + return -EINVAL; + } + return __dl_argv_handle(str, p_bus_name, p_dev_name); +} + +static int __dl_argv_handle_port(char *str, + char **p_bus_name, char **p_dev_name, + uint32_t *p_port_index) +{ + char *handlestr = handlestr; + char *portstr = portstr; + int err; + + strslashrsplit(str, &handlestr, &portstr); + err = strtouint32_t(portstr, p_port_index); + if (err) { + pr_err("Port index \"%s\" is not a number or not within range\n", + portstr); + return err; + } + strslashrsplit(handlestr, p_bus_name, p_dev_name); + return 0; +} + +static int __dl_argv_handle_port_ifname(struct dl *dl, char *str, + char **p_bus_name, char **p_dev_name, + uint32_t *p_port_index) +{ + int err; + + err = ifname_map_lookup(dl, str, p_bus_name, p_dev_name, + p_port_index); + if (err) { + pr_err("Netdevice \"%s\" not found\n", str); + return err; + } + return 0; +} + +static int dl_argv_handle_port(struct dl *dl, char **p_bus_name, + char **p_dev_name, uint32_t *p_port_index) +{ + char *str = dl_argv_next(dl); + unsigned int slash_count; + + if (!str) { + pr_err("Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\") expected.\n"); + return -EINVAL; + } + slash_count = strslashcount(str); + switch (slash_count) { + case 0: + return __dl_argv_handle_port_ifname(dl, str, p_bus_name, + p_dev_name, p_port_index); + case 2: + return __dl_argv_handle_port(str, p_bus_name, + p_dev_name, p_port_index); + default: + pr_err("Wrong port identification string format.\n"); + pr_err("Expected \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n"); + return -EINVAL; + } +} + +static int dl_argv_handle_both(struct dl *dl, char **p_bus_name, + char **p_dev_name, uint32_t *p_port_index, + uint32_t *p_handle_bit) +{ + char *str = dl_argv_next(dl); + unsigned int slash_count; + int err; + + if (!str) { + pr_err("One of following identifications expected:\n" + "Devlink identification (\"bus_name/dev_name\")\n" + "Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\")\n"); + return -EINVAL; + } + slash_count = strslashcount(str); + if (slash_count == 1) { + err = __dl_argv_handle(str, p_bus_name, p_dev_name); + if (err) + return err; + *p_handle_bit = DL_OPT_HANDLE; + } else if (slash_count == 2) { + err = __dl_argv_handle_port(str, p_bus_name, + p_dev_name, p_port_index); + if (err) + return err; + *p_handle_bit = DL_OPT_HANDLEP; + } else if (slash_count == 0) { + err = __dl_argv_handle_port_ifname(dl, str, p_bus_name, + p_dev_name, p_port_index); + if (err) + return err; + *p_handle_bit = DL_OPT_HANDLEP; + } else { + pr_err("Wrong port identification string format.\n"); + pr_err("Expected \"bus_name/dev_name\" or \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n"); + return -EINVAL; + } + return 0; +} + +static int dl_argv_uint32_t(struct dl *dl, uint32_t *p_val) +{ + char *str = dl_argv_next(dl); + int err; + + if (!str) { + pr_err("Unsigned number argument expected\n"); + return -EINVAL; + } + + err = strtouint32_t(str, p_val); + if (err) { + pr_err("\"%s\" is not a number or not within range\n", str); + return err; + } + return 0; +} + +static int dl_argv_uint16_t(struct dl *dl, uint16_t *p_val) +{ + char *str = dl_argv_next(dl); + int err; + + if (!str) { + pr_err("Unsigned number argument expected\n"); + return -EINVAL; + } + + err = strtouint16_t(str, p_val); + if (err) { + pr_err("\"%s\" is not a number or not within range\n", str); + return err; + } + return 0; +} + +static int dl_argv_str(struct dl *dl, const char **p_str) +{ + const char *str = dl_argv_next(dl); + + if (!str) { + pr_err("String parameter expected\n"); + return -EINVAL; + } + *p_str = str; + return 0; +} + +static int port_type_get(const char *typestr, enum devlink_port_type *p_type) +{ + if (strcmp(typestr, "auto") == 0) { + *p_type = DEVLINK_PORT_TYPE_AUTO; + } else if (strcmp(typestr, "eth") == 0) { + *p_type = DEVLINK_PORT_TYPE_ETH; + } else if (strcmp(typestr, "ib") == 0) { + *p_type = DEVLINK_PORT_TYPE_IB; + } else { + pr_err("Unknown port type \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int pool_type_get(const char *typestr, enum devlink_sb_pool_type *p_type) +{ + if (strcmp(typestr, "ingress") == 0) { + *p_type = DEVLINK_SB_POOL_TYPE_INGRESS; + } else if (strcmp(typestr, "egress") == 0) { + *p_type = DEVLINK_SB_POOL_TYPE_EGRESS; + } else { + pr_err("Unknown pool type \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int threshold_type_get(const char *typestr, + enum devlink_sb_threshold_type *p_type) +{ + if (strcmp(typestr, "static") == 0) { + *p_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC; + } else if (strcmp(typestr, "dynamic") == 0) { + *p_type = DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC; + } else { + pr_err("Unknown threshold type \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int eswitch_mode_get(const char *typestr, + enum devlink_eswitch_mode *p_mode) +{ + if (strcmp(typestr, ESWITCH_MODE_LEGACY) == 0) { + *p_mode = DEVLINK_ESWITCH_MODE_LEGACY; + } else if (strcmp(typestr, ESWITCH_MODE_SWITCHDEV) == 0) { + *p_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; + } else { + pr_err("Unknown eswitch mode \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int dl_argv_parse(struct dl *dl, uint32_t o_required, + uint32_t o_optional) +{ + struct dl_opts *opts = &dl->opts; + uint32_t o_all = o_required | o_optional; + uint32_t o_found = 0; + int err; + + if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) { + uint32_t handle_bit = handle_bit; + + err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name, + &opts->port_index, &handle_bit); + if (err) + return err; + o_found |= handle_bit; + } else if (o_required & DL_OPT_HANDLE) { + err = dl_argv_handle(dl, &opts->bus_name, &opts->dev_name); + if (err) + return err; + o_found |= DL_OPT_HANDLE; + } else if (o_required & DL_OPT_HANDLEP) { + err = dl_argv_handle_port(dl, &opts->bus_name, &opts->dev_name, + &opts->port_index); + if (err) + return err; + o_found |= DL_OPT_HANDLEP; + } + + while (dl_argc(dl)) { + if (dl_argv_match(dl, "type") && + (o_all & DL_OPT_PORT_TYPE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = port_type_get(typestr, &opts->port_type); + if (err) + return err; + o_found |= DL_OPT_PORT_TYPE; + } else if (dl_argv_match(dl, "count") && + (o_all & DL_OPT_PORT_COUNT)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->port_count); + if (err) + return err; + o_found |= DL_OPT_PORT_COUNT; + } else if (dl_argv_match(dl, "sb") && + (o_all & DL_OPT_SB)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->sb_index); + if (err) + return err; + o_found |= DL_OPT_SB; + } else if (dl_argv_match(dl, "pool") && + (o_all & DL_OPT_SB_POOL)) { + dl_arg_inc(dl); + err = dl_argv_uint16_t(dl, &opts->sb_pool_index); + if (err) + return err; + o_found |= DL_OPT_SB_POOL; + } else if (dl_argv_match(dl, "size") && + (o_all & DL_OPT_SB_SIZE)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->sb_pool_size); + if (err) + return err; + o_found |= DL_OPT_SB_SIZE; + } else if (dl_argv_match(dl, "type") && + (o_all & DL_OPT_SB_TYPE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = pool_type_get(typestr, &opts->sb_pool_type); + if (err) + return err; + o_found |= DL_OPT_SB_TYPE; + } else if (dl_argv_match(dl, "thtype") && + (o_all & DL_OPT_SB_THTYPE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = threshold_type_get(typestr, + &opts->sb_pool_thtype); + if (err) + return err; + o_found |= DL_OPT_SB_THTYPE; + } else if (dl_argv_match(dl, "th") && + (o_all & DL_OPT_SB_TH)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->sb_threshold); + if (err) + return err; + o_found |= DL_OPT_SB_TH; + } else if (dl_argv_match(dl, "tc") && + (o_all & DL_OPT_SB_TC)) { + dl_arg_inc(dl); + err = dl_argv_uint16_t(dl, &opts->sb_tc_index); + if (err) + return err; + o_found |= DL_OPT_SB_TC; + } else if (dl_argv_match(dl, "mode") && + (o_all & DL_OPT_ESWITCH_MODE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = eswitch_mode_get(typestr, &opts->eswitch_mode); + if (err) + return err; + o_found |= DL_OPT_ESWITCH_MODE; + } else { + pr_err("Unknown option \"%s\"\n", dl_argv(dl)); + return -EINVAL; + } + } + + opts->present = o_found; + + if ((o_optional & DL_OPT_SB) && !(o_found & DL_OPT_SB)) { + opts->sb_index = 0; + opts->present |= DL_OPT_SB; + } + + if ((o_required & DL_OPT_PORT_TYPE) && !(o_found & DL_OPT_PORT_TYPE)) { + pr_err("Port type option expected.\n"); + return -EINVAL; + } + + if ((o_required & DL_OPT_PORT_COUNT) && + !(o_found & DL_OPT_PORT_COUNT)) { + pr_err("Port split count option expected.\n"); + return -EINVAL; + } + + if ((o_required & DL_OPT_SB_POOL) && !(o_found & DL_OPT_SB_POOL)) { + pr_err("Pool index option expected.\n"); + return -EINVAL; + } + + if ((o_required & DL_OPT_SB_SIZE) && !(o_found & DL_OPT_SB_SIZE)) { + pr_err("Pool size option expected.\n"); + return -EINVAL; + } + + if ((o_required & DL_OPT_SB_TYPE) && !(o_found & DL_OPT_SB_TYPE)) { + pr_err("Pool type option expected.\n"); + return -EINVAL; + } + + if ((o_required & DL_OPT_SB_THTYPE) && !(o_found & DL_OPT_SB_THTYPE)) { + pr_err("Pool threshold type option expected.\n"); + return -EINVAL; + } + + if ((o_required & DL_OPT_SB_TH) && !(o_found & DL_OPT_SB_TH)) { + pr_err("Threshold option expected.\n"); + return -EINVAL; + } + + if ((o_required & DL_OPT_SB_TC) && !(o_found & DL_OPT_SB_TC)) { + pr_err("TC index option expected.\n"); + return -EINVAL; + } + + if ((o_required & DL_OPT_ESWITCH_MODE) && + !(o_found & DL_OPT_ESWITCH_MODE)) { + pr_err("E-Switch mode option expected.\n"); + return -EINVAL; + } + + return 0; +} + +static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl) +{ + struct dl_opts *opts = &dl->opts; + + if (opts->present & DL_OPT_HANDLE) { + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name); + } else if (opts->present & DL_OPT_HANDLEP) { + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name); + mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_INDEX, + opts->port_index); + } + if (opts->present & DL_OPT_PORT_TYPE) + mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_TYPE, + opts->port_type); + if (opts->present & DL_OPT_PORT_COUNT) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_SPLIT_COUNT, + opts->port_count); + if (opts->present & DL_OPT_SB) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_INDEX, + opts->sb_index); + if (opts->present & DL_OPT_SB_POOL) + mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_POOL_INDEX, + opts->sb_pool_index); + if (opts->present & DL_OPT_SB_SIZE) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_POOL_SIZE, + opts->sb_pool_size); + if (opts->present & DL_OPT_SB_TYPE) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_TYPE, + opts->sb_pool_type); + if (opts->present & DL_OPT_SB_THTYPE) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE, + opts->sb_pool_thtype); + if (opts->present & DL_OPT_SB_TH) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_THRESHOLD, + opts->sb_threshold); + if (opts->present & DL_OPT_SB_TC) + mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_TC_INDEX, + opts->sb_tc_index); + if (opts->present & DL_OPT_ESWITCH_MODE) + mnl_attr_put_u16(nlh, DEVLINK_ATTR_ESWITCH_MODE, + opts->eswitch_mode); +} + +static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, + uint32_t o_required, uint32_t o_optional) +{ + int err; + + err = dl_argv_parse(dl, o_required, o_optional); + if (err) + return err; + dl_opts_put(nlh, dl); + return 0; +} + +static bool dl_dump_filter(struct dl *dl, struct nlattr **tb) +{ + struct dl_opts *opts = &dl->opts; + struct nlattr *attr_bus_name = tb[DEVLINK_ATTR_BUS_NAME]; + struct nlattr *attr_dev_name = tb[DEVLINK_ATTR_DEV_NAME]; + struct nlattr *attr_port_index = tb[DEVLINK_ATTR_PORT_INDEX]; + struct nlattr *attr_sb_index = tb[DEVLINK_ATTR_SB_INDEX]; + + if (opts->present & DL_OPT_HANDLE && + attr_bus_name && attr_dev_name) { + const char *bus_name = mnl_attr_get_str(attr_bus_name); + const char *dev_name = mnl_attr_get_str(attr_dev_name); + + if (strcmp(bus_name, opts->bus_name) != 0 || + strcmp(dev_name, opts->dev_name) != 0) + return false; + } + if (opts->present & DL_OPT_HANDLEP && + attr_bus_name && attr_dev_name && attr_port_index) { + const char *bus_name = mnl_attr_get_str(attr_bus_name); + const char *dev_name = mnl_attr_get_str(attr_dev_name); + uint32_t port_index = mnl_attr_get_u32(attr_port_index); + + if (strcmp(bus_name, opts->bus_name) != 0 || + strcmp(dev_name, opts->dev_name) != 0 || + port_index != opts->port_index) + return false; + } + if (opts->present & DL_OPT_SB && attr_sb_index) { + uint32_t sb_index = mnl_attr_get_u32(attr_sb_index); + + if (sb_index != opts->sb_index) + return false; + } + return true; +} + +static void cmd_dev_help(void) +{ + pr_err("Usage: devlink dev show [ DEV ]\n"); +} + +static bool cmp_arr_last_handle(struct dl *dl, const char *bus_name, + const char *dev_name) +{ + if (!dl->arr_last.present) + return false; + return strcmp(dl->arr_last.bus_name, bus_name) == 0 && + strcmp(dl->arr_last.dev_name, dev_name) == 0; +} + +static void arr_last_handle_set(struct dl *dl, const char *bus_name, + const char *dev_name) +{ + dl->arr_last.present = true; + free(dl->arr_last.dev_name); + free(dl->arr_last.bus_name); + dl->arr_last.bus_name = strdup(bus_name); + dl->arr_last.dev_name = strdup(dev_name); +} + +static bool should_arr_last_handle_start(struct dl *dl, const char *bus_name, + const char *dev_name) +{ + return !cmp_arr_last_handle(dl, bus_name, dev_name); +} + +static bool should_arr_last_handle_end(struct dl *dl, const char *bus_name, + const char *dev_name) +{ + return dl->arr_last.present && + !cmp_arr_last_handle(dl, bus_name, dev_name); +} + +static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb, + bool content, bool array) +{ + const char *bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + char buf[32]; + + sprintf(buf, "%s/%s", bus_name, dev_name); + + if (dl->json_output) { + if (array) { + if (should_arr_last_handle_end(dl, bus_name, dev_name)) + jsonw_end_array(dl->jw); + if (should_arr_last_handle_start(dl, bus_name, + dev_name)) { + jsonw_name(dl->jw, buf); + jsonw_start_array(dl->jw); + jsonw_start_object(dl->jw); + arr_last_handle_set(dl, bus_name, dev_name); + } else { + jsonw_start_object(dl->jw); + } + } else { + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + } + } else { + pr_out("%s%s", buf, content ? ":" : ""); + } +} + +static void pr_out_handle_start_arr(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, true, true); +} + +static void pr_out_handle_end(struct dl *dl) +{ + if (dl->json_output) + jsonw_end_object(dl->jw); + else + pr_out("\n"); +} + +static void pr_out_handle(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, false, false); + pr_out_handle_end(dl); +} + +static bool cmp_arr_last_port_handle(struct dl *dl, const char *bus_name, + const char *dev_name, uint32_t port_index) +{ + return cmp_arr_last_handle(dl, bus_name, dev_name) && + dl->arr_last.port_index == port_index; +} + +static void arr_last_port_handle_set(struct dl *dl, const char *bus_name, + const char *dev_name, uint32_t port_index) +{ + arr_last_handle_set(dl, bus_name, dev_name); + dl->arr_last.port_index = port_index; +} + +static bool should_arr_last_port_handle_start(struct dl *dl, + const char *bus_name, + const char *dev_name, + uint32_t port_index) +{ + return !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index); +} + +static bool should_arr_last_port_handle_end(struct dl *dl, + const char *bus_name, + const char *dev_name, + uint32_t port_index) +{ + return dl->arr_last.present && + !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index); +} + +static void __pr_out_port_handle_start(struct dl *dl, const char *bus_name, + const char *dev_name, + uint32_t port_index, bool try_nice, + bool array) +{ + static char buf[32]; + char *ifname = NULL; + + if (dl->no_nice_names || !try_nice || + ifname_map_rev_lookup(dl, bus_name, dev_name, + port_index, &ifname) != 0) + sprintf(buf, "%s/%s/%d", bus_name, dev_name, port_index); + else + sprintf(buf, "%s", ifname); + + if (dl->json_output) { + if (array) { + if (should_arr_last_port_handle_end(dl, bus_name, + dev_name, + port_index)) + jsonw_end_array(dl->jw); + if (should_arr_last_port_handle_start(dl, bus_name, + dev_name, + port_index)) { + jsonw_name(dl->jw, buf); + jsonw_start_array(dl->jw); + jsonw_start_object(dl->jw); + arr_last_port_handle_set(dl, bus_name, dev_name, + port_index); + } else { + jsonw_start_object(dl->jw); + } + } else { + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + } + } else { + pr_out("%s:", buf); + } +} + +static void pr_out_port_handle_start(struct dl *dl, struct nlattr **tb, bool try_nice) +{ + const char *bus_name; + const char *dev_name; + uint32_t port_index; + + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); + __pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, false); +} + +static void pr_out_port_handle_start_arr(struct dl *dl, struct nlattr **tb, bool try_nice) +{ + const char *bus_name; + const char *dev_name; + uint32_t port_index; + + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); + __pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, true); +} + +static void pr_out_port_handle_end(struct dl *dl) +{ + if (dl->json_output) + jsonw_end_object(dl->jw); + else + pr_out("\n"); +} + + +static void pr_out_str(struct dl *dl, const char *name, const char *val) +{ + if (dl->json_output) + jsonw_string_field(dl->jw, name, val); + else + pr_out(" %s %s", name, val); +} + +static void pr_out_uint(struct dl *dl, const char *name, unsigned int val) +{ + if (dl->json_output) + jsonw_uint_field(dl->jw, name, val); + else + pr_out(" %s %u", name, val); +} + +static void pr_out_dev(struct dl *dl, struct nlattr **tb) +{ + pr_out_handle(dl, tb); +} + +static void pr_out_section_start(struct dl *dl, const char *name) +{ + if (dl->json_output) { + jsonw_start_object(dl->jw); + jsonw_name(dl->jw, name); + jsonw_start_object(dl->jw); + } +} + +static void pr_out_section_end(struct dl *dl) +{ + if (dl->json_output) { + if (dl->arr_last.present) + jsonw_end_array(dl->jw); + jsonw_end_object(dl->jw); + jsonw_end_object(dl->jw); + } +} + +static const char *eswitch_mode_name(uint32_t mode) +{ + switch (mode) { + case DEVLINK_ESWITCH_MODE_LEGACY: return ESWITCH_MODE_LEGACY; + case DEVLINK_ESWITCH_MODE_SWITCHDEV: return ESWITCH_MODE_SWITCHDEV; + default: return ""; + } +} + +static void pr_out_eswitch(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, true, false); + + if (tb[DEVLINK_ATTR_ESWITCH_MODE]) + pr_out_str(dl, "mode", + eswitch_mode_name(mnl_attr_get_u16(tb[DEVLINK_ATTR_ESWITCH_MODE]))); + pr_out_handle_end(dl); +} + +static int cmd_dev_eswitch_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + pr_out_eswitch(dl, tb); + return MNL_CB_OK; +} + +static int cmd_dev_eswitch_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_ESWITCH_MODE_GET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0); + if (err) + return err; + + pr_out_section_start(dl, "dev"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_eswitch_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_dev_eswitch_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_ESWITCH_MODE_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_ESWITCH_MODE, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_dev_eswitch(struct dl *dl) +{ + if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_dev_eswitch_set(dl); + } else if (dl_argv_match(dl, "show")) { + dl_arg_inc(dl); + return cmd_dev_eswitch_show(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + pr_out_dev(dl, tb); + return MNL_CB_OK; +} + +static int cmd_dev_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0); + if (err) + return err; + } + + pr_out_section_start(dl, "dev"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_dev(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_dev_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_dev_show(dl); + } else if (dl_argv_match(dl, "eswitch")) { + dl_arg_inc(dl); + return cmd_dev_eswitch(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void cmd_port_help(void) +{ + pr_err("Usage: devlink port show [ DEV/PORT_INDEX ]\n"); + pr_err(" devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n"); + pr_err(" devlink port split DEV/PORT_INDEX count COUNT\n"); + pr_err(" devlink port unsplit DEV/PORT_INDEX\n"); +} + +static const char *port_type_name(uint32_t type) +{ + switch (type) { + case DEVLINK_PORT_TYPE_NOTSET: return "notset"; + case DEVLINK_PORT_TYPE_AUTO: return "auto"; + case DEVLINK_PORT_TYPE_ETH: return "eth"; + case DEVLINK_PORT_TYPE_IB: return "ib"; + default: return ""; + } +} + +static void pr_out_port(struct dl *dl, struct nlattr **tb) +{ + struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE]; + struct nlattr *dpt_attr = tb[DEVLINK_ATTR_PORT_DESIRED_TYPE]; + + pr_out_port_handle_start(dl, tb, false); + if (pt_attr) { + uint16_t port_type = mnl_attr_get_u16(pt_attr); + + pr_out_str(dl, "type", port_type_name(port_type)); + if (dpt_attr) { + uint16_t des_port_type = mnl_attr_get_u16(dpt_attr); + + if (port_type != des_port_type) + pr_out_str(dl, "des_type", + port_type_name(des_port_type)); + } + } + if (tb[DEVLINK_ATTR_PORT_NETDEV_NAME]) + pr_out_str(dl, "netdev", + mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME])); + if (tb[DEVLINK_ATTR_PORT_IBDEV_NAME]) + pr_out_str(dl, "ibdev", + mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_IBDEV_NAME])); + if (tb[DEVLINK_ATTR_PORT_SPLIT_GROUP]) + pr_out_uint(dl, "split_group", + mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])); + pr_out_port_handle_end(dl); +} + +static int cmd_port_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX]) + return MNL_CB_ERROR; + pr_out_port(dl, tb); + return MNL_CB_OK; +} + +static int cmd_port_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0); + if (err) + return err; + } + + pr_out_section_start(dl, "port"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_port_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_TYPE, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_port_split(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SPLIT, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_COUNT, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_port_unsplit(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_UNSPLIT, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_port(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_port_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_port_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_port_set(dl); + } else if (dl_argv_match(dl, "split")) { + dl_arg_inc(dl); + return cmd_port_split(dl); + } else if (dl_argv_match(dl, "unsplit")) { + dl_arg_inc(dl); + return cmd_port_unsplit(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void cmd_sb_help(void) +{ + pr_err("Usage: devlink sb show [ DEV [ sb SB_INDEX ] ]\n"); + pr_err(" devlink sb pool show [ DEV [ sb SB_INDEX ] pool POOL_INDEX ]\n"); + pr_err(" devlink sb pool set DEV [ sb SB_INDEX ] pool POOL_INDEX\n"); + pr_err(" size POOL_SIZE thtype { static | dynamic }\n"); + pr_err(" devlink sb port pool show [ DEV/PORT_INDEX [ sb SB_INDEX ]\n"); + pr_err(" pool POOL_INDEX ]\n"); + pr_err(" devlink sb port pool set DEV/PORT_INDEX [ sb SB_INDEX ]\n"); + pr_err(" pool POOL_INDEX th THRESHOLD\n"); + pr_err(" devlink sb tc bind show [ DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n"); + pr_err(" type { ingress | egress } ]\n"); + pr_err(" devlink sb tc bind set DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n"); + pr_err(" type { ingress | egress } pool POOL_INDEX\n"); + pr_err(" th THRESHOLD\n"); + pr_err(" devlink sb occupancy show { DEV | DEV/PORT_INDEX } [ sb SB_INDEX ]\n"); + pr_err(" devlink sb occupancy snapshot DEV [ sb SB_INDEX ]\n"); + pr_err(" devlink sb occupancy clearmax DEV [ sb SB_INDEX ]\n"); +} + +static void pr_out_sb(struct dl *dl, struct nlattr **tb) +{ + pr_out_handle_start_arr(dl, tb); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "size", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE])); + pr_out_uint(dl, "ing_pools", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT])); + pr_out_uint(dl, "eg_pools", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT])); + pr_out_uint(dl, "ing_tcs", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT])); + pr_out_uint(dl, "eg_tcs", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])); + pr_out_handle_end(dl); +} + +static int cmd_sb_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_SIZE] || + !tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT] || + !tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT] || + !tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT] || + !tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT]) + return MNL_CB_ERROR; + pr_out_sb(dl, tb); + return MNL_CB_OK; +} + +static int cmd_sb_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB); + if (err) + return err; + } + + pr_out_section_start(dl, "sb"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static const char *pool_type_name(uint8_t type) +{ + switch (type) { + case DEVLINK_SB_POOL_TYPE_INGRESS: return "ingress"; + case DEVLINK_SB_POOL_TYPE_EGRESS: return "egress"; + default: return ""; + } +} + +static const char *threshold_type_name(uint8_t type) +{ + switch (type) { + case DEVLINK_SB_THRESHOLD_TYPE_STATIC: return "static"; + case DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC: return "dynamic"; + default: return ""; + } +} + +static void pr_out_sb_pool(struct dl *dl, struct nlattr **tb) +{ + pr_out_handle_start_arr(dl, tb); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "pool", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + pr_out_str(dl, "type", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + pr_out_uint(dl, "size", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE])); + pr_out_str(dl, "thtype", + threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]))); + pr_out_handle_end(dl); +} + +static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_INDEX] || + !tb[DEVLINK_ATTR_SB_POOL_TYPE] || !tb[DEVLINK_ATTR_SB_POOL_SIZE] || + !tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]) + return MNL_CB_ERROR; + pr_out_sb_pool(dl, tb); + return MNL_CB_OK; +} + +static int cmd_sb_pool_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL, + DL_OPT_SB); + if (err) + return err; + } + + pr_out_section_start(dl, "pool"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_sb_pool_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL | + DL_OPT_SB_SIZE | DL_OPT_SB_THTYPE, DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_pool(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_sb_pool_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_sb_pool_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb) +{ + pr_out_port_handle_start_arr(dl, tb, true); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "pool", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + pr_out_uint(dl, "threshold", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); + pr_out_port_handle_end(dl); +} + +static int cmd_sb_port_pool_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] || + !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD]) + return MNL_CB_ERROR; + pr_out_sb_port_pool(dl, tb); + return MNL_CB_OK; +} + +static int cmd_sb_port_pool_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLEP | DL_OPT_SB_POOL, + DL_OPT_SB); + if (err) + return err; + } + + pr_out_section_start(dl, "port_pool"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl); + pr_out_section_end(dl); + return 0; +} + +static int cmd_sb_port_pool_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_POOL | + DL_OPT_SB_TH, DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_port_pool(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_sb_port_pool_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_sb_port_pool_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_sb_port(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "pool")) { + dl_arg_inc(dl); + return cmd_sb_port_pool(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb) +{ + pr_out_port_handle_start_arr(dl, tb, true); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "tc", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX])); + pr_out_str(dl, "type", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + pr_out_uint(dl, "pool", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + pr_out_uint(dl, "threshold", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); + pr_out_port_handle_end(dl); +} + +static int cmd_sb_tc_bind_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] || + !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] || + !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD]) + return MNL_CB_ERROR; + pr_out_sb_tc_bind(dl, tb); + return MNL_CB_OK; +} + +static int cmd_sb_tc_bind_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC | + DL_OPT_SB_TYPE, DL_OPT_SB); + if (err) + return err; + } + + pr_out_section_start(dl, "tc_bind"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_sb_tc_bind_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC | + DL_OPT_SB_TYPE | DL_OPT_SB_POOL | DL_OPT_SB_TH, + DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_tc_bind(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_sb_tc_bind_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_sb_tc_bind_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_sb_tc(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "bind")) { + dl_arg_inc(dl); + return cmd_sb_tc_bind(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +struct occ_item { + struct list_head list; + uint32_t index; + uint32_t cur; + uint32_t max; + uint32_t bound_pool_index; +}; + +struct occ_port { + struct list_head list; + char *bus_name; + char *dev_name; + uint32_t port_index; + uint32_t sb_index; + struct list_head pool_list; + struct list_head ing_tc_list; + struct list_head eg_tc_list; +}; + +struct occ_show { + struct dl *dl; + int err; + struct list_head port_list; +}; + +static struct occ_item *occ_item_alloc(void) +{ + return calloc(1, sizeof(struct occ_item)); +} + +static void occ_item_free(struct occ_item *occ_item) +{ + free(occ_item); +} + +static struct occ_port *occ_port_alloc(uint32_t port_index) +{ + struct occ_port *occ_port; + + occ_port = calloc(1, sizeof(*occ_port)); + if (!occ_port) + return NULL; + occ_port->port_index = port_index; + INIT_LIST_HEAD(&occ_port->pool_list); + INIT_LIST_HEAD(&occ_port->ing_tc_list); + INIT_LIST_HEAD(&occ_port->eg_tc_list); + return occ_port; +} + +static void occ_port_free(struct occ_port *occ_port) +{ + struct occ_item *occ_item, *tmp; + + list_for_each_entry_safe(occ_item, tmp, &occ_port->pool_list, list) + occ_item_free(occ_item); + list_for_each_entry_safe(occ_item, tmp, &occ_port->ing_tc_list, list) + occ_item_free(occ_item); + list_for_each_entry_safe(occ_item, tmp, &occ_port->eg_tc_list, list) + occ_item_free(occ_item); +} + +static struct occ_show *occ_show_alloc(struct dl *dl) +{ + struct occ_show *occ_show; + + occ_show = calloc(1, sizeof(*occ_show)); + if (!occ_show) + return NULL; + occ_show->dl = dl; + INIT_LIST_HEAD(&occ_show->port_list); + return occ_show; +} + +static void occ_show_free(struct occ_show *occ_show) +{ + struct occ_port *occ_port, *tmp; + + list_for_each_entry_safe(occ_port, tmp, &occ_show->port_list, list) + occ_port_free(occ_port); +} + +static struct occ_port *occ_port_get(struct occ_show *occ_show, + struct nlattr **tb) +{ + struct occ_port *occ_port; + uint32_t port_index; + + port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); + + list_for_each_entry_reverse(occ_port, &occ_show->port_list, list) { + if (occ_port->port_index == port_index) + return occ_port; + } + occ_port = occ_port_alloc(port_index); + if (!occ_port) + return NULL; + list_add_tail(&occ_port->list, &occ_show->port_list); + return occ_port; +} + +static void pr_out_occ_show_item_list(const char *label, struct list_head *list, + bool bound_pool) +{ + struct occ_item *occ_item; + int i = 1; + + pr_out_sp(7, " %s:", label); + list_for_each_entry(occ_item, list, list) { + if ((i - 1) % 4 == 0 && i != 1) + pr_out_sp(7, " "); + if (bound_pool) + pr_out_sp(7, "%2u(%u):", occ_item->index, + occ_item->bound_pool_index); + else + pr_out_sp(7, "%2u:", occ_item->index); + pr_out_sp(15, "%7u/%u", occ_item->cur, occ_item->max); + if (i++ % 4 == 0) + pr_out("\n"); + } + if ((i - 1) % 4 != 0) + pr_out("\n"); +} + +static void pr_out_json_occ_show_item_list(struct dl *dl, const char *label, + struct list_head *list, + bool bound_pool) +{ + struct occ_item *occ_item; + char buf[32]; + + jsonw_name(dl->jw, label); + jsonw_start_object(dl->jw); + list_for_each_entry(occ_item, list, list) { + sprintf(buf, "%u", occ_item->index); + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + if (bound_pool) + jsonw_uint_field(dl->jw, "bound_pool", + occ_item->bound_pool_index); + jsonw_uint_field(dl->jw, "current", occ_item->cur); + jsonw_uint_field(dl->jw, "max", occ_item->max); + jsonw_end_object(dl->jw); + } + jsonw_end_object(dl->jw); +} + +static void pr_out_occ_show_port(struct dl *dl, struct occ_port *occ_port) +{ + if (dl->json_output) { + pr_out_json_occ_show_item_list(dl, "pool", + &occ_port->pool_list, false); + pr_out_json_occ_show_item_list(dl, "itc", + &occ_port->ing_tc_list, true); + pr_out_json_occ_show_item_list(dl, "etc", + &occ_port->eg_tc_list, true); + } else { + pr_out("\n"); + pr_out_occ_show_item_list("pool", &occ_port->pool_list, false); + pr_out_occ_show_item_list("itc", &occ_port->ing_tc_list, true); + pr_out_occ_show_item_list("etc", &occ_port->eg_tc_list, true); + } +} + +static void pr_out_occ_show(struct occ_show *occ_show) +{ + struct dl *dl = occ_show->dl; + struct dl_opts *opts = &dl->opts; + struct occ_port *occ_port; + + list_for_each_entry(occ_port, &occ_show->port_list, list) { + __pr_out_port_handle_start(dl, opts->bus_name, opts->dev_name, + occ_port->port_index, true, false); + pr_out_occ_show_port(dl, occ_port); + pr_out_port_handle_end(dl); + } +} + +static void cmd_sb_occ_port_pool_process(struct occ_show *occ_show, + struct nlattr **tb) +{ + struct occ_port *occ_port; + struct occ_item *occ_item; + + if (occ_show->err || !dl_dump_filter(occ_show->dl, tb)) + return; + + occ_port = occ_port_get(occ_show, tb); + if (!occ_port) { + occ_show->err = -ENOMEM; + return; + } + + occ_item = occ_item_alloc(); + if (!occ_item) { + occ_show->err = -ENOMEM; + return; + } + occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]); + occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]); + occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]); + list_add_tail(&occ_item->list, &occ_port->pool_list); +} + +static int cmd_sb_occ_port_pool_process_cb(const struct nlmsghdr *nlh, void *data) +{ + struct occ_show *occ_show = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] || + !tb[DEVLINK_ATTR_SB_POOL_INDEX] || + !tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX]) + return MNL_CB_ERROR; + cmd_sb_occ_port_pool_process(occ_show, tb); + return MNL_CB_OK; +} + +static void cmd_sb_occ_tc_pool_process(struct occ_show *occ_show, + struct nlattr **tb) +{ + struct occ_port *occ_port; + struct occ_item *occ_item; + uint8_t pool_type; + + if (occ_show->err || !dl_dump_filter(occ_show->dl, tb)) + return; + + occ_port = occ_port_get(occ_show, tb); + if (!occ_port) { + occ_show->err = -ENOMEM; + return; + } + + occ_item = occ_item_alloc(); + if (!occ_item) { + occ_show->err = -ENOMEM; + return; + } + occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]); + occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]); + occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]); + occ_item->bound_pool_index = + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]); + pool_type = mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]); + if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS) + list_add_tail(&occ_item->list, &occ_port->ing_tc_list); + else if (pool_type == DEVLINK_SB_POOL_TYPE_EGRESS) + list_add_tail(&occ_item->list, &occ_port->eg_tc_list); + else + occ_item_free(occ_item); +} + +static int cmd_sb_occ_tc_pool_process_cb(const struct nlmsghdr *nlh, void *data) +{ + struct occ_show *occ_show = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] || + !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] || + !tb[DEVLINK_ATTR_SB_POOL_INDEX] || + !tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX]) + return MNL_CB_ERROR; + cmd_sb_occ_tc_pool_process(occ_show, tb); + return MNL_CB_OK; +} + +static int cmd_sb_occ_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + struct occ_show *occ_show; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP; + int err; + + err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HANDLEP, DL_OPT_SB); + if (err) + return err; + + occ_show = occ_show_alloc(dl); + if (!occ_show) + return -ENOMEM; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags); + + err = _mnlg_socket_sndrcv(dl->nlg, nlh, + cmd_sb_occ_port_pool_process_cb, occ_show); + if (err) + goto out; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags); + + err = _mnlg_socket_sndrcv(dl->nlg, nlh, + cmd_sb_occ_tc_pool_process_cb, occ_show); + if (err) + goto out; + + pr_out_section_start(dl, "occupancy"); + pr_out_occ_show(occ_show); + pr_out_section_end(dl); + +out: + occ_show_free(occ_show); + return err; +} + +static int cmd_sb_occ_snapshot(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_SNAPSHOT, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_occ_clearmax(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_MAX_CLEAR, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_occ(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list")) { + dl_arg_inc(dl); + return cmd_sb_occ_show(dl); + } else if (dl_argv_match(dl, "snapshot")) { + dl_arg_inc(dl); + return cmd_sb_occ_snapshot(dl); + } else if (dl_argv_match(dl, "clearmax")) { + dl_arg_inc(dl); + return cmd_sb_occ_clearmax(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_sb(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_sb_show(dl); + } else if (dl_argv_match(dl, "pool")) { + dl_arg_inc(dl); + return cmd_sb_pool(dl); + } else if (dl_argv_match(dl, "port")) { + dl_arg_inc(dl); + return cmd_sb_port(dl); + } else if (dl_argv_match(dl, "tc")) { + dl_arg_inc(dl); + return cmd_sb_tc(dl); + } else if (dl_argv_match(dl, "occupancy")) { + dl_arg_inc(dl); + return cmd_sb_occ(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static const char *cmd_name(uint8_t cmd) +{ + switch (cmd) { + case DEVLINK_CMD_UNSPEC: return "unspec"; + case DEVLINK_CMD_GET: return "get"; + case DEVLINK_CMD_SET: return "set"; + case DEVLINK_CMD_NEW: return "new"; + case DEVLINK_CMD_DEL: return "del"; + case DEVLINK_CMD_PORT_GET: return "get"; + case DEVLINK_CMD_PORT_SET: return "set"; + case DEVLINK_CMD_PORT_NEW: return "net"; + case DEVLINK_CMD_PORT_DEL: return "del"; + default: return ""; + } +} + +static const char *cmd_obj(uint8_t cmd) +{ + switch (cmd) { + case DEVLINK_CMD_UNSPEC: return "unspec"; + case DEVLINK_CMD_GET: + case DEVLINK_CMD_SET: + case DEVLINK_CMD_NEW: + case DEVLINK_CMD_DEL: + return "dev"; + case DEVLINK_CMD_PORT_GET: + case DEVLINK_CMD_PORT_SET: + case DEVLINK_CMD_PORT_NEW: + case DEVLINK_CMD_PORT_DEL: + return "port"; + default: return ""; + } +} + +static void pr_out_mon_header(uint8_t cmd) +{ + pr_out("[%s,%s] ", cmd_obj(cmd), cmd_name(cmd)); +} + +static bool cmd_filter_check(struct dl *dl, uint8_t cmd) +{ + const char *obj = cmd_obj(cmd); + unsigned int index = 0; + const char *cur_obj; + + if (dl_no_arg(dl)) + return true; + while ((cur_obj = dl_argv_index(dl, index++))) { + if (strcmp(cur_obj, obj) == 0 || strcmp(cur_obj, "all") == 0) + return true; + } + return false; +} + +static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + uint8_t cmd = genl->cmd; + + if (!cmd_filter_check(dl, cmd)) + return MNL_CB_OK; + + switch (cmd) { + case DEVLINK_CMD_GET: /* fall through */ + case DEVLINK_CMD_SET: /* fall through */ + case DEVLINK_CMD_NEW: /* fall through */ + case DEVLINK_CMD_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_dev(dl, tb); + break; + case DEVLINK_CMD_PORT_GET: /* fall through */ + case DEVLINK_CMD_PORT_SET: /* fall through */ + case DEVLINK_CMD_PORT_NEW: /* fall through */ + case DEVLINK_CMD_PORT_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_port(dl, tb); + break; + } + return MNL_CB_OK; +} + +static int cmd_mon_show(struct dl *dl) +{ + int err; + unsigned int index = 0; + const char *cur_obj; + + while ((cur_obj = dl_argv_index(dl, index++))) { + if (strcmp(cur_obj, "all") != 0 && + strcmp(cur_obj, "dev") != 0 && + strcmp(cur_obj, "port") != 0) { + pr_err("Unknown object \"%s\"\n", cur_obj); + return -EINVAL; + } + } + err = _mnlg_socket_group_add(dl->nlg, DEVLINK_GENL_MCGRP_CONFIG_NAME); + if (err) + return err; + err = _mnlg_socket_recv_run(dl->nlg, cmd_mon_show_cb, dl); + if (err) + return err; + return 0; +} + +static void cmd_mon_help(void) +{ + pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n" + "where OBJECT-LIST := { dev | port }\n"); +} + +static int cmd_mon(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_mon_help(); + return 0; + } else if (dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_mon_show(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void help(void) +{ + pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n" + "where OBJECT := { dev | port | sb | monitor }\n" + " OPTIONS := { -V[ersion] | -n[no-nice-names] }\n"); +} + +static int dl_cmd(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + help(); + return 0; + } else if (dl_argv_match(dl, "dev")) { + dl_arg_inc(dl); + return cmd_dev(dl); + } else if (dl_argv_match(dl, "port")) { + dl_arg_inc(dl); + return cmd_port(dl); + } else if (dl_argv_match(dl, "sb")) { + dl_arg_inc(dl); + return cmd_sb(dl); + } else if (dl_argv_match(dl, "monitor")) { + dl_arg_inc(dl); + return cmd_mon(dl); + } + pr_err("Object \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int dl_init(struct dl *dl, int argc, char **argv) +{ + int err; + + dl->argc = argc; + dl->argv = argv; + + dl->nlg = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION); + if (!dl->nlg) { + pr_err("Failed to connect to devlink Netlink\n"); + return -errno; + } + + err = ifname_map_init(dl); + if (err) { + pr_err("Failed to create index map\n"); + goto err_ifname_map_create; + } + if (dl->json_output) { + dl->jw = jsonw_new(stdout); + if (!dl->jw) { + pr_err("Failed to create JSON writer\n"); + goto err_json_new; + } + jsonw_pretty(dl->jw, dl->pretty_output); + } + return 0; + +err_json_new: + ifname_map_fini(dl); +err_ifname_map_create: + mnlg_socket_close(dl->nlg); + return err; +} + +static void dl_fini(struct dl *dl) +{ + if (dl->json_output) + jsonw_destroy(&dl->jw); + ifname_map_fini(dl); + mnlg_socket_close(dl->nlg); +} + +static struct dl *dl_alloc(void) +{ + struct dl *dl; + + dl = calloc(1, sizeof(*dl)); + if (!dl) + return NULL; + return dl; +} + +static void dl_free(struct dl *dl) +{ + free(dl); +} + +int main(int argc, char **argv) +{ + static const struct option long_options[] = { + { "Version", no_argument, NULL, 'V' }, + { "no-nice-names", no_argument, NULL, 'n' }, + { "json", no_argument, NULL, 'j' }, + { "pretty", no_argument, NULL, 'p' }, + { NULL, 0, NULL, 0 } + }; + struct dl *dl; + int opt; + int err; + int ret; + + dl = dl_alloc(); + if (!dl) { + pr_err("Failed to allocate memory for devlink\n"); + return EXIT_FAILURE; + } + + while ((opt = getopt_long(argc, argv, "Vnjp", + long_options, NULL)) >= 0) { + + switch (opt) { + case 'V': + printf("devlink utility, iproute2-ss%s\n", SNAPSHOT); + return EXIT_SUCCESS; + case 'n': + dl->no_nice_names = true; + break; + case 'j': + dl->json_output = true; + break; + case 'p': + dl->pretty_output = true; + break; + default: + pr_err("Unknown option.\n"); + help(); + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + err = dl_init(dl, argc, argv); + if (err) { + ret = EXIT_FAILURE; + goto dl_free; + } + + err = dl_cmd(dl); + if (err) { + ret = EXIT_FAILURE; + goto dl_fini; + } + + ret = EXIT_SUCCESS; + +dl_fini: + dl_fini(dl); +dl_free: + dl_free(dl); + + return ret; +} diff -Nru iproute2-4.3.0/devlink/.gitignore iproute2-4.9.0/devlink/.gitignore --- iproute2-4.3.0/devlink/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/devlink/.gitignore 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1 @@ +devlink diff -Nru iproute2-4.3.0/devlink/Makefile iproute2-4.9.0/devlink/Makefile --- iproute2-4.3.0/devlink/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/devlink/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,21 @@ +include ../Config +ifeq ($(HAVE_MNL),y) + +DEVLINKOBJ = devlink.o mnlg.o +TARGETS=devlink + +CFLAGS += $(shell $(PKG_CONFIG) libmnl --cflags) +LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) + +endif + +all: $(TARGETS) $(LIBS) + +devlink: $(DEVLINKOBJ) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +install: all + install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(DEVLINKOBJ) $(TARGETS) diff -Nru iproute2-4.3.0/devlink/mnlg.c iproute2-4.9.0/devlink/mnlg.c --- iproute2-4.3.0/devlink/mnlg.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/devlink/mnlg.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,274 @@ +/* + * mnlg.c Generic Netlink helpers for libmnl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Jiri Pirko + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mnlg.h" + +struct mnlg_socket { + struct mnl_socket *nl; + char *buf; + uint32_t id; + uint8_t version; + unsigned int seq; + unsigned int portid; +}; + +static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags, uint32_t id, + uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + + nlh = mnl_nlmsg_put_header(nlg->buf); + nlh->nlmsg_type = id; + nlh->nlmsg_flags = flags; + nlg->seq = time(NULL); + nlh->nlmsg_seq = nlg->seq; + + genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd = cmd; + genl->version = version; + + return nlh; +} + +struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags) +{ + return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version); +} + +int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh) +{ + return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len); +} + +int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data) +{ + int err; + + do { + err = mnl_socket_recvfrom(nlg->nl, nlg->buf, + MNL_SOCKET_BUFFER_SIZE); + if (err <= 0) + break; + err = mnl_cb_run(nlg->buf, err, nlg->seq, nlg->portid, + data_cb, data); + } while (err > 0); + + return err; +} + +struct group_info { + bool found; + uint32_t id; + const char *name; +}; + +static int parse_mc_grps_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case CTRL_ATTR_MCAST_GRP_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case CTRL_ATTR_MCAST_GRP_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_genl_mc_grps(struct nlattr *nested, + struct group_info *group_info) +{ + struct nlattr *pos; + const char *name; + + mnl_attr_for_each_nested(pos, nested) { + struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {}; + + mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb); + if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || + !tb[CTRL_ATTR_MCAST_GRP_ID]) + continue; + + name = mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]); + if (strcmp(name, group_info->name) != 0) + continue; + + group_info->id = mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); + group_info->found = true; + } +} + +static int get_group_id_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + if (type == CTRL_ATTR_MCAST_GROUPS && + mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + tb[type] = attr; + return MNL_CB_OK; +} + +static int get_group_id_cb(const struct nlmsghdr *nlh, void *data) +{ + struct group_info *group_info = data; + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), get_group_id_attr_cb, tb); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return MNL_CB_ERROR; + parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS], group_info); + return MNL_CB_OK; +} + +int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name) +{ + struct nlmsghdr *nlh; + struct group_info group_info; + int err; + + nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, + NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); + mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, nlg->id); + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) + return err; + + group_info.found = false; + group_info.name = group_name; + err = mnlg_socket_recv_run(nlg, get_group_id_cb, &group_info); + if (err < 0) + return err; + + if (!group_info.found) { + errno = ENOENT; + return -1; + } + + err = mnl_socket_setsockopt(nlg->nl, NETLINK_ADD_MEMBERSHIP, + &group_info.id, sizeof(group_info.id)); + if (err < 0) + return err; + + return 0; +} + +static int get_family_id_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + if (type == CTRL_ATTR_FAMILY_ID && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + tb[type] = attr; + return MNL_CB_OK; +} + +static int get_family_id_cb(const struct nlmsghdr *nlh, void *data) +{ + uint32_t *p_id = data; + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb); + if (!tb[CTRL_ATTR_FAMILY_ID]) + return MNL_CB_ERROR; + *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + return MNL_CB_OK; +} + +struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version) +{ + struct mnlg_socket *nlg; + struct nlmsghdr *nlh; + int err; + + nlg = malloc(sizeof(*nlg)); + if (!nlg) + return NULL; + + nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE); + if (!nlg->buf) + goto err_buf_alloc; + + nlg->nl = mnl_socket_open(NETLINK_GENERIC); + if (!nlg->nl) + goto err_mnl_socket_open; + + err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID); + if (err < 0) + goto err_mnl_socket_bind; + + nlg->portid = mnl_socket_get_portid(nlg->nl); + + nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, + NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name); + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) + goto err_mnlg_socket_send; + + err = mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id); + if (err < 0) + goto err_mnlg_socket_recv_run; + + nlg->version = version; + return nlg; + +err_mnlg_socket_recv_run: +err_mnlg_socket_send: +err_mnl_socket_bind: + mnl_socket_close(nlg->nl); +err_mnl_socket_open: + free(nlg->buf); +err_buf_alloc: + free(nlg); + return NULL; +} + +void mnlg_socket_close(struct mnlg_socket *nlg) +{ + mnl_socket_close(nlg->nl); + free(nlg->buf); + free(nlg); +} diff -Nru iproute2-4.3.0/devlink/mnlg.h iproute2-4.9.0/devlink/mnlg.h --- iproute2-4.3.0/devlink/mnlg.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/devlink/mnlg.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,27 @@ +/* + * mnlg.h Generic Netlink helpers for libmnl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Jiri Pirko + */ + +#ifndef _MNLG_H_ +#define _MNLG_H_ + +#include + +struct mnlg_socket; + +struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags); +int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh); +int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data); +int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name); +struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version); +void mnlg_socket_close(struct mnlg_socket *nlg); + +#endif /* _MNLG_H_ */ diff -Nru iproute2-4.3.0/doc/ip-cref.tex iproute2-4.9.0/doc/ip-cref.tex --- iproute2-4.3.0/doc/ip-cref.tex 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/doc/ip-cref.tex 2016-12-12 23:07:42.000000000 +0000 @@ -2049,9 +2049,6 @@ The \verb|local| table is a special routing table containing high priority control routes for local and broadcast addresses. -Rule 0 is special. It cannot be deleted or overridden. - - \item Priority: 32766, Selector: match anything, Action: lookup routing table \verb|main| (ID 254). The \verb|main| table is the normal routing table containing all non-policy diff -Nru iproute2-4.3.0/doc/Makefile iproute2-4.9.0/doc/Makefile --- iproute2-4.3.0/doc/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/doc/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -1,4 +1,4 @@ -PSFILES=ip-cref.ps ip-tunnels.ps api-ip6-flowlabels.ps ss.ps nstat.ps arpd.ps rtstat.ps +PSFILES=ip-cref.ps ip-tunnels.ps api-ip6-flowlabels.ps ss.ps nstat.ps arpd.ps rtstat.ps tc-filters.ps # tc-cref.ps # api-rtnl.tex api-pmtudisc.tex api-news.tex # iki-netdev.ps iki-neighdst.ps diff -Nru iproute2-4.3.0/doc/tc-filters.tex iproute2-4.9.0/doc/tc-filters.tex --- iproute2-4.3.0/doc/tc-filters.tex 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/doc/tc-filters.tex 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,514 @@ +\documentclass[12pt,twoside]{article} + +\usepackage[hidelinks]{hyperref} % \url +\usepackage{booktabs} % nicer tabulars +\usepackage{fancyvrb} +\usepackage{fullpage} +\usepackage{float} + +\newcommand{\iface}{\textit} +\newcommand{\cmd}{\texttt} +\newcommand{\man}{\textit} +\newcommand{\qdisc}{\texttt} +\newcommand{\filter}{\texttt} + +\begin{document} +\title{QoS in Linux with TC and Filters} +\author{Phil Sutter (phil@nwl.cc)} +\date{January 2016} +\maketitle + +Standard practice when transmitting packets over a medium which may block (due +to congestion, e.g.) is to use a queue which temporarily holds these packets. In +Linux, this queueing approach is where QoS happens: A Queueing Discipline +(qdisc) holds multiple packet queues with different priorities for dequeueing to +the network driver. The classification (i.e. deciding which queue a packet +should go into) is typically done based on Type Of Service (IPv4) or Traffic +Class (IPv6) header fields but depending on qdisc implementation, might be +controlled by the user as well. + +Qdiscs come in two flavors, classful or classless. While classless qdiscs are +not as flexible as classful ones, they also require much less customizing. Often +it is enough to just attach them to an interface, without exact knowledge of +what is done internally. Classful qdiscs are the exact opposite: flexible in +application, they are often not even usable without insightful configuration. + +As the name implies, classful qdiscs provide configurable classes to sort +traffic into. In it's basic form, this is not much different than, say, the +classless \qdisc{pfifo\_fast} which holds three queues and classifies per +packet upon priority field. Though typically classes go beyond that by +supporting nesting and additional characteristics like e.g. maximum traffic +rate or quantum. + +When it comes to controlling the classification process, filters come into play. +They attach to the parent of a set of classes (i.e. either the qdisc itself or +a parent class) and specify how a packet (or it's associated flow) has to look +like in order to suit a given class. To overcome this simplification, it is +possible to attach multiple filters to the same parent, which then consults each +of them in row until the first one accepts the packet. + +Before getting into detail about what filters there are and how to use them, a +simple setup of a qdisc with classes is necessary: +\begin{figure}[H] +\begin{Verbatim} + .-------------------------------------------------------. + | | + | HTB | + | | + | .----------------------------------------------------.| + | | || + | | Class 1:1 || + | | || + | | .---------------..---------------..---------------.|| + | | | || || ||| + | | | Class 1:10 || Class 1:20 || Class 1:30 ||| + | | | || || ||| + | | | .------------.|| .------------.|| .------------.||| + | | | | ||| | ||| | |||| + | | | | fq_codel ||| | fq_codel ||| | fq_codel |||| + | | | | ||| | ||| | |||| + | | | '------------'|| '------------'|| '------------'||| + | | '---------------''---------------''---------------'|| + | '----------------------------------------------------'| + '-------------------------------------------------------' +\end{Verbatim} +\end{figure} +\noindent +The following commands establish the basic setup shown: +\begin{Verbatim} +(1) # tc qdisc replace dev eth0 root handle 1: htb default 30 +(2) # tc class add dev eth0 parent 1: classid 1:1 htb rate 95mbit +(3) # alias tclass='tc class add dev eth0 parent 1:1' +(4) # tclass classid 1:10 htb rate 1mbit ceil 20mbit prio 1 +(4) # tclass classid 1:20 htb rate 90mbit ceil 95mbit prio 2 +(4) # tclass classid 1:30 htb rate 1mbit ceil 95mbit prio 3 +(5) # tc qdisc add dev eth0 parent 1:10 fq_codel +(5) # tc qdisc add dev eth0 parent 1:20 fq_codel +(5) # tc qdisc add dev eth0 parent 1:30 fq_codel +\end{Verbatim} +A little explanation for the unfamiliar reader: +\begin{enumerate} +\item Replace the root qdisc of \iface{eth0} by an instance of \qdisc{HTB}. + Specifying the handle is necessary so it can be referenced in consecutive + calls to \cmd{tc}. The default class for unclassified traffic is set to + 30. +\item Create a single top-level class with handle 1:1 which limits the total + bandwidth allowed to 95mbit/s. It is assumed that \iface{eth0} is a 100mbit/s link, + staying a little below that helps to keep the main point of enqueueing in + the qdisc layer instead of the interface hardware queue or at another + bottleneck in the network. +\item Define an alias for the common part of the remaining three calls in order + to improve readability. This means all remaining classes are attached to the + common parent class from (2). +\item Create three child classes for different uses: Class 1:10 has highest + priority but is tightly limited in bandwidth - fine for interactive + connections. Class 1:20 has mid priority and high guaranteed bandwidth, for + high priority bulk traffic. Finally, there's the default class 1:30 with + lowest priority, low guaranteed bandwidth and the ability to use the full + link in case it's unused otherwise. This should be fine for uninteresting + traffic not explicitly taken care of. +\item Attach a leaf qdisc to each of the child classes created in (4). Since + \qdisc{HTB} by default attaches \qdisc{pfifo} as leaf qdisc, this step is optional. Still, + the fairness between different flows provided by the classless \qdisc{fq\_codel} is + worth the effort. +\end{enumerate} +More information about the qdiscs and fine-tuning parameters can be found in +\man{tc-htb(8)} and \man{tc-fq\_codel(8)}. + +Without any additional setup done, now all traffic leaving \iface{eth0} is shaped to +95mbit/s and directed through class 1:30. This can be verified by looking at the +\texttt{Sent} field of the class statistics printed via \cmd{tc -s class show dev eth0}: +Only the root class 1:1 and it's child 1:30 should show any traffic. + + +\section*{Finally time to start filtering!} + +Let's begin with a simple one, i.e. reestablishing what \qdisc{pfifo\_fast} did +automatically based on TOS/Priority field. Linux internally translates the +header field into the priority field of struct skbuff, which +\qdisc{pfifo\_fast} uses for +classification. \man{tc-prio(8)} contains a table listing the priority (and +ultimately, \qdisc{pfifo\_fast} queue index) each TOS value is being translated into. +Here is a shorter version: +\begin{center} +\begin{tabular}{lll} +TOS Values & Linux Priority (Number) & Queue Index \\ +\midrule +0x0 - 0x6 & Best Effort (0) & 1 \\ +0x8 - 0xe & Bulk (2) & 2 \\ +0x10 - 0x16 & Interactive (6) & 0 \\ +0x18 - 0x1e & Interactive Bulk (4) & 1 \\ +\end{tabular} +\end{center} +Using the \filter{basic} filter, it is possible to match packets based on that skbuff +field, which has the added benefit of being IP version agnostic. Since the +\qdisc{HTB} setup above defaults to class ID 1:30, the Bulk priority can be +ignored. The \filter{basic} filter allows to combine matches, therefore we get along +with only two filters: +\begin{Verbatim} +# tc filter add dev eth0 parent 1: basic \ + match 'meta(priority eq 6)' classid 1:10 +# tc filter add dev eth0 parent 1: basic \ + match 'meta(priority eq 0)' \ + or 'meta(priority eq 4)' classid 1:20 +\end{Verbatim} +A detailed description of the \filter{basic} filter and the ematch syntax it uses can be +found in \man{tc-basic(8)} and \man{tc-ematch(8)}. + +Obviously, this first example cries for optimization. A simple one would be to +just change the default class from 1:30 to 1:20, so filters are only needed for +Bulk and Interactive priorities: +\begin{Verbatim} +# tc filter add dev eth0 parent 1: basic \ + match 'meta(priority eq 6)' classid 1:10 +# tc filter add dev eth0 parent 1: basic \ + match 'meta(priority eq 2)' classid 1:20 +\end{Verbatim} +Given that class IDs are random, choosing them wisely allows for a direct +mapping. So first, recreate the qdisc and classes configuration: +\begin{Verbatim} +# tc qdisc replace dev eth0 root handle 1: htb default 10 +# tc class add dev eth0 parent 1: classid 1:1 htb rate 95mbit +# alias tclass='tc class add dev eth0 parent 1:1' +# tclass classid 1:16 htb rate 1mbit ceil 20mbit prio 1 +# tclass classid 1:10 htb rate 90mbit ceil 95mbit prio 2 +# tclass classid 1:12 htb rate 1mbit ceil 95mbit prio 3 +# tc qdisc add dev eth0 parent 1:16 fq_codel +# tc qdisc add dev eth0 parent 1:10 fq_codel +# tc qdisc add dev eth0 parent 1:12 fq_codel +\end{Verbatim} +This is basically identical to above, but with changed leaf class IDs and the +second priority class being the default. Using the \filter{flow} filter with it's \texttt{map} +functionality, a single filter command is enough: +\begin{Verbatim} +# tc filter add dev eth0 parent 1: handle 0x1337 flow \ + map key priority baseclass 1:10 +\end{Verbatim} +The \filter{flow} filter now uses the priority value to construct a destination class ID +by adding it to the value of \texttt{baseclass}. While this works for priority values of +0, 2 and 6, it will result in non-existent class ID 1:14 for Interactive Bulk +traffic. In that case, the \qdisc{HTB} default applies so that traffic goes into class +ID 1:10 just as intended. Please note that specifying a handle is a mandatory +requirement by the \filter{flow} filter, although I didn't see where one would use that +later. For more information about \filter{flow}, see \man{tc-flow(8)}. + +While \filter{flow} and \filter{basic} filters are relatively easy to apply and understand, they +are as well quite limited to their intended purpose. A more flexible option is +the \filter{u32} filter, which allows to match on arbitrary parts of the packet data - +yet only on that, not any meta data associated to it by the kernel (with the +exception of firewall mark value). So in order to continue this little +exercise with \filter{u32}, we have to base classification directly upon the actual TOS +value. An intuitive attempt might look like this: +\begin{Verbatim} +# alias tcfilter='tc filter add dev eth0 parent 1:' +# tcfilter u32 match ip dsfield 0x10 0x1e classid 1:16 +# tcfilter u32 match ip dsfield 0x12 0x1e classid 1:16 +# tcfilter u32 match ip dsfield 0x14 0x1e classid 1:16 +# tcfilter u32 match ip dsfield 0x16 0x1e classid 1:16 +# tcfilter u32 match ip dsfield 0x8 0x1e classid 1:12 +# tcfilter u32 match ip dsfield 0xa 0x1e classid 1:12 +# tcfilter u32 match ip dsfield 0xc 0x1e classid 1:12 +# tcfilter u32 match ip dsfield 0xe 0x1e classid 1:12 +\end{Verbatim} +The obvious drawback here is the amount of filters needed. And without the +default class, eight more filters would be necessary. This also has performance +implications: A packet with TOS value 0xe will be checked eight times in total +in order to determine it's destination class. While there's not much to be done +about the number of filters, at least the performance problem can be eliminated +by using \filter{u32}'s hash table support: +\begin{Verbatim} +# tc filter add dev eth0 parent 1: prio 99 handle 1: u32 divisor 16 +\end{Verbatim} +This creates a hash table with 16 buckets. The table size is arbitrary, but not +random: Since the first bit of the TOS field is not interesting, it can be +ignored and therefore the range of values to consider is just [0;15], i.e. a +number of 16 different values. The next step is to populate the hash table: +\begin{Verbatim} +# alias tcfilter='tc filter add dev eth0 parent 1: prio 99' +# tcfilter u32 match u8 0 0 ht 1:0: classid 1:16 +# tcfilter u32 match u8 0 0 ht 1:1: classid 1:16 +# tcfilter u32 match u8 0 0 ht 1:2: classid 1:16 +# tcfilter u32 match u8 0 0 ht 1:3: classid 1:16 +# tcfilter u32 match u8 0 0 ht 1:4: classid 1:12 +# tcfilter u32 match u8 0 0 ht 1:5: classid 1:12 +# tcfilter u32 match u8 0 0 ht 1:6: classid 1:12 +# tcfilter u32 match u8 0 0 ht 1:7: classid 1:12 +# tcfilter u32 match u8 0 0 ht 1:8: classid 1:16 +# tcfilter u32 match u8 0 0 ht 1:9: classid 1:16 +# tcfilter u32 match u8 0 0 ht 1:a: classid 1:16 +# tcfilter u32 match u8 0 0 ht 1:b: classid 1:16 +# tcfilter u32 match u8 0 0 ht 1:c: classid 1:10 +# tcfilter u32 match u8 0 0 ht 1:d: classid 1:10 +# tcfilter u32 match u8 0 0 ht 1:e: classid 1:10 +# tcfilter u32 match u8 0 0 ht 1:f: classid 1:10 +\end{Verbatim} +The parameter \texttt{ht} denotes the hash table and bucket the filter should be added +to. Since the first TOS bit is ignored, it's value has to be divided by two in +order to get to the bucket it maps to. E.g. a TOS value of 0x10 will therefore +map to bucket 0x8. For the sake of completeness, all possible values are mapped +and therefore a configurable default class is not required. Note that the used +match expression is not necessary, but mandatory. Therefore anything that +matches any packet will suffice. Finally, a filter which links to the defined +hash table is needed: +\begin{Verbatim} +# tc filter add dev eth0 parent 1: prio 1 protocol ip u32 \ + link 1: hashkey mask 0x001e0000 match u8 0 0 +\end{Verbatim} +Here again, the actual match statement is not necessary, but syntactically +required. All the magic lies within the \texttt{hashkey} parameter, which defines which +part of the packet should be used directly as hash key. Here's a drawing of the +first four bytes of the IPv4 header, with the area selected by \texttt{hashkey mask} +highlighted: +\begin{figure}[H] +\begin{Verbatim} + 0 1 2 3 + .-----------------------------------------------------------------. + | | | ######## | | | + | Version| IHL | #DSCP### | ECN| Total Length | + | | | ######## | | | + '-----------------------------------------------------------------' +\end{Verbatim} +\end{figure} +\noindent +This may look confusing at first, but keep in mind that bit- as well as +byte-ordering here is LSB while the mask value is written in MSB we humans use. +Therefore reading the mask is done like so, starting from left: +\begin{enumerate} +\item Skip the first byte (which contains Version and IHL fields). +\item Skip the lowest bit of the second byte (0x1e is even). +\item Mark the four following bits (0x1e is 11110 in binary). +\item Skip the remaining three bits of the second byte as well as the remaining two + bytes. +\end{enumerate} +Before doing the lookup, the kernel right-shifts the masked value by the amount +of zero-bits in \texttt{mask}, which implicitly also does the division by two which the +hash table depends on. With this setup, every packet has to pass exactly two +filters to be classified. Note that this filter is limited to IPv4 packets: Due +to the related Traffic Class field being at a different offset in the packet, it +would not work for IPv6. To use the same setup for IPv6 as well, a second +entry-level filter is necessary: +\begin{Verbatim} +# tc filter add dev eth0 parent 1: prio 2 protocol ipv6 u32 \ + link 1: hashkey mask 0x01e00000 match u8 0 0 +\end{Verbatim} +For illustration purposes, here again is a drawing of the first four bytes of +the IPv6 header, again with masked area highlighted: +\begin{figure}[H] +\begin{Verbatim} + 0 1 2 3 + .-----------------------------------------------------------------. + | | ######## | | + | Version| #Traffic Class| Flow Label | + | | ######## | | + '-----------------------------------------------------------------' +\end{Verbatim} +\end{figure} +\noindent +Reading the mask value is analogous to IPv4 with the added complexity that +Traffic Class spans over two bytes. Yet, for comparison there's a simple trick: +IPv6 has the interesting field shifted by four bits to the left, and the new +mask's value is shifted by the same amount. For further information about +\filter{u32} and what can be done with it, consult it's man page +\man{tc-u32(8)}. + +Of course, the kernel provides many more filters than just \filter{basic}, +\filter{flow} and \filter{u32} which have been presented above. As of now, the +remaining ones are: +\begin{description} +\item[bpf] + Filtering using Berkeley Packet Filter programs. The program's return + code determines the packet's destination class ID. + +\item[cgroup] + Filter packets based on control groups. This is only useful for packets + originating from the local host, as control groups only exist in that + scope. + +\item[flower] + An extended variant of the flow filter. + +\item[fw] + Matches on firewall mark values previously assigned to the packet by + netfilter (or a filter action, see below for details). This allows to + export the classification algorithm into netfilter, which is very + convenient if appropriate rules exist on the same system in there + already. + +\item[route] + Filter packets based on matching routing table entry. Basically + equivalent to the \texttt{fw} filter above, to make use of an already existing + extensive routing table setup. + +\item[rsvp, rsvp6] + Implementation of the Resource Reservation Protocol in Linux, to react + upon requests sent by an RSVP daemon. + +\item[tcindex] + Match packets based on tcindex value, which is usually set by the dsmark + qdisc. This is part of an approach to support Differentiated Services in + Linux, which is another topic on it's own. +\end{description} + + +\section*{Filter Actions} + +The tc filter framework provides the infrastructure to another extensible set of +tools as well, namely tc actions. As the name suggests, they allow to do things +with packets (or associated data). (The list of) Actions are part of a given +filter. If it matches, each action it contains is executed in order before +returning the classification result. Since the action has direct access to the +latter, it is in theory possible for an action to react upon or even change the +filtering result - as long as the packet matched, of course. Yet none of the +currently in-tree actions make use of this. + +The Generic Actions framework originally evolved out of the filters' ability to +police traffic to a given maximum bandwidth. One common use case for that is to +limit ingress traffic, dropping packets which exceed the threshold. A classic +setup example is like so: +\begin{Verbatim} +# tc qdisc add dev eth0 handle ffff: ingress +# tc filter add dev eth0 parent ffff: u32 \ + match u32 0 0 + police rate 1mbit burst 100k +\end{Verbatim} +The ingress qdisc is not a real one, but merely a point of reference for filters +to attach to which should get applied to incoming traffic. The \filter{u32} filter added +above matches on any packet and therefore limits the total incoming bandwidth to +1mbit/s, allowing bursts of up to 100kbytes. Using the new syntax, the filter +command changes slightly: +\begin{Verbatim} +# tc filter add dev eth0 parent ffff: u32 \ + match u32 0 0 \ + action police rate 1mbit burst 100k +\end{Verbatim} +The important detail is that this syntax allows to define multiple actions. +E.g. for testing purposes, it is possible to redirect exceeding traffic to the +loopback interface instead of dropping it: +\begin{Verbatim} +# tc filter add dev eth0 parent ffff: u32 \ + match u32 0 0 \ + action police rate 1mbit burst 100k conform-exceed pipe \ + action mirred egress redirect dev lo +\end{Verbatim} +The added parameter \texttt{conform-exceed pipe} tells the police action to allow for +further actions to handle the exceeding packet. + +Apart from \texttt{police} and \texttt{mirred} actions, there are a few more. Here's a full +list of the currently implemented ones: +\begin{description} +\item[bpf] + Apply a Berkeley Packet Filter program to the packet. + +\item[connmark] + Set the packet's firewall mark to that of it's connection. This works by + searching the conntrack table for a matching entry. If found, the mark + is restored. + +\item[csum] + Trigger recalculation of packet checksums. The supported protocols are: + IPv4, ICMP, IGMP, TCP, UDP and UDPLite. + +\item[ipt] + Pass the packet to an iptables target. This allows to use iptables + extensions directly instead of having to go the extra mile via setting + an arbitrary firewall mark and matching on that from within netfilter. + +\item[mirred] + Mirror or redirect packets. This is often combined with the ifb pseudo + device to share a common QoS setup between multiple interfaces or even + ingress traffic. + +\item[nat] + Perform stateless Native Address Translation. This is certainly not + complete and therefore inferior to NAT using iptables: Although the + kernel module decides between TCP, UDP and ICMP traffic, it does not + handle typical problematic protocols such as active FTP or SIP. + +\item[pedit] + Generic packet editing. This allows to alter arbitrary bytes of the + packet, either by specifying an offset into the packet or by naming a + packet header and field name to change. Currently, the latter is + implemented only for IPv4 yet. + +\item[police] + Apply a bandwidth rate limiting policy. Packets exceeding it are dropped + by default, but may optionally be handled differently. + +\item[simple] + This is rather an example than real action. All it does is print a + user-defined string together with a packet counter. Useful maybe for + debugging when filter statistics are not available or too complicated. + +\item[skbedit] + Edit associated packet data, supports changing queue mapping, priority + field and firewall mark value. + +\item[vlan] + Add/remove a VLAN header to/from the packet. This might serve as + alternative to using 802.1Q pseudo-interfaces in combination with + routing rules when e.g. packets for a given destination need to be + encapsulated. +\end{description} + + +\section*{Intermediate Functional Block} + +The Intermediate Functional Block (\texttt{ifb}) pseudo network interface acts as a QoS +concentrator for multiple different sources of traffic. Packets from or to other +interfaces have to be redirected to it using the \texttt{mirred} action in order to be +handled, regularly routed traffic will be dropped. This way, a single stack of +qdiscs, classes and filters can be shared between multiple interfaces. + +Here's a simple example to feed incoming traffic from multiple interfaces +through a Stochastic Fairness Queue (\qdisc{sfq}): +\begin{Verbatim} +(1) # modprobe ifb +(2) # ip link set ifb0 up +(3) # tc qdisc add dev ifb0 root sfq +\end{Verbatim} +The first step is to load the \texttt{ifb} kernel module (1). By default, this will +create two ifb devices: \iface{ifb0} and \iface{ifb1}. After setting +\iface{ifb0} up in (2), the root +qdisc is replaced by \qdisc{sfq} in (3). Finally, one can start redirecting ingress +traffic to \iface{ifb0}, e.g. from \iface{eth0}: +\begin{Verbatim} +# tc qdisc add dev eth0 handle ffff: ingress +# tc filter add dev eth0 parent ffff: u32 \ + match u32 0 0 \ + action mirred egress redirect dev ifb0 +\end{Verbatim} +The same can be done for other interfaces, just replacing \iface{eth0} in the two +commands above. One thing to keep in mind here is the asymmetrical routing this +creates within the host doing the QoS: Incoming packets enter the system via +\iface{ifb0}, while corresponding replies leave directly via \iface{eth0}. This can be observed +using \cmd{tcpdump} on \iface{ifb0}, which shows the input part of the traffic only. What's +more confusing is that \cmd{tcpdump} on \iface{eth0} shows both incoming and outgoing traffic, +but the redirection is still effective - a simple prove is setting +\iface{ifb0} down, +which will interrupt the communication. Obviously \cmd{tcpdump} catches the packets to +dump before they enter the ingress qdisc, which is why it sees them while the +kernel itself doesn't. + + +\section*{Conclusion} + +Once the steep learning curve has been mastered, the conglomerate of (classful) +qdiscs, filters and actions provides a highly sophisticated and flexible +infrastructure to perform QoS, which plays nicely along with routing and +firewalling setups. + + +\section*{Further Reading} + +A good starting point for novice users and experienced ones diving into unknown +areas is the extensive HOWTO at \url{http://lartc.org}. The iproute2 package ships +some examples (usually in /usr/share/doc/, depending on distribution) as well as +man pages for \cmd{tc} in general, qdiscs and filters. The latter have been added +just recently though, so if your distribution does not ship iproute2 version +4.3.0 yet, these are not in there. Apart from that, the internet is a spring of +HOWTOs and scripts people wrote - though these should be taken with a grain of +salt: The complexity of the matter often leads to copying others' solutions +without much validation, which allows for less optimal or even obsolete +implementations to survive much longer than desired. + +\end{document} diff -Nru iproute2-4.3.0/etc/iproute2/bpf_pinning iproute2-4.9.0/etc/iproute2/bpf_pinning --- iproute2-4.3.0/etc/iproute2/bpf_pinning 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/etc/iproute2/bpf_pinning 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,6 @@ +# +# subpath mappings from mount point for pinning +# +#3 tracing +#4 foo/bar +#5 tc/cls1 diff -Nru iproute2-4.3.0/etc/iproute2/rt_tables.d/README iproute2-4.9.0/etc/iproute2/rt_tables.d/README --- iproute2-4.3.0/etc/iproute2/rt_tables.d/README 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/etc/iproute2/rt_tables.d/README 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,3 @@ +Each file in this directory is an rt_tables configuration file. iproute2 +commands scan this directory processing all files that end in '.conf'. + diff -Nru iproute2-4.3.0/examples/bpf/bpf_cyclic.c iproute2-4.9.0/examples/bpf/bpf_cyclic.c --- iproute2-4.3.0/examples/bpf/bpf_cyclic.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/examples/bpf/bpf_cyclic.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,35 @@ +#include "../../include/bpf_api.h" + +/* Cyclic dependency example to test the kernel's runtime upper + * bound on loops. Also demonstrates on how to use direct-actions, + * loaded as: tc filter add [...] bpf da obj [...] + */ +#define JMP_MAP_ID 0xabccba + +struct bpf_elf_map __section_maps jmp_tc = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .id = JMP_MAP_ID, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, + .max_elem = 1, +}; + +__section_tail(JMP_MAP_ID, 0) +int cls_loop(struct __sk_buff *skb) +{ + printt("cb: %u\n", skb->cb[0]++); + tail_call(skb, &jmp_tc, 0); + + skb->tc_classid = TC_H_MAKE(1, 42); + return TC_ACT_OK; +} + +__section_cls_entry +int cls_entry(struct __sk_buff *skb) +{ + tail_call(skb, &jmp_tc, 0); + return TC_ACT_SHOT; +} + +BPF_LICENSE("GPL"); diff -Nru iproute2-4.3.0/examples/bpf/bpf_funcs.h iproute2-4.9.0/examples/bpf/bpf_funcs.h --- iproute2-4.3.0/examples/bpf/bpf_funcs.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/examples/bpf/bpf_funcs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -#ifndef __BPF_FUNCS__ -#define __BPF_FUNCS__ - -/* Misc macros. */ -#ifndef __maybe_unused -# define __maybe_unused __attribute__ ((__unused__)) -#endif - -#ifndef __section -# define __section(NAME) __attribute__((section(NAME), used)) -#endif - -#ifndef offsetof -# define offsetof __builtin_offsetof -#endif - -#ifndef htons -# define htons(x) __constant_htons((x)) -#endif - -#ifndef likely -# define likely(x) __builtin_expect(!!(x), 1) -#endif - -#ifndef unlikely -# define unlikely(x) __builtin_expect(!!(x), 0) -#endif - -/* The verifier will translate them to actual function calls. */ -static void *(*bpf_map_lookup_elem)(void *map, void *key) __maybe_unused = - (void *) BPF_FUNC_map_lookup_elem; - -static int (*bpf_map_update_elem)(void *map, void *key, void *value, - unsigned long long flags) __maybe_unused = - (void *) BPF_FUNC_map_update_elem; - -static int (*bpf_map_delete_elem)(void *map, void *key) __maybe_unused = - (void *) BPF_FUNC_map_delete_elem; - -static unsigned int (*get_smp_processor_id)(void) __maybe_unused = - (void *) BPF_FUNC_get_smp_processor_id; - -static unsigned int (*get_prandom_u32)(void) __maybe_unused = - (void *) BPF_FUNC_get_prandom_u32; - -/* LLVM built-in functions that an eBPF C program may use to emit - * BPF_LD_ABS and BPF_LD_IND instructions. - */ -unsigned long long load_byte(void *skb, unsigned long long off) - asm ("llvm.bpf.load.byte"); - -unsigned long long load_half(void *skb, unsigned long long off) - asm ("llvm.bpf.load.half"); - -unsigned long long load_word(void *skb, unsigned long long off) - asm ("llvm.bpf.load.word"); - -#endif /* __BPF_FUNCS__ */ diff -Nru iproute2-4.3.0/examples/bpf/bpf_graft.c iproute2-4.9.0/examples/bpf/bpf_graft.c --- iproute2-4.3.0/examples/bpf/bpf_graft.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/examples/bpf/bpf_graft.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,66 @@ +#include "../../include/bpf_api.h" + +/* This example demonstrates how classifier run-time behaviour + * can be altered with tail calls. We start out with an empty + * jmp_tc array, then add section aaa to the array slot 0, and + * later on atomically replace it with section bbb. Note that + * as shown in other examples, the tc loader can prepopulate + * tail called sections, here we start out with an empty one + * on purpose to show it can also be done this way. + * + * tc filter add dev foo parent ffff: bpf obj graft.o + * tc exec bpf dbg + * [...] + * Socket Thread-20229 [001] ..s. 138993.003923: : fallthrough + * -0 [001] ..s. 138993.202265: : fallthrough + * Socket Thread-20229 [001] ..s. 138994.004149: : fallthrough + * [...] + * + * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec aaa + * tc exec bpf dbg + * [...] + * Socket Thread-19818 [002] ..s. 139012.053587: : aaa + * -0 [002] ..s. 139012.172359: : aaa + * Socket Thread-19818 [001] ..s. 139012.173556: : aaa + * [...] + * + * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec bbb + * tc exec bpf dbg + * [...] + * Socket Thread-19818 [002] ..s. 139022.102967: : bbb + * -0 [002] ..s. 139022.155640: : bbb + * Socket Thread-19818 [001] ..s. 139022.156730: : bbb + * [...] + */ + +struct bpf_elf_map __section_maps jmp_tc = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_GLOBAL_NS, + .max_elem = 1, +}; + +__section("aaa") +int cls_aaa(struct __sk_buff *skb) +{ + printt("aaa\n"); + return TC_H_MAKE(1, 42); +} + +__section("bbb") +int cls_bbb(struct __sk_buff *skb) +{ + printt("bbb\n"); + return TC_H_MAKE(1, 43); +} + +__section_cls_entry +int cls_entry(struct __sk_buff *skb) +{ + tail_call(skb, &jmp_tc, 0); + printt("fallthrough\n"); + return BPF_H_DEFAULT; +} + +BPF_LICENSE("GPL"); diff -Nru iproute2-4.3.0/examples/bpf/bpf_prog.c iproute2-4.9.0/examples/bpf/bpf_prog.c --- iproute2-4.3.0/examples/bpf/bpf_prog.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/examples/bpf/bpf_prog.c 2016-12-12 23:07:42.000000000 +0000 @@ -168,8 +168,8 @@ /* Common, shared definitions with ebpf_agent.c. */ #include "bpf_shared.h" -/* Selection of BPF helper functions for our example. */ -#include "bpf_funcs.h" +/* BPF helper functions for our example. */ +#include "../../include/bpf_api.h" /* Could be defined here as well, or included from the header. */ #define TC_ACT_UNSPEC (-1) @@ -192,6 +192,7 @@ .size_key = sizeof(uint8_t), .size_value = sizeof(struct count_tuple), .max_elem = 256, + .flags = BPF_F_NO_PREALLOC, }; struct bpf_elf_map __section("maps") map_queue = { @@ -200,6 +201,7 @@ .size_key = sizeof(uint32_t), .size_value = sizeof(struct count_queue), .max_elem = 1024, + .flags = BPF_F_NO_PREALLOC, }; struct bpf_elf_map __section("maps") map_drops = { @@ -233,7 +235,7 @@ __u8 ip_proto; }; -static inline int flow_ports_offset(__u8 ip_proto) +static __inline__ int flow_ports_offset(__u8 ip_proto) { switch (ip_proto) { case IPPROTO_TCP: @@ -249,14 +251,14 @@ } } -static inline bool flow_is_frag(struct __sk_buff *skb, int nh_off) +static __inline__ bool flow_is_frag(struct __sk_buff *skb, int nh_off) { return !!(load_half(skb, nh_off + offsetof(struct iphdr, frag_off)) & (IP_MF | IP_OFFSET)); } -static inline int flow_parse_ipv4(struct __sk_buff *skb, int nh_off, - __u8 *ip_proto, struct flow_keys *flow) +static __inline__ int flow_parse_ipv4(struct __sk_buff *skb, int nh_off, + __u8 *ip_proto, struct flow_keys *flow) { __u8 ip_ver_len; @@ -279,7 +281,7 @@ return nh_off; } -static inline __u32 flow_addr_hash_ipv6(struct __sk_buff *skb, int off) +static __inline__ __u32 flow_addr_hash_ipv6(struct __sk_buff *skb, int off) { __u32 w0 = load_word(skb, off); __u32 w1 = load_word(skb, off + sizeof(w0)); @@ -289,8 +291,8 @@ return w0 ^ w1 ^ w2 ^ w3; } -static inline int flow_parse_ipv6(struct __sk_buff *skb, int nh_off, - __u8 *ip_proto, struct flow_keys *flow) +static __inline__ int flow_parse_ipv6(struct __sk_buff *skb, int nh_off, + __u8 *ip_proto, struct flow_keys *flow) { *ip_proto = load_byte(skb, nh_off + offsetof(struct ipv6hdr, nexthdr)); @@ -300,8 +302,8 @@ return nh_off + sizeof(struct ipv6hdr); } -static inline bool flow_dissector(struct __sk_buff *skb, - struct flow_keys *flow) +static __inline__ bool flow_dissector(struct __sk_buff *skb, + struct flow_keys *flow) { int poff, nh_off = BPF_LL_OFF + ETH_HLEN; __be16 proto = skb->protocol; @@ -381,16 +383,16 @@ return true; } -static inline void cls_update_proto_map(const struct __sk_buff *skb, - const struct flow_keys *flow) +static __inline__ void cls_update_proto_map(const struct __sk_buff *skb, + const struct flow_keys *flow) { uint8_t proto = flow->ip_proto; struct count_tuple *ct, _ct; - ct = bpf_map_lookup_elem(&map_proto, &proto); + ct = map_lookup_elem(&map_proto, &proto); if (likely(ct)) { - __sync_fetch_and_add(&ct->packets, 1); - __sync_fetch_and_add(&ct->bytes, skb->len); + lock_xadd(&ct->packets, 1); + lock_xadd(&ct->bytes, skb->len); return; } @@ -398,10 +400,10 @@ _ct.packets = 1; _ct.bytes = skb->len; - bpf_map_update_elem(&map_proto, &proto, &_ct, BPF_ANY); + map_update_elem(&map_proto, &proto, &_ct, BPF_ANY); } -static inline void cls_update_queue_map(const struct __sk_buff *skb) +static __inline__ void cls_update_queue_map(const struct __sk_buff *skb) { uint32_t queue = skb->queue_mapping; struct count_queue *cq, _cq; @@ -409,11 +411,11 @@ mismatch = skb->queue_mapping != get_smp_processor_id(); - cq = bpf_map_lookup_elem(&map_queue, &queue); + cq = map_lookup_elem(&map_queue, &queue); if (likely(cq)) { - __sync_fetch_and_add(&cq->total, 1); + lock_xadd(&cq->total, 1); if (mismatch) - __sync_fetch_and_add(&cq->mismatch, 1); + lock_xadd(&cq->mismatch, 1); return; } @@ -421,7 +423,7 @@ _cq.total = 1; _cq.mismatch = mismatch ? 1 : 0; - bpf_map_update_elem(&map_queue, &queue, &_cq, BPF_ANY); + map_update_elem(&map_queue, &queue, &_cq, BPF_ANY); } /* eBPF program definitions, placed in various sections, which can @@ -439,7 +441,8 @@ * It is however not required to have multiple programs sharing * a file. */ -__section("classifier") int cls_main(struct __sk_buff *skb) +__section("classifier") +int cls_main(struct __sk_buff *skb) { struct flow_keys flow; @@ -452,17 +455,18 @@ return flow.ip_proto; } -static inline void act_update_drop_map(void) +static __inline__ void act_update_drop_map(void) { uint32_t *count, cpu = get_smp_processor_id(); - count = bpf_map_lookup_elem(&map_drops, &cpu); + count = map_lookup_elem(&map_drops, &cpu); if (count) /* Only this cpu is accessing this element. */ (*count)++; } -__section("action-mark") int act_mark_main(struct __sk_buff *skb) +__section("action-mark") +int act_mark_main(struct __sk_buff *skb) { /* You could also mangle skb data here with the helper function * BPF_FUNC_skb_store_bytes, etc. Or, alternatively you could @@ -479,7 +483,8 @@ return TC_ACT_UNSPEC; } -__section("action-rand") int act_rand_main(struct __sk_buff *skb) +__section("action-rand") +int act_rand_main(struct __sk_buff *skb) { /* Sorry, we're near event horizon ... */ if ((get_prandom_u32() & 3) == 0) { @@ -493,4 +498,4 @@ /* Last but not least, the file contains a license. Some future helper * functions may only be available with a GPL license. */ -char __license[] __section("license") = "GPL"; +BPF_LICENSE("GPL"); diff -Nru iproute2-4.3.0/examples/bpf/bpf_shared.c iproute2-4.9.0/examples/bpf/bpf_shared.c --- iproute2-4.3.0/examples/bpf/bpf_shared.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/examples/bpf/bpf_shared.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,53 @@ +#include "../../include/bpf_api.h" + +/* Minimal, stand-alone toy map pinning example: + * + * clang -target bpf -O2 [...] -o bpf_shared.o -c bpf_shared.c + * tc filter add dev foo parent 1: bpf obj bpf_shared.o sec egress + * tc filter add dev foo parent ffff: bpf obj bpf_shared.o sec ingress + * + * Both classifier will share the very same map instance in this example, + * so map content can be accessed from ingress *and* egress side! + * + * This example has a pinning of PIN_OBJECT_NS, so it's private and + * thus shared among various program sections within the object. + * + * A setting of PIN_GLOBAL_NS would place it into a global namespace, + * so that it can be shared among different object files. A setting + * of PIN_NONE (= 0) means no sharing, so each tc invocation a new map + * instance is being created. + */ + +struct bpf_elf_map __section_maps map_sh = { + .type = BPF_MAP_TYPE_ARRAY, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, /* or PIN_GLOBAL_NS, or PIN_NONE */ + .max_elem = 1, +}; + +__section("egress") +int emain(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + lock_xadd(val, 1); + + return BPF_H_DEFAULT; +} + +__section("ingress") +int imain(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + printt("map val: %d\n", *val); + + return BPF_H_DEFAULT; +} + +BPF_LICENSE("GPL"); diff -Nru iproute2-4.3.0/examples/bpf/bpf_shared.h iproute2-4.9.0/examples/bpf/bpf_shared.h --- iproute2-4.3.0/examples/bpf/bpf_shared.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/examples/bpf/bpf_shared.h 2016-12-12 23:07:42.000000000 +0000 @@ -1,10 +1,6 @@ #ifndef __BPF_SHARED__ #define __BPF_SHARED__ -#include - -#include "../../include/bpf_elf.h" - enum { BPF_MAP_ID_PROTO, BPF_MAP_ID_QUEUE, @@ -14,7 +10,7 @@ }; struct count_tuple { - long packets; /* type long for __sync_fetch_and_add() */ + long packets; /* type long for lock_xadd() */ long bytes; }; diff -Nru iproute2-4.3.0/examples/bpf/bpf_tailcall.c iproute2-4.9.0/examples/bpf/bpf_tailcall.c --- iproute2-4.3.0/examples/bpf/bpf_tailcall.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/examples/bpf/bpf_tailcall.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,116 @@ +#include "../../include/bpf_api.h" + +#define ENTRY_INIT 3 +#define ENTRY_0 0 +#define ENTRY_1 1 +#define MAX_JMP_SIZE 2 + +#define FOO 42 +#define BAR 43 + +/* This example doesn't really do anything useful, but it's purpose is to + * demonstrate eBPF tail calls on a very simple example. + * + * cls_entry() is our classifier entry point, from there we jump based on + * skb->hash into cls_case1() or cls_case2(). They are both part of the + * program array jmp_tc. Indicated via __section_tail(), the tc loader + * populates the program arrays with the loaded file descriptors already. + * + * To demonstrate nested jumps, cls_case2() jumps within the same jmp_tc + * array to cls_case1(). And whenever we arrive at cls_case1(), we jump + * into cls_exit(), part of the jump array jmp_ex. + * + * Also, to show it's possible, all programs share map_sh and dump the value + * that the entry point incremented. The sections that are loaded into a + * program array can be atomically replaced during run-time, e.g. to change + * classifier behaviour. + */ + +struct bpf_elf_map __section_maps jmp_tc = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .id = FOO, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, + .max_elem = MAX_JMP_SIZE, +}; + +struct bpf_elf_map __section_maps jmp_ex = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .id = BAR, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, + .max_elem = 1, +}; + +struct bpf_elf_map __section_maps map_sh = { + .type = BPF_MAP_TYPE_ARRAY, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, + .max_elem = 1, +}; + +__section_tail(FOO, ENTRY_0) +int cls_case1(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + printt("case1: map-val: %d from:%u\n", *val, skb->cb[0]); + + skb->cb[0] = ENTRY_0; + tail_call(skb, &jmp_ex, ENTRY_0); + + return BPF_H_DEFAULT; +} + +__section_tail(FOO, ENTRY_1) +int cls_case2(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + printt("case2: map-val: %d from:%u\n", *val, skb->cb[0]); + + skb->cb[0] = ENTRY_1; + tail_call(skb, &jmp_tc, ENTRY_0); + + return BPF_H_DEFAULT; +} + +__section_tail(BAR, ENTRY_0) +int cls_exit(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + printt("exit: map-val: %d from:%u\n", *val, skb->cb[0]); + + /* Termination point. */ + return BPF_H_DEFAULT; +} + +__section_cls_entry +int cls_entry(struct __sk_buff *skb) +{ + int key = 0, *val; + + /* For transferring state, we can use skb->cb[0] ... skb->cb[4]. */ + val = map_lookup_elem(&map_sh, &key); + if (val) { + lock_xadd(val, 1); + + skb->cb[0] = ENTRY_INIT; + tail_call(skb, &jmp_tc, skb->hash & (MAX_JMP_SIZE - 1)); + } + + printt("fallthrough\n"); + return BPF_H_DEFAULT; +} + +BPF_LICENSE("GPL"); diff -Nru iproute2-4.3.0/examples/bpf/README iproute2-4.9.0/examples/bpf/README --- iproute2-4.3.0/examples/bpf/README 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/examples/bpf/README 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,13 @@ +eBPF toy code examples (running in kernel) to familiarize yourself +with syntax and features: + + - bpf_prog.c -> Classifier examples with using maps + - bpf_shared.c -> Ingress/egress map sharing example + - bpf_tailcall.c -> Using tail call chains + - bpf_cyclic.c -> Simple cycle as tail calls + - bpf_graft.c -> Demo on altering runtime behaviour + +User space code example: + + - bpf_agent.c -> Counterpart to bpf_prog.c for user + space to transfer/read out map data diff -Nru iproute2-4.3.0/genl/ctrl.c iproute2-4.9.0/genl/ctrl.c --- iproute2-4.3.0/genl/ctrl.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/genl/ctrl.c 2016-12-12 23:07:42.000000000 +0000 @@ -42,23 +42,19 @@ int genl_ctrl_resolve_family(const char *family) { struct rtnl_handle rth; - struct nlmsghdr *nlh; - struct genlmsghdr *ghdr; int ret = 0; struct { struct nlmsghdr n; + struct genlmsghdr g; char buf[4096]; - } req; - - memset(&req, 0, sizeof(req)); - - nlh = &req.n; - nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - nlh->nlmsg_type = GENL_ID_CTRL; - - ghdr = NLMSG_DATA(&req.n); - ghdr->cmd = CTRL_CMD_GETFAMILY; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN), + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .n.nlmsg_type = GENL_ID_CTRL, + .g.cmd = CTRL_CMD_GETFAMILY, + }; + struct nlmsghdr *nlh = &req.n; + struct genlmsghdr *ghdr = &req.g; if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) { fprintf(stderr, "Cannot open generic netlink socket\n"); @@ -74,7 +70,6 @@ { struct rtattr *tb[CTRL_ATTR_MAX + 1]; - struct genlmsghdr *ghdr = NLMSG_DATA(nlh); int len = nlh->nlmsg_len; struct rtattr *attrs; @@ -132,7 +127,7 @@ fprintf(fp, "\n"); } - + static int print_ctrl_cmds(FILE *fp, struct rtattr *arg, __u32 ctrl_ver) { struct rtattr *tb[CTRL_ATTR_OP_MAX + 1]; @@ -291,24 +286,19 @@ static int ctrl_list(int cmd, int argc, char **argv) { struct rtnl_handle rth; - struct nlmsghdr *nlh; - struct genlmsghdr *ghdr; int ret = -1; char d[GENL_NAMSIZ]; struct { struct nlmsghdr n; + struct genlmsghdr g; char buf[4096]; - } req; - - memset(&req, 0, sizeof(req)); - - nlh = &req.n; - nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - nlh->nlmsg_type = GENL_ID_CTRL; - - ghdr = NLMSG_DATA(&req.n); - ghdr->cmd = CTRL_CMD_GETFAMILY; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN), + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .n.nlmsg_type = GENL_ID_CTRL, + .g.cmd = CTRL_CMD_GETFAMILY, + }; + struct nlmsghdr *nlh = &req.n; if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) { fprintf(stderr, "Cannot open generic netlink socket\n"); diff -Nru iproute2-4.3.0/genl/genl.c iproute2-4.9.0/genl/genl.c --- iproute2-4.3.0/genl/genl.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/genl/genl.c 2016-12-12 23:07:42.000000000 +0000 @@ -54,7 +54,7 @@ return 0; } -static struct genl_util *get_genl_kind(char *str) +static struct genl_util *get_genl_kind(const char *str) { void *dlh; char buf[256]; @@ -86,9 +86,8 @@ return f; noexist: - f = malloc(sizeof(*f)); + f = calloc(1, sizeof(*f)); if (f) { - memset(f, 0, sizeof(*f)); strncpy(f->name, str, 15); f->parse_genlopt = parse_nofopt; f->print_genlopt = print_nofopt; diff -Nru iproute2-4.3.0/genl/Makefile iproute2-4.9.0/genl/Makefile --- iproute2-4.3.0/genl/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/genl/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -20,6 +20,7 @@ all: genl genl: $(GENLOBJ) $(LIBNETLINK) $(LIBUTIL) $(GENLLIB) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ install: all install -m 0755 genl $(DESTDIR)$(SBINDIR) diff -Nru iproute2-4.3.0/.gitignore iproute2-4.9.0/.gitignore --- iproute2-4.3.0/.gitignore 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/.gitignore 2016-12-12 23:07:42.000000000 +0000 @@ -10,6 +10,7 @@ # cscope cscope.* ncscope.* +tags TAGS # git files that we don't want to ignore even it they are dot-files @@ -44,3 +45,4 @@ doc/*.dvi doc/*.html doc/*.pdf +doc/*.out diff -Nru iproute2-4.3.0/include/bpf_api.h iproute2-4.9.0/include/bpf_api.h --- iproute2-4.3.0/include/bpf_api.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/bpf_api.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,227 @@ +#ifndef __BPF_API__ +#define __BPF_API__ + +/* Note: + * + * This file can be included into eBPF kernel programs. It contains + * a couple of useful helper functions, map/section ABI (bpf_elf.h), + * misc macros and some eBPF specific LLVM built-ins. + */ + +#include + +#include +#include +#include + +#include + +#include "bpf_elf.h" + +/** Misc macros. */ + +#ifndef __stringify +# define __stringify(X) #X +#endif + +#ifndef __maybe_unused +# define __maybe_unused __attribute__((__unused__)) +#endif + +#ifndef offsetof +# define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER) +#endif + +#ifndef likely +# define likely(X) __builtin_expect(!!(X), 1) +#endif + +#ifndef unlikely +# define unlikely(X) __builtin_expect(!!(X), 0) +#endif + +#ifndef htons +# define htons(X) __constant_htons((X)) +#endif + +#ifndef ntohs +# define ntohs(X) __constant_ntohs((X)) +#endif + +#ifndef htonl +# define htonl(X) __constant_htonl((X)) +#endif + +#ifndef ntohl +# define ntohl(X) __constant_ntohl((X)) +#endif + +#ifndef __inline__ +# define __inline__ __attribute__((always_inline)) +#endif + +/** Section helper macros. */ + +#ifndef __section +# define __section(NAME) \ + __attribute__((section(NAME), used)) +#endif + +#ifndef __section_tail +# define __section_tail(ID, KEY) \ + __section(__stringify(ID) "/" __stringify(KEY)) +#endif + +#ifndef __section_cls_entry +# define __section_cls_entry \ + __section(ELF_SECTION_CLASSIFIER) +#endif + +#ifndef __section_act_entry +# define __section_act_entry \ + __section(ELF_SECTION_ACTION) +#endif + +#ifndef __section_license +# define __section_license \ + __section(ELF_SECTION_LICENSE) +#endif + +#ifndef __section_maps +# define __section_maps \ + __section(ELF_SECTION_MAPS) +#endif + +/** Declaration helper macros. */ + +#ifndef BPF_LICENSE +# define BPF_LICENSE(NAME) \ + char ____license[] __section_license = NAME +#endif + +/** Classifier helper */ + +#ifndef BPF_H_DEFAULT +# define BPF_H_DEFAULT -1 +#endif + +/** BPF helper functions for tc. Individual flags are in linux/bpf.h */ + +#ifndef BPF_FUNC +# define BPF_FUNC(NAME, ...) \ + (* NAME)(__VA_ARGS__) __maybe_unused = (void *) BPF_FUNC_##NAME +#endif + +/* Map access/manipulation */ +static void *BPF_FUNC(map_lookup_elem, void *map, const void *key); +static int BPF_FUNC(map_update_elem, void *map, const void *key, + const void *value, uint32_t flags); +static int BPF_FUNC(map_delete_elem, void *map, const void *key); + +/* Time access */ +static uint64_t BPF_FUNC(ktime_get_ns); + +/* Debugging */ + +/* FIXME: __attribute__ ((format(printf, 1, 3))) not possible unless + * llvm bug https://llvm.org/bugs/show_bug.cgi?id=26243 gets resolved. + * It would require ____fmt to be made const, which generates a reloc + * entry (non-map). + */ +static void BPF_FUNC(trace_printk, const char *fmt, int fmt_size, ...); + +#ifndef printt +# define printt(fmt, ...) \ + ({ \ + char ____fmt[] = fmt; \ + trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \ + }) +#endif + +/* Random numbers */ +static uint32_t BPF_FUNC(get_prandom_u32); + +/* Tail calls */ +static void BPF_FUNC(tail_call, struct __sk_buff *skb, void *map, + uint32_t index); + +/* System helpers */ +static uint32_t BPF_FUNC(get_smp_processor_id); + +/* Packet misc meta data */ +static uint32_t BPF_FUNC(get_cgroup_classid, struct __sk_buff *skb); +static uint32_t BPF_FUNC(get_route_realm, struct __sk_buff *skb); + +/* Packet redirection */ +static int BPF_FUNC(redirect, int ifindex, uint32_t flags); +static int BPF_FUNC(clone_redirect, struct __sk_buff *skb, int ifindex, + uint32_t flags); + +/* Packet manipulation */ +static int BPF_FUNC(skb_load_bytes, struct __sk_buff *skb, uint32_t off, + void *to, uint32_t len); +static int BPF_FUNC(skb_store_bytes, struct __sk_buff *skb, uint32_t off, + const void *from, uint32_t len, uint32_t flags); + +static int BPF_FUNC(l3_csum_replace, struct __sk_buff *skb, uint32_t off, + uint32_t from, uint32_t to, uint32_t flags); +static int BPF_FUNC(l4_csum_replace, struct __sk_buff *skb, uint32_t off, + uint32_t from, uint32_t to, uint32_t flags); +static int BPF_FUNC(csum_diff, const void *from, uint32_t from_size, + const void *to, uint32_t to_size, uint32_t seed); + +/* Packet vlan encap/decap */ +static int BPF_FUNC(skb_vlan_push, struct __sk_buff *skb, uint16_t proto, + uint16_t vlan_tci); +static int BPF_FUNC(skb_vlan_pop, struct __sk_buff *skb); + +/* Packet tunnel encap/decap */ +static int BPF_FUNC(skb_get_tunnel_key, struct __sk_buff *skb, + struct bpf_tunnel_key *to, uint32_t size, uint32_t flags); +static int BPF_FUNC(skb_set_tunnel_key, struct __sk_buff *skb, + const struct bpf_tunnel_key *from, uint32_t size, + uint32_t flags); + +static int BPF_FUNC(skb_get_tunnel_opt, struct __sk_buff *skb, + void *to, uint32_t size); +static int BPF_FUNC(skb_set_tunnel_opt, struct __sk_buff *skb, + const void *from, uint32_t size); + +/** LLVM built-ins, mem*() routines work for constant size */ + +#ifndef lock_xadd +# define lock_xadd(ptr, val) ((void) __sync_fetch_and_add(ptr, val)) +#endif + +#ifndef memset +# define memset(s, c, n) __builtin_memset((s), (c), (n)) +#endif + +#ifndef memcpy +# define memcpy(d, s, n) __builtin_memcpy((d), (s), (n)) +#endif + +#ifndef memmove +# define memmove(d, s, n) __builtin_memmove((d), (s), (n)) +#endif + +/* FIXME: __builtin_memcmp() is not yet fully useable unless llvm bug + * https://llvm.org/bugs/show_bug.cgi?id=26218 gets resolved. Also + * this one would generate a reloc entry (non-map), otherwise. + */ +#if 0 +#ifndef memcmp +# define memcmp(a, b, n) __builtin_memcmp((a), (b), (n)) +#endif +#endif + +unsigned long long load_byte(void *skb, unsigned long long off) + asm ("llvm.bpf.load.byte"); + +unsigned long long load_half(void *skb, unsigned long long off) + asm ("llvm.bpf.load.half"); + +unsigned long long load_word(void *skb, unsigned long long off) + asm ("llvm.bpf.load.word"); + +#endif /* __BPF_API__ */ diff -Nru iproute2-4.3.0/include/bpf_elf.h iproute2-4.9.0/include/bpf_elf.h --- iproute2-4.3.0/include/bpf_elf.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/bpf_elf.h 2016-12-12 23:07:42.000000000 +0000 @@ -21,13 +21,20 @@ #define ELF_MAX_MAPS 64 #define ELF_MAX_LICENSE_LEN 128 +/* Object pinning settings */ +#define PIN_NONE 0 +#define PIN_OBJECT_NS 1 +#define PIN_GLOBAL_NS 2 + /* ELF map definition */ struct bpf_elf_map { __u32 type; __u32 size_key; __u32 size_value; __u32 max_elem; + __u32 flags; __u32 id; + __u32 pinning; }; #endif /* __BPF_ELF__ */ diff -Nru iproute2-4.3.0/include/color.h iproute2-4.9.0/include/color.h --- iproute2-4.3.0/include/color.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/color.h 2016-12-12 23:07:42.000000000 +0000 @@ -7,10 +7,13 @@ COLOR_INET, COLOR_INET6, COLOR_OPERSTATE_UP, - COLOR_OPERSTATE_DOWN + COLOR_OPERSTATE_DOWN, + COLOR_CLEAR }; void enable_color(void); int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...); +enum color_attr ifa_family_color(__u8 ifa_family); +enum color_attr oper_state_color(__u8 state); #endif diff -Nru iproute2-4.3.0/include/hlist.h iproute2-4.9.0/include/hlist.h --- iproute2-4.3.0/include/hlist.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/hlist.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -#ifndef __HLIST_H__ -#define __HLIST_H__ 1 -/* Hash list stuff from kernel */ - -#include - -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) - -struct hlist_head { - struct hlist_node *first; -}; - -struct hlist_node { - struct hlist_node *next, **pprev; -}; - -static inline void hlist_del(struct hlist_node *n) -{ - struct hlist_node *next = n->next; - struct hlist_node **pprev = n->pprev; - *pprev = next; - if (next) - next->pprev = pprev; -} - -static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) -{ - struct hlist_node *first = h->first; - n->next = first; - if (first) - first->pprev = &n->next; - h->first = n; - n->pprev = &h->first; -} - -#define hlist_for_each(pos, head) \ - for (pos = (head)->first; pos ; pos = pos->next) - - -#define hlist_for_each_safe(pos, n, head) \ - for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ - pos = n) - -#define hlist_entry_safe(ptr, type, member) \ - ({ typeof(ptr) ____ptr = (ptr); \ - ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ - }) - -#define hlist_for_each_entry(pos, head, member) \ - for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\ - pos; \ - pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) - -#endif /* __HLIST_H__ */ diff -Nru iproute2-4.3.0/include/ip6tables.h iproute2-4.9.0/include/ip6tables.h --- iproute2-4.3.0/include/ip6tables.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/ip6tables.h 2016-12-12 23:07:42.000000000 +0000 @@ -1,141 +1,20 @@ #ifndef _IP6TABLES_USER_H #define _IP6TABLES_USER_H -#include "iptables_common.h" -#include "libiptc/libip6tc.h" - -struct ip6tables_rule_match -{ - struct ip6tables_rule_match *next; - - struct ip6tables_match *match; -}; - -/* Include file for additions: new matches and targets. */ -struct ip6tables_match -{ - struct ip6tables_match *next; - - ip6t_chainlabel name; - - const char *version; - - /* Size of match data. */ - size_t size; - - /* Size of match data relevent for userspace comparison purposes */ - size_t userspacesize; - - /* Function which prints out usage message. */ - void (*help)(void); - - /* Initialize the match. */ - void (*init)(struct ip6t_entry_match *m, unsigned int *nfcache); - - /* Function which parses command options; returns true if it - ate an option */ - int (*parse)(int c, char **argv, int invert, unsigned int *flags, - const struct ip6t_entry *entry, - unsigned int *nfcache, - struct ip6t_entry_match **match); - - /* Final check; exit if not ok. */ - void (*final_check)(unsigned int flags); - - /* Prints out the match iff non-NULL: put space at end */ - void (*print)(const struct ip6t_ip6 *ip, - const struct ip6t_entry_match *match, int numeric); - - /* Saves the union ipt_matchinfo in parsable form to stdout. */ - void (*save)(const struct ip6t_ip6 *ip, - const struct ip6t_entry_match *match); - - /* Pointer to list of extra command-line options */ - const struct option *extra_opts; - - /* Ignore these men behind the curtain: */ - unsigned int option_offset; - struct ip6t_entry_match *m; - unsigned int mflags; -#ifdef NO_SHARED_LIBS - unsigned int loaded; /* simulate loading so options are merged properly */ -#endif -}; - -struct ip6tables_target -{ - struct ip6tables_target *next; - - ip6t_chainlabel name; - - const char *version; - - /* Size of target data. */ - size_t size; - - /* Size of target data relevent for userspace comparison purposes */ - size_t userspacesize; - - /* Function which prints out usage message. */ - void (*help)(void); - - /* Initialize the target. */ - void (*init)(struct ip6t_entry_target *t, unsigned int *nfcache); - - /* Function which parses command options; returns true if it - ate an option */ - int (*parse)(int c, char **argv, int invert, unsigned int *flags, - const struct ip6t_entry *entry, - struct ip6t_entry_target **target); - - /* Final check; exit if not ok. */ - void (*final_check)(unsigned int flags); - - /* Prints out the target iff non-NULL: put space at end */ - void (*print)(const struct ip6t_ip6 *ip, - const struct ip6t_entry_target *target, int numeric); - - /* Saves the targinfo in parsable form to stdout. */ - void (*save)(const struct ip6t_ip6 *ip, - const struct ip6t_entry_target *target); - - /* Pointer to list of extra command-line options */ - struct option *extra_opts; - - /* Ignore these men behind the curtain: */ - unsigned int option_offset; - struct ip6t_entry_target *t; - unsigned int tflags; - unsigned int used; -#ifdef NO_SHARED_LIBS - unsigned int loaded; /* simulate loading so options are merged properly */ -#endif -}; - -extern int line; +#include +#include +#include +#include /* Your shared library should call one of these. */ -extern void register_match6(struct ip6tables_match *me); -extern void register_target6(struct ip6tables_target *me); - extern int do_command6(int argc, char *argv[], char **table, - ip6tc_handle_t *handle); -/* Keeping track of external matches and targets: linked lists. */ -extern struct ip6tables_match *ip6tables_matches; -extern struct ip6tables_target *ip6tables_targets; - -enum ip6t_tryload { - DONT_LOAD, - TRY_LOAD, - LOAD_MUST_SUCCEED -}; + struct xtc_handle **handle, bool restore); -extern struct ip6tables_target *find_target(const char *name, enum ip6t_tryload); -extern struct ip6tables_match *find_match(const char *name, enum ip6t_tryload, struct ip6tables_rule_match **match); +extern int for_each_chain6(int (*fn)(const xt_chainlabel, int, struct xtc_handle *), int verbose, int builtinstoo, struct xtc_handle *handle); +extern int flush_entries6(const xt_chainlabel chain, int verbose, struct xtc_handle *handle); +extern int delete_chain6(const xt_chainlabel chain, int verbose, struct xtc_handle *handle); +void print_rule6(const struct ip6t_entry *e, struct xtc_handle *h, const char *chain, int counters); -extern int for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *), int verbose, int builtinstoo, ip6tc_handle_t *handle); -extern int flush_entries(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle); -extern int delete_chain(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle); -extern int ip6tables_insmod(const char *modname, const char *modprobe); +extern struct xtables_globals ip6tables_globals; #endif /*_IP6TABLES_USER_H*/ diff -Nru iproute2-4.3.0/include/iptables/internal.h iproute2-4.9.0/include/iptables/internal.h --- iproute2-4.3.0/include/iptables/internal.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/iptables/internal.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,13 @@ +#ifndef IPTABLES_INTERNAL_H +#define IPTABLES_INTERNAL_H 1 + +#define IPTABLES_VERSION "1.6.0" + +/** + * Program's own name and version. + */ +extern const char *program_name, *program_version; + +extern int line; + +#endif /* IPTABLES_INTERNAL_H */ diff -Nru iproute2-4.3.0/include/iptables.h iproute2-4.9.0/include/iptables.h --- iproute2-4.3.0/include/iptables.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/iptables.h 2016-12-12 23:07:42.000000000 +0000 @@ -1,179 +1,25 @@ #ifndef _IPTABLES_USER_H #define _IPTABLES_USER_H -#include "iptables_common.h" -#include "libiptc/libiptc.h" - -#ifndef IPT_LIB_DIR -#define IPT_LIB_DIR "/usr/local/lib/iptables" -#endif - -#ifndef IPPROTO_SCTP -#define IPPROTO_SCTP 132 -#endif - -#ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */ -#define IPT_SO_GET_REVISION_MATCH (IPT_BASE_CTL + 2) -#define IPT_SO_GET_REVISION_TARGET (IPT_BASE_CTL + 3) - -struct ipt_get_revision -{ - char name[IPT_FUNCTION_MAXNAMELEN-1]; - - u_int8_t revision; -}; -#endif /* IPT_SO_GET_REVISION_MATCH Old kernel source */ - -struct iptables_rule_match -{ - struct iptables_rule_match *next; - - struct iptables_match *match; -}; - -/* Include file for additions: new matches and targets. */ -struct iptables_match -{ - struct iptables_match *next; - - ipt_chainlabel name; - - /* Revision of match (0 by default). */ - u_int8_t revision; - - const char *version; - - /* Size of match data. */ - size_t size; - - /* Size of match data relevent for userspace comparison purposes */ - size_t userspacesize; - - /* Function which prints out usage message. */ - void (*help)(void); - - /* Initialize the match. */ - void (*init)(struct ipt_entry_match *m, unsigned int *nfcache); - - /* Function which parses command options; returns true if it - ate an option */ - int (*parse)(int c, char **argv, int invert, unsigned int *flags, - const struct ipt_entry *entry, - unsigned int *nfcache, - struct ipt_entry_match **match); - - /* Final check; exit if not ok. */ - void (*final_check)(unsigned int flags); - - /* Prints out the match iff non-NULL: put space at end */ - void (*print)(const struct ipt_ip *ip, - const struct ipt_entry_match *match, int numeric); - - /* Saves the match info in parsable form to stdout. */ - void (*save)(const struct ipt_ip *ip, - const struct ipt_entry_match *match); - - /* Pointer to list of extra command-line options */ - const struct option *extra_opts; - - /* Ignore these men behind the curtain: */ - unsigned int option_offset; - struct ipt_entry_match *m; - unsigned int mflags; -#ifdef NO_SHARED_LIBS - unsigned int loaded; /* simulate loading so options are merged properly */ -#endif -}; - -struct iptables_target -{ - struct iptables_target *next; - - ipt_chainlabel name; - - /* Revision of target (0 by default). */ - u_int8_t revision; - - const char *version; - - /* Size of target data. */ - size_t size; - - /* Size of target data relevent for userspace comparison purposes */ - size_t userspacesize; - - /* Function which prints out usage message. */ - void (*help)(void); - - /* Initialize the target. */ - void (*init)(struct ipt_entry_target *t, unsigned int *nfcache); - - /* Function which parses command options; returns true if it - ate an option */ - int (*parse)(int c, char **argv, int invert, unsigned int *flags, - const struct ipt_entry *entry, - struct ipt_entry_target **target); - - /* Final check; exit if not ok. */ - void (*final_check)(unsigned int flags); - - /* Prints out the target iff non-NULL: put space at end */ - void (*print)(const struct ipt_ip *ip, - const struct ipt_entry_target *target, int numeric); - - /* Saves the targinfo in parsable form to stdout. */ - void (*save)(const struct ipt_ip *ip, - const struct ipt_entry_target *target); - - /* Pointer to list of extra command-line options */ - struct option *extra_opts; - - /* Ignore these men behind the curtain: */ - unsigned int option_offset; - struct ipt_entry_target *t; - unsigned int tflags; - unsigned int used; -#ifdef NO_SHARED_LIBS - unsigned int loaded; /* simulate loading so options are merged properly */ -#endif -}; - -extern int line; +#include +#include +#include +#include /* Your shared library should call one of these. */ -extern void register_match(struct iptables_match *me); -extern void register_target(struct iptables_target *me); -extern void xtables_register_target(struct iptables_target *me); -extern int build_st(struct iptables_target *target, struct ipt_entry_target *t); - -extern struct in_addr *dotted_to_addr(const char *dotted); -extern char *addr_to_dotted(const struct in_addr *addrp); -extern char *addr_to_anyname(const struct in_addr *addr); -extern char *mask_to_dotted(const struct in_addr *mask); - -extern void parse_hostnetworkmask(const char *name, struct in_addr **addrpp, - struct in_addr *maskp, unsigned int *naddrs); -extern u_int16_t parse_protocol(const char *s); - -extern int do_command(int argc, char *argv[], char **table, - iptc_handle_t *handle); -/* Keeping track of external matches and targets: linked lists. */ -extern struct iptables_match *iptables_matches; -extern struct iptables_target *iptables_targets; +extern int do_command4(int argc, char *argv[], char **table, + struct xtc_handle **handle, bool restore); +extern int delete_chain4(const xt_chainlabel chain, int verbose, + struct xtc_handle *handle); +extern int flush_entries4(const xt_chainlabel chain, int verbose, + struct xtc_handle *handle); +extern int for_each_chain4(int (*fn)(const xt_chainlabel, int, struct xtc_handle *), + int verbose, int builtinstoo, struct xtc_handle *handle); +extern void print_rule4(const struct ipt_entry *e, + struct xtc_handle *handle, const char *chain, int counters); -enum ipt_tryload { - DONT_LOAD, - TRY_LOAD, - LOAD_MUST_SUCCEED -}; +extern struct xtables_globals iptables_globals; -extern struct iptables_target *find_target(const char *name, enum ipt_tryload); -extern struct iptables_match *find_match(const char *name, enum ipt_tryload, struct iptables_rule_match **match); +extern struct xtables_globals xtables_globals; -extern int delete_chain(const ipt_chainlabel chain, int verbose, - iptc_handle_t *handle); -extern int flush_entries(const ipt_chainlabel chain, int verbose, - iptc_handle_t *handle); -extern int for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), - int verbose, int builtinstoo, iptc_handle_t *handle); #endif /*_IPTABLES_USER_H*/ diff -Nru iproute2-4.3.0/include/libgenl.h iproute2-4.9.0/include/libgenl.h --- iproute2-4.3.0/include/libgenl.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/libgenl.h 2016-12-12 23:07:42.000000000 +0000 @@ -21,5 +21,7 @@ } extern int genl_resolve_family(struct rtnl_handle *grth, const char *family); +extern int genl_init_handle(struct rtnl_handle *grth, const char *family, + int *genl_family); #endif /* __LIBGENL_H__ */ diff -Nru iproute2-4.3.0/include/libiptc/ipt_kernel_headers.h iproute2-4.9.0/include/libiptc/ipt_kernel_headers.h --- iproute2-4.3.0/include/libiptc/ipt_kernel_headers.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/libiptc/ipt_kernel_headers.h 2016-12-12 23:07:42.000000000 +0000 @@ -5,22 +5,11 @@ #include -#if defined(__GLIBC__) && __GLIBC__ == 2 #include #include #include #include #include +#include #include -#else /* libc5 */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif #endif diff -Nru iproute2-4.3.0/include/libiptc/libip6tc.h iproute2-4.9.0/include/libiptc/libip6tc.h --- iproute2-4.3.0/include/libiptc/libip6tc.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/libiptc/libip6tc.h 2016-12-12 23:07:42.000000000 +0000 @@ -2,153 +2,160 @@ #define _LIBIP6TC_H /* Library which manipulates firewall rules. Version 0.2. */ +#include #include -#include - -#ifndef IP6T_MIN_ALIGN -#define IP6T_MIN_ALIGN (__alignof__(struct ip6t_entry)) +#ifdef __cplusplus +# include +#else +# include /* INT_MAX in ip6_tables.h */ #endif -#define IP6T_ALIGN(s) (((s) + (IP6T_MIN_ALIGN-1)) & ~(IP6T_MIN_ALIGN-1)) +#include +#include -typedef char ip6t_chainlabel[32]; +#define ip6tc_handle xtc_handle +#define ip6t_chainlabel xt_chainlabel #define IP6TC_LABEL_ACCEPT "ACCEPT" #define IP6TC_LABEL_DROP "DROP" #define IP6TC_LABEL_QUEUE "QUEUE" #define IP6TC_LABEL_RETURN "RETURN" -/* Transparent handle type. */ -typedef struct ip6tc_handle *ip6tc_handle_t; - /* Does this chain exist? */ -int ip6tc_is_chain(const char *chain, const ip6tc_handle_t handle); +int ip6tc_is_chain(const char *chain, struct xtc_handle *const handle); /* Take a snapshot of the rules. Returns NULL on error. */ -ip6tc_handle_t ip6tc_init(const char *tablename); +struct xtc_handle *ip6tc_init(const char *tablename); /* Cleanup after ip6tc_init(). */ -void ip6tc_free(ip6tc_handle_t *h); +void ip6tc_free(struct xtc_handle *h); /* Iterator functions to run through the chains. Returns NULL at end. */ -const char *ip6tc_first_chain(ip6tc_handle_t *handle); -const char *ip6tc_next_chain(ip6tc_handle_t *handle); +const char *ip6tc_first_chain(struct xtc_handle *handle); +const char *ip6tc_next_chain(struct xtc_handle *handle); /* Get first rule in the given chain: NULL for empty chain. */ const struct ip6t_entry *ip6tc_first_rule(const char *chain, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* Returns NULL when rules run out. */ const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* Returns a pointer to the target name of this position. */ const char *ip6tc_get_target(const struct ip6t_entry *e, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* Is this a built-in chain? */ -int ip6tc_builtin(const char *chain, const ip6tc_handle_t handle); +int ip6tc_builtin(const char *chain, struct xtc_handle *const handle); /* Get the policy of a given built-in chain */ const char *ip6tc_get_policy(const char *chain, - struct ip6t_counters *counters, - ip6tc_handle_t *handle); + struct xt_counters *counters, + struct xtc_handle *handle); /* These functions return TRUE for OK or 0 and set errno. If errno == 0, it means there was a version error (ie. upgrade libiptc). */ /* Rule numbers start at 1 for the first rule. */ /* Insert the entry `fw' in chain `chain' into position `rulenum'. */ -int ip6tc_insert_entry(const ip6t_chainlabel chain, +int ip6tc_insert_entry(const xt_chainlabel chain, const struct ip6t_entry *e, unsigned int rulenum, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* Atomically replace rule `rulenum' in `chain' with `fw'. */ -int ip6tc_replace_entry(const ip6t_chainlabel chain, +int ip6tc_replace_entry(const xt_chainlabel chain, const struct ip6t_entry *e, unsigned int rulenum, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* Append entry `fw' to chain `chain'. Equivalent to insert with rulenum = length of chain. */ -int ip6tc_append_entry(const ip6t_chainlabel chain, +int ip6tc_append_entry(const xt_chainlabel chain, const struct ip6t_entry *e, - ip6tc_handle_t *handle); + struct xtc_handle *handle); + +/* Check whether a matching rule exists */ +int ip6tc_check_entry(const xt_chainlabel chain, + const struct ip6t_entry *origfw, + unsigned char *matchmask, + struct xtc_handle *handle); /* Delete the first rule in `chain' which matches `fw'. */ -int ip6tc_delete_entry(const ip6t_chainlabel chain, +int ip6tc_delete_entry(const xt_chainlabel chain, const struct ip6t_entry *origfw, unsigned char *matchmask, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* Delete the rule in position `rulenum' in `chain'. */ -int ip6tc_delete_num_entry(const ip6t_chainlabel chain, +int ip6tc_delete_num_entry(const xt_chainlabel chain, unsigned int rulenum, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* Check the packet `fw' on chain `chain'. Returns the verdict, or NULL and sets errno. */ -const char *ip6tc_check_packet(const ip6t_chainlabel chain, +const char *ip6tc_check_packet(const xt_chainlabel chain, struct ip6t_entry *, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* Flushes the entries in the given chain (ie. empties chain). */ -int ip6tc_flush_entries(const ip6t_chainlabel chain, - ip6tc_handle_t *handle); +int ip6tc_flush_entries(const xt_chainlabel chain, + struct xtc_handle *handle); /* Zeroes the counters in a chain. */ -int ip6tc_zero_entries(const ip6t_chainlabel chain, - ip6tc_handle_t *handle); +int ip6tc_zero_entries(const xt_chainlabel chain, + struct xtc_handle *handle); /* Creates a new chain. */ -int ip6tc_create_chain(const ip6t_chainlabel chain, - ip6tc_handle_t *handle); +int ip6tc_create_chain(const xt_chainlabel chain, + struct xtc_handle *handle); /* Deletes a chain. */ -int ip6tc_delete_chain(const ip6t_chainlabel chain, - ip6tc_handle_t *handle); +int ip6tc_delete_chain(const xt_chainlabel chain, + struct xtc_handle *handle); /* Renames a chain. */ -int ip6tc_rename_chain(const ip6t_chainlabel oldname, - const ip6t_chainlabel newname, - ip6tc_handle_t *handle); +int ip6tc_rename_chain(const xt_chainlabel oldname, + const xt_chainlabel newname, + struct xtc_handle *handle); /* Sets the policy on a built-in chain. */ -int ip6tc_set_policy(const ip6t_chainlabel chain, - const ip6t_chainlabel policy, - struct ip6t_counters *counters, - ip6tc_handle_t *handle); +int ip6tc_set_policy(const xt_chainlabel chain, + const xt_chainlabel policy, + struct xt_counters *counters, + struct xtc_handle *handle); /* Get the number of references to this chain */ -int ip6tc_get_references(unsigned int *ref, const ip6t_chainlabel chain, - ip6tc_handle_t *handle); +int ip6tc_get_references(unsigned int *ref, const xt_chainlabel chain, + struct xtc_handle *handle); /* read packet and byte counters for a specific rule */ -struct ip6t_counters *ip6tc_read_counter(const ip6t_chainlabel chain, +struct xt_counters *ip6tc_read_counter(const xt_chainlabel chain, unsigned int rulenum, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* zero packet and byte counters for a specific rule */ -int ip6tc_zero_counter(const ip6t_chainlabel chain, +int ip6tc_zero_counter(const xt_chainlabel chain, unsigned int rulenum, - ip6tc_handle_t *handle); + struct xtc_handle *handle); /* set packet and byte counters for a specific rule */ -int ip6tc_set_counter(const ip6t_chainlabel chain, +int ip6tc_set_counter(const xt_chainlabel chain, unsigned int rulenum, - struct ip6t_counters *counters, - ip6tc_handle_t *handle); + struct xt_counters *counters, + struct xtc_handle *handle); /* Makes the actual changes. */ -int ip6tc_commit(ip6tc_handle_t *handle); +int ip6tc_commit(struct xtc_handle *handle); /* Get raw socket. */ -int ip6tc_get_raw_socket(); +int ip6tc_get_raw_socket(void); /* Translates errno numbers into more human-readable form than strerror. */ const char *ip6tc_strerror(int err); -/* Return prefix length, or -1 if not contiguous */ -int ipv6_prefix_length(const struct in6_addr *a); +extern void dump_entries6(struct xtc_handle *const); + +extern const struct xtc_ops ip6tc_ops; #endif /* _LIBIP6TC_H */ diff -Nru iproute2-4.3.0/include/libiptc/libiptc.h iproute2-4.9.0/include/libiptc/libiptc.h --- iproute2-4.3.0/include/libiptc/libiptc.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/libiptc/libiptc.h 2016-12-12 23:07:42.000000000 +0000 @@ -2,155 +2,157 @@ #define _LIBIPTC_H /* Library which manipulates filtering rules. */ +#include #include +#ifdef __cplusplus +# include +#else +# include /* INT_MAX in ip_tables.h */ +#endif #include +#include #ifdef __cplusplus extern "C" { #endif -#ifndef IPT_MIN_ALIGN -/* ipt_entry has pointers and u_int64_t's in it, so if you align to - it, you'll also align to any crazy matches and targets someone - might write */ -#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry)) -#endif - -#define IPT_ALIGN(s) (((s) + ((IPT_MIN_ALIGN)-1)) & ~((IPT_MIN_ALIGN)-1)) - -typedef char ipt_chainlabel[32]; +#define iptc_handle xtc_handle +#define ipt_chainlabel xt_chainlabel #define IPTC_LABEL_ACCEPT "ACCEPT" #define IPTC_LABEL_DROP "DROP" #define IPTC_LABEL_QUEUE "QUEUE" #define IPTC_LABEL_RETURN "RETURN" -/* Transparent handle type. */ -typedef struct iptc_handle *iptc_handle_t; - /* Does this chain exist? */ -int iptc_is_chain(const char *chain, const iptc_handle_t handle); +int iptc_is_chain(const char *chain, struct xtc_handle *const handle); /* Take a snapshot of the rules. Returns NULL on error. */ -iptc_handle_t iptc_init(const char *tablename); +struct xtc_handle *iptc_init(const char *tablename); /* Cleanup after iptc_init(). */ -void iptc_free(iptc_handle_t *h); +void iptc_free(struct xtc_handle *h); /* Iterator functions to run through the chains. Returns NULL at end. */ -const char *iptc_first_chain(iptc_handle_t *handle); -const char *iptc_next_chain(iptc_handle_t *handle); +const char *iptc_first_chain(struct xtc_handle *handle); +const char *iptc_next_chain(struct xtc_handle *handle); /* Get first rule in the given chain: NULL for empty chain. */ const struct ipt_entry *iptc_first_rule(const char *chain, - iptc_handle_t *handle); + struct xtc_handle *handle); /* Returns NULL when rules run out. */ const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev, - iptc_handle_t *handle); + struct xtc_handle *handle); /* Returns a pointer to the target name of this entry. */ const char *iptc_get_target(const struct ipt_entry *e, - iptc_handle_t *handle); + struct xtc_handle *handle); /* Is this a built-in chain? */ -int iptc_builtin(const char *chain, const iptc_handle_t handle); +int iptc_builtin(const char *chain, struct xtc_handle *const handle); /* Get the policy of a given built-in chain */ const char *iptc_get_policy(const char *chain, - struct ipt_counters *counter, - iptc_handle_t *handle); + struct xt_counters *counter, + struct xtc_handle *handle); /* These functions return TRUE for OK or 0 and set errno. If errno == 0, it means there was a version error (ie. upgrade libiptc). */ /* Rule numbers start at 1 for the first rule. */ /* Insert the entry `e' in chain `chain' into position `rulenum'. */ -int iptc_insert_entry(const ipt_chainlabel chain, +int iptc_insert_entry(const xt_chainlabel chain, const struct ipt_entry *e, unsigned int rulenum, - iptc_handle_t *handle); + struct xtc_handle *handle); /* Atomically replace rule `rulenum' in `chain' with `e'. */ -int iptc_replace_entry(const ipt_chainlabel chain, +int iptc_replace_entry(const xt_chainlabel chain, const struct ipt_entry *e, unsigned int rulenum, - iptc_handle_t *handle); + struct xtc_handle *handle); /* Append entry `e' to chain `chain'. Equivalent to insert with rulenum = length of chain. */ -int iptc_append_entry(const ipt_chainlabel chain, +int iptc_append_entry(const xt_chainlabel chain, const struct ipt_entry *e, - iptc_handle_t *handle); + struct xtc_handle *handle); + +/* Check whether a mathching rule exists */ +int iptc_check_entry(const xt_chainlabel chain, + const struct ipt_entry *origfw, + unsigned char *matchmask, + struct xtc_handle *handle); /* Delete the first rule in `chain' which matches `e', subject to matchmask (array of length == origfw) */ -int iptc_delete_entry(const ipt_chainlabel chain, +int iptc_delete_entry(const xt_chainlabel chain, const struct ipt_entry *origfw, unsigned char *matchmask, - iptc_handle_t *handle); + struct xtc_handle *handle); /* Delete the rule in position `rulenum' in `chain'. */ -int iptc_delete_num_entry(const ipt_chainlabel chain, +int iptc_delete_num_entry(const xt_chainlabel chain, unsigned int rulenum, - iptc_handle_t *handle); + struct xtc_handle *handle); /* Check the packet `e' on chain `chain'. Returns the verdict, or NULL and sets errno. */ -const char *iptc_check_packet(const ipt_chainlabel chain, +const char *iptc_check_packet(const xt_chainlabel chain, struct ipt_entry *entry, - iptc_handle_t *handle); + struct xtc_handle *handle); /* Flushes the entries in the given chain (ie. empties chain). */ -int iptc_flush_entries(const ipt_chainlabel chain, - iptc_handle_t *handle); +int iptc_flush_entries(const xt_chainlabel chain, + struct xtc_handle *handle); /* Zeroes the counters in a chain. */ -int iptc_zero_entries(const ipt_chainlabel chain, - iptc_handle_t *handle); +int iptc_zero_entries(const xt_chainlabel chain, + struct xtc_handle *handle); /* Creates a new chain. */ -int iptc_create_chain(const ipt_chainlabel chain, - iptc_handle_t *handle); +int iptc_create_chain(const xt_chainlabel chain, + struct xtc_handle *handle); /* Deletes a chain. */ -int iptc_delete_chain(const ipt_chainlabel chain, - iptc_handle_t *handle); +int iptc_delete_chain(const xt_chainlabel chain, + struct xtc_handle *handle); /* Renames a chain. */ -int iptc_rename_chain(const ipt_chainlabel oldname, - const ipt_chainlabel newname, - iptc_handle_t *handle); +int iptc_rename_chain(const xt_chainlabel oldname, + const xt_chainlabel newname, + struct xtc_handle *handle); /* Sets the policy on a built-in chain. */ -int iptc_set_policy(const ipt_chainlabel chain, - const ipt_chainlabel policy, - struct ipt_counters *counters, - iptc_handle_t *handle); +int iptc_set_policy(const xt_chainlabel chain, + const xt_chainlabel policy, + struct xt_counters *counters, + struct xtc_handle *handle); /* Get the number of references to this chain */ int iptc_get_references(unsigned int *ref, - const ipt_chainlabel chain, - iptc_handle_t *handle); + const xt_chainlabel chain, + struct xtc_handle *handle); /* read packet and byte counters for a specific rule */ -struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain, +struct xt_counters *iptc_read_counter(const xt_chainlabel chain, unsigned int rulenum, - iptc_handle_t *handle); + struct xtc_handle *handle); /* zero packet and byte counters for a specific rule */ -int iptc_zero_counter(const ipt_chainlabel chain, +int iptc_zero_counter(const xt_chainlabel chain, unsigned int rulenum, - iptc_handle_t *handle); + struct xtc_handle *handle); /* set packet and byte counters for a specific rule */ -int iptc_set_counter(const ipt_chainlabel chain, +int iptc_set_counter(const xt_chainlabel chain, unsigned int rulenum, - struct ipt_counters *counters, - iptc_handle_t *handle); + struct xt_counters *counters, + struct xtc_handle *handle); /* Makes the actual changes. */ -int iptc_commit(iptc_handle_t *handle); +int iptc_commit(struct xtc_handle *handle); /* Get raw socket. */ int iptc_get_raw_socket(void); @@ -158,6 +160,10 @@ /* Translates errno numbers into more human-readable form than strerror. */ const char *iptc_strerror(int err); +extern void dump_entries(struct xtc_handle *const); + +extern const struct xtc_ops iptc_ops; + #ifdef __cplusplus } #endif diff -Nru iproute2-4.3.0/include/libiptc/libxtc.h iproute2-4.9.0/include/libiptc/libxtc.h --- iproute2-4.3.0/include/libiptc/libxtc.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/libiptc/libxtc.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,33 @@ +#ifndef _LIBXTC_H +#define _LIBXTC_H +/* Library which manipulates filtering rules. */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef XT_MIN_ALIGN +/* xt_entry has pointers and u_int64_t's in it, so if you align to + it, you'll also align to any crazy matches and targets someone + might write */ +#define XT_MIN_ALIGN (__alignof__(struct xt_entry)) +#endif + +#ifndef XT_ALIGN +#define XT_ALIGN(s) (((s) + ((XT_MIN_ALIGN)-1)) & ~((XT_MIN_ALIGN)-1)) +#endif + +#define XTC_LABEL_ACCEPT "ACCEPT" +#define XTC_LABEL_DROP "DROP" +#define XTC_LABEL_QUEUE "QUEUE" +#define XTC_LABEL_RETURN "RETURN" + + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBXTC_H */ diff -Nru iproute2-4.3.0/include/libiptc/xtcshared.h iproute2-4.9.0/include/libiptc/xtcshared.h --- iproute2-4.3.0/include/libiptc/xtcshared.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/libiptc/xtcshared.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,20 @@ +#ifndef _LIBXTC_SHARED_H +#define _LIBXTC_SHARED_H 1 + +typedef char xt_chainlabel[32]; +struct xtc_handle; +struct xt_counters; + +struct xtc_ops { + int (*commit)(struct xtc_handle *); + void (*free)(struct xtc_handle *); + int (*builtin)(const char *, struct xtc_handle *const); + int (*is_chain)(const char *, struct xtc_handle *const); + int (*flush_entries)(const xt_chainlabel, struct xtc_handle *); + int (*create_chain)(const xt_chainlabel, struct xtc_handle *); + int (*set_policy)(const xt_chainlabel, const xt_chainlabel, + struct xt_counters *, struct xtc_handle *); + const char *(*strerror)(int); +}; + +#endif /* _LIBXTC_SHARED_H */ diff -Nru iproute2-4.3.0/include/libnetlink.h iproute2-4.9.0/include/libnetlink.h --- iproute2-4.3.0/include/libnetlink.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/libnetlink.h 2016-12-12 23:07:42.000000000 +0000 @@ -11,8 +11,7 @@ #include #include -struct rtnl_handle -{ +struct rtnl_handle { int fd; struct sockaddr_nl local; struct sockaddr_nl peer; @@ -26,10 +25,10 @@ extern int rcvbuf; -int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions) __attribute__((warn_unused_result)); -int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions, int protocol) __attribute__((warn_unused_result)); @@ -39,9 +38,20 @@ int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int fam, int type, __u32 filt_mask) __attribute__((warn_unused_result)); + +typedef int (*req_filter_fn_t)(struct nlmsghdr *nlh, int reqlen); + +int rtnl_wilddump_req_filter_fn(struct rtnl_handle *rth, int fam, int type, + req_filter_fn_t fn) + __attribute__((warn_unused_result)); +int rtnl_wilddump_stats_req_filter(struct rtnl_handle *rth, int fam, int type, + __u32 filt_mask) + __attribute__((warn_unused_result)); int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) __attribute__((warn_unused_result)); +int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n) + __attribute__((warn_unused_result)); struct rtnl_ctrl_data { int nsid; @@ -54,15 +64,19 @@ struct rtnl_ctrl_data *, struct nlmsghdr *n, void *); -struct rtnl_dump_filter_arg -{ +struct rtnl_dump_filter_arg { rtnl_filter_t filter; void *arg1; + __u16 nc_flags; }; int rtnl_dump_filter_l(struct rtnl_handle *rth, const struct rtnl_dump_filter_arg *arg); -int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter, void *arg); +int rtnl_dump_filter_nc(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg, __u16 nc_flags); +#define rtnl_dump_filter(rth, filter, arg) \ + rtnl_dump_filter_nc(rth, filter, arg, 0) int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, struct nlmsghdr *answer, size_t len) __attribute__((warn_unused_result)); @@ -133,6 +147,7 @@ static inline __u64 rta_getattr_u64(const struct rtattr *rta) { __u64 tmp; + memcpy(&tmp, RTA_DATA(rta), sizeof(__u64)); return tmp; } @@ -152,42 +167,47 @@ #ifndef IFA_RTA #define IFA_RTA(r) \ - ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) #endif #ifndef IFA_PAYLOAD -#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg)) #endif #ifndef IFLA_RTA #define IFLA_RTA(r) \ - ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) #endif #ifndef IFLA_PAYLOAD -#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg)) #endif #ifndef NDA_RTA #define NDA_RTA(r) \ - ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) #endif #ifndef NDA_PAYLOAD -#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg)) +#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ndmsg)) #endif #ifndef NDTA_RTA #define NDTA_RTA(r) \ - ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg)))) + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg)))) #endif #ifndef NDTA_PAYLOAD -#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg)) +#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ndtmsg)) #endif #ifndef NETNS_RTA #define NETNS_RTA(r) \ - ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg)))) + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg)))) #endif #ifndef NETNS_PAYLOAD -#define NETNS_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtgenmsg)) +#define NETNS_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct rtgenmsg)) +#endif + +#ifndef IFLA_STATS_RTA +#define IFLA_STATS_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct if_stats_msg)))) #endif /* User defined nlmsg_type which is used mostly for logging netlink @@ -195,4 +215,3 @@ #define NLMSG_TSTAMP 15 #endif /* __LIBNETLINK_H__ */ - diff -Nru iproute2-4.3.0/include/linux/bpf.h iproute2-4.9.0/include/linux/bpf.h --- iproute2-4.3.0/include/linux/bpf.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/bpf.h 2016-12-12 23:07:42.000000000 +0000 @@ -63,50 +63,16 @@ __s32 imm; /* signed immediate constant */ }; -/* BPF syscall commands */ +/* BPF syscall commands, see bpf(2) man-page for details. */ enum bpf_cmd { - /* create a map with given type and attributes - * fd = bpf(BPF_MAP_CREATE, union bpf_attr *, u32 size) - * returns fd or negative error - * map is deleted when fd is closed - */ BPF_MAP_CREATE, - - /* lookup key in a given map - * err = bpf(BPF_MAP_LOOKUP_ELEM, union bpf_attr *attr, u32 size) - * Using attr->map_fd, attr->key, attr->value - * returns zero and stores found elem into value - * or negative error - */ BPF_MAP_LOOKUP_ELEM, - - /* create or update key/value pair in a given map - * err = bpf(BPF_MAP_UPDATE_ELEM, union bpf_attr *attr, u32 size) - * Using attr->map_fd, attr->key, attr->value, attr->flags - * returns zero or negative error - */ BPF_MAP_UPDATE_ELEM, - - /* find and delete elem by key in a given map - * err = bpf(BPF_MAP_DELETE_ELEM, union bpf_attr *attr, u32 size) - * Using attr->map_fd, attr->key - * returns zero or negative error - */ BPF_MAP_DELETE_ELEM, - - /* lookup key in a given map and return next key - * err = bpf(BPF_MAP_GET_NEXT_KEY, union bpf_attr *attr, u32 size) - * Using attr->map_fd, attr->key, attr->next_key - * returns zero and stores next key or negative error - */ BPF_MAP_GET_NEXT_KEY, - - /* verify and load eBPF program - * prog_fd = bpf(BPF_PROG_LOAD, union bpf_attr *attr, u32 size) - * Using attr->prog_type, attr->insns, attr->license - * returns fd or negative error - */ BPF_PROG_LOAD, + BPF_OBJ_PIN, + BPF_OBJ_GET, }; enum bpf_map_type { @@ -115,6 +81,10 @@ BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PROG_ARRAY, BPF_MAP_TYPE_PERF_EVENT_ARRAY, + BPF_MAP_TYPE_PERCPU_HASH, + BPF_MAP_TYPE_PERCPU_ARRAY, + BPF_MAP_TYPE_STACK_TRACE, + BPF_MAP_TYPE_CGROUP_ARRAY, }; enum bpf_prog_type { @@ -123,6 +93,9 @@ BPF_PROG_TYPE_KPROBE, BPF_PROG_TYPE_SCHED_CLS, BPF_PROG_TYPE_SCHED_ACT, + BPF_PROG_TYPE_TRACEPOINT, + BPF_PROG_TYPE_XDP, + BPF_PROG_TYPE_PERF_EVENT, }; #define BPF_PSEUDO_MAP_FD 1 @@ -132,12 +105,15 @@ #define BPF_NOEXIST 1 /* create new element if it didn't exist */ #define BPF_EXIST 2 /* update existing element */ +#define BPF_F_NO_PREALLOC (1U << 0) + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ __u32 key_size; /* size of key in bytes */ __u32 value_size; /* size of value in bytes */ __u32 max_entries; /* max number of entries in a map */ + __u32 map_flags; /* prealloc or not */ }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ @@ -160,6 +136,11 @@ __aligned_u64 log_buf; /* user supplied buffer */ __u32 kern_version; /* checked when prog_type=kprobe */ }; + + struct { /* anonymous struct used by BPF_OBJ_* commands */ + __aligned_u64 pathname; + __u32 bpf_fd; + }; } __attribute__((aligned(8))); /* integer value in 'imm' field of BPF_CALL instruction selects which helper @@ -272,9 +253,219 @@ BPF_FUNC_skb_get_tunnel_key, BPF_FUNC_skb_set_tunnel_key, BPF_FUNC_perf_event_read, /* u64 bpf_perf_event_read(&map, index) */ + /** + * bpf_redirect(ifindex, flags) - redirect to another netdev + * @ifindex: ifindex of the net device + * @flags: bit 0 - if set, redirect to ingress instead of egress + * other bits - reserved + * Return: TC_ACT_REDIRECT + */ + BPF_FUNC_redirect, + + /** + * bpf_get_route_realm(skb) - retrieve a dst's tclassid + * @skb: pointer to skb + * Return: realm if != 0 + */ + BPF_FUNC_get_route_realm, + + /** + * bpf_perf_event_output(ctx, map, index, data, size) - output perf raw sample + * @ctx: struct pt_regs* + * @map: pointer to perf_event_array map + * @index: index of event in the map + * @data: data on stack to be output as raw data + * @size: size of data + * Return: 0 on success + */ + BPF_FUNC_perf_event_output, + BPF_FUNC_skb_load_bytes, + + /** + * bpf_get_stackid(ctx, map, flags) - walk user or kernel stack and return id + * @ctx: struct pt_regs* + * @map: pointer to stack_trace map + * @flags: bits 0-7 - numer of stack frames to skip + * bit 8 - collect user stack instead of kernel + * bit 9 - compare stacks by hash only + * bit 10 - if two different stacks hash into the same stackid + * discard old + * other bits - reserved + * Return: >= 0 stackid on success or negative error + */ + BPF_FUNC_get_stackid, + + /** + * bpf_csum_diff(from, from_size, to, to_size, seed) - calculate csum diff + * @from: raw from buffer + * @from_size: length of from buffer + * @to: raw to buffer + * @to_size: length of to buffer + * @seed: optional seed + * Return: csum result + */ + BPF_FUNC_csum_diff, + + /** + * bpf_skb_[gs]et_tunnel_opt(skb, opt, size) + * retrieve or populate tunnel options metadata + * @skb: pointer to skb + * @opt: pointer to raw tunnel option data + * @size: size of @opt + * Return: 0 on success for set, option size for get + */ + BPF_FUNC_skb_get_tunnel_opt, + BPF_FUNC_skb_set_tunnel_opt, + + /** + * bpf_skb_change_proto(skb, proto, flags) + * Change protocol of the skb. Currently supported is + * v4 -> v6, v6 -> v4 transitions. The helper will also + * resize the skb. eBPF program is expected to fill the + * new headers via skb_store_bytes and lX_csum_replace. + * @skb: pointer to skb + * @proto: new skb->protocol type + * @flags: reserved + * Return: 0 on success or negative error + */ + BPF_FUNC_skb_change_proto, + + /** + * bpf_skb_change_type(skb, type) + * Change packet type of skb. + * @skb: pointer to skb + * @type: new skb->pkt_type type + * Return: 0 on success or negative error + */ + BPF_FUNC_skb_change_type, + + /** + * bpf_skb_under_cgroup(skb, map, index) - Check cgroup2 membership of skb + * @skb: pointer to skb + * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type + * @index: index of the cgroup in the bpf_map + * Return: + * == 0 skb failed the cgroup2 descendant test + * == 1 skb succeeded the cgroup2 descendant test + * < 0 error + */ + BPF_FUNC_skb_under_cgroup, + + /** + * bpf_get_hash_recalc(skb) + * Retrieve and possibly recalculate skb->hash. + * @skb: pointer to skb + * Return: hash + */ + BPF_FUNC_get_hash_recalc, + + /** + * u64 bpf_get_current_task(void) + * Returns current task_struct + * Return: current + */ + BPF_FUNC_get_current_task, + + /** + * bpf_probe_write_user(void *dst, void *src, int len) + * safely attempt to write to a location + * @dst: destination address in userspace + * @src: source address on stack + * @len: number of bytes to copy + * Return: 0 on success or negative error + */ + BPF_FUNC_probe_write_user, + + /** + * bpf_current_task_under_cgroup(map, index) - Check cgroup2 membership of current task + * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type + * @index: index of the cgroup in the bpf_map + * Return: + * == 0 current failed the cgroup2 descendant test + * == 1 current succeeded the cgroup2 descendant test + * < 0 error + */ + BPF_FUNC_current_task_under_cgroup, + + /** + * bpf_skb_change_tail(skb, len, flags) + * The helper will resize the skb to the given new size, + * to be used f.e. with control messages. + * @skb: pointer to skb + * @len: new skb length + * @flags: reserved + * Return: 0 on success or negative error + */ + BPF_FUNC_skb_change_tail, + + /** + * bpf_skb_pull_data(skb, len) + * The helper will pull in non-linear data in case the + * skb is non-linear and not all of len are part of the + * linear section. Only needed for read/write with direct + * packet access. + * @skb: pointer to skb + * @len: len to make read/writeable + * Return: 0 on success or negative error + */ + BPF_FUNC_skb_pull_data, + + /** + * bpf_csum_update(skb, csum) + * Adds csum into skb->csum in case of CHECKSUM_COMPLETE. + * @skb: pointer to skb + * @csum: csum to add + * Return: csum on success or negative error + */ + BPF_FUNC_csum_update, + + /** + * bpf_set_hash_invalid(skb) + * Invalidate current skb>hash. + * @skb: pointer to skb + */ + BPF_FUNC_set_hash_invalid, + __BPF_FUNC_MAX_ID, }; +/* All flags used by eBPF helper functions, placed here. */ + +/* BPF_FUNC_skb_store_bytes flags. */ +#define BPF_F_RECOMPUTE_CSUM (1ULL << 0) +#define BPF_F_INVALIDATE_HASH (1ULL << 1) + +/* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags. + * First 4 bits are for passing the header field size. + */ +#define BPF_F_HDR_FIELD_MASK 0xfULL + +/* BPF_FUNC_l4_csum_replace flags. */ +#define BPF_F_PSEUDO_HDR (1ULL << 4) +#define BPF_F_MARK_MANGLED_0 (1ULL << 5) + +/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */ +#define BPF_F_INGRESS (1ULL << 0) + +/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ +#define BPF_F_TUNINFO_IPV6 (1ULL << 0) + +/* BPF_FUNC_get_stackid flags. */ +#define BPF_F_SKIP_FIELD_MASK 0xffULL +#define BPF_F_USER_STACK (1ULL << 8) +#define BPF_F_FAST_STACK_CMP (1ULL << 9) +#define BPF_F_REUSE_STACKID (1ULL << 10) + +/* BPF_FUNC_skb_set_tunnel_key flags. */ +#define BPF_F_ZERO_CSUM_TX (1ULL << 1) +#define BPF_F_DONT_FRAGMENT (1ULL << 2) + +/* BPF_FUNC_perf_event_output and BPF_FUNC_perf_event_read flags. */ +#define BPF_F_INDEX_MASK 0xffffffffULL +#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK +/* BPF_FUNC_perf_event_output for sk_buff input context. */ +#define BPF_F_CTXLEN_MASK (0xfffffULL << 32) + /* user accessible mirror of in-kernel sk_buff. * new fields can only be added to the end of this structure */ @@ -293,11 +484,41 @@ __u32 tc_index; __u32 cb[5]; __u32 hash; + __u32 tc_classid; + __u32 data; + __u32 data_end; }; struct bpf_tunnel_key { __u32 tunnel_id; - __u32 remote_ipv4; + union { + __u32 remote_ipv4; + __u32 remote_ipv6[4]; + }; + __u8 tunnel_tos; + __u8 tunnel_ttl; + __u16 tunnel_ext; + __u32 tunnel_label; +}; + +/* User return codes for XDP prog type. + * A valid XDP program must return one of these defined values. All other + * return codes are reserved for future use. Unknown return codes will result + * in packet drop. + */ +enum xdp_action { + XDP_ABORTED = 0, + XDP_DROP, + XDP_PASS, + XDP_TX, +}; + +/* user accessible metadata for XDP packet hook + * new fields must be added to the end of this structure + */ +struct xdp_md { + __u32 data; + __u32 data_end; }; #endif /* __LINUX_BPF_H__ */ diff -Nru iproute2-4.3.0/include/linux/can.h iproute2-4.9.0/include/linux/can.h --- iproute2-4.3.0/include/linux/can.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/can.h 2016-12-12 23:07:42.000000000 +0000 @@ -196,5 +196,6 @@ }; #define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */ +#define CAN_RAW_FILTER_MAX 512 /* maximum number of can_filter set via setsockopt() */ #endif /* !_UAPI_CAN_H */ diff -Nru iproute2-4.3.0/include/linux/devlink.h iproute2-4.9.0/include/linux/devlink.h --- iproute2-4.3.0/include/linux/devlink.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/devlink.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,143 @@ +/* + * include/uapi/linux/devlink.h - Network physical device Netlink interface + * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016 Jiri Pirko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _LINUX_DEVLINK_H_ +#define _LINUX_DEVLINK_H_ + +#define DEVLINK_GENL_NAME "devlink" +#define DEVLINK_GENL_VERSION 0x1 +#define DEVLINK_GENL_MCGRP_CONFIG_NAME "config" + +enum devlink_command { + /* don't change the order or add anything between, this is ABI! */ + DEVLINK_CMD_UNSPEC, + + DEVLINK_CMD_GET, /* can dump */ + DEVLINK_CMD_SET, + DEVLINK_CMD_NEW, + DEVLINK_CMD_DEL, + + DEVLINK_CMD_PORT_GET, /* can dump */ + DEVLINK_CMD_PORT_SET, + DEVLINK_CMD_PORT_NEW, + DEVLINK_CMD_PORT_DEL, + + DEVLINK_CMD_PORT_SPLIT, + DEVLINK_CMD_PORT_UNSPLIT, + + DEVLINK_CMD_SB_GET, /* can dump */ + DEVLINK_CMD_SB_SET, + DEVLINK_CMD_SB_NEW, + DEVLINK_CMD_SB_DEL, + + DEVLINK_CMD_SB_POOL_GET, /* can dump */ + DEVLINK_CMD_SB_POOL_SET, + DEVLINK_CMD_SB_POOL_NEW, + DEVLINK_CMD_SB_POOL_DEL, + + DEVLINK_CMD_SB_PORT_POOL_GET, /* can dump */ + DEVLINK_CMD_SB_PORT_POOL_SET, + DEVLINK_CMD_SB_PORT_POOL_NEW, + DEVLINK_CMD_SB_PORT_POOL_DEL, + + DEVLINK_CMD_SB_TC_POOL_BIND_GET, /* can dump */ + DEVLINK_CMD_SB_TC_POOL_BIND_SET, + DEVLINK_CMD_SB_TC_POOL_BIND_NEW, + DEVLINK_CMD_SB_TC_POOL_BIND_DEL, + + /* Shared buffer occupancy monitoring commands */ + DEVLINK_CMD_SB_OCC_SNAPSHOT, + DEVLINK_CMD_SB_OCC_MAX_CLEAR, + + DEVLINK_CMD_ESWITCH_MODE_GET, + DEVLINK_CMD_ESWITCH_MODE_SET, + /* add new commands above here */ + + __DEVLINK_CMD_MAX, + DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1 +}; + +enum devlink_port_type { + DEVLINK_PORT_TYPE_NOTSET, + DEVLINK_PORT_TYPE_AUTO, + DEVLINK_PORT_TYPE_ETH, + DEVLINK_PORT_TYPE_IB, +}; + +enum devlink_sb_pool_type { + DEVLINK_SB_POOL_TYPE_INGRESS, + DEVLINK_SB_POOL_TYPE_EGRESS, +}; + +/* static threshold - limiting the maximum number of bytes. + * dynamic threshold - limiting the maximum number of bytes + * based on the currently available free space in the shared buffer pool. + * In this mode, the maximum quota is calculated based + * on the following formula: + * max_quota = alpha / (1 + alpha) * Free_Buffer + * While Free_Buffer is the amount of none-occupied buffer associated to + * the relevant pool. + * The value range which can be passed is 0-20 and serves + * for computation of alpha by following formula: + * alpha = 2 ^ (passed_value - 10) + */ + +enum devlink_sb_threshold_type { + DEVLINK_SB_THRESHOLD_TYPE_STATIC, + DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC, +}; + +#define DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX 20 + +enum devlink_eswitch_mode { + DEVLINK_ESWITCH_MODE_LEGACY, + DEVLINK_ESWITCH_MODE_SWITCHDEV, +}; + +enum devlink_attr { + /* don't change the order or add anything between, this is ABI! */ + DEVLINK_ATTR_UNSPEC, + + /* bus name + dev name together are a handle for devlink entity */ + DEVLINK_ATTR_BUS_NAME, /* string */ + DEVLINK_ATTR_DEV_NAME, /* string */ + + DEVLINK_ATTR_PORT_INDEX, /* u32 */ + DEVLINK_ATTR_PORT_TYPE, /* u16 */ + DEVLINK_ATTR_PORT_DESIRED_TYPE, /* u16 */ + DEVLINK_ATTR_PORT_NETDEV_IFINDEX, /* u32 */ + DEVLINK_ATTR_PORT_NETDEV_NAME, /* string */ + DEVLINK_ATTR_PORT_IBDEV_NAME, /* string */ + DEVLINK_ATTR_PORT_SPLIT_COUNT, /* u32 */ + DEVLINK_ATTR_PORT_SPLIT_GROUP, /* u32 */ + DEVLINK_ATTR_SB_INDEX, /* u32 */ + DEVLINK_ATTR_SB_SIZE, /* u32 */ + DEVLINK_ATTR_SB_INGRESS_POOL_COUNT, /* u16 */ + DEVLINK_ATTR_SB_EGRESS_POOL_COUNT, /* u16 */ + DEVLINK_ATTR_SB_INGRESS_TC_COUNT, /* u16 */ + DEVLINK_ATTR_SB_EGRESS_TC_COUNT, /* u16 */ + DEVLINK_ATTR_SB_POOL_INDEX, /* u16 */ + DEVLINK_ATTR_SB_POOL_TYPE, /* u8 */ + DEVLINK_ATTR_SB_POOL_SIZE, /* u32 */ + DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE, /* u8 */ + DEVLINK_ATTR_SB_THRESHOLD, /* u32 */ + DEVLINK_ATTR_SB_TC_INDEX, /* u16 */ + DEVLINK_ATTR_SB_OCC_CUR, /* u32 */ + DEVLINK_ATTR_SB_OCC_MAX, /* u32 */ + DEVLINK_ATTR_ESWITCH_MODE, /* u16 */ + + /* add new attributes above here, update the policy in devlink.c */ + + __DEVLINK_ATTR_MAX, + DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1 +}; + +#endif /* _LINUX_DEVLINK_H_ */ diff -Nru iproute2-4.3.0/include/linux/fib_rules.h iproute2-4.9.0/include/linux/fib_rules.h --- iproute2-4.3.0/include/linux/fib_rules.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/fib_rules.h 2016-12-12 23:07:42.000000000 +0000 @@ -49,6 +49,8 @@ FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ FRA_OIFNAME, + FRA_PAD, + FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ __FRA_MAX }; diff -Nru iproute2-4.3.0/include/linux/genetlink.h iproute2-4.9.0/include/linux/genetlink.h --- iproute2-4.3.0/include/linux/genetlink.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/genetlink.h 2016-12-12 23:07:42.000000000 +0000 @@ -21,6 +21,7 @@ #define GENL_CMD_CAP_DO 0x02 #define GENL_CMD_CAP_DUMP 0x04 #define GENL_CMD_CAP_HASPOL 0x08 +#define GENL_UNS_ADMIN_PERM 0x10 /* * List of reserved static generic netlink identifiers: diff -Nru iproute2-4.3.0/include/linux/gen_stats.h iproute2-4.9.0/include/linux/gen_stats.h --- iproute2-4.3.0/include/linux/gen_stats.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/gen_stats.h 2016-12-12 23:07:42.000000000 +0000 @@ -10,6 +10,7 @@ TCA_STATS_QUEUE, TCA_STATS_APP, TCA_STATS_RATE_EST64, + TCA_STATS_PAD, __TCA_STATS_MAX, }; #define TCA_STATS_MAX (__TCA_STATS_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/if_alg.h iproute2-4.9.0/include/linux/if_alg.h --- iproute2-4.3.0/include/linux/if_alg.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/if_alg.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * if_alg: User-space algorithm interface + * + * Copyright (c) 2010 Herbert Xu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + */ + +#ifndef _LINUX_IF_ALG_H +#define _LINUX_IF_ALG_H + +#include + +struct sockaddr_alg { + __u16 salg_family; + __u8 salg_type[14]; + __u32 salg_feat; + __u32 salg_mask; + __u8 salg_name[64]; +}; + +struct af_alg_iv { + __u32 ivlen; + __u8 iv[0]; +}; + +/* Socket options */ +#define ALG_SET_KEY 1 +#define ALG_SET_IV 2 +#define ALG_SET_OP 3 +#define ALG_SET_AEAD_ASSOCLEN 4 +#define ALG_SET_AEAD_AUTHSIZE 5 + +/* Operations */ +#define ALG_OP_DECRYPT 0 +#define ALG_OP_ENCRYPT 1 + +#endif /* _LINUX_IF_ALG_H */ diff -Nru iproute2-4.3.0/include/linux/if_bonding.h iproute2-4.9.0/include/linux/if_bonding.h --- iproute2-4.3.0/include/linux/if_bonding.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/if_bonding.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,130 @@ +/* + * Bond several ethernet interfaces into a Cisco, running 'Etherchannel'. + * + * + * Portions are (c) Copyright 1995 Simon "Guru Aleph-Null" Janes + * NCM: Network and Communications Management, Inc. + * + * BUT, I'm the one who modified it for ethernet, so: + * (c) Copyright 1999, Thomas Davis, tadavis@lbl.gov + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * 2003/03/18 - Amir Noam + * - Added support for getting slave's speed and duplex via ethtool. + * Needed for 802.3ad and other future modes. + * + * 2003/03/18 - Tsippy Mendelson and + * Shmulik Hen + * - Enable support of modes that need to use the unique mac address of + * each slave. + * + * 2003/03/18 - Tsippy Mendelson and + * Amir Noam + * - Moved driver's private data types to bonding.h + * + * 2003/03/18 - Amir Noam , + * Tsippy Mendelson and + * Shmulik Hen + * - Added support for IEEE 802.3ad Dynamic link aggregation mode. + * + * 2003/05/01 - Amir Noam + * - Added ABI version control to restore compatibility between + * new/old ifenslave and new/old bonding. + * + * 2003/12/01 - Shmulik Hen + * - Code cleanup and style changes + * + * 2005/05/05 - Jason Gabler + * - added definitions for various XOR hashing policies + */ + +#ifndef _LINUX_IF_BONDING_H +#define _LINUX_IF_BONDING_H + +#include +#include +#include + +/* userland - kernel ABI version (2003/05/08) */ +#define BOND_ABI_VERSION 2 + +/* + * We can remove these ioctl definitions in 2.5. People should use the + * SIOC*** versions of them instead + */ +#define BOND_ENSLAVE_OLD (SIOCDEVPRIVATE) +#define BOND_RELEASE_OLD (SIOCDEVPRIVATE + 1) +#define BOND_SETHWADDR_OLD (SIOCDEVPRIVATE + 2) +#define BOND_SLAVE_INFO_QUERY_OLD (SIOCDEVPRIVATE + 11) +#define BOND_INFO_QUERY_OLD (SIOCDEVPRIVATE + 12) +#define BOND_CHANGE_ACTIVE_OLD (SIOCDEVPRIVATE + 13) + +#define BOND_CHECK_MII_STATUS (SIOCGMIIPHY) + +#define BOND_MODE_ROUNDROBIN 0 +#define BOND_MODE_ACTIVEBACKUP 1 +#define BOND_MODE_XOR 2 +#define BOND_MODE_BROADCAST 3 +#define BOND_MODE_8023AD 4 +#define BOND_MODE_TLB 5 +#define BOND_MODE_ALB 6 /* TLB + RLB (receive load balancing) */ + +/* each slave's link has 4 states */ +#define BOND_LINK_UP 0 /* link is up and running */ +#define BOND_LINK_FAIL 1 /* link has just gone down */ +#define BOND_LINK_DOWN 2 /* link has been down for too long time */ +#define BOND_LINK_BACK 3 /* link is going back */ + +/* each slave has several states */ +#define BOND_STATE_ACTIVE 0 /* link is active */ +#define BOND_STATE_BACKUP 1 /* link is backup */ + +#define BOND_DEFAULT_MAX_BONDS 1 /* Default maximum number of devices to support */ + +#define BOND_DEFAULT_TX_QUEUES 16 /* Default number of tx queues per device */ + +#define BOND_DEFAULT_RESEND_IGMP 1 /* Default number of IGMP membership reports */ + +/* hashing types */ +#define BOND_XMIT_POLICY_LAYER2 0 /* layer 2 (MAC only), default */ +#define BOND_XMIT_POLICY_LAYER34 1 /* layer 3+4 (IP ^ (TCP || UDP)) */ +#define BOND_XMIT_POLICY_LAYER23 2 /* layer 2+3 (IP ^ MAC) */ +#define BOND_XMIT_POLICY_ENCAP23 3 /* encapsulated layer 2+3 */ +#define BOND_XMIT_POLICY_ENCAP34 4 /* encapsulated layer 3+4 */ + +typedef struct ifbond { + __s32 bond_mode; + __s32 num_slaves; + __s32 miimon; +} ifbond; + +typedef struct ifslave { + __s32 slave_id; /* Used as an IN param to the BOND_SLAVE_INFO_QUERY ioctl */ + char slave_name[IFNAMSIZ]; + __s8 link; + __s8 state; + __u32 link_failure_count; +} ifslave; + +struct ad_info { + __u16 aggregator_id; + __u16 ports; + __u16 actor_key; + __u16 partner_key; + __u8 partner_system[ETH_ALEN]; +}; + +#endif /* _LINUX_IF_BONDING_H */ + +/* + * Local variables: + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ + diff -Nru iproute2-4.3.0/include/linux/if_bridge.h iproute2-4.9.0/include/linux/if_bridge.h --- iproute2-4.3.0/include/linux/if_bridge.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/if_bridge.h 2016-12-12 23:07:42.000000000 +0000 @@ -127,20 +127,37 @@ #define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */ #define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */ #define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ +#define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */ struct bridge_vlan_info { __u16 flags; __u16 vid; }; +struct bridge_vlan_xstats { + __u64 rx_bytes; + __u64 rx_packets; + __u64 tx_bytes; + __u64 tx_packets; + __u16 vid; + __u16 flags; + __u32 pad2; +}; + /* Bridge multicast database attributes * [MDBA_MDB] = { * [MDBA_MDB_ENTRY] = { - * [MDBA_MDB_ENTRY_INFO] + * [MDBA_MDB_ENTRY_INFO] { + * struct br_mdb_entry + * [MDBA_MDB_EATTR attributes] + * } * } * } * [MDBA_ROUTER] = { - * [MDBA_ROUTER_PORT] + * [MDBA_ROUTER_PORT] = { + * u32 ifindex + * [MDBA_ROUTER_PATTR attributes] + * } * } */ enum { @@ -165,6 +182,22 @@ }; #define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1) +/* per mdb entry additional attributes */ +enum { + MDBA_MDB_EATTR_UNSPEC, + MDBA_MDB_EATTR_TIMER, + __MDBA_MDB_EATTR_MAX +}; +#define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1) + +/* multicast router types */ +enum { + MDB_RTR_TYPE_DISABLED, + MDB_RTR_TYPE_TEMP_QUERY, + MDB_RTR_TYPE_PERM, + MDB_RTR_TYPE_TEMP +}; + enum { MDBA_ROUTER_UNSPEC, MDBA_ROUTER_PORT, @@ -172,6 +205,15 @@ }; #define MDBA_ROUTER_MAX (__MDBA_ROUTER_MAX - 1) +/* router port attributes */ +enum { + MDBA_ROUTER_PATTR_UNSPEC, + MDBA_ROUTER_PATTR_TIMER, + MDBA_ROUTER_PATTR_TYPE, + __MDBA_ROUTER_PATTR_MAX +}; +#define MDBA_ROUTER_PATTR_MAX (__MDBA_ROUTER_PATTR_MAX - 1) + struct br_port_msg { __u8 family; __u32 ifindex; @@ -182,6 +224,8 @@ #define MDB_TEMPORARY 0 #define MDB_PERMANENT 1 __u8 state; +#define MDB_FLAGS_OFFLOAD (1 << 0) + __u8 flags; __u16 vid; struct { union { @@ -199,4 +243,41 @@ }; #define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1) +/* Embedded inside LINK_XSTATS_TYPE_BRIDGE */ +enum { + BRIDGE_XSTATS_UNSPEC, + BRIDGE_XSTATS_VLAN, + BRIDGE_XSTATS_MCAST, + BRIDGE_XSTATS_PAD, + __BRIDGE_XSTATS_MAX +}; +#define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1) + +enum { + BR_MCAST_DIR_RX, + BR_MCAST_DIR_TX, + BR_MCAST_DIR_SIZE +}; + +/* IGMP/MLD statistics */ +struct br_mcast_stats { + __u64 igmp_v1queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_v2queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_v3queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_leaves[BR_MCAST_DIR_SIZE]; + __u64 igmp_v1reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_v2reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_v3reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_parse_errors; + + __u64 mld_v1queries[BR_MCAST_DIR_SIZE]; + __u64 mld_v2queries[BR_MCAST_DIR_SIZE]; + __u64 mld_leaves[BR_MCAST_DIR_SIZE]; + __u64 mld_v1reports[BR_MCAST_DIR_SIZE]; + __u64 mld_v2reports[BR_MCAST_DIR_SIZE]; + __u64 mld_parse_errors; + + __u64 mcast_bytes[BR_MCAST_DIR_SIZE]; + __u64 mcast_packets[BR_MCAST_DIR_SIZE]; +}; #endif /* _LINUX_IF_BRIDGE_H */ diff -Nru iproute2-4.3.0/include/linux/if_ether.h iproute2-4.9.0/include/linux/if_ether.h --- iproute2-4.3.0/include/linux/if_ether.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/if_ether.h 2016-12-12 23:07:42.000000000 +0000 @@ -83,14 +83,17 @@ #define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ #define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ #define ETH_P_TIPC 0x88CA /* TIPC */ +#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */ #define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ #define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ #define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ +#define ETH_P_NCSI 0x88F8 /* NCSI protocol */ #define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */ #define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ #define ETH_P_TDLS 0x890D /* TDLS */ #define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ #define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */ +#define ETH_P_HSR 0x892F /* IEC 62439-3 HSRv1 */ #define ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */ #define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ diff -Nru iproute2-4.3.0/include/linux/if.h iproute2-4.9.0/include/linux/if.h --- iproute2-4.3.0/include/linux/if.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/if.h 2016-12-12 23:07:42.000000000 +0000 @@ -19,14 +19,20 @@ #ifndef _LINUX_IF_H #define _LINUX_IF_H +#include /* for compatibility with glibc */ #include /* for "__kernel_caddr_t" et al */ #include /* for "struct sockaddr" et al */ /* for "__user" et al */ +#if __UAPI_DEF_IF_IFNAMSIZ #define IFNAMSIZ 16 +#endif /* __UAPI_DEF_IF_IFNAMSIZ */ #define IFALIASZ 256 #include +/* For glibc compatibility. An empty enum does not compile. */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || \ + __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 /** * enum net_device_flags - &struct net_device flags * @@ -37,7 +43,7 @@ * are shared for all types of net_devices. The sysfs entries are available * via /sys/class/net//flags. Flags which can be toggled through sysfs * are annotated below, note that only a few flags can be toggled and some - * other flags are always always preserved from the original net_device flags + * other flags are always preserved from the original net_device flags * even if you try to set them via sysfs. Flags which are always preserved * are kept under the flag grouping @IFF_VOLATILE. Flags which are __volatile__ * are annotated below as such. @@ -68,6 +74,8 @@ * @IFF_ECHO: echo sent packets. Volatile. */ enum net_device_flags { +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS IFF_UP = 1<<0, /* sysfs */ IFF_BROADCAST = 1<<1, /* __volatile__ */ IFF_DEBUG = 1<<2, /* sysfs */ @@ -84,11 +92,17 @@ IFF_PORTSEL = 1<<13, /* sysfs */ IFF_AUTOMEDIA = 1<<14, /* sysfs */ IFF_DYNAMIC = 1<<15, /* sysfs */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO IFF_LOWER_UP = 1<<16, /* __volatile__ */ IFF_DORMANT = 1<<17, /* __volatile__ */ IFF_ECHO = 1<<18, /* __volatile__ */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ }; +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 */ +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS #define IFF_UP IFF_UP #define IFF_BROADCAST IFF_BROADCAST #define IFF_DEBUG IFF_DEBUG @@ -105,9 +119,13 @@ #define IFF_PORTSEL IFF_PORTSEL #define IFF_AUTOMEDIA IFF_AUTOMEDIA #define IFF_DYNAMIC IFF_DYNAMIC +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ + +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO #define IFF_LOWER_UP IFF_LOWER_UP #define IFF_DORMANT IFF_DORMANT #define IFF_ECHO IFF_ECHO +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ #define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) @@ -166,6 +184,8 @@ * being very small might be worth keeping for clean configuration. */ +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFMAP struct ifmap { unsigned long mem_start; unsigned long mem_end; @@ -175,6 +195,7 @@ unsigned char port; /* 3 bytes spare */ }; +#endif /* __UAPI_DEF_IF_IFMAP */ struct if_settings { unsigned int type; /* Type of physical device or protocol */ @@ -200,6 +221,8 @@ * remainder may be interface specific. */ +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFREQ struct ifreq { #define IFHWADDRLEN 6 union @@ -223,6 +246,7 @@ struct if_settings ifru_settings; } ifr_ifru; }; +#endif /* __UAPI_DEF_IF_IFREQ */ #define ifr_name ifr_ifrn.ifrn_name /* interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ @@ -249,6 +273,8 @@ * must know all networks accessible). */ +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFCONF struct ifconf { int ifc_len; /* size of buffer */ union { @@ -256,6 +282,8 @@ struct ifreq *ifcu_req; } ifc_ifcu; }; +#endif /* __UAPI_DEF_IF_IFCONF */ + #define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ #define ifc_req ifc_ifcu.ifcu_req /* array of structures */ diff -Nru iproute2-4.3.0/include/linux/if_link.h iproute2-4.9.0/include/linux/if_link.h --- iproute2-4.3.0/include/linux/if_link.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/if_link.h 2016-12-12 23:07:42.000000000 +0000 @@ -35,6 +35,8 @@ /* for cslip etc */ __u32 rx_compressed; __u32 tx_compressed; + + __u32 rx_nohandler; /* dropped, no handler found */ }; /* The main device statistics structure */ @@ -68,6 +70,8 @@ /* for cslip etc */ __u64 rx_compressed; __u64 tx_compressed; + + __u64 rx_nohandler; /* dropped, no handler found */ }; /* The struct should be in sync with struct ifmap */ @@ -149,6 +153,10 @@ IFLA_LINK_NETNSID, IFLA_PHYS_PORT_NAME, IFLA_PROTO_DOWN, + IFLA_GSO_MAX_SEGS, + IFLA_GSO_MAX_SIZE, + IFLA_PAD, + IFLA_XDP, __IFLA_MAX }; @@ -216,6 +224,7 @@ IN6_ADDR_GEN_MODE_EUI64, IN6_ADDR_GEN_MODE_NONE, IN6_ADDR_GEN_MODE_STABLE_PRIVACY, + IN6_ADDR_GEN_MODE_RANDOM, }; /* Bridge section */ @@ -230,11 +239,50 @@ IFLA_BR_PRIORITY, IFLA_BR_VLAN_FILTERING, IFLA_BR_VLAN_PROTOCOL, + IFLA_BR_GROUP_FWD_MASK, + IFLA_BR_ROOT_ID, + IFLA_BR_BRIDGE_ID, + IFLA_BR_ROOT_PORT, + IFLA_BR_ROOT_PATH_COST, + IFLA_BR_TOPOLOGY_CHANGE, + IFLA_BR_TOPOLOGY_CHANGE_DETECTED, + IFLA_BR_HELLO_TIMER, + IFLA_BR_TCN_TIMER, + IFLA_BR_TOPOLOGY_CHANGE_TIMER, + IFLA_BR_GC_TIMER, + IFLA_BR_GROUP_ADDR, + IFLA_BR_FDB_FLUSH, + IFLA_BR_MCAST_ROUTER, + IFLA_BR_MCAST_SNOOPING, + IFLA_BR_MCAST_QUERY_USE_IFADDR, + IFLA_BR_MCAST_QUERIER, + IFLA_BR_MCAST_HASH_ELASTICITY, + IFLA_BR_MCAST_HASH_MAX, + IFLA_BR_MCAST_LAST_MEMBER_CNT, + IFLA_BR_MCAST_STARTUP_QUERY_CNT, + IFLA_BR_MCAST_LAST_MEMBER_INTVL, + IFLA_BR_MCAST_MEMBERSHIP_INTVL, + IFLA_BR_MCAST_QUERIER_INTVL, + IFLA_BR_MCAST_QUERY_INTVL, + IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + IFLA_BR_MCAST_STARTUP_QUERY_INTVL, + IFLA_BR_NF_CALL_IPTABLES, + IFLA_BR_NF_CALL_IP6TABLES, + IFLA_BR_NF_CALL_ARPTABLES, + IFLA_BR_VLAN_DEFAULT_PVID, + IFLA_BR_PAD, + IFLA_BR_VLAN_STATS_ENABLED, + IFLA_BR_MCAST_STATS_ENABLED, __IFLA_BR_MAX, }; #define IFLA_BR_MAX (__IFLA_BR_MAX - 1) +struct ifla_bridge_id { + __u8 prio[2]; + __u8 addr[6]; /* ETH_ALEN */ +}; + enum { BRIDGE_MODE_UNSPEC, BRIDGE_MODE_HAIRPIN, @@ -254,6 +302,21 @@ IFLA_BRPORT_PROXYARP, /* proxy ARP */ IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */ IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ + IFLA_BRPORT_ROOT_ID, /* designated root */ + IFLA_BRPORT_BRIDGE_ID, /* designated bridge */ + IFLA_BRPORT_DESIGNATED_PORT, + IFLA_BRPORT_DESIGNATED_COST, + IFLA_BRPORT_ID, + IFLA_BRPORT_NO, + IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, + IFLA_BRPORT_CONFIG_PENDING, + IFLA_BRPORT_MESSAGE_AGE_TIMER, + IFLA_BRPORT_FORWARD_DELAY_TIMER, + IFLA_BRPORT_HOLD_TIMER, + IFLA_BRPORT_FLUSH, + IFLA_BRPORT_MULTICAST_ROUTER, + IFLA_BRPORT_PAD, + IFLA_BRPORT_MCAST_FLOOD, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) @@ -349,6 +412,44 @@ #define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1) +enum { + IFLA_VRF_PORT_UNSPEC, + IFLA_VRF_PORT_TABLE, + __IFLA_VRF_PORT_MAX +}; + +#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1) + +/* MACSEC section */ +enum { + IFLA_MACSEC_UNSPEC, + IFLA_MACSEC_SCI, + IFLA_MACSEC_PORT, + IFLA_MACSEC_ICV_LEN, + IFLA_MACSEC_CIPHER_SUITE, + IFLA_MACSEC_WINDOW, + IFLA_MACSEC_ENCODING_SA, + IFLA_MACSEC_ENCRYPT, + IFLA_MACSEC_PROTECT, + IFLA_MACSEC_INC_SCI, + IFLA_MACSEC_ES, + IFLA_MACSEC_SCB, + IFLA_MACSEC_REPLAY_PROTECT, + IFLA_MACSEC_VALIDATION, + IFLA_MACSEC_PAD, + __IFLA_MACSEC_MAX, +}; + +#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1) + +enum macsec_validation_type { + MACSEC_VALIDATE_DISABLED = 0, + MACSEC_VALIDATE_CHECK = 1, + MACSEC_VALIDATE_STRICT = 2, + __MACSEC_VALIDATE_END, + MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, +}; + /* IPVLAN section */ enum { IFLA_IPVLAN_UNSPEC, @@ -361,6 +462,7 @@ enum ipvlan_mode { IPVLAN_MODE_L2 = 0, IPVLAN_MODE_L3, + IPVLAN_MODE_L3S, IPVLAN_MODE_MAX }; @@ -392,6 +494,8 @@ IFLA_VXLAN_GBP, IFLA_VXLAN_REMCSUM_NOPARTIAL, IFLA_VXLAN_COLLECT_METADATA, + IFLA_VXLAN_LABEL, + IFLA_VXLAN_GPE, __IFLA_VXLAN_MAX }; #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) @@ -410,10 +514,33 @@ IFLA_GENEVE_TOS, IFLA_GENEVE_PORT, /* destination port */ IFLA_GENEVE_COLLECT_METADATA, + IFLA_GENEVE_REMOTE6, + IFLA_GENEVE_UDP_CSUM, + IFLA_GENEVE_UDP_ZERO_CSUM6_TX, + IFLA_GENEVE_UDP_ZERO_CSUM6_RX, + IFLA_GENEVE_LABEL, __IFLA_GENEVE_MAX }; #define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) +/* PPP section */ +enum { + IFLA_PPP_UNSPEC, + IFLA_PPP_DEV_FD, + __IFLA_PPP_MAX +}; +#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1) + +/* GTP section */ +enum { + IFLA_GTP_UNSPEC, + IFLA_GTP_FD0, + IFLA_GTP_FD1, + IFLA_GTP_PDP_HASHSIZE, + __IFLA_GTP_MAX, +}; +#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) + /* Bonding section */ enum { @@ -490,7 +617,7 @@ enum { IFLA_VF_UNSPEC, IFLA_VF_MAC, /* Hardware queue specific attributes */ - IFLA_VF_VLAN, + IFLA_VF_VLAN, /* VLAN ID and QoS */ IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */ IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */ IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */ @@ -499,6 +626,10 @@ * on/off switch */ IFLA_VF_STATS, /* network device statistics */ + IFLA_VF_TRUST, /* Trust VF */ + IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */ + IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */ + IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */ __IFLA_VF_MAX, }; @@ -515,6 +646,22 @@ __u32 qos; }; +enum { + IFLA_VF_VLAN_INFO_UNSPEC, + IFLA_VF_VLAN_INFO, /* VLAN ID, QoS and VLAN protocol */ + __IFLA_VF_VLAN_INFO_MAX, +}; + +#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1) +#define MAX_VLAN_LIST_LEN 1 + +struct ifla_vf_vlan_info { + __u32 vf; + __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + __u32 qos; + __be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */ +}; + struct ifla_vf_tx_rate { __u32 vf; __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ @@ -531,6 +678,11 @@ __u32 setting; }; +struct ifla_vf_guid { + __u32 vf; + __u64 guid; +}; + enum { IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */ IFLA_VF_LINK_STATE_ENABLE, /* link always up */ @@ -555,11 +707,17 @@ IFLA_VF_STATS_TX_BYTES, IFLA_VF_STATS_BROADCAST, IFLA_VF_STATS_MULTICAST, + IFLA_VF_STATS_PAD, __IFLA_VF_STATS_MAX, }; #define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1) +struct ifla_vf_trust { + __u32 vf; + __u32 setting; +}; + /* VF ports management section * * Nested layout of set/get msg is: @@ -660,9 +818,67 @@ IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */ IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */ IFLA_HSR_SEQ_NR, + IFLA_HSR_VERSION, /* HSR version */ __IFLA_HSR_MAX, }; #define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1) +/* STATS section */ + +struct if_stats_msg { + __u8 family; + __u8 pad1; + __u16 pad2; + __u32 ifindex; + __u32 filter_mask; +}; + +/* A stats attribute can be netdev specific or a global stat. + * For netdev stats, lets use the prefix IFLA_STATS_LINK_* + */ +enum { + IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */ + IFLA_STATS_LINK_64, + IFLA_STATS_LINK_XSTATS, + IFLA_STATS_LINK_XSTATS_SLAVE, + IFLA_STATS_LINK_OFFLOAD_XSTATS, + __IFLA_STATS_MAX, +}; + +#define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1) + +#define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1)) + +/* These are embedded into IFLA_STATS_LINK_XSTATS: + * [IFLA_STATS_LINK_XSTATS] + * -> [LINK_XSTATS_TYPE_xxx] + * -> [rtnl link type specific attributes] + */ +enum { + LINK_XSTATS_TYPE_UNSPEC, + LINK_XSTATS_TYPE_BRIDGE, + __LINK_XSTATS_TYPE_MAX +}; +#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1) + +/* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */ +enum { + IFLA_OFFLOAD_XSTATS_UNSPEC, + IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */ + __IFLA_OFFLOAD_XSTATS_MAX +}; +#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1) + +/* XDP section */ + +enum { + IFLA_XDP_UNSPEC, + IFLA_XDP_FD, + IFLA_XDP_ATTACHED, + __IFLA_XDP_MAX, +}; + +#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1) + #endif /* _LINUX_IF_LINK_H */ diff -Nru iproute2-4.3.0/include/linux/if_macsec.h iproute2-4.9.0/include/linux/if_macsec.h --- iproute2-4.3.0/include/linux/if_macsec.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/if_macsec.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,171 @@ +/* + * include/uapi/linux/if_macsec.h - MACsec device + * + * Copyright (c) 2015 Sabrina Dubroca + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _MACSEC_H +#define _MACSEC_H + +#include + +#define MACSEC_GENL_NAME "macsec" +#define MACSEC_GENL_VERSION 1 + +#define MACSEC_MAX_KEY_LEN 128 + +#define MACSEC_KEYID_LEN 16 + +#define MACSEC_DEFAULT_CIPHER_ID 0x0080020001000001ULL +#define MACSEC_DEFAULT_CIPHER_ALT 0x0080C20001000001ULL + +#define MACSEC_MIN_ICV_LEN 8 +#define MACSEC_MAX_ICV_LEN 32 +/* upper limit for ICV length as recommended by IEEE802.1AE-2006 */ +#define MACSEC_STD_ICV_LEN 16 + +enum macsec_attrs { + MACSEC_ATTR_UNSPEC, + MACSEC_ATTR_IFINDEX, /* u32, ifindex of the MACsec netdevice */ + MACSEC_ATTR_RXSC_CONFIG, /* config, nested macsec_rxsc_attrs */ + MACSEC_ATTR_SA_CONFIG, /* config, nested macsec_sa_attrs */ + MACSEC_ATTR_SECY, /* dump, nested macsec_secy_attrs */ + MACSEC_ATTR_TXSA_LIST, /* dump, nested, macsec_sa_attrs for each TXSA */ + MACSEC_ATTR_RXSC_LIST, /* dump, nested, macsec_rxsc_attrs for each RXSC */ + MACSEC_ATTR_TXSC_STATS, /* dump, nested, macsec_txsc_stats_attr */ + MACSEC_ATTR_SECY_STATS, /* dump, nested, macsec_secy_stats_attr */ + __MACSEC_ATTR_END, + NUM_MACSEC_ATTR = __MACSEC_ATTR_END, + MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1, +}; + +enum macsec_secy_attrs { + MACSEC_SECY_ATTR_UNSPEC, + MACSEC_SECY_ATTR_SCI, + MACSEC_SECY_ATTR_ENCODING_SA, + MACSEC_SECY_ATTR_WINDOW, + MACSEC_SECY_ATTR_CIPHER_SUITE, + MACSEC_SECY_ATTR_ICV_LEN, + MACSEC_SECY_ATTR_PROTECT, + MACSEC_SECY_ATTR_REPLAY, + MACSEC_SECY_ATTR_OPER, + MACSEC_SECY_ATTR_VALIDATE, + MACSEC_SECY_ATTR_ENCRYPT, + MACSEC_SECY_ATTR_INC_SCI, + MACSEC_SECY_ATTR_ES, + MACSEC_SECY_ATTR_SCB, + MACSEC_SECY_ATTR_PAD, + __MACSEC_SECY_ATTR_END, + NUM_MACSEC_SECY_ATTR = __MACSEC_SECY_ATTR_END, + MACSEC_SECY_ATTR_MAX = __MACSEC_SECY_ATTR_END - 1, +}; + +enum macsec_rxsc_attrs { + MACSEC_RXSC_ATTR_UNSPEC, + MACSEC_RXSC_ATTR_SCI, /* config/dump, u64 */ + MACSEC_RXSC_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_RXSC_ATTR_SA_LIST, /* dump, nested */ + MACSEC_RXSC_ATTR_STATS, /* dump, nested, macsec_rxsc_stats_attr */ + MACSEC_RXSC_ATTR_PAD, + __MACSEC_RXSC_ATTR_END, + NUM_MACSEC_RXSC_ATTR = __MACSEC_RXSC_ATTR_END, + MACSEC_RXSC_ATTR_MAX = __MACSEC_RXSC_ATTR_END - 1, +}; + +enum macsec_sa_attrs { + MACSEC_SA_ATTR_UNSPEC, + MACSEC_SA_ATTR_AN, /* config/dump, u8 0..3 */ + MACSEC_SA_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_SA_ATTR_PN, /* config/dump, u32 */ + MACSEC_SA_ATTR_KEY, /* config, data */ + MACSEC_SA_ATTR_KEYID, /* config/dump, 128-bit */ + MACSEC_SA_ATTR_STATS, /* dump, nested, macsec_sa_stats_attr */ + MACSEC_SA_ATTR_PAD, + __MACSEC_SA_ATTR_END, + NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END, + MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1, +}; + +enum macsec_nl_commands { + MACSEC_CMD_GET_TXSC, + MACSEC_CMD_ADD_RXSC, + MACSEC_CMD_DEL_RXSC, + MACSEC_CMD_UPD_RXSC, + MACSEC_CMD_ADD_TXSA, + MACSEC_CMD_DEL_TXSA, + MACSEC_CMD_UPD_TXSA, + MACSEC_CMD_ADD_RXSA, + MACSEC_CMD_DEL_RXSA, + MACSEC_CMD_UPD_RXSA, +}; + +/* u64 per-RXSC stats */ +enum macsec_rxsc_stats_attr { + MACSEC_RXSC_STATS_ATTR_UNSPEC, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_RXSC_STATS_ATTR_PAD, + __MACSEC_RXSC_STATS_ATTR_END, + NUM_MACSEC_RXSC_STATS_ATTR = __MACSEC_RXSC_STATS_ATTR_END, + MACSEC_RXSC_STATS_ATTR_MAX = __MACSEC_RXSC_STATS_ATTR_END - 1, +}; + +/* u32 per-{RX,TX}SA stats */ +enum macsec_sa_stats_attr { + MACSEC_SA_STATS_ATTR_UNSPEC, + MACSEC_SA_STATS_ATTR_IN_PKTS_OK, + MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, + __MACSEC_SA_STATS_ATTR_END, + NUM_MACSEC_SA_STATS_ATTR = __MACSEC_SA_STATS_ATTR_END, + MACSEC_SA_STATS_ATTR_MAX = __MACSEC_SA_STATS_ATTR_END - 1, +}; + +/* u64 per-TXSC stats */ +enum macsec_txsc_stats_attr { + MACSEC_TXSC_STATS_ATTR_UNSPEC, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_PAD, + __MACSEC_TXSC_STATS_ATTR_END, + NUM_MACSEC_TXSC_STATS_ATTR = __MACSEC_TXSC_STATS_ATTR_END, + MACSEC_TXSC_STATS_ATTR_MAX = __MACSEC_TXSC_STATS_ATTR_END - 1, +}; + +/* u64 per-SecY stats */ +enum macsec_secy_stats_attr { + MACSEC_SECY_STATS_ATTR_UNSPEC, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN, + MACSEC_SECY_STATS_ATTR_PAD, + __MACSEC_SECY_STATS_ATTR_END, + NUM_MACSEC_SECY_STATS_ATTR = __MACSEC_SECY_STATS_ATTR_END, + MACSEC_SECY_STATS_ATTR_MAX = __MACSEC_SECY_STATS_ATTR_END - 1, +}; + +#endif /* _MACSEC_H */ diff -Nru iproute2-4.3.0/include/linux/if_packet.h iproute2-4.9.0/include/linux/if_packet.h --- iproute2-4.3.0/include/linux/if_packet.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/if_packet.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,301 @@ +#ifndef __LINUX_IF_PACKET_H +#define __LINUX_IF_PACKET_H + +#include + +struct sockaddr_pkt { + unsigned short spkt_family; + unsigned char spkt_device[14]; + __be16 spkt_protocol; +}; + +struct sockaddr_ll { + unsigned short sll_family; + __be16 sll_protocol; + int sll_ifindex; + unsigned short sll_hatype; + unsigned char sll_pkttype; + unsigned char sll_halen; + unsigned char sll_addr[8]; +}; + +/* Packet types */ + +#define PACKET_HOST 0 /* To us */ +#define PACKET_BROADCAST 1 /* To all */ +#define PACKET_MULTICAST 2 /* To group */ +#define PACKET_OTHERHOST 3 /* To someone else */ +#define PACKET_OUTGOING 4 /* Outgoing of any type */ +#define PACKET_LOOPBACK 5 /* MC/BRD frame looped back */ +#define PACKET_USER 6 /* To user space */ +#define PACKET_KERNEL 7 /* To kernel space */ +/* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */ +#define PACKET_FASTROUTE 6 /* Fastrouted frame */ + +/* Packet socket options */ + +#define PACKET_ADD_MEMBERSHIP 1 +#define PACKET_DROP_MEMBERSHIP 2 +#define PACKET_RECV_OUTPUT 3 +/* Value 4 is still used by obsolete turbo-packet. */ +#define PACKET_RX_RING 5 +#define PACKET_STATISTICS 6 +#define PACKET_COPY_THRESH 7 +#define PACKET_AUXDATA 8 +#define PACKET_ORIGDEV 9 +#define PACKET_VERSION 10 +#define PACKET_HDRLEN 11 +#define PACKET_RESERVE 12 +#define PACKET_TX_RING 13 +#define PACKET_LOSS 14 +#define PACKET_VNET_HDR 15 +#define PACKET_TX_TIMESTAMP 16 +#define PACKET_TIMESTAMP 17 +#define PACKET_FANOUT 18 +#define PACKET_TX_HAS_OFF 19 +#define PACKET_QDISC_BYPASS 20 +#define PACKET_ROLLOVER_STATS 21 +#define PACKET_FANOUT_DATA 22 + +#define PACKET_FANOUT_HASH 0 +#define PACKET_FANOUT_LB 1 +#define PACKET_FANOUT_CPU 2 +#define PACKET_FANOUT_ROLLOVER 3 +#define PACKET_FANOUT_RND 4 +#define PACKET_FANOUT_QM 5 +#define PACKET_FANOUT_CBPF 6 +#define PACKET_FANOUT_EBPF 7 +#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 +#define PACKET_FANOUT_FLAG_DEFRAG 0x8000 + +struct tpacket_stats { + unsigned int tp_packets; + unsigned int tp_drops; +}; + +struct tpacket_stats_v3 { + unsigned int tp_packets; + unsigned int tp_drops; + unsigned int tp_freeze_q_cnt; +}; + +struct tpacket_rollover_stats { + __aligned_u64 tp_all; + __aligned_u64 tp_huge; + __aligned_u64 tp_failed; +}; + +union tpacket_stats_u { + struct tpacket_stats stats1; + struct tpacket_stats_v3 stats3; +}; + +struct tpacket_auxdata { + __u32 tp_status; + __u32 tp_len; + __u32 tp_snaplen; + __u16 tp_mac; + __u16 tp_net; + __u16 tp_vlan_tci; + __u16 tp_vlan_tpid; +}; + +/* Rx ring - header status */ +#define TP_STATUS_KERNEL 0 +#define TP_STATUS_USER (1 << 0) +#define TP_STATUS_COPY (1 << 1) +#define TP_STATUS_LOSING (1 << 2) +#define TP_STATUS_CSUMNOTREADY (1 << 3) +#define TP_STATUS_VLAN_VALID (1 << 4) /* auxdata has valid tp_vlan_tci */ +#define TP_STATUS_BLK_TMO (1 << 5) +#define TP_STATUS_VLAN_TPID_VALID (1 << 6) /* auxdata has valid tp_vlan_tpid */ +#define TP_STATUS_CSUM_VALID (1 << 7) + +/* Tx ring - header status */ +#define TP_STATUS_AVAILABLE 0 +#define TP_STATUS_SEND_REQUEST (1 << 0) +#define TP_STATUS_SENDING (1 << 1) +#define TP_STATUS_WRONG_FORMAT (1 << 2) + +/* Rx and Tx ring - header status */ +#define TP_STATUS_TS_SOFTWARE (1 << 29) +#define TP_STATUS_TS_SYS_HARDWARE (1 << 30) /* deprecated, never set */ +#define TP_STATUS_TS_RAW_HARDWARE (1 << 31) + +/* Rx ring - feature request bits */ +#define TP_FT_REQ_FILL_RXHASH 0x1 + +struct tpacket_hdr { + unsigned long tp_status; + unsigned int tp_len; + unsigned int tp_snaplen; + unsigned short tp_mac; + unsigned short tp_net; + unsigned int tp_sec; + unsigned int tp_usec; +}; + +#define TPACKET_ALIGNMENT 16 +#define TPACKET_ALIGN(x) (((x)+TPACKET_ALIGNMENT-1)&~(TPACKET_ALIGNMENT-1)) +#define TPACKET_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + sizeof(struct sockaddr_ll)) + +struct tpacket2_hdr { + __u32 tp_status; + __u32 tp_len; + __u32 tp_snaplen; + __u16 tp_mac; + __u16 tp_net; + __u32 tp_sec; + __u32 tp_nsec; + __u16 tp_vlan_tci; + __u16 tp_vlan_tpid; + __u8 tp_padding[4]; +}; + +struct tpacket_hdr_variant1 { + __u32 tp_rxhash; + __u32 tp_vlan_tci; + __u16 tp_vlan_tpid; + __u16 tp_padding; +}; + +struct tpacket3_hdr { + __u32 tp_next_offset; + __u32 tp_sec; + __u32 tp_nsec; + __u32 tp_snaplen; + __u32 tp_len; + __u32 tp_status; + __u16 tp_mac; + __u16 tp_net; + /* pkt_hdr variants */ + union { + struct tpacket_hdr_variant1 hv1; + }; + __u8 tp_padding[8]; +}; + +struct tpacket_bd_ts { + unsigned int ts_sec; + union { + unsigned int ts_usec; + unsigned int ts_nsec; + }; +}; + +struct tpacket_hdr_v1 { + __u32 block_status; + __u32 num_pkts; + __u32 offset_to_first_pkt; + + /* Number of valid bytes (including padding) + * blk_len <= tp_block_size + */ + __u32 blk_len; + + /* + * Quite a few uses of sequence number: + * 1. Make sure cache flush etc worked. + * Well, one can argue - why not use the increasing ts below? + * But look at 2. below first. + * 2. When you pass around blocks to other user space decoders, + * you can see which blk[s] is[are] outstanding etc. + * 3. Validate kernel code. + */ + __aligned_u64 seq_num; + + /* + * ts_last_pkt: + * + * Case 1. Block has 'N'(N >=1) packets and TMO'd(timed out) + * ts_last_pkt == 'time-stamp of last packet' and NOT the + * time when the timer fired and the block was closed. + * By providing the ts of the last packet we can absolutely + * guarantee that time-stamp wise, the first packet in the + * next block will never precede the last packet of the + * previous block. + * Case 2. Block has zero packets and TMO'd + * ts_last_pkt = time when the timer fired and the block + * was closed. + * Case 3. Block has 'N' packets and NO TMO. + * ts_last_pkt = time-stamp of the last pkt in the block. + * + * ts_first_pkt: + * Is always the time-stamp when the block was opened. + * Case a) ZERO packets + * No packets to deal with but atleast you know the + * time-interval of this block. + * Case b) Non-zero packets + * Use the ts of the first packet in the block. + * + */ + struct tpacket_bd_ts ts_first_pkt, ts_last_pkt; +}; + +union tpacket_bd_header_u { + struct tpacket_hdr_v1 bh1; +}; + +struct tpacket_block_desc { + __u32 version; + __u32 offset_to_priv; + union tpacket_bd_header_u hdr; +}; + +#define TPACKET2_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket2_hdr)) + sizeof(struct sockaddr_ll)) +#define TPACKET3_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket3_hdr)) + sizeof(struct sockaddr_ll)) + +enum tpacket_versions { + TPACKET_V1, + TPACKET_V2, + TPACKET_V3 +}; + +/* + Frame structure: + + - Start. Frame must be aligned to TPACKET_ALIGNMENT=16 + - struct tpacket_hdr + - pad to TPACKET_ALIGNMENT=16 + - struct sockaddr_ll + - Gap, chosen so that packet data (Start+tp_net) alignes to TPACKET_ALIGNMENT=16 + - Start+tp_mac: [ Optional MAC header ] + - Start+tp_net: Packet data, aligned to TPACKET_ALIGNMENT=16. + - Pad to align to TPACKET_ALIGNMENT=16 + */ + +struct tpacket_req { + unsigned int tp_block_size; /* Minimal size of contiguous block */ + unsigned int tp_block_nr; /* Number of blocks */ + unsigned int tp_frame_size; /* Size of frame */ + unsigned int tp_frame_nr; /* Total number of frames */ +}; + +struct tpacket_req3 { + unsigned int tp_block_size; /* Minimal size of contiguous block */ + unsigned int tp_block_nr; /* Number of blocks */ + unsigned int tp_frame_size; /* Size of frame */ + unsigned int tp_frame_nr; /* Total number of frames */ + unsigned int tp_retire_blk_tov; /* timeout in msecs */ + unsigned int tp_sizeof_priv; /* offset to private data area */ + unsigned int tp_feature_req_word; +}; + +union tpacket_req_u { + struct tpacket_req req; + struct tpacket_req3 req3; +}; + +struct packet_mreq { + int mr_ifindex; + unsigned short mr_type; + unsigned short mr_alen; + unsigned char mr_address[8]; +}; + +#define PACKET_MR_MULTICAST 0 +#define PACKET_MR_PROMISC 1 +#define PACKET_MR_ALLMULTI 2 +#define PACKET_MR_UNICAST 3 + +#endif diff -Nru iproute2-4.3.0/include/linux/if_tunnel.h iproute2-4.9.0/include/linux/if_tunnel.h --- iproute2-4.3.0/include/linux/if_tunnel.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/if_tunnel.h 2016-12-12 23:07:42.000000000 +0000 @@ -2,6 +2,9 @@ #define _IF_TUNNEL_H_ #include +#include +#include +#include #include @@ -24,9 +27,23 @@ #define GRE_SEQ __cpu_to_be16(0x1000) #define GRE_STRICT __cpu_to_be16(0x0800) #define GRE_REC __cpu_to_be16(0x0700) -#define GRE_FLAGS __cpu_to_be16(0x00F8) +#define GRE_ACK __cpu_to_be16(0x0080) +#define GRE_FLAGS __cpu_to_be16(0x0078) #define GRE_VERSION __cpu_to_be16(0x0007) +#define GRE_IS_CSUM(f) ((f) & GRE_CSUM) +#define GRE_IS_ROUTING(f) ((f) & GRE_ROUTING) +#define GRE_IS_KEY(f) ((f) & GRE_KEY) +#define GRE_IS_SEQ(f) ((f) & GRE_SEQ) +#define GRE_IS_STRICT(f) ((f) & GRE_STRICT) +#define GRE_IS_REC(f) ((f) & GRE_REC) +#define GRE_IS_ACK(f) ((f) & GRE_ACK) + +#define GRE_VERSION_0 __cpu_to_be16(0x0000) +#define GRE_VERSION_1 __cpu_to_be16(0x0001) +#define GRE_PROTO_PPP __cpu_to_be16(0x880b) +#define GRE_PPTP_KEY_MASK __cpu_to_be32(0xffff) + struct ip_tunnel_parm { char name[IFNAMSIZ]; int link; @@ -57,6 +74,7 @@ IFLA_IPTUN_ENCAP_FLAGS, IFLA_IPTUN_ENCAP_SPORT, IFLA_IPTUN_ENCAP_DPORT, + IFLA_IPTUN_COLLECT_METADATA, __IFLA_IPTUN_MAX, }; #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) @@ -113,6 +131,7 @@ IFLA_GRE_ENCAP_SPORT, IFLA_GRE_ENCAP_DPORT, IFLA_GRE_COLLECT_METADATA, + IFLA_GRE_IGNORE_DF, __IFLA_GRE_MAX, }; diff -Nru iproute2-4.3.0/include/linux/ila.h iproute2-4.9.0/include/linux/ila.h --- iproute2-4.3.0/include/linux/ila.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/ila.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,45 @@ +/* ila.h - ILA Interface */ + +#ifndef _LINUX_ILA_H +#define _LINUX_ILA_H + +/* NETLINK_GENERIC related info */ +#define ILA_GENL_NAME "ila" +#define ILA_GENL_VERSION 0x1 + +enum { + ILA_ATTR_UNSPEC, + ILA_ATTR_LOCATOR, /* u64 */ + ILA_ATTR_IDENTIFIER, /* u64 */ + ILA_ATTR_LOCATOR_MATCH, /* u64 */ + ILA_ATTR_IFINDEX, /* s32 */ + ILA_ATTR_DIR, /* u32 */ + ILA_ATTR_PAD, + ILA_ATTR_CSUM_MODE, /* u8 */ + + __ILA_ATTR_MAX, +}; + +#define ILA_ATTR_MAX (__ILA_ATTR_MAX - 1) + +enum { + ILA_CMD_UNSPEC, + ILA_CMD_ADD, + ILA_CMD_DEL, + ILA_CMD_GET, + + __ILA_CMD_MAX, +}; + +#define ILA_CMD_MAX (__ILA_CMD_MAX - 1) + +#define ILA_DIR_IN (1 << 0) +#define ILA_DIR_OUT (1 << 1) + +enum { + ILA_CSUM_ADJUST_TRANSPORT, + ILA_CSUM_NEUTRAL_MAP, + ILA_CSUM_NO_ACTION, +}; + +#endif /* _LINUX_ILA_H */ diff -Nru iproute2-4.3.0/include/linux/in6.h iproute2-4.9.0/include/linux/in6.h --- iproute2-4.3.0/include/linux/in6.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/in6.h 2016-12-12 23:07:42.000000000 +0000 @@ -143,6 +143,7 @@ #define IPV6_TLV_PAD1 0 #define IPV6_TLV_PADN 1 #define IPV6_TLV_ROUTERALERT 5 +#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */ #define IPV6_TLV_JUMBO 194 #define IPV6_TLV_HAO 201 /* home address option */ @@ -196,6 +197,7 @@ #define IPV6_IPSEC_POLICY 34 #define IPV6_XFRM_POLICY 35 +#define IPV6_HDRINCL 36 #endif /* diff -Nru iproute2-4.3.0/include/linux/inet_diag.h iproute2-4.9.0/include/linux/inet_diag.h --- iproute2-4.3.0/include/linux/inet_diag.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/inet_diag.h 2016-12-12 23:07:42.000000000 +0000 @@ -72,6 +72,8 @@ INET_DIAG_BC_AUTO, INET_DIAG_BC_S_COND, INET_DIAG_BC_D_COND, + INET_DIAG_BC_DEV_COND, /* u32 ifindex */ + INET_DIAG_BC_MARK_COND, }; struct inet_diag_hostcond { @@ -81,6 +83,11 @@ __be32 addr[0]; }; +struct inet_diag_markcond { + __u32 mark; + __u32 mask; +}; + /* Base info structure. It contains socket identity (addrs/ports/cookie) * and, alas, the information shown by netstat. */ struct inet_diag_msg { @@ -113,9 +120,15 @@ INET_DIAG_DCTCPINFO, INET_DIAG_PROTOCOL, /* response attribute only */ INET_DIAG_SKV6ONLY, + INET_DIAG_LOCALS, + INET_DIAG_PEERS, + INET_DIAG_PAD, + INET_DIAG_MARK, + INET_DIAG_BBRINFO, + __INET_DIAG_MAX, }; -#define INET_DIAG_MAX INET_DIAG_SKV6ONLY +#define INET_DIAG_MAX (__INET_DIAG_MAX - 1) /* INET_DIAG_MEM */ @@ -145,8 +158,20 @@ __u32 dctcp_ab_tot; }; +/* INET_DIAG_BBRINFO */ + +struct tcp_bbr_info { + /* u64 bw: max-filtered BW (app throughput) estimate in Byte per sec: */ + __u32 bbr_bw_lo; /* lower 32 bits of bw */ + __u32 bbr_bw_hi; /* upper 32 bits of bw */ + __u32 bbr_min_rtt; /* min-filtered RTT in uSec */ + __u32 bbr_pacing_gain; /* pacing gain shifted left 8 bits */ + __u32 bbr_cwnd_gain; /* cwnd gain shifted left 8 bits */ +}; + union tcp_cc_info { struct tcpvegas_info vegas; struct tcp_dctcp_info dctcp; + struct tcp_bbr_info bbr; }; #endif /* _INET_DIAG_H_ */ diff -Nru iproute2-4.3.0/include/linux/l2tp.h iproute2-4.9.0/include/linux/l2tp.h --- iproute2-4.3.0/include/linux/l2tp.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/l2tp.h 2016-12-12 23:07:42.000000000 +0000 @@ -124,6 +124,7 @@ L2TP_ATTR_IP6_DADDR, /* struct in6_addr */ L2TP_ATTR_UDP_ZERO_CSUM6_TX, /* u8 */ L2TP_ATTR_UDP_ZERO_CSUM6_RX, /* u8 */ + L2TP_ATTR_PAD, __L2TP_ATTR_MAX, }; @@ -140,6 +141,7 @@ L2TP_ATTR_RX_SEQ_DISCARDS, /* u64 */ L2TP_ATTR_RX_OOS_PACKETS, /* u64 */ L2TP_ATTR_RX_ERRORS, /* u64 */ + L2TP_ATTR_STATS_PAD, __L2TP_ATTR_STATS_MAX, }; diff -Nru iproute2-4.3.0/include/linux/libc-compat.h iproute2-4.9.0/include/linux/libc-compat.h --- iproute2-4.3.0/include/linux/libc-compat.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/libc-compat.h 2016-12-12 23:07:42.000000000 +0000 @@ -51,6 +51,40 @@ /* We have included glibc headers... */ #if defined(__GLIBC__) +/* Coordinate with glibc net/if.h header. */ +#if defined(_NET_IF_H) && defined(__USE_MISC) + +/* GLIBC headers included first so don't define anything + * that would already be defined. */ + +#define __UAPI_DEF_IF_IFCONF 0 +#define __UAPI_DEF_IF_IFMAP 0 +#define __UAPI_DEF_IF_IFNAMSIZ 0 +#define __UAPI_DEF_IF_IFREQ 0 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 0 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ + +#else /* _NET_IF_H */ + +/* Linux headers included first, and we must define everything + * we need. The expectation is that glibc will check the + * __UAPI_DEF_* defines and adjust appropriately. */ + +#define __UAPI_DEF_IF_IFCONF 1 +#define __UAPI_DEF_IF_IFMAP 1 +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#define __UAPI_DEF_IF_IFREQ 1 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 + +#endif /* _NET_IF_H */ + /* Coordinate with glibc netinet/in.h header. */ #if defined(_NETINET_IN_H) @@ -105,6 +139,25 @@ #endif /* _NETINET_IN_H */ +/* Coordinate with glibc netipx/ipx.h header. */ +#if defined(__NETIPX_IPX_H) + +#define __UAPI_DEF_SOCKADDR_IPX 0 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 0 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 0 +#define __UAPI_DEF_IPX_CONFIG_DATA 0 +#define __UAPI_DEF_IPX_ROUTE_DEF 0 + +#else /* defined(__NETIPX_IPX_H) */ + +#define __UAPI_DEF_SOCKADDR_IPX 1 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#define __UAPI_DEF_IPX_ROUTE_DEF 1 + +#endif /* defined(__NETIPX_IPX_H) */ + /* Definitions for xattr.h */ #if defined(_SYS_XATTR_H) #define __UAPI_DEF_XATTR 0 @@ -117,6 +170,16 @@ * that we need. */ #else /* !defined(__GLIBC__) */ +/* Definitions for if.h */ +#define __UAPI_DEF_IF_IFCONF 1 +#define __UAPI_DEF_IF_IFMAP 1 +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#define __UAPI_DEF_IF_IFREQ 1 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 + /* Definitions for in.h */ #define __UAPI_DEF_IN_ADDR 1 #define __UAPI_DEF_IN_IPPROTO 1 @@ -135,6 +198,13 @@ #define __UAPI_DEF_IN6_PKTINFO 1 #define __UAPI_DEF_IP6_MTUINFO 1 +/* Definitions for ipx.h */ +#define __UAPI_DEF_SOCKADDR_IPX 1 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#define __UAPI_DEF_IPX_ROUTE_DEF 1 + /* Definitions for xattr.h */ #define __UAPI_DEF_XATTR 1 diff -Nru iproute2-4.3.0/include/linux/lwtunnel.h iproute2-4.9.0/include/linux/lwtunnel.h --- iproute2-4.3.0/include/linux/lwtunnel.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/lwtunnel.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,45 @@ +#ifndef _LWTUNNEL_H_ +#define _LWTUNNEL_H_ + +#include + +enum lwtunnel_encap_types { + LWTUNNEL_ENCAP_NONE, + LWTUNNEL_ENCAP_MPLS, + LWTUNNEL_ENCAP_IP, + LWTUNNEL_ENCAP_ILA, + LWTUNNEL_ENCAP_IP6, + __LWTUNNEL_ENCAP_MAX, +}; + +#define LWTUNNEL_ENCAP_MAX (__LWTUNNEL_ENCAP_MAX - 1) + +enum lwtunnel_ip_t { + LWTUNNEL_IP_UNSPEC, + LWTUNNEL_IP_ID, + LWTUNNEL_IP_DST, + LWTUNNEL_IP_SRC, + LWTUNNEL_IP_TTL, + LWTUNNEL_IP_TOS, + LWTUNNEL_IP_FLAGS, + LWTUNNEL_IP_PAD, + __LWTUNNEL_IP_MAX, +}; + +#define LWTUNNEL_IP_MAX (__LWTUNNEL_IP_MAX - 1) + +enum lwtunnel_ip6_t { + LWTUNNEL_IP6_UNSPEC, + LWTUNNEL_IP6_ID, + LWTUNNEL_IP6_DST, + LWTUNNEL_IP6_SRC, + LWTUNNEL_IP6_HOPLIMIT, + LWTUNNEL_IP6_TC, + LWTUNNEL_IP6_FLAGS, + LWTUNNEL_IP6_PAD, + __LWTUNNEL_IP6_MAX, +}; + +#define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_MAX - 1) + +#endif /* _LWTUNNEL_H_ */ diff -Nru iproute2-4.3.0/include/linux/mpls_iptunnel.h iproute2-4.9.0/include/linux/mpls_iptunnel.h --- iproute2-4.3.0/include/linux/mpls_iptunnel.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/mpls_iptunnel.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,28 @@ +/* + * mpls tunnel api + * + * Authors: + * Roopa Prabhu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_MPLS_IPTUNNEL_H +#define _LINUX_MPLS_IPTUNNEL_H + +/* MPLS tunnel attributes + * [RTA_ENCAP] = { + * [MPLS_IPTUNNEL_DST] + * } + */ +enum { + MPLS_IPTUNNEL_UNSPEC, + MPLS_IPTUNNEL_DST, + __MPLS_IPTUNNEL_MAX, +}; +#define MPLS_IPTUNNEL_MAX (__MPLS_IPTUNNEL_MAX - 1) + +#endif /* _LINUX_MPLS_IPTUNNEL_H */ diff -Nru iproute2-4.3.0/include/linux/neighbour.h iproute2-4.9.0/include/linux/neighbour.h --- iproute2-4.3.0/include/linux/neighbour.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/neighbour.h 2016-12-12 23:07:42.000000000 +0000 @@ -128,6 +128,7 @@ NDTPA_LOCKTIME, /* u64, msecs */ NDTPA_QUEUE_LENBYTES, /* u32 */ NDTPA_MCAST_REPROBES, /* u32 */ + NDTPA_PAD, __NDTPA_MAX }; #define NDTPA_MAX (__NDTPA_MAX - 1) @@ -160,6 +161,7 @@ NDTA_PARMS, /* nested TLV NDTPA_* */ NDTA_STATS, /* struct ndt_stats, read-only */ NDTA_GC_INTERVAL, /* u64, msecs */ + NDTA_PAD, __NDTA_MAX }; #define NDTA_MAX (__NDTA_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/netconf.h iproute2-4.9.0/include/linux/netconf.h --- iproute2-4.3.0/include/linux/netconf.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/netconf.h 2016-12-12 23:07:42.000000000 +0000 @@ -19,6 +19,7 @@ __NETCONFA_MAX }; #define NETCONFA_MAX (__NETCONFA_MAX - 1) +#define NETCONFA_ALL -1 #define NETCONFA_IFINDEX_ALL -1 #define NETCONFA_IFINDEX_DEFAULT -2 diff -Nru iproute2-4.3.0/include/linux/netfilter_ipv4/ip_tables.h iproute2-4.9.0/include/linux/netfilter_ipv4/ip_tables.h --- iproute2-4.3.0/include/linux/netfilter_ipv4/ip_tables.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/netfilter_ipv4/ip_tables.h 2016-12-12 23:07:42.000000000 +0000 @@ -17,6 +17,7 @@ #include +#include #include #include diff -Nru iproute2-4.3.0/include/linux/netlink_diag.h iproute2-4.9.0/include/linux/netlink_diag.h --- iproute2-4.3.0/include/linux/netlink_diag.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/netlink_diag.h 2016-12-12 23:07:42.000000000 +0000 @@ -48,6 +48,7 @@ #define NDIAG_SHOW_MEMINFO 0x00000001 /* show memory info of a socket */ #define NDIAG_SHOW_GROUPS 0x00000002 /* show groups of a netlink socket */ +/* deprecated since 4.6 */ #define NDIAG_SHOW_RING_CFG 0x00000004 /* show ring configuration */ #endif diff -Nru iproute2-4.3.0/include/linux/netlink.h iproute2-4.9.0/include/linux/netlink.h --- iproute2-4.3.0/include/linux/netlink.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/netlink.h 2016-12-12 23:07:42.000000000 +0000 @@ -54,6 +54,7 @@ #define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ #define NLM_F_ECHO 8 /* Echo this request */ #define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */ +#define NLM_F_DUMP_FILTERED 32 /* Dump was filtered as requested */ /* Modifiers to GET request */ #define NLM_F_ROOT 0x100 /* specify tree root */ diff -Nru iproute2-4.3.0/include/linux/pkt_cls.h iproute2-4.9.0/include/linux/pkt_cls.h --- iproute2-4.3.0/include/linux/pkt_cls.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/pkt_cls.h 2016-12-12 23:07:42.000000000 +0000 @@ -12,6 +12,7 @@ TCA_ACT_OPTIONS, TCA_ACT_INDEX, TCA_ACT_STATS, + TCA_ACT_PAD, __TCA_ACT_MAX }; @@ -33,6 +34,7 @@ #define TC_ACT_STOLEN 4 #define TC_ACT_QUEUED 5 #define TC_ACT_REPEAT 6 +#define TC_ACT_REDIRECT 7 #define TC_ACT_JUMP 0x10000000 /* Action type identifiers*/ @@ -59,8 +61,8 @@ __u32 mtu; struct tc_ratespec rate; struct tc_ratespec peakrate; - int refcnt; - int bindcnt; + int refcnt; + int bindcnt; __u32 capab; }; @@ -68,10 +70,11 @@ __u64 install; __u64 lastuse; __u64 expires; + __u64 firstuse; }; struct tc_cnt { - int refcnt; + int refcnt; int bindcnt; }; @@ -89,12 +92,18 @@ TCA_POLICE_PEAKRATE, TCA_POLICE_AVRATE, TCA_POLICE_RESULT, + TCA_POLICE_TM, + TCA_POLICE_PAD, __TCA_POLICE_MAX #define TCA_POLICE_RESULT TCA_POLICE_RESULT }; #define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) +/* tca flags definitions */ +#define TCA_CLS_FLAGS_SKIP_HW (1 << 0) +#define TCA_CLS_FLAGS_SKIP_SW (1 << 1) + /* U32 filters */ #define TC_U32_HTID(h) ((h)&0xFFF00000) @@ -113,10 +122,12 @@ TCA_U32_DIVISOR, TCA_U32_SEL, TCA_U32_POLICE, - TCA_U32_ACT, + TCA_U32_ACT, TCA_U32_INDEV, TCA_U32_PCNT, TCA_U32_MARK, + TCA_U32_FLAGS, + TCA_U32_PAD, __TCA_U32_MAX }; @@ -319,6 +330,8 @@ /* BPF classifier */ +#define TCA_BPF_FLAG_ACT_DIRECT (1 << 0) + enum { TCA_BPF_UNSPEC, TCA_BPF_ACT, @@ -328,6 +341,8 @@ TCA_BPF_OPS, TCA_BPF_FD, TCA_BPF_NAME, + TCA_BPF_FLAGS, + TCA_BPF_FLAGS_GEN, __TCA_BPF_MAX, }; @@ -358,11 +373,43 @@ TCA_FLOWER_KEY_TCP_DST, /* be16 */ TCA_FLOWER_KEY_UDP_SRC, /* be16 */ TCA_FLOWER_KEY_UDP_DST, /* be16 */ + + TCA_FLOWER_FLAGS, + TCA_FLOWER_KEY_VLAN_ID, /* be16 */ + TCA_FLOWER_KEY_VLAN_PRIO, /* u8 */ + TCA_FLOWER_KEY_VLAN_ETH_TYPE, /* be16 */ + + TCA_FLOWER_KEY_ENC_KEY_ID, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_SRC, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,/* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_DST, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,/* be32 */ + TCA_FLOWER_KEY_ENC_IPV6_SRC, /* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,/* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_DST, /* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,/* struct in6_addr */ + + TCA_FLOWER_KEY_TCP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_TCP_DST_MASK, /* be16 */ + TCA_FLOWER_KEY_UDP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_UDP_DST_MASK, /* be16 */ __TCA_FLOWER_MAX, }; #define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1) +/* Match-all classifier */ + +enum { + TCA_MATCHALL_UNSPEC, + TCA_MATCHALL_CLASSID, + TCA_MATCHALL_ACT, + TCA_MATCHALL_FLAGS, + __TCA_MATCHALL_MAX, +}; + +#define TCA_MATCHALL_MAX (__TCA_MATCHALL_MAX - 1) + /* Extended Matches */ struct tcf_ematch_tree_hdr { diff -Nru iproute2-4.3.0/include/linux/pkt_sched.h iproute2-4.9.0/include/linux/pkt_sched.h --- iproute2-4.3.0/include/linux/pkt_sched.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/pkt_sched.h 2016-12-12 23:07:42.000000000 +0000 @@ -72,6 +72,10 @@ #define TC_H_UNSPEC (0U) #define TC_H_ROOT (0xFFFFFFFFU) #define TC_H_INGRESS (0xFFFFFFF1U) +#define TC_H_CLSACT TC_H_INGRESS + +#define TC_H_MIN_INGRESS 0xFFF2U +#define TC_H_MIN_EGRESS 0xFFF3U /* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */ enum tc_link_layer { @@ -175,6 +179,7 @@ TCA_TBF_PRATE64, TCA_TBF_BURST, TCA_TBF_PBURST, + TCA_TBF_PAD, __TCA_TBF_MAX, }; @@ -364,6 +369,7 @@ TCA_HTB_DIRECT_QLEN, TCA_HTB_RATE64, TCA_HTB_CEIL64, + TCA_HTB_PAD, __TCA_HTB_MAX, }; @@ -527,6 +533,7 @@ TCA_NETEM_RATE, TCA_NETEM_ECN, TCA_NETEM_RATE64, + TCA_NETEM_PAD, __TCA_NETEM_MAX, }; @@ -711,6 +718,8 @@ TCA_FQ_CODEL_FLOWS, TCA_FQ_CODEL_QUANTUM, TCA_FQ_CODEL_CE_THRESHOLD, + TCA_FQ_CODEL_DROP_BATCH_SIZE, + TCA_FQ_CODEL_MEMORY_LIMIT, __TCA_FQ_CODEL_MAX }; @@ -735,6 +744,8 @@ __u32 new_flows_len; /* count of flows in new list */ __u32 old_flows_len; /* count of flows in old list */ __u32 ce_mark; /* packets above ce_threshold */ + __u32 memory_usage; /* in bytes */ + __u32 drop_overmemory; }; struct tc_fq_codel_cl_stats { @@ -781,6 +792,8 @@ TCA_FQ_ORPHAN_MASK, /* mask applied to orphaned skb hashes */ + TCA_FQ_LOW_RATE_THRESHOLD, /* per packet delay under this rate */ + __TCA_FQ_MAX }; @@ -798,7 +811,7 @@ __u32 flows; __u32 inactive_flows; __u32 throttled_flows; - __u32 pad; + __u32 unthrottle_latency_ns; }; /* Heavy-Hitter Filter */ diff -Nru iproute2-4.3.0/include/linux/rtnetlink.h iproute2-4.9.0/include/linux/rtnetlink.h --- iproute2-4.3.0/include/linux/rtnetlink.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/rtnetlink.h 2016-12-12 23:07:42.000000000 +0000 @@ -139,6 +139,11 @@ RTM_GETNSID = 90, #define RTM_GETNSID RTM_GETNSID + RTM_NEWSTATS = 92, +#define RTM_NEWSTATS RTM_NEWSTATS + RTM_GETSTATS = 94, +#define RTM_GETSTATS RTM_GETSTATS + __RTM_MAX, #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) }; @@ -270,6 +275,7 @@ #define RTM_F_CLONED 0x200 /* This route is cloned */ #define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ #define RTM_F_PREFIX 0x800 /* Prefix addresses */ +#define RTM_F_LOOKUP_TABLE 0x1000 /* set rtm_table to FIB lookup result */ /* Reserved table identifiers */ @@ -310,6 +316,8 @@ RTA_PREF, RTA_ENCAP_TYPE, RTA_ENCAP, + RTA_EXPIRES, + RTA_PAD, __RTA_MAX }; @@ -342,7 +350,7 @@ #define RTNH_F_OFFLOAD 8 /* offloaded route */ #define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */ -#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN) +#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD) /* Macros to handle hexthops */ @@ -534,6 +542,7 @@ TCA_FCNT, TCA_STATS2, TCA_STAB, + TCA_PAD, __TCA_MAX }; @@ -664,6 +673,7 @@ #define RTEXT_FILTER_VF (1 << 0) #define RTEXT_FILTER_BRVLAN (1 << 1) #define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2) +#define RTEXT_FILTER_SKIP_STATS (1 << 3) /* End of information exported to user level */ diff -Nru iproute2-4.3.0/include/linux/sctp.h iproute2-4.9.0/include/linux/sctp.h --- iproute2-4.3.0/include/linux/sctp.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/sctp.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,1005 @@ +/* SCTP kernel implementation + * (C) Copyright IBM Corp. 2001, 2004 + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2002 Intel Corp. + * + * This file is part of the SCTP kernel implementation + * + * This header represents the structures and constants needed to support + * the SCTP Extension to the Sockets API. + * + * This SCTP implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This SCTP implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, see + * . + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * R. Stewart + * K. Morneau + * Q. Xie + * Karl Knutson + * Jon Grimm + * Daisy Chang + * Ryan Layer + * Ardelle Fan + * Sridhar Samudrala + * Inaky Perez-Gonzalez + * Vlad Yasevich + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#ifndef _SCTP_H +#define _SCTP_H + +#include +#include + +typedef __s32 sctp_assoc_t; + +/* The following symbols come from the Sockets API Extensions for + * SCTP . + */ +#define SCTP_RTOINFO 0 +#define SCTP_ASSOCINFO 1 +#define SCTP_INITMSG 2 +#define SCTP_NODELAY 3 /* Get/set nodelay option. */ +#define SCTP_AUTOCLOSE 4 +#define SCTP_SET_PEER_PRIMARY_ADDR 5 +#define SCTP_PRIMARY_ADDR 6 +#define SCTP_ADAPTATION_LAYER 7 +#define SCTP_DISABLE_FRAGMENTS 8 +#define SCTP_PEER_ADDR_PARAMS 9 +#define SCTP_DEFAULT_SEND_PARAM 10 +#define SCTP_EVENTS 11 +#define SCTP_I_WANT_MAPPED_V4_ADDR 12 /* Turn on/off mapped v4 addresses */ +#define SCTP_MAXSEG 13 /* Get/set maximum fragment. */ +#define SCTP_STATUS 14 +#define SCTP_GET_PEER_ADDR_INFO 15 +#define SCTP_DELAYED_ACK_TIME 16 +#define SCTP_DELAYED_ACK SCTP_DELAYED_ACK_TIME +#define SCTP_DELAYED_SACK SCTP_DELAYED_ACK_TIME +#define SCTP_CONTEXT 17 +#define SCTP_FRAGMENT_INTERLEAVE 18 +#define SCTP_PARTIAL_DELIVERY_POINT 19 /* Set/Get partial delivery point */ +#define SCTP_MAX_BURST 20 /* Set/Get max burst */ +#define SCTP_AUTH_CHUNK 21 /* Set only: add a chunk type to authenticate */ +#define SCTP_HMAC_IDENT 22 +#define SCTP_AUTH_KEY 23 +#define SCTP_AUTH_ACTIVE_KEY 24 +#define SCTP_AUTH_DELETE_KEY 25 +#define SCTP_PEER_AUTH_CHUNKS 26 /* Read only */ +#define SCTP_LOCAL_AUTH_CHUNKS 27 /* Read only */ +#define SCTP_GET_ASSOC_NUMBER 28 /* Read only */ +#define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */ +#define SCTP_AUTO_ASCONF 30 +#define SCTP_PEER_ADDR_THLDS 31 +#define SCTP_RECVRCVINFO 32 +#define SCTP_RECVNXTINFO 33 +#define SCTP_DEFAULT_SNDINFO 34 + +/* Internal Socket Options. Some of the sctp library functions are + * implemented using these socket options. + */ +#define SCTP_SOCKOPT_BINDX_ADD 100 /* BINDX requests for adding addrs */ +#define SCTP_SOCKOPT_BINDX_REM 101 /* BINDX requests for removing addrs. */ +#define SCTP_SOCKOPT_PEELOFF 102 /* peel off association. */ +/* Options 104-106 are deprecated and removed. Do not use this space */ +#define SCTP_SOCKOPT_CONNECTX_OLD 107 /* CONNECTX old requests. */ +#define SCTP_GET_PEER_ADDRS 108 /* Get all peer address. */ +#define SCTP_GET_LOCAL_ADDRS 109 /* Get all local address. */ +#define SCTP_SOCKOPT_CONNECTX 110 /* CONNECTX requests. */ +#define SCTP_SOCKOPT_CONNECTX3 111 /* CONNECTX requests (updated) */ +#define SCTP_GET_ASSOC_STATS 112 /* Read only */ +#define SCTP_PR_SUPPORTED 113 +#define SCTP_DEFAULT_PRINFO 114 +#define SCTP_PR_ASSOC_STATUS 115 + +/* PR-SCTP policies */ +#define SCTP_PR_SCTP_NONE 0x0000 +#define SCTP_PR_SCTP_TTL 0x0010 +#define SCTP_PR_SCTP_RTX 0x0020 +#define SCTP_PR_SCTP_PRIO 0x0030 +#define SCTP_PR_SCTP_MAX SCTP_PR_SCTP_PRIO +#define SCTP_PR_SCTP_MASK 0x0030 + +#define __SCTP_PR_INDEX(x) ((x >> 4) - 1) +#define SCTP_PR_INDEX(x) __SCTP_PR_INDEX(SCTP_PR_SCTP_ ## x) + +#define SCTP_PR_POLICY(x) ((x) & SCTP_PR_SCTP_MASK) +#define SCTP_PR_SET_POLICY(flags, x) \ + do { \ + flags &= ~SCTP_PR_SCTP_MASK; \ + flags |= x; \ + } while (0) + +#define SCTP_PR_TTL_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_TTL) +#define SCTP_PR_RTX_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_RTX) +#define SCTP_PR_PRIO_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_PRIO) + +/* These are bit fields for msghdr->msg_flags. See section 5.1. */ +/* On user space Linux, these live in as an enum. */ +enum sctp_msg_flags { + MSG_NOTIFICATION = 0x8000, +#define MSG_NOTIFICATION MSG_NOTIFICATION +}; + +/* 5.3.1 SCTP Initiation Structure (SCTP_INIT) + * + * This cmsghdr structure provides information for initializing new + * SCTP associations with sendmsg(). The SCTP_INITMSG socket option + * uses this same data structure. This structure is not used for + * recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_INIT struct sctp_initmsg + */ +struct sctp_initmsg { + __u16 sinit_num_ostreams; + __u16 sinit_max_instreams; + __u16 sinit_max_attempts; + __u16 sinit_max_init_timeo; +}; + +/* 5.3.2 SCTP Header Information Structure (SCTP_SNDRCV) + * + * This cmsghdr structure specifies SCTP options for sendmsg() and + * describes SCTP header information about a received message through + * recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo + */ +struct sctp_sndrcvinfo { + __u16 sinfo_stream; + __u16 sinfo_ssn; + __u16 sinfo_flags; + __u32 sinfo_ppid; + __u32 sinfo_context; + __u32 sinfo_timetolive; + __u32 sinfo_tsn; + __u32 sinfo_cumtsn; + sctp_assoc_t sinfo_assoc_id; +}; + +/* 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_SNDINFO struct sctp_sndinfo + */ +struct sctp_sndinfo { + __u16 snd_sid; + __u16 snd_flags; + __u32 snd_ppid; + __u32 snd_context; + sctp_assoc_t snd_assoc_id; +}; + +/* 5.3.5 SCTP Receive Information Structure (SCTP_RCVINFO) + * + * This cmsghdr structure describes SCTP receive information + * about a received message through recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_RCVINFO struct sctp_rcvinfo + */ +struct sctp_rcvinfo { + __u16 rcv_sid; + __u16 rcv_ssn; + __u16 rcv_flags; + __u32 rcv_ppid; + __u32 rcv_tsn; + __u32 rcv_cumtsn; + __u32 rcv_context; + sctp_assoc_t rcv_assoc_id; +}; + +/* 5.3.6 SCTP Next Receive Information Structure (SCTP_NXTINFO) + * + * This cmsghdr structure describes SCTP receive information + * of the next message that will be delivered through recvmsg() + * if this information is already available when delivering + * the current message. + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_NXTINFO struct sctp_nxtinfo + */ +struct sctp_nxtinfo { + __u16 nxt_sid; + __u16 nxt_flags; + __u32 nxt_ppid; + __u32 nxt_length; + sctp_assoc_t nxt_assoc_id; +}; + +/* + * sinfo_flags: 16 bits (unsigned integer) + * + * This field may contain any of the following flags and is composed of + * a bitwise OR of these values. + */ +enum sctp_sinfo_flags { + SCTP_UNORDERED = (1 << 0), /* Send/receive message unordered. */ + SCTP_ADDR_OVER = (1 << 1), /* Override the primary destination. */ + SCTP_ABORT = (1 << 2), /* Send an ABORT message to the peer. */ + SCTP_SACK_IMMEDIATELY = (1 << 3), /* SACK should be sent without delay. */ + SCTP_NOTIFICATION = MSG_NOTIFICATION, /* Next message is not user msg but notification. */ + SCTP_EOF = MSG_FIN, /* Initiate graceful shutdown process. */ +}; + +typedef union { + __u8 raw; + struct sctp_initmsg init; + struct sctp_sndrcvinfo sndrcv; +} sctp_cmsg_data_t; + +/* These are cmsg_types. */ +typedef enum sctp_cmsg_type { + SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */ +#define SCTP_INIT SCTP_INIT + SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */ +#define SCTP_SNDRCV SCTP_SNDRCV + SCTP_SNDINFO, /* 5.3.4 SCTP Send Information Structure */ +#define SCTP_SNDINFO SCTP_SNDINFO + SCTP_RCVINFO, /* 5.3.5 SCTP Receive Information Structure */ +#define SCTP_RCVINFO SCTP_RCVINFO + SCTP_NXTINFO, /* 5.3.6 SCTP Next Receive Information Structure */ +#define SCTP_NXTINFO SCTP_NXTINFO +} sctp_cmsg_t; + +/* + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * Communication notifications inform the ULP that an SCTP association + * has either begun or ended. The identifier for a new association is + * provided by this notificaion. The notification information has the + * following format: + * + */ +struct sctp_assoc_change { + __u16 sac_type; + __u16 sac_flags; + __u32 sac_length; + __u16 sac_state; + __u16 sac_error; + __u16 sac_outbound_streams; + __u16 sac_inbound_streams; + sctp_assoc_t sac_assoc_id; + __u8 sac_info[0]; +}; + +/* + * sac_state: 32 bits (signed integer) + * + * This field holds one of a number of values that communicate the + * event that happened to the association. They include: + * + * Note: The following state names deviate from the API draft as + * the names clash too easily with other kernel symbols. + */ +enum sctp_sac_state { + SCTP_COMM_UP, + SCTP_COMM_LOST, + SCTP_RESTART, + SCTP_SHUTDOWN_COMP, + SCTP_CANT_STR_ASSOC, +}; + +/* + * 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * When a destination address on a multi-homed peer encounters a change + * an interface details event is sent. The information has the + * following structure: + */ +struct sctp_paddr_change { + __u16 spc_type; + __u16 spc_flags; + __u32 spc_length; + struct sockaddr_storage spc_aaddr; + int spc_state; + int spc_error; + sctp_assoc_t spc_assoc_id; +} __attribute__((packed, aligned(4))); + +/* + * spc_state: 32 bits (signed integer) + * + * This field holds one of a number of values that communicate the + * event that happened to the address. They include: + */ +enum sctp_spc_state { + SCTP_ADDR_AVAILABLE, + SCTP_ADDR_UNREACHABLE, + SCTP_ADDR_REMOVED, + SCTP_ADDR_ADDED, + SCTP_ADDR_MADE_PRIM, + SCTP_ADDR_CONFIRMED, +}; + + +/* + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * A remote peer may send an Operational Error message to its peer. + * This message indicates a variety of error conditions on an + * association. The entire error TLV as it appears on the wire is + * included in a SCTP_REMOTE_ERROR event. Please refer to the SCTP + * specification [SCTP] and any extensions for a list of possible + * error formats. SCTP error TLVs have the format: + */ +struct sctp_remote_error { + __u16 sre_type; + __u16 sre_flags; + __u32 sre_length; + __u16 sre_error; + sctp_assoc_t sre_assoc_id; + __u8 sre_data[0]; +}; + + +/* + * 5.3.1.4 SCTP_SEND_FAILED + * + * If SCTP cannot deliver a message it may return the message as a + * notification. + */ +struct sctp_send_failed { + __u16 ssf_type; + __u16 ssf_flags; + __u32 ssf_length; + __u32 ssf_error; + struct sctp_sndrcvinfo ssf_info; + sctp_assoc_t ssf_assoc_id; + __u8 ssf_data[0]; +}; + +/* + * ssf_flags: 16 bits (unsigned integer) + * + * The flag value will take one of the following values + * + * SCTP_DATA_UNSENT - Indicates that the data was never put on + * the wire. + * + * SCTP_DATA_SENT - Indicates that the data was put on the wire. + * Note that this does not necessarily mean that the + * data was (or was not) successfully delivered. + */ +enum sctp_ssf_flags { + SCTP_DATA_UNSENT, + SCTP_DATA_SENT, +}; + +/* + * 5.3.1.5 SCTP_SHUTDOWN_EVENT + * + * When a peer sends a SHUTDOWN, SCTP delivers this notification to + * inform the application that it should cease sending data. + */ +struct sctp_shutdown_event { + __u16 sse_type; + __u16 sse_flags; + __u32 sse_length; + sctp_assoc_t sse_assoc_id; +}; + +/* + * 5.3.1.6 SCTP_ADAPTATION_INDICATION + * + * When a peer sends a Adaptation Layer Indication parameter , SCTP + * delivers this notification to inform the application + * that of the peers requested adaptation layer. + */ +struct sctp_adaptation_event { + __u16 sai_type; + __u16 sai_flags; + __u32 sai_length; + __u32 sai_adaptation_ind; + sctp_assoc_t sai_assoc_id; +}; + +/* + * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT + * + * When a receiver is engaged in a partial delivery of a + * message this notification will be used to indicate + * various events. + */ +struct sctp_pdapi_event { + __u16 pdapi_type; + __u16 pdapi_flags; + __u32 pdapi_length; + __u32 pdapi_indication; + sctp_assoc_t pdapi_assoc_id; +}; + +enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, }; + +/* + * 5.3.1.8. SCTP_AUTHENTICATION_EVENT + * + * When a receiver is using authentication this message will provide + * notifications regarding new keys being made active as well as errors. + */ +struct sctp_authkey_event { + __u16 auth_type; + __u16 auth_flags; + __u32 auth_length; + __u16 auth_keynumber; + __u16 auth_altkeynumber; + __u32 auth_indication; + sctp_assoc_t auth_assoc_id; +}; + +enum { SCTP_AUTH_NEWKEY = 0, }; + +/* + * 6.1.9. SCTP_SENDER_DRY_EVENT + * + * When the SCTP stack has no more user data to send or retransmit, this + * notification is given to the user. Also, at the time when a user app + * subscribes to this event, if there is no data to be sent or + * retransmit, the stack will immediately send up this notification. + */ +struct sctp_sender_dry_event { + __u16 sender_dry_type; + __u16 sender_dry_flags; + __u32 sender_dry_length; + sctp_assoc_t sender_dry_assoc_id; +}; + +/* + * Described in Section 7.3 + * Ancillary Data and Notification Interest Options + */ +struct sctp_event_subscribe { + __u8 sctp_data_io_event; + __u8 sctp_association_event; + __u8 sctp_address_event; + __u8 sctp_send_failure_event; + __u8 sctp_peer_error_event; + __u8 sctp_shutdown_event; + __u8 sctp_partial_delivery_event; + __u8 sctp_adaptation_layer_event; + __u8 sctp_authentication_event; + __u8 sctp_sender_dry_event; +}; + +/* + * 5.3.1 SCTP Notification Structure + * + * The notification structure is defined as the union of all + * notification types. + * + */ +union sctp_notification { + struct { + __u16 sn_type; /* Notification type. */ + __u16 sn_flags; + __u32 sn_length; + } sn_header; + struct sctp_assoc_change sn_assoc_change; + struct sctp_paddr_change sn_paddr_change; + struct sctp_remote_error sn_remote_error; + struct sctp_send_failed sn_send_failed; + struct sctp_shutdown_event sn_shutdown_event; + struct sctp_adaptation_event sn_adaptation_event; + struct sctp_pdapi_event sn_pdapi_event; + struct sctp_authkey_event sn_authkey_event; + struct sctp_sender_dry_event sn_sender_dry_event; +}; + +/* Section 5.3.1 + * All standard values for sn_type flags are greater than 2^15. + * Values from 2^15 and down are reserved. + */ + +enum sctp_sn_type { + SCTP_SN_TYPE_BASE = (1<<15), + SCTP_ASSOC_CHANGE, +#define SCTP_ASSOC_CHANGE SCTP_ASSOC_CHANGE + SCTP_PEER_ADDR_CHANGE, +#define SCTP_PEER_ADDR_CHANGE SCTP_PEER_ADDR_CHANGE + SCTP_SEND_FAILED, +#define SCTP_SEND_FAILED SCTP_SEND_FAILED + SCTP_REMOTE_ERROR, +#define SCTP_REMOTE_ERROR SCTP_REMOTE_ERROR + SCTP_SHUTDOWN_EVENT, +#define SCTP_SHUTDOWN_EVENT SCTP_SHUTDOWN_EVENT + SCTP_PARTIAL_DELIVERY_EVENT, +#define SCTP_PARTIAL_DELIVERY_EVENT SCTP_PARTIAL_DELIVERY_EVENT + SCTP_ADAPTATION_INDICATION, +#define SCTP_ADAPTATION_INDICATION SCTP_ADAPTATION_INDICATION + SCTP_AUTHENTICATION_EVENT, +#define SCTP_AUTHENTICATION_INDICATION SCTP_AUTHENTICATION_EVENT + SCTP_SENDER_DRY_EVENT, +#define SCTP_SENDER_DRY_EVENT SCTP_SENDER_DRY_EVENT +}; + +/* Notification error codes used to fill up the error fields in some + * notifications. + * SCTP_PEER_ADDRESS_CHAGE : spc_error + * SCTP_ASSOC_CHANGE : sac_error + * These names should be potentially included in the draft 04 of the SCTP + * sockets API specification. + */ +typedef enum sctp_sn_error { + SCTP_FAILED_THRESHOLD, + SCTP_RECEIVED_SACK, + SCTP_HEARTBEAT_SUCCESS, + SCTP_RESPONSE_TO_USER_REQ, + SCTP_INTERNAL_ERROR, + SCTP_SHUTDOWN_GUARD_EXPIRES, + SCTP_PEER_FAULTY, +} sctp_sn_error_t; + +/* + * 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO) + * + * The protocol parameters used to initialize and bound retransmission + * timeout (RTO) are tunable. See [SCTP] for more information on how + * these parameters are used in RTO calculation. + */ +struct sctp_rtoinfo { + sctp_assoc_t srto_assoc_id; + __u32 srto_initial; + __u32 srto_max; + __u32 srto_min; +}; + +/* + * 7.1.2 Association Parameters (SCTP_ASSOCINFO) + * + * This option is used to both examine and set various association and + * endpoint parameters. + */ +struct sctp_assocparams { + sctp_assoc_t sasoc_assoc_id; + __u16 sasoc_asocmaxrxt; + __u16 sasoc_number_peer_destinations; + __u32 sasoc_peer_rwnd; + __u32 sasoc_local_rwnd; + __u32 sasoc_cookie_life; +}; + +/* + * 7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR) + * + * Requests that the peer mark the enclosed address as the association + * primary. The enclosed address must be one of the association's + * locally bound addresses. The following structure is used to make a + * set primary request: + */ +struct sctp_setpeerprim { + sctp_assoc_t sspp_assoc_id; + struct sockaddr_storage sspp_addr; +} __attribute__((packed, aligned(4))); + +/* + * 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR) + * + * Requests that the local SCTP stack use the enclosed peer address as + * the association primary. The enclosed address must be one of the + * association peer's addresses. The following structure is used to + * make a set peer primary request: + */ +struct sctp_prim { + sctp_assoc_t ssp_assoc_id; + struct sockaddr_storage ssp_addr; +} __attribute__((packed, aligned(4))); + +/* For backward compatibility use, define the old name too */ +#define sctp_setprim sctp_prim + +/* + * 7.1.11 Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER) + * + * Requests that the local endpoint set the specified Adaptation Layer + * Indication parameter for all future INIT and INIT-ACK exchanges. + */ +struct sctp_setadaptation { + __u32 ssb_adaptation_ind; +}; + +/* + * 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS) + * + * Applications can enable or disable heartbeats for any peer address + * of an association, modify an address's heartbeat interval, force a + * heartbeat to be sent immediately, and adjust the address's maximum + * number of retransmissions sent before an address is considered + * unreachable. The following structure is used to access and modify an + * address's parameters: + */ +enum sctp_spp_flags { + SPP_HB_ENABLE = 1<<0, /*Enable heartbeats*/ + SPP_HB_DISABLE = 1<<1, /*Disable heartbeats*/ + SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE, + SPP_HB_DEMAND = 1<<2, /*Send heartbeat immediately*/ + SPP_PMTUD_ENABLE = 1<<3, /*Enable PMTU discovery*/ + SPP_PMTUD_DISABLE = 1<<4, /*Disable PMTU discovery*/ + SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE, + SPP_SACKDELAY_ENABLE = 1<<5, /*Enable SACK*/ + SPP_SACKDELAY_DISABLE = 1<<6, /*Disable SACK*/ + SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE, + SPP_HB_TIME_IS_ZERO = 1<<7, /* Set HB delay to 0 */ +}; + +struct sctp_paddrparams { + sctp_assoc_t spp_assoc_id; + struct sockaddr_storage spp_address; + __u32 spp_hbinterval; + __u16 spp_pathmaxrxt; + __u32 spp_pathmtu; + __u32 spp_sackdelay; + __u32 spp_flags; +} __attribute__((packed, aligned(4))); + +/* + * 7.1.18. Add a chunk that must be authenticated (SCTP_AUTH_CHUNK) + * + * This set option adds a chunk type that the user is requesting to be + * received only in an authenticated way. Changes to the list of chunks + * will only effect future associations on the socket. + */ +struct sctp_authchunk { + __u8 sauth_chunk; +}; + +/* + * 7.1.19. Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT) + * + * This option gets or sets the list of HMAC algorithms that the local + * endpoint requires the peer to use. + */ +/* This here is only used by user space as is. It might not be a good idea + * to export/reveal the whole structure with reserved fields etc. + */ +enum { + SCTP_AUTH_HMAC_ID_SHA1 = 1, + SCTP_AUTH_HMAC_ID_SHA256 = 3, +}; + +struct sctp_hmacalgo { + __u32 shmac_num_idents; + __u16 shmac_idents[]; +}; + +/* Sadly, user and kernel space have different names for + * this structure member, so this is to not break anything. + */ +#define shmac_number_of_idents shmac_num_idents + +/* + * 7.1.20. Set a shared key (SCTP_AUTH_KEY) + * + * This option will set a shared secret key which is used to build an + * association shared key. + */ +struct sctp_authkey { + sctp_assoc_t sca_assoc_id; + __u16 sca_keynumber; + __u16 sca_keylength; + __u8 sca_key[]; +}; + +/* + * 7.1.21. Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY) + * + * This option will get or set the active shared key to be used to build + * the association shared key. + */ + +struct sctp_authkeyid { + sctp_assoc_t scact_assoc_id; + __u16 scact_keynumber; +}; + + +/* + * 7.1.23. Get or set delayed ack timer (SCTP_DELAYED_SACK) + * + * This option will effect the way delayed acks are performed. This + * option allows you to get or set the delayed ack time, in + * milliseconds. It also allows changing the delayed ack frequency. + * Changing the frequency to 1 disables the delayed sack algorithm. If + * the assoc_id is 0, then this sets or gets the endpoints default + * values. If the assoc_id field is non-zero, then the set or get + * effects the specified association for the one to many model (the + * assoc_id field is ignored by the one to one model). Note that if + * sack_delay or sack_freq are 0 when setting this option, then the + * current values will remain unchanged. + */ +struct sctp_sack_info { + sctp_assoc_t sack_assoc_id; + uint32_t sack_delay; + uint32_t sack_freq; +}; + +struct sctp_assoc_value { + sctp_assoc_t assoc_id; + uint32_t assoc_value; +}; + +/* + * 7.2.2 Peer Address Information + * + * Applications can retrieve information about a specific peer address + * of an association, including its reachability state, congestion + * window, and retransmission timer values. This information is + * read-only. The following structure is used to access this + * information: + */ +struct sctp_paddrinfo { + sctp_assoc_t spinfo_assoc_id; + struct sockaddr_storage spinfo_address; + __s32 spinfo_state; + __u32 spinfo_cwnd; + __u32 spinfo_srtt; + __u32 spinfo_rto; + __u32 spinfo_mtu; +} __attribute__((packed, aligned(4))); + +/* Peer addresses's state. */ +/* UNKNOWN: Peer address passed by the upper layer in sendmsg or connect[x] + * calls. + * UNCONFIRMED: Peer address received in INIT/INIT-ACK address parameters. + * Not yet confirmed by a heartbeat and not available for data + * transfers. + * ACTIVE : Peer address confirmed, active and available for data transfers. + * INACTIVE: Peer address inactive and not available for data transfers. + */ +enum sctp_spinfo_state { + SCTP_INACTIVE, + SCTP_PF, + SCTP_ACTIVE, + SCTP_UNCONFIRMED, + SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */ +}; + +/* + * 7.2.1 Association Status (SCTP_STATUS) + * + * Applications can retrieve current status information about an + * association, including association state, peer receiver window size, + * number of unacked data chunks, and number of data chunks pending + * receipt. This information is read-only. The following structure is + * used to access this information: + */ +struct sctp_status { + sctp_assoc_t sstat_assoc_id; + __s32 sstat_state; + __u32 sstat_rwnd; + __u16 sstat_unackdata; + __u16 sstat_penddata; + __u16 sstat_instrms; + __u16 sstat_outstrms; + __u32 sstat_fragmentation_point; + struct sctp_paddrinfo sstat_primary; +}; + +/* + * 7.2.3. Get the list of chunks the peer requires to be authenticated + * (SCTP_PEER_AUTH_CHUNKS) + * + * This option gets a list of chunks for a specified association that + * the peer requires to be received authenticated only. + */ +struct sctp_authchunks { + sctp_assoc_t gauth_assoc_id; + __u32 gauth_number_of_chunks; + uint8_t gauth_chunks[]; +}; + +/* The broken spelling has been released already in lksctp-tools header, + * so don't break anyone, now that it's fixed. + */ +#define guth_number_of_chunks gauth_number_of_chunks + +/* Association states. */ +enum sctp_sstat_state { + SCTP_EMPTY = 0, + SCTP_CLOSED = 1, + SCTP_COOKIE_WAIT = 2, + SCTP_COOKIE_ECHOED = 3, + SCTP_ESTABLISHED = 4, + SCTP_SHUTDOWN_PENDING = 5, + SCTP_SHUTDOWN_SENT = 6, + SCTP_SHUTDOWN_RECEIVED = 7, + SCTP_SHUTDOWN_ACK_SENT = 8, +}; + +/* + * 8.2.6. Get the Current Identifiers of Associations + * (SCTP_GET_ASSOC_ID_LIST) + * + * This option gets the current list of SCTP association identifiers of + * the SCTP associations handled by a one-to-many style socket. + */ +struct sctp_assoc_ids { + __u32 gaids_number_of_ids; + sctp_assoc_t gaids_assoc_id[]; +}; + +/* + * 8.3, 8.5 get all peer/local addresses in an association. + * This parameter struct is used by SCTP_GET_PEER_ADDRS and + * SCTP_GET_LOCAL_ADDRS socket options used internally to implement + * sctp_getpaddrs() and sctp_getladdrs() API. + */ +struct sctp_getaddrs_old { + sctp_assoc_t assoc_id; + int addr_num; + struct sockaddr *addrs; +}; + +struct sctp_getaddrs { + sctp_assoc_t assoc_id; /*input*/ + __u32 addr_num; /*output*/ + __u8 addrs[0]; /*output, variable size*/ +}; + +/* A socket user request obtained via SCTP_GET_ASSOC_STATS that retrieves + * association stats. All stats are counts except sas_maxrto and + * sas_obs_rto_ipaddr. maxrto is the max observed rto + transport since + * the last call. Will return 0 when RTO was not update since last call + */ +struct sctp_assoc_stats { + sctp_assoc_t sas_assoc_id; /* Input */ + /* Transport of observed max RTO */ + struct sockaddr_storage sas_obs_rto_ipaddr; + __u64 sas_maxrto; /* Maximum Observed RTO for period */ + __u64 sas_isacks; /* SACKs received */ + __u64 sas_osacks; /* SACKs sent */ + __u64 sas_opackets; /* Packets sent */ + __u64 sas_ipackets; /* Packets received */ + __u64 sas_rtxchunks; /* Retransmitted Chunks */ + __u64 sas_outofseqtsns;/* TSN received > next expected */ + __u64 sas_idupchunks; /* Dups received (ordered+unordered) */ + __u64 sas_gapcnt; /* Gap Acknowledgements Received */ + __u64 sas_ouodchunks; /* Unordered data chunks sent */ + __u64 sas_iuodchunks; /* Unordered data chunks received */ + __u64 sas_oodchunks; /* Ordered data chunks sent */ + __u64 sas_iodchunks; /* Ordered data chunks received */ + __u64 sas_octrlchunks; /* Control chunks sent */ + __u64 sas_ictrlchunks; /* Control chunks received */ +}; + +/* + * 8.1 sctp_bindx() + * + * The flags parameter is formed from the bitwise OR of zero or more of the + * following currently defined flags: + */ +#define SCTP_BINDX_ADD_ADDR 0x01 +#define SCTP_BINDX_REM_ADDR 0x02 + +/* This is the structure that is passed as an argument(optval) to + * getsockopt(SCTP_SOCKOPT_PEELOFF). + */ +typedef struct { + sctp_assoc_t associd; + int sd; +} sctp_peeloff_arg_t; + +/* + * Peer Address Thresholds socket option + */ +struct sctp_paddrthlds { + sctp_assoc_t spt_assoc_id; + struct sockaddr_storage spt_address; + __u16 spt_pathmaxrxt; + __u16 spt_pathpfthld; +}; + +/* + * Socket Option for Getting the Association/Stream-Specific PR-SCTP Status + */ +struct sctp_prstatus { + sctp_assoc_t sprstat_assoc_id; + __u16 sprstat_sid; + __u16 sprstat_policy; + __u64 sprstat_abandoned_unsent; + __u64 sprstat_abandoned_sent; +}; + +struct sctp_default_prinfo { + sctp_assoc_t pr_assoc_id; + __u32 pr_value; + __u16 pr_policy; +}; + +struct sctp_info { + __u32 sctpi_tag; + __u32 sctpi_state; + __u32 sctpi_rwnd; + __u16 sctpi_unackdata; + __u16 sctpi_penddata; + __u16 sctpi_instrms; + __u16 sctpi_outstrms; + __u32 sctpi_fragmentation_point; + __u32 sctpi_inqueue; + __u32 sctpi_outqueue; + __u32 sctpi_overall_error; + __u32 sctpi_max_burst; + __u32 sctpi_maxseg; + __u32 sctpi_peer_rwnd; + __u32 sctpi_peer_tag; + __u8 sctpi_peer_capable; + __u8 sctpi_peer_sack; + __u16 __reserved1; + + /* assoc status info */ + __u64 sctpi_isacks; + __u64 sctpi_osacks; + __u64 sctpi_opackets; + __u64 sctpi_ipackets; + __u64 sctpi_rtxchunks; + __u64 sctpi_outofseqtsns; + __u64 sctpi_idupchunks; + __u64 sctpi_gapcnt; + __u64 sctpi_ouodchunks; + __u64 sctpi_iuodchunks; + __u64 sctpi_oodchunks; + __u64 sctpi_iodchunks; + __u64 sctpi_octrlchunks; + __u64 sctpi_ictrlchunks; + + /* primary transport info */ + struct sockaddr_storage sctpi_p_address; + __s32 sctpi_p_state; + __u32 sctpi_p_cwnd; + __u32 sctpi_p_srtt; + __u32 sctpi_p_rto; + __u32 sctpi_p_hbinterval; + __u32 sctpi_p_pathmaxrxt; + __u32 sctpi_p_sackdelay; + __u32 sctpi_p_sackfreq; + __u32 sctpi_p_ssthresh; + __u32 sctpi_p_partial_bytes_acked; + __u32 sctpi_p_flight_size; + __u16 sctpi_p_error; + __u16 __reserved2; + + /* sctp sock info */ + __u32 sctpi_s_autoclose; + __u32 sctpi_s_adaptation_ind; + __u32 sctpi_s_pd_point; + __u8 sctpi_s_nodelay; + __u8 sctpi_s_disable_fragments; + __u8 sctpi_s_v4mapped; + __u8 sctpi_s_frag_interleave; + __u32 sctpi_s_type; + __u32 __reserved3; +}; + +#endif /* _SCTP_H */ diff -Nru iproute2-4.3.0/include/linux/sock_diag.h iproute2-4.9.0/include/linux/sock_diag.h --- iproute2-4.3.0/include/linux/sock_diag.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/sock_diag.h 2016-12-12 23:07:42.000000000 +0000 @@ -4,6 +4,7 @@ #include #define SOCK_DIAG_BY_FAMILY 20 +#define SOCK_DESTROY 21 struct sock_diag_req { __u8 sdiag_family; @@ -19,6 +20,7 @@ SK_MEMINFO_WMEM_QUEUED, SK_MEMINFO_OPTMEM, SK_MEMINFO_BACKLOG, + SK_MEMINFO_DROPS, SK_MEMINFO_VARS, }; diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_bpf.h iproute2-4.9.0/include/linux/tc_act/tc_bpf.h --- iproute2-4.3.0/include/linux/tc_act/tc_bpf.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_bpf.h 2016-12-12 23:07:42.000000000 +0000 @@ -26,6 +26,7 @@ TCA_ACT_BPF_OPS, TCA_ACT_BPF_FD, TCA_ACT_BPF_NAME, + TCA_ACT_BPF_PAD, __TCA_ACT_BPF_MAX, }; #define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_connmark.h iproute2-4.9.0/include/linux/tc_act/tc_connmark.h --- iproute2-4.3.0/include/linux/tc_act/tc_connmark.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_connmark.h 2016-12-12 23:07:42.000000000 +0000 @@ -15,6 +15,7 @@ TCA_CONNMARK_UNSPEC, TCA_CONNMARK_PARMS, TCA_CONNMARK_TM, + TCA_CONNMARK_PAD, __TCA_CONNMARK_MAX }; #define TCA_CONNMARK_MAX (__TCA_CONNMARK_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_csum.h iproute2-4.9.0/include/linux/tc_act/tc_csum.h --- iproute2-4.3.0/include/linux/tc_act/tc_csum.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_csum.h 2016-12-12 23:07:42.000000000 +0000 @@ -10,6 +10,7 @@ TCA_CSUM_UNSPEC, TCA_CSUM_PARMS, TCA_CSUM_TM, + TCA_CSUM_PAD, __TCA_CSUM_MAX }; #define TCA_CSUM_MAX (__TCA_CSUM_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_defact.h iproute2-4.9.0/include/linux/tc_act/tc_defact.h --- iproute2-4.3.0/include/linux/tc_act/tc_defact.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_defact.h 2016-12-12 23:07:42.000000000 +0000 @@ -12,6 +12,7 @@ TCA_DEF_TM, TCA_DEF_PARMS, TCA_DEF_DATA, + TCA_DEF_PAD, __TCA_DEF_MAX }; #define TCA_DEF_MAX (__TCA_DEF_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_gact.h iproute2-4.9.0/include/linux/tc_act/tc_gact.h --- iproute2-4.3.0/include/linux/tc_act/tc_gact.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_gact.h 2016-12-12 23:07:42.000000000 +0000 @@ -25,6 +25,7 @@ TCA_GACT_TM, TCA_GACT_PARMS, TCA_GACT_PROB, + TCA_GACT_PAD, __TCA_GACT_MAX }; #define TCA_GACT_MAX (__TCA_GACT_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_ife.h iproute2-4.9.0/include/linux/tc_act/tc_ife.h --- iproute2-4.3.0/include/linux/tc_act/tc_ife.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_ife.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,40 @@ +#ifndef __UAPI_TC_IFE_H +#define __UAPI_TC_IFE_H + +#include +#include + +#define TCA_ACT_IFE 25 +/* Flag bits for now just encoding/decoding; mutually exclusive */ +#define IFE_ENCODE 1 +#define IFE_DECODE 0 + +struct tc_ife { + tc_gen; + __u16 flags; +}; + +/*XXX: We need to encode the total number of bytes consumed */ +enum { + TCA_IFE_UNSPEC, + TCA_IFE_PARMS, + TCA_IFE_TM, + TCA_IFE_DMAC, + TCA_IFE_SMAC, + TCA_IFE_TYPE, + TCA_IFE_METALST, + TCA_IFE_PAD, + __TCA_IFE_MAX +}; +#define TCA_IFE_MAX (__TCA_IFE_MAX - 1) + +#define IFE_META_SKBMARK 1 +#define IFE_META_HASHID 2 +#define IFE_META_PRIO 3 +#define IFE_META_QMAP 4 +#define IFE_META_TCINDEX 5 +/*Can be overridden at runtime by module option*/ +#define __IFE_META_MAX 6 +#define IFE_META_MAX (__IFE_META_MAX - 1) + +#endif diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_ipt.h iproute2-4.9.0/include/linux/tc_act/tc_ipt.h --- iproute2-4.3.0/include/linux/tc_act/tc_ipt.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_ipt.h 2016-12-12 23:07:42.000000000 +0000 @@ -14,6 +14,7 @@ TCA_IPT_CNT, TCA_IPT_TM, TCA_IPT_TARG, + TCA_IPT_PAD, __TCA_IPT_MAX }; #define TCA_IPT_MAX (__TCA_IPT_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_mirred.h iproute2-4.9.0/include/linux/tc_act/tc_mirred.h --- iproute2-4.3.0/include/linux/tc_act/tc_mirred.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_mirred.h 2016-12-12 23:07:42.000000000 +0000 @@ -20,6 +20,7 @@ TCA_MIRRED_UNSPEC, TCA_MIRRED_TM, TCA_MIRRED_PARMS, + TCA_MIRRED_PAD, __TCA_MIRRED_MAX }; #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_nat.h iproute2-4.9.0/include/linux/tc_act/tc_nat.h --- iproute2-4.3.0/include/linux/tc_act/tc_nat.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_nat.h 2016-12-12 23:07:42.000000000 +0000 @@ -10,6 +10,7 @@ TCA_NAT_UNSPEC, TCA_NAT_PARMS, TCA_NAT_TM, + TCA_NAT_PAD, __TCA_NAT_MAX }; #define TCA_NAT_MAX (__TCA_NAT_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_pedit.h iproute2-4.9.0/include/linux/tc_act/tc_pedit.h --- iproute2-4.3.0/include/linux/tc_act/tc_pedit.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_pedit.h 2016-12-12 23:07:42.000000000 +0000 @@ -10,6 +10,7 @@ TCA_PEDIT_UNSPEC, TCA_PEDIT_TM, TCA_PEDIT_PARMS, + TCA_PEDIT_PAD, __TCA_PEDIT_MAX }; #define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_skbedit.h iproute2-4.9.0/include/linux/tc_act/tc_skbedit.h --- iproute2-4.3.0/include/linux/tc_act/tc_skbedit.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_skbedit.h 2016-12-12 23:07:42.000000000 +0000 @@ -27,6 +27,7 @@ #define SKBEDIT_F_PRIORITY 0x1 #define SKBEDIT_F_QUEUE_MAPPING 0x2 #define SKBEDIT_F_MARK 0x4 +#define SKBEDIT_F_PTYPE 0x8 struct tc_skbedit { tc_gen; @@ -39,6 +40,8 @@ TCA_SKBEDIT_PRIORITY, TCA_SKBEDIT_QUEUE_MAPPING, TCA_SKBEDIT_MARK, + TCA_SKBEDIT_PAD, + TCA_SKBEDIT_PTYPE, __TCA_SKBEDIT_MAX }; #define TCA_SKBEDIT_MAX (__TCA_SKBEDIT_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tc_act/tc_vlan.h iproute2-4.9.0/include/linux/tc_act/tc_vlan.h --- iproute2-4.3.0/include/linux/tc_act/tc_vlan.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tc_act/tc_vlan.h 2016-12-12 23:07:42.000000000 +0000 @@ -16,6 +16,7 @@ #define TCA_VLAN_ACT_POP 1 #define TCA_VLAN_ACT_PUSH 2 +#define TCA_VLAN_ACT_MODIFY 3 struct tc_vlan { tc_gen; @@ -28,6 +29,8 @@ TCA_VLAN_PARMS, TCA_VLAN_PUSH_VLAN_ID, TCA_VLAN_PUSH_VLAN_PROTOCOL, + TCA_VLAN_PAD, + TCA_VLAN_PUSH_VLAN_PRIORITY, __TCA_VLAN_MAX, }; #define TCA_VLAN_MAX (__TCA_VLAN_MAX - 1) diff -Nru iproute2-4.3.0/include/linux/tcp.h iproute2-4.9.0/include/linux/tcp.h --- iproute2-4.3.0/include/linux/tcp.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tcp.h 2016-12-12 23:07:42.000000000 +0000 @@ -115,12 +115,22 @@ #define TCP_CC_INFO 26 /* Get Congestion Control (optional) info */ #define TCP_SAVE_SYN 27 /* Record SYN headers for new connections */ #define TCP_SAVED_SYN 28 /* Get SYN headers recorded for connection */ +#define TCP_REPAIR_WINDOW 29 /* Get/set window parameters */ struct tcp_repair_opt { __u32 opt_code; __u32 opt_val; }; +struct tcp_repair_window { + __u32 snd_wl1; + __u32 snd_wnd; + __u32 max_window; + + __u32 rcv_wnd; + __u32 rcv_wup; +}; + enum { TCP_NO_QUEUE, TCP_RECV_QUEUE, @@ -157,6 +167,7 @@ __u8 tcpi_backoff; __u8 tcpi_options; __u8 tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; + __u8 tcpi_delivery_rate_app_limited:1; __u32 tcpi_rto; __u32 tcpi_ato; @@ -196,6 +207,13 @@ __u64 tcpi_bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived */ __u32 tcpi_segs_out; /* RFC4898 tcpEStatsPerfSegsOut */ __u32 tcpi_segs_in; /* RFC4898 tcpEStatsPerfSegsIn */ + + __u32 tcpi_notsent_bytes; + __u32 tcpi_min_rtt; + __u32 tcpi_data_segs_in; /* RFC4898 tcpEStatsDataSegsIn */ + __u32 tcpi_data_segs_out; /* RFC4898 tcpEStatsDataSegsOut */ + + __u64 tcpi_delivery_rate; }; /* for TCP_MD5SIG socket option */ diff -Nru iproute2-4.3.0/include/linux/tcp_metrics.h iproute2-4.9.0/include/linux/tcp_metrics.h --- iproute2-4.3.0/include/linux/tcp_metrics.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tcp_metrics.h 2016-12-12 23:07:42.000000000 +0000 @@ -40,6 +40,7 @@ TCP_METRICS_ATTR_FOPEN_COOKIE, /* binary */ TCP_METRICS_ATTR_SADDR_IPV4, /* u32 */ TCP_METRICS_ATTR_SADDR_IPV6, /* binary */ + TCP_METRICS_ATTR_PAD, __TCP_METRICS_ATTR_MAX, }; diff -Nru iproute2-4.3.0/include/linux/tipc.h iproute2-4.9.0/include/linux/tipc.h --- iproute2-4.3.0/include/linux/tipc.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tipc.h 2016-12-12 23:07:42.000000000 +0000 @@ -60,26 +60,48 @@ __u32 upper; }; +/* TIPC Address Size, Offset, Mask specification for Z.C.N + */ +#define TIPC_NODE_BITS 12 +#define TIPC_CLUSTER_BITS 12 +#define TIPC_ZONE_BITS 8 + +#define TIPC_NODE_OFFSET 0 +#define TIPC_CLUSTER_OFFSET TIPC_NODE_BITS +#define TIPC_ZONE_OFFSET (TIPC_CLUSTER_OFFSET + TIPC_CLUSTER_BITS) + +#define TIPC_NODE_SIZE ((1UL << TIPC_NODE_BITS) - 1) +#define TIPC_CLUSTER_SIZE ((1UL << TIPC_CLUSTER_BITS) - 1) +#define TIPC_ZONE_SIZE ((1UL << TIPC_ZONE_BITS) - 1) + +#define TIPC_NODE_MASK (TIPC_NODE_SIZE << TIPC_NODE_OFFSET) +#define TIPC_CLUSTER_MASK (TIPC_CLUSTER_SIZE << TIPC_CLUSTER_OFFSET) +#define TIPC_ZONE_MASK (TIPC_ZONE_SIZE << TIPC_ZONE_OFFSET) + +#define TIPC_ZONE_CLUSTER_MASK (TIPC_ZONE_MASK | TIPC_CLUSTER_MASK) + static __inline__ __u32 tipc_addr(unsigned int zone, unsigned int cluster, unsigned int node) { - return (zone << 24) | (cluster << 12) | node; + return (zone << TIPC_ZONE_OFFSET) | + (cluster << TIPC_CLUSTER_OFFSET) | + node; } static __inline__ unsigned int tipc_zone(__u32 addr) { - return addr >> 24; + return addr >> TIPC_ZONE_OFFSET; } static __inline__ unsigned int tipc_cluster(__u32 addr) { - return (addr >> 12) & 0xfff; + return (addr & TIPC_CLUSTER_MASK) >> TIPC_CLUSTER_OFFSET; } static __inline__ unsigned int tipc_node(__u32 addr) { - return addr & 0xfff; + return addr & TIPC_NODE_MASK; } /* diff -Nru iproute2-4.3.0/include/linux/tipc_netlink.h iproute2-4.9.0/include/linux/tipc_netlink.h --- iproute2-4.3.0/include/linux/tipc_netlink.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/tipc_netlink.h 2016-12-12 23:07:42.000000000 +0000 @@ -56,6 +56,12 @@ TIPC_NL_NET_GET, TIPC_NL_NET_SET, TIPC_NL_NAME_TABLE_GET, + TIPC_NL_MON_SET, + TIPC_NL_MON_GET, + TIPC_NL_MON_PEER_GET, + TIPC_NL_PEER_REMOVE, + TIPC_NL_BEARER_ADD, + TIPC_NL_UDP_GET_REMOTEIP, __TIPC_NL_CMD_MAX, TIPC_NL_CMD_MAX = __TIPC_NL_CMD_MAX - 1 @@ -72,6 +78,8 @@ TIPC_NLA_NODE, /* nest */ TIPC_NLA_NET, /* nest */ TIPC_NLA_NAME_TABLE, /* nest */ + TIPC_NLA_MON, /* nest */ + TIPC_NLA_MON_PEER, /* nest */ __TIPC_NLA_MAX, TIPC_NLA_MAX = __TIPC_NLA_MAX - 1 @@ -93,6 +101,7 @@ TIPC_NLA_UDP_UNSPEC, TIPC_NLA_UDP_LOCAL, /* sockaddr_storage */ TIPC_NLA_UDP_REMOTE, /* sockaddr_storage */ + TIPC_NLA_UDP_MULTI_REMOTEIP, /* flag */ __TIPC_NLA_UDP_MAX, TIPC_NLA_UDP_MAX = __TIPC_NLA_UDP_MAX - 1 @@ -166,6 +175,20 @@ TIPC_NLA_NAME_TABLE_MAX = __TIPC_NLA_NAME_TABLE_MAX - 1 }; +/* Monitor info */ +enum { + TIPC_NLA_MON_UNSPEC, + TIPC_NLA_MON_ACTIVATION_THRESHOLD, /* u32 */ + TIPC_NLA_MON_REF, /* u32 */ + TIPC_NLA_MON_ACTIVE, /* flag */ + TIPC_NLA_MON_BEARER_NAME, /* string */ + TIPC_NLA_MON_PEERCNT, /* u32 */ + TIPC_NLA_MON_LISTGEN, /* u32 */ + + __TIPC_NLA_MON_MAX, + TIPC_NLA_MON_MAX = __TIPC_NLA_MON_MAX - 1 +}; + /* Publication info */ enum { TIPC_NLA_PUBL_UNSPEC, @@ -182,6 +205,24 @@ TIPC_NLA_PUBL_MAX = __TIPC_NLA_PUBL_MAX - 1 }; +/* Monitor peer info */ +enum { + TIPC_NLA_MON_PEER_UNSPEC, + + TIPC_NLA_MON_PEER_ADDR, /* u32 */ + TIPC_NLA_MON_PEER_DOMGEN, /* u32 */ + TIPC_NLA_MON_PEER_APPLIED, /* u32 */ + TIPC_NLA_MON_PEER_UPMAP, /* u64 */ + TIPC_NLA_MON_PEER_MEMBERS, /* tlv */ + TIPC_NLA_MON_PEER_UP, /* flag */ + TIPC_NLA_MON_PEER_HEAD, /* flag */ + TIPC_NLA_MON_PEER_LOCAL, /* flag */ + TIPC_NLA_MON_PEER_PAD, /* flag */ + + __TIPC_NLA_MON_PEER_MAX, + TIPC_NLA_MON_PEER_MAX = __TIPC_NLA_MON_PEER_MAX - 1 +}; + /* Nest, connection info */ enum { TIPC_NLA_CON_UNSPEC, diff -Nru iproute2-4.3.0/include/linux/xfrm.h iproute2-4.9.0/include/linux/xfrm.h --- iproute2-4.3.0/include/linux/xfrm.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/linux/xfrm.h 2016-12-12 23:07:42.000000000 +0000 @@ -298,10 +298,11 @@ XFRMA_ALG_AUTH_TRUNC, /* struct xfrm_algo_auth */ XFRMA_MARK, /* struct xfrm_mark */ XFRMA_TFCPAD, /* __u32 */ - XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_esn */ + XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_state_esn */ XFRMA_SA_EXTRA_FLAGS, /* __u32 */ XFRMA_PROTO, /* __u8 */ XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ + XFRMA_PAD, __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) diff -Nru iproute2-4.3.0/include/list.h iproute2-4.9.0/include/list.h --- iproute2-4.3.0/include/list.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/list.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,128 @@ +#ifndef __LIST_H__ +#define __LIST_H__ 1 +/* List and hash list stuff from kernel */ + +#include + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct list_head { + struct list_head *next, *prev; +}; + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, typeof(*(pos)), member) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_prev_entry(pos, member)) + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +static inline void hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos ; pos = pos->next) + + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +#define hlist_entry_safe(ptr, type, member) \ + ({ typeof(ptr) ____ptr = (ptr); \ + ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ + }) + +#define hlist_for_each_entry(pos, head, member) \ + for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\ + pos; \ + pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) + +#endif /* __LIST_H__ */ diff -Nru iproute2-4.3.0/include/SNAPSHOT.h iproute2-4.9.0/include/SNAPSHOT.h --- iproute2-4.3.0/include/SNAPSHOT.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/SNAPSHOT.h 2016-12-12 23:07:42.000000000 +0000 @@ -1 +1 @@ -static const char SNAPSHOT[] = "151103"; +static const char SNAPSHOT[] = "161212"; diff -Nru iproute2-4.3.0/include/utils.h iproute2-4.9.0/include/utils.h --- iproute2-4.3.0/include/utils.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/include/utils.h 2016-12-12 23:07:42.000000000 +0000 @@ -40,6 +40,10 @@ #define IPSEC_PROTO_ANY 255 #endif +#ifndef CONFDIR +#define CONFDIR "/etc/iproute2" +#endif + #define SPRINT_BSIZE 64 #define SPRINT_BUF(x) char x[SPRINT_BSIZE] @@ -47,6 +51,7 @@ #define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0) #define NEXT_ARG_OK() (argc - 1 > 0) +#define NEXT_ARG_FWD() do { argv++; argc--; } while(0) #define PREV_ARG() do { argv--; argc++; } while(0) typedef struct @@ -92,7 +97,9 @@ int get_addr(inet_prefix *dst, const char *arg, int family); int get_prefix(inet_prefix *dst, char *arg, int family); int mask2bits(__u32 netmask); +int get_addr_ila(__u64 *val, const char *arg); +int get_hex(char c); int get_integer(int *val, const char *arg, int base); int get_unsigned(unsigned *val, const char *arg, int base); int get_time_rtt(unsigned *val, const char *arg, int *raw); @@ -106,17 +113,29 @@ int get_s16(__s16 *val, const char *arg, int base); int get_u8(__u8 *val, const char *arg, int base); int get_s8(__s8 *val, const char *arg, int base); - -char* hexstring_n2a(const __u8 *str, int len, char *buf, int blen); -__u8* hexstring_a2n(const char *str, __u8 *buf, int blen); +int get_be64(__be64 *val, const char *arg, int base); +int get_be32(__be32 *val, const char *arg, int base); +int get_be16(__be16 *val, const char *arg, int base); +int get_addr64(__u64 *ap, const char *cp); + +char *hexstring_n2a(const __u8 *str, int len, char *buf, int blen); +__u8 *hexstring_a2n(const char *str, __u8 *buf, int blen, unsigned int *len); +#define ADDR64_BUF_SIZE sizeof("xxxx:xxxx:xxxx:xxxx") +int addr64_n2a(__u64 addr, char *buff, size_t len); int af_bit_len(int af); int af_byte_len(int af); -const char *format_host(int af, int len, const void *addr, +const char *format_host_r(int af, int len, const void *addr, char *buf, int buflen); -const char *rt_addr_n2a(int af, int len, const void *addr, +const char *format_host(int af, int lne, const void *addr); +#define format_host_rta(af, rta) \ + format_host(af, RTA_PAYLOAD(rta), RTA_DATA(rta)) +const char *rt_addr_n2a_r(int af, int len, const void *addr, char *buf, int buflen); +const char *rt_addr_n2a(int af, int len, const void *addr); +#define rt_addr_n2a_rta(af, rta) \ + rt_addr_n2a(af, RTA_PAYLOAD(rta), RTA_DATA(rta)) int read_family(const char *name); const char *family_name(int family); @@ -166,6 +185,24 @@ return group ? (1 << (group - 1)) : 0; } +/* courtesy of bridge-utils */ +static inline unsigned long __tv_to_jiffies(const struct timeval *tv) +{ + unsigned long long jif; + + jif = 1000000ULL * tv->tv_sec + tv->tv_usec; + + return jif/10000; +} + +static inline void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies) +{ + unsigned long long tvusec; + + tvusec = 10000ULL*jiffies; + tv->tv_sec = tvusec/1000000; + tv->tv_usec = tvusec - 1000000 * tv->tv_sec; +} int print_timestamp(FILE *fp); void print_nlmsg_timestamp(FILE *fp, const struct nlmsghdr *n); @@ -191,6 +228,12 @@ __attribute__ ((format (printf, (pos_str), (pos_args)))) #endif +#define _textify(x) #x +#define textify(x) _textify(x) + +#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) +#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) + extern int cmdlineno; ssize_t getcmdline(char **line, size_t *len, FILE *in); int makeargs(char *line, char *argv[], int maxargs); @@ -205,5 +248,7 @@ bool show_label); char *int_to_str(int val, char *buf); +int get_guid(__u64 *guid, const char *arg); +int get_real_family(int rtm_type, int rtm_family); #endif /* __UTILS_H__ */ diff -Nru iproute2-4.3.0/include/xtables.h iproute2-4.9.0/include/xtables.h --- iproute2-4.3.0/include/xtables.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/include/xtables.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,567 @@ +#ifndef _XTABLES_H +#define _XTABLES_H + +/* + * Changing any structs/functions may incur a needed change + * in libxtables_vcurrent/vage too. + */ + +#include /* PF_* */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif +#ifndef IPPROTO_DCCP +#define IPPROTO_DCCP 33 +#endif +#ifndef IPPROTO_MH +# define IPPROTO_MH 135 +#endif +#ifndef IPPROTO_UDPLITE +#define IPPROTO_UDPLITE 136 +#endif + +#include + +struct in_addr; + +/* + * .size is here so that there is a somewhat reasonable check + * against the chosen .type. + */ +#define XTOPT_POINTER(stype, member) \ + .ptroff = offsetof(stype, member), \ + .size = sizeof(((stype *)NULL)->member) +#define XTOPT_TABLEEND {.name = NULL} + +/** + * Select the format the input has to conform to, as well as the target type + * (area pointed to with XTOPT_POINTER). Note that the storing is not always + * uniform. @cb->val will be populated with as much as there is space, i.e. + * exactly 2 items for ranges, but the target area can receive more values + * (e.g. in case of ranges), or less values (e.g. %XTTYPE_HOSTMASK). + * + * %XTTYPE_NONE: option takes no argument + * %XTTYPE_UINT*: standard integer + * %XTTYPE_UINT*RC: colon-separated range of standard integers + * %XTTYPE_DOUBLE: double-precision floating point number + * %XTTYPE_STRING: arbitrary string + * %XTTYPE_TOSMASK: 8-bit TOS value with optional mask + * %XTTYPE_MARKMASK32: 32-bit mark with optional mask + * %XTTYPE_SYSLOGLEVEL: syslog level by name or number + * %XTTYPE_HOST: one host or address (ptr: union nf_inet_addr) + * %XTTYPE_HOSTMASK: one host or address, with an optional prefix length + * (ptr: union nf_inet_addr; only host portion is stored) + * %XTTYPE_PROTOCOL: protocol number/name from /etc/protocols (ptr: uint8_t) + * %XTTYPE_PORT: 16-bit port name or number (supports %XTOPT_NBO) + * %XTTYPE_PORTRC: colon-separated port range (names acceptable), + * (supports %XTOPT_NBO) + * %XTTYPE_PLEN: prefix length + * %XTTYPE_PLENMASK: prefix length (ptr: union nf_inet_addr) + * %XTTYPE_ETHERMAC: Ethernet MAC address in hex form + */ +enum xt_option_type { + XTTYPE_NONE, + XTTYPE_UINT8, + XTTYPE_UINT16, + XTTYPE_UINT32, + XTTYPE_UINT64, + XTTYPE_UINT8RC, + XTTYPE_UINT16RC, + XTTYPE_UINT32RC, + XTTYPE_UINT64RC, + XTTYPE_DOUBLE, + XTTYPE_STRING, + XTTYPE_TOSMASK, + XTTYPE_MARKMASK32, + XTTYPE_SYSLOGLEVEL, + XTTYPE_HOST, + XTTYPE_HOSTMASK, + XTTYPE_PROTOCOL, + XTTYPE_PORT, + XTTYPE_PORTRC, + XTTYPE_PLEN, + XTTYPE_PLENMASK, + XTTYPE_ETHERMAC, +}; + +/** + * %XTOPT_INVERT: option is invertible (usable with !) + * %XTOPT_MAND: option is mandatory + * %XTOPT_MULTI: option may be specified multiple times + * %XTOPT_PUT: store value into memory at @ptroff + * %XTOPT_NBO: store value in network-byte order + * (only certain XTTYPEs recognize this) + */ +enum xt_option_flags { + XTOPT_INVERT = 1 << 0, + XTOPT_MAND = 1 << 1, + XTOPT_MULTI = 1 << 2, + XTOPT_PUT = 1 << 3, + XTOPT_NBO = 1 << 4, +}; + +/** + * @name: name of option + * @type: type of input and validation method, see %XTTYPE_* + * @id: unique number (within extension) for option, 0-31 + * @excl: bitmask of flags that cannot be used with this option + * @also: bitmask of flags that must be used with this option + * @flags: bitmask of option flags, see %XTOPT_* + * @ptroff: offset into private structure for member + * @size: size of the item pointed to by @ptroff; this is a safeguard + * @min: lowest allowed value (for singular integral types) + * @max: highest allowed value (for singular integral types) + */ +struct xt_option_entry { + const char *name; + enum xt_option_type type; + unsigned int id, excl, also, flags; + unsigned int ptroff; + size_t size; + unsigned int min, max; +}; + +/** + * @arg: input from command line + * @ext_name: name of extension currently being processed + * @entry: current option being processed + * @data: per-extension kernel data block + * @xflags: options of the extension that have been used + * @invert: whether option was used with ! + * @nvals: number of results in uXX_multi + * @val: parsed result + * @udata: per-extension private scratch area + * (cf. xtables_{match,target}->udata_size) + */ +struct xt_option_call { + const char *arg, *ext_name; + const struct xt_option_entry *entry; + void *data; + unsigned int xflags; + bool invert; + uint8_t nvals; + union { + uint8_t u8, u8_range[2], syslog_level, protocol; + uint16_t u16, u16_range[2], port, port_range[2]; + uint32_t u32, u32_range[2]; + uint64_t u64, u64_range[2]; + double dbl; + struct { + union nf_inet_addr haddr, hmask; + uint8_t hlen; + }; + struct { + uint8_t tos_value, tos_mask; + }; + struct { + uint32_t mark, mask; + }; + uint8_t ethermac[6]; + } val; + /* Wished for a world where the ones below were gone: */ + union { + struct xt_entry_match **match; + struct xt_entry_target **target; + }; + void *xt_entry; + void *udata; +}; + +/** + * @ext_name: name of extension currently being processed + * @data: per-extension (kernel) data block + * @udata: per-extension private scratch area + * (cf. xtables_{match,target}->udata_size) + * @xflags: options of the extension that have been used + */ +struct xt_fcheck_call { + const char *ext_name; + void *data, *udata; + unsigned int xflags; +}; + +/** + * A "linear"/linked-list based name<->id map, for files similar to + * /etc/iproute2/. + */ +struct xtables_lmap { + char *name; + int id; + struct xtables_lmap *next; +}; + +enum xtables_ext_flags { + XTABLES_EXT_ALIAS = 1 << 0, +}; + +/* Include file for additions: new matches and targets. */ +struct xtables_match +{ + /* + * ABI/API version this module requires. Must be first member, + * as the rest of this struct may be subject to ABI changes. + */ + const char *version; + + struct xtables_match *next; + + const char *name; + const char *real_name; + + /* Revision of match (0 by default). */ + uint8_t revision; + + /* Extension flags */ + uint8_t ext_flags; + + uint16_t family; + + /* Size of match data. */ + size_t size; + + /* Size of match data relevant for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the match. */ + void (*init)(struct xt_entry_match *m); + + /* Function which parses command options; returns true if it + ate an option */ + /* entry is struct ipt_entry for example */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const void *entry, + struct xt_entry_match **match); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the match iff non-NULL: put space at end */ + /* ip is struct ipt_ip * for example */ + void (*print)(const void *ip, + const struct xt_entry_match *match, int numeric); + + /* Saves the match info in parsable form to stdout. */ + /* ip is struct ipt_ip * for example */ + void (*save)(const void *ip, const struct xt_entry_match *match); + + /* Print match name or alias */ + const char *(*alias)(const struct xt_entry_match *match); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* New parser */ + void (*x6_parse)(struct xt_option_call *); + void (*x6_fcheck)(struct xt_fcheck_call *); + const struct xt_option_entry *x6_options; + + /* Size of per-extension instance extra "global" scratch space */ + size_t udata_size; + + /* Ignore these men behind the curtain: */ + void *udata; + unsigned int option_offset; + struct xt_entry_match *m; + unsigned int mflags; + unsigned int loaded; /* simulate loading so options are merged properly */ +}; + +struct xtables_target +{ + /* + * ABI/API version this module requires. Must be first member, + * as the rest of this struct may be subject to ABI changes. + */ + const char *version; + + struct xtables_target *next; + + + const char *name; + + /* Real target behind this, if any. */ + const char *real_name; + + /* Revision of target (0 by default). */ + uint8_t revision; + + /* Extension flags */ + uint8_t ext_flags; + + uint16_t family; + + + /* Size of target data. */ + size_t size; + + /* Size of target data relevant for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the target. */ + void (*init)(struct xt_entry_target *t); + + /* Function which parses command options; returns true if it + ate an option */ + /* entry is struct ipt_entry for example */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const void *entry, + struct xt_entry_target **targetinfo); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the target iff non-NULL: put space at end */ + void (*print)(const void *ip, + const struct xt_entry_target *target, int numeric); + + /* Saves the targinfo in parsable form to stdout. */ + void (*save)(const void *ip, + const struct xt_entry_target *target); + + /* Print target name or alias */ + const char *(*alias)(const struct xt_entry_target *target); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* New parser */ + void (*x6_parse)(struct xt_option_call *); + void (*x6_fcheck)(struct xt_fcheck_call *); + const struct xt_option_entry *x6_options; + + size_t udata_size; + + /* Ignore these men behind the curtain: */ + void *udata; + unsigned int option_offset; + struct xt_entry_target *t; + unsigned int tflags; + unsigned int used; + unsigned int loaded; /* simulate loading so options are merged properly */ +}; + +struct xtables_rule_match { + struct xtables_rule_match *next; + struct xtables_match *match; + /* Multiple matches of the same type: the ones before + the current one are completed from parsing point of view */ + bool completed; +}; + +/** + * struct xtables_pprot - + * + * A few hardcoded protocols for 'all' and in case the user has no + * /etc/protocols. + */ +struct xtables_pprot { + const char *name; + uint8_t num; +}; + +enum xtables_tryload { + XTF_DONT_LOAD, + XTF_DURING_LOAD, + XTF_TRY_LOAD, + XTF_LOAD_MUST_SUCCEED, +}; + +enum xtables_exittype { + OTHER_PROBLEM = 1, + PARAMETER_PROBLEM, + VERSION_PROBLEM, + RESOURCE_PROBLEM, + XTF_ONLY_ONCE, + XTF_NO_INVERT, + XTF_BAD_VALUE, + XTF_ONE_ACTION, +}; + +struct xtables_globals +{ + unsigned int option_offset; + const char *program_name, *program_version; + struct option *orig_opts; + struct option *opts; + void (*exit_err)(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); + int (*compat_rev)(const char *name, uint8_t rev, int opt); +}; + +#define XT_GETOPT_TABLEEND {.name = NULL, .has_arg = false} + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char *xtables_modprobe_program; +extern struct xtables_match *xtables_matches; +extern struct xtables_target *xtables_targets; + +extern void xtables_init(void); +extern void xtables_set_nfproto(uint8_t); +extern void *xtables_calloc(size_t, size_t); +extern void *xtables_malloc(size_t); +extern void *xtables_realloc(void *, size_t); + +extern int xtables_insmod(const char *, const char *, bool); +extern int xtables_load_ko(const char *, bool); +extern int xtables_set_params(struct xtables_globals *xtp); +extern void xtables_free_opts(int reset_offset); +extern struct option *xtables_merge_options(struct option *origopts, + struct option *oldopts, const struct option *newopts, + unsigned int *option_offset); + +extern int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto); +extern struct xtables_match *xtables_find_match(const char *name, + enum xtables_tryload, struct xtables_rule_match **match); +extern struct xtables_target *xtables_find_target(const char *name, + enum xtables_tryload); +extern int xtables_compatible_revision(const char *name, uint8_t revision, + int opt); + +extern void xtables_rule_matches_free(struct xtables_rule_match **matches); + +/* Your shared library should call one of these. */ +extern void xtables_register_match(struct xtables_match *me); +extern void xtables_register_matches(struct xtables_match *, unsigned int); +extern void xtables_register_target(struct xtables_target *me); +extern void xtables_register_targets(struct xtables_target *, unsigned int); + +extern bool xtables_strtoul(const char *, char **, uintmax_t *, + uintmax_t, uintmax_t); +extern bool xtables_strtoui(const char *, char **, unsigned int *, + unsigned int, unsigned int); +extern int xtables_service_to_port(const char *name, const char *proto); +extern uint16_t xtables_parse_port(const char *port, const char *proto); +extern void +xtables_parse_interface(const char *arg, char *vianame, unsigned char *mask); + +/* this is a special 64bit data type that is 8-byte aligned */ +#define aligned_u64 uint64_t __attribute__((aligned(8))) + +extern struct xtables_globals *xt_params; +#define xtables_error (xt_params->exit_err) + +extern void xtables_param_act(unsigned int, const char *, ...); + +extern const char *xtables_ipaddr_to_numeric(const struct in_addr *); +extern const char *xtables_ipaddr_to_anyname(const struct in_addr *); +extern const char *xtables_ipmask_to_numeric(const struct in_addr *); +extern struct in_addr *xtables_numeric_to_ipaddr(const char *); +extern struct in_addr *xtables_numeric_to_ipmask(const char *); +extern int xtables_ipmask_to_cidr(const struct in_addr *); +extern void xtables_ipparse_any(const char *, struct in_addr **, + struct in_addr *, unsigned int *); +extern void xtables_ipparse_multiple(const char *, struct in_addr **, + struct in_addr **, unsigned int *); + +extern struct in6_addr *xtables_numeric_to_ip6addr(const char *); +extern const char *xtables_ip6addr_to_numeric(const struct in6_addr *); +extern const char *xtables_ip6addr_to_anyname(const struct in6_addr *); +extern const char *xtables_ip6mask_to_numeric(const struct in6_addr *); +extern int xtables_ip6mask_to_cidr(const struct in6_addr *); +extern void xtables_ip6parse_any(const char *, struct in6_addr **, + struct in6_addr *, unsigned int *); +extern void xtables_ip6parse_multiple(const char *, struct in6_addr **, + struct in6_addr **, unsigned int *); + +/** + * Print the specified value to standard output, quoting dangerous + * characters if required. + */ +extern void xtables_save_string(const char *value); + +#define FMT_NUMERIC 0x0001 +#define FMT_NOCOUNTS 0x0002 +#define FMT_KILOMEGAGIGA 0x0004 +#define FMT_OPTIONS 0x0008 +#define FMT_NOTABLE 0x0010 +#define FMT_NOTARGET 0x0020 +#define FMT_VIA 0x0040 +#define FMT_NONEWLINE 0x0080 +#define FMT_LINENUMBERS 0x0100 + +#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ + | FMT_NUMERIC | FMT_NOTABLE) +#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) + +extern void xtables_print_num(uint64_t number, unsigned int format); + +#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) +# ifdef _INIT +# undef _init +# define _init _INIT +# endif + extern void init_extensions(void); + extern void init_extensions4(void); + extern void init_extensions6(void); +#else +# define _init __attribute__((constructor)) _INIT +#endif + +extern const struct xtables_pprot xtables_chain_protos[]; +extern uint16_t xtables_parse_protocol(const char *s); + +/* kernel revision handling */ +extern int kernel_version; +extern void get_kernel_version(void); +#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z) +#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF) +#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF) +#define LINUX_VERSION_PATCH(x) ( (x) & 0xFF) + +/* xtoptions.c */ +extern void xtables_option_metavalidate(const char *, + const struct xt_option_entry *); +extern struct option *xtables_options_xfrm(struct option *, struct option *, + const struct xt_option_entry *, + unsigned int *); +extern void xtables_option_parse(struct xt_option_call *); +extern void xtables_option_tpcall(unsigned int, char **, bool, + struct xtables_target *, void *); +extern void xtables_option_mpcall(unsigned int, char **, bool, + struct xtables_match *, void *); +extern void xtables_option_tfcall(struct xtables_target *); +extern void xtables_option_mfcall(struct xtables_match *); +extern void xtables_options_fcheck(const char *, unsigned int, + const struct xt_option_entry *); + +extern struct xtables_lmap *xtables_lmap_init(const char *); +extern void xtables_lmap_free(struct xtables_lmap *); +extern int xtables_lmap_name2id(const struct xtables_lmap *, const char *); +extern const char *xtables_lmap_id2name(const struct xtables_lmap *, int); + +#ifdef XTABLES_INTERNAL + +/* Shipped modules rely on this... */ + +# ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) +# endif + +extern void _init(void); + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _XTABLES_H */ diff -Nru iproute2-4.3.0/ip/ip6tunnel.c iproute2-4.9.0/ip/ip6tunnel.c --- iproute2-4.3.0/ip/ip6tunnel.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ip6tunnel.c 2016-12-12 23:07:42.000000000 +0000 @@ -50,7 +50,7 @@ fprintf(stderr, " [ mode { ip6ip6 | ipip6 | ip6gre | vti6 | any } ]\n"); fprintf(stderr, " [ remote ADDR local ADDR ] [ dev PHYS_DEV ]\n"); fprintf(stderr, " [ encaplimit ELIM ]\n"); - fprintf(stderr ," [ hoplimit TTL ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n"); + fprintf(stderr, " [ hoplimit TTL ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n"); fprintf(stderr, " [ dscp inherit ]\n"); fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n"); fprintf(stderr, "\n"); @@ -68,16 +68,20 @@ static void print_tunnel(struct ip6_tnl_parm2 *p) { - char remote[64]; - char local[64]; - - inet_ntop(AF_INET6, &p->raddr, remote, sizeof(remote)); - inet_ntop(AF_INET6, &p->laddr, local, sizeof(local)); + char s1[1024]; + char s2[1024]; + /* Do not use format_host() for local addr, + * symbolic name will not be useful. + */ printf("%s: %s/ipv6 remote %s local %s", - p->name, tnl_strproto(p->proto), remote, local); + p->name, + tnl_strproto(p->proto), + format_host_r(AF_INET6, 16, &p->raddr, s1, sizeof(s1)), + rt_addr_n2a_r(AF_INET6, 16, &p->laddr, s2, sizeof(s2))); if (p->link) { const char *n = ll_index_to_name(p->link); + if (n) printf(" dev %s", n); } @@ -93,6 +97,7 @@ printf(" tclass inherit"); else { __u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_TCLASS); + printf(" tclass 0x%02x", (__u8)(val >> 20)); } @@ -107,22 +112,22 @@ printf(" dscp inherit"); if (p->proto == IPPROTO_GRE) { - if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key) + if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key) printf(" key %u", ntohl(p->i_key)); - else if ((p->i_flags|p->o_flags)&GRE_KEY) { - if (p->i_flags&GRE_KEY) - printf(" ikey %u ", ntohl(p->i_key)); - if (p->o_flags&GRE_KEY) - printf(" okey %u ", ntohl(p->o_key)); + else if ((p->i_flags | p->o_flags) & GRE_KEY) { + if (p->i_flags & GRE_KEY) + printf(" ikey %u", ntohl(p->i_key)); + if (p->o_flags & GRE_KEY) + printf(" okey %u", ntohl(p->o_key)); } - if (p->i_flags&GRE_SEQ) + if (p->i_flags & GRE_SEQ) printf("%s Drop packets out of sequence.", _SL_); - if (p->i_flags&GRE_CSUM) + if (p->i_flags & GRE_CSUM) printf("%s Checksum in received packet is required.", _SL_); - if (p->o_flags&GRE_SEQ) + if (p->o_flags & GRE_SEQ) printf("%s Sequence packets on output.", _SL_); - if (p->o_flags&GRE_CSUM) + if (p->o_flags & GRE_CSUM) printf("%s Checksum output packets.", _SL_); } } @@ -130,9 +135,7 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm2 *p) { int count = 0; - char medium[IFNAMSIZ]; - - memset(medium, 0, sizeof(medium)); + char medium[IFNAMSIZ] = {}; while (argc > 0) { if (strcmp(*argv, "mode") == 0) { @@ -155,11 +158,12 @@ strcmp(*argv, "any") == 0) p->proto = 0; else { - fprintf(stderr,"Unknown tunnel mode \"%s\"\n", *argv); + fprintf(stderr, "Unknown tunnel mode \"%s\"\n", *argv); exit(-1); } } else if (strcmp(*argv, "remote") == 0) { inet_prefix raddr; + NEXT_ARG(); get_prefix(&raddr, *argv, preferred_family); if (raddr.family == AF_UNSPEC) @@ -167,6 +171,7 @@ memcpy(&p->raddr, &raddr.data, sizeof(p->raddr)); } else if (strcmp(*argv, "local") == 0) { inet_prefix laddr; + NEXT_ARG(); get_prefix(&laddr, *argv, preferred_family); if (laddr.family == AF_UNSPEC) @@ -181,6 +186,7 @@ p->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; } else { __u8 uval; + if (get_u8(&uval, *argv, 0) < -1) invarg("invalid ELIM", *argv); p->encap_limit = uval; @@ -190,6 +196,7 @@ strcmp(*argv, "ttl") == 0 || strcmp(*argv, "hlim") == 0) { __u8 uval; + NEXT_ARG(); if (get_u8(&uval, *argv, 0)) invarg("invalid TTL", *argv); @@ -199,6 +206,7 @@ strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { __u8 uval; + NEXT_ARG(); p->flowinfo &= ~IP6_FLOWINFO_TCLASS; if (strcmp(*argv, "inherit") == 0) @@ -212,6 +220,7 @@ } else if (strcmp(*argv, "flowlabel") == 0 || strcmp(*argv, "fl") == 0) { __u32 uval; + NEXT_ARG(); p->flowinfo &= ~IP6_FLOWINFO_FLOWLABEL; if (strcmp(*argv, "inherit") == 0) @@ -230,45 +239,18 @@ invarg("not inherit", *argv); p->flags |= IP6_TNL_F_RCV_DSCP_COPY; } else if (strcmp(*argv, "key") == 0) { - unsigned uval; NEXT_ARG(); p->i_flags |= GRE_KEY; p->o_flags |= GRE_KEY; - if (strchr(*argv, '.')) - p->i_key = p->o_key = get_addr32(*argv); - else { - if (get_unsigned(&uval, *argv, 0) < 0) { - fprintf(stderr, "invalid value of \"key\"\n"); - exit(-1); - } - p->i_key = p->o_key = htonl(uval); - } + p->i_key = p->o_key = tnl_parse_key("key", *argv); } else if (strcmp(*argv, "ikey") == 0) { - unsigned uval; NEXT_ARG(); p->i_flags |= GRE_KEY; - if (strchr(*argv, '.')) - p->i_key = get_addr32(*argv); - else { - if (get_unsigned(&uval, *argv, 0)<0) { - fprintf(stderr, "invalid value of \"ikey\"\n"); - exit(-1); - } - p->i_key = htonl(uval); - } + p->i_key = tnl_parse_key("ikey", *argv); } else if (strcmp(*argv, "okey") == 0) { - unsigned uval; NEXT_ARG(); p->o_flags |= GRE_KEY; - if (strchr(*argv, '.')) - p->o_key = get_addr32(*argv); - else { - if (get_unsigned(&uval, *argv, 0)<0) { - fprintf(stderr, "invalid value of \"okey\"\n"); - exit(-1); - } - p->o_key = htonl(uval); - } + p->o_key = tnl_parse_key("okey", *argv); } else if (strcmp(*argv, "seq") == 0) { p->i_flags |= GRE_SEQ; p->o_flags |= GRE_SEQ; @@ -286,15 +268,14 @@ } else { if (strcmp(*argv, "name") == 0) { NEXT_ARG(); - } - else if (matches(*argv, "help") == 0) + } else if (matches(*argv, "help") == 0) usage(); if (p->name[0]) duparg2("name", *argv); strncpy(p->name, *argv, IFNAMSIZ - 1); if (cmd == SIOCCHGTUNNEL && count == 0) { - struct ip6_tnl_parm2 old_p; - memset(&old_p, 0, sizeof(old_p)); + struct ip6_tnl_parm2 old_p = {}; + if (tnl_get_ioctl(*argv, &old_p)) return -1; *p = old_p; @@ -305,8 +286,10 @@ } if (medium[0]) { p->link = ll_name_to_index(medium); - if (p->link == 0) + if (p->link == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", medium); return -1; + } } return 0; } @@ -330,10 +313,10 @@ { return ((!p1->link || p1->link == p2->link) && (!p1->name[0] || strcmp(p1->name, p2->name) == 0) && - (memcmp(&p1->laddr, &in6addr_any, sizeof(p1->laddr)) == 0 || - memcmp(&p1->laddr, &p2->laddr, sizeof(p1->laddr)) == 0) && - (memcmp(&p1->raddr, &in6addr_any, sizeof(p1->raddr)) == 0 || - memcmp(&p1->raddr, &p2->raddr, sizeof(p1->raddr)) == 0) && + (IN6_IS_ADDR_UNSPECIFIED(&p1->laddr) || + IN6_ARE_ADDR_EQUAL(&p1->laddr, &p2->laddr)) && + (IN6_IS_ADDR_UNSPECIFIED(&p1->raddr) || + IN6_ARE_ADDR_EQUAL(&p1->raddr, &p2->raddr)) && (!p1->proto || !p2->proto || p1->proto == p2->proto) && (!p1->encap_limit || p1->encap_limit == p2->encap_limit) && (!p1->hop_limit || p1->hop_limit == p2->hop_limit) && @@ -349,26 +332,23 @@ char buf[512]; int err = -1; FILE *fp = fopen("/proc/net/dev", "r"); + if (fp == NULL) { perror("fopen"); - goto end; + return -1; } /* skip two lines at the begenning of the file */ if (!fgets(buf, sizeof(buf), fp) || !fgets(buf, sizeof(buf), fp)) { fprintf(stderr, "/proc/net/dev read error\n"); - return -1; + goto end; } while (fgets(buf, sizeof(buf), fp) != NULL) { char name[IFNAMSIZ]; int index, type; - unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, - rx_fifo, rx_frame, - tx_bytes, tx_packets, tx_errs, tx_drops, - tx_fifo, tx_colls, tx_carrier, rx_multi; - struct ip6_tnl_parm2 p1; + struct ip6_tnl_parm2 p1 = {}; char *ptr; buf[sizeof(buf) - 1] = '\0'; @@ -377,12 +357,6 @@ fprintf(stderr, "Wrong format for /proc/net/dev. Giving up.\n"); goto end; } - if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld", - &rx_bytes, &rx_packets, &rx_errs, &rx_drops, - &rx_fifo, &rx_frame, &rx_multi, - &tx_bytes, &tx_packets, &tx_errs, &tx_drops, - &tx_fifo, &tx_colls, &tx_carrier) != 14) - continue; if (p->name[0] && strcmp(p->name, name)) continue; index = ll_name_to_index(name); @@ -395,7 +369,6 @@ } if (type != ARPHRD_TUNNEL6 && type != ARPHRD_IP6GRE) continue; - memset(&p1, 0, sizeof(p1)); ip6_tnl_parm_init(&p1, 0); if (type == ARPHRD_IP6GRE) p1.proto = IPPROTO_GRE; @@ -408,35 +381,26 @@ if (!ip6_tnl_parm_match(p, &p1)) continue; print_tunnel(&p1); - if (show_stats) { - printf("%s", _SL_); - printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_); - printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s", - rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_); - printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_); - printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld", - tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops); - } + if (show_stats) + tnl_print_stats(ptr); printf("\n"); } err = 0; - end: - if (fp) - fclose(fp); + fclose(fp); return err; } static int do_show(int argc, char **argv) { - struct ip6_tnl_parm2 p; + struct ip6_tnl_parm2 p; ll_init_map(&rth); ip6_tnl_parm_init(&p, 0); p.proto = 0; /* default to any */ - if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) - return -1; + if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) + return -1; if (!p.name[0] || show_stats) do_tunnels_list(&p); @@ -447,7 +411,7 @@ printf("\n"); } - return 0; + return 0; } static int do_add(int cmd, int argc, char **argv) diff -Nru iproute2-4.3.0/ip/ipaddress.c iproute2-4.9.0/ip/ipaddress.c --- iproute2-4.3.0/ip/ipaddress.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipaddress.c 2016-12-12 23:07:42.000000000 +0000 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ int group; int master; char *kind; + char *slave_kind; } filter; static int do_link; @@ -74,8 +76,11 @@ fprintf(stderr, "Usage: ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ]\n"); fprintf(stderr, " [ CONFFLAG-LIST ]\n"); fprintf(stderr, " ip address del IFADDR dev IFNAME [mngtmpaddr]\n"); - fprintf(stderr, " ip address {show|save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n"); + fprintf(stderr, " ip address {save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n"); fprintf(stderr, " [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n"); + fprintf(stderr, " ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n"); + fprintf(stderr, " [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n"); + fprintf(stderr, " [ label LABEL ] [up] [ vrf NAME ] ]\n"); fprintf(stderr, " ip address {showdump|restore}\n"); fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n"); fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n"); @@ -89,11 +94,15 @@ fprintf(stderr, "CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]\n"); fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n"); fprintf(stderr, "LFT := forever | SECONDS\n"); + fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n"); + fprintf(stderr, " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | lowpan |\n"); + fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti | nlmon | can |\n"); + fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf | hsr | macsec }\n"); exit(-1); } -static void print_link_flags(FILE *fp, unsigned flags, unsigned mdown) +static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown) { fprintf(fp, "<"); if (flags & IFF_UP && !(flags & IFF_RUNNING)) @@ -135,25 +144,15 @@ static void print_operstate(FILE *f, __u8 state) { - if (state >= sizeof(oper_states)/sizeof(oper_states[0])) + if (state >= ARRAY_SIZE(oper_states)) { fprintf(f, "state %#x ", state); - else { - if (brief) { - if (strcmp(oper_states[state], "UP") == 0) - color_fprintf(f, COLOR_OPERSTATE_UP, "%-14s ", oper_states[state]); - else if (strcmp(oper_states[state], "DOWN") == 0) - color_fprintf(f, COLOR_OPERSTATE_DOWN, "%-14s ", oper_states[state]); - else - fprintf(f, "%-14s ", oper_states[state]); - } else { - fprintf(f, "state "); - if (strcmp(oper_states[state], "UP") == 0) - color_fprintf(f, COLOR_OPERSTATE_UP, "%s ", oper_states[state]); - else if (strcmp(oper_states[state], "DOWN") == 0) - color_fprintf(f, COLOR_OPERSTATE_DOWN, "%s ", oper_states[state]); - else - fprintf(f, "%s ", oper_states[state]); - } + } else if (brief) { + color_fprintf(f, oper_state_color(state), + "%-14s ", oper_states[state]); + } else { + fprintf(f, "state "); + color_fprintf(f, oper_state_color(state), + "%s ", oper_states[state]); } } @@ -161,7 +160,7 @@ { int i; - for (i = 0; i < sizeof(oper_states)/sizeof(oper_states[0]); i++) + for (i = 0; i < ARRAY_SIZE(oper_states); i++) if (strcasecmp(name, oper_states[i]) == 0) return i; return -1; @@ -174,13 +173,12 @@ if (tb[IFLA_TXQLEN]) qlen = *(int *)RTA_DATA(tb[IFLA_TXQLEN]); else { - struct ifreq ifr; + struct ifreq ifr = {}; int s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return; - memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, rta_getattr_str(tb[IFLA_IFNAME])); if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { fprintf(f, "ioctl(SIOCGIFTXQLEN) failed: %s\n", strerror(errno)); @@ -202,29 +200,39 @@ { unsigned int mode = rta_getattr_u8(tb); - if (mode >= sizeof(link_modes) / sizeof(link_modes[0])) + if (mode >= ARRAY_SIZE(link_modes)) fprintf(f, "mode %d ", mode); else fprintf(f, "mode %s ", link_modes[mode]); } -static char *parse_link_kind(struct rtattr *tb) +static char *parse_link_kind(struct rtattr *tb, bool slave) { struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + int attr = slave ? IFLA_INFO_SLAVE_KIND : IFLA_INFO_KIND; parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb); - if (linkinfo[IFLA_INFO_KIND]) - return RTA_DATA(linkinfo[IFLA_INFO_KIND]); + if (linkinfo[attr]) + return RTA_DATA(linkinfo[attr]); return ""; } +static int match_link_kind(struct rtattr **tb, const char *kind, bool slave) +{ + if (!tb[IFLA_LINKINFO]) + return -1; + + return strcmp(parse_link_kind(tb[IFLA_LINKINFO], slave), kind); +} + static void print_linktype(FILE *fp, struct rtattr *tb) { struct rtattr *linkinfo[IFLA_INFO_MAX+1]; struct link_util *lu; struct link_util *slave_lu; + char slave[32]; char *kind; char *slave_kind; @@ -258,8 +266,9 @@ fprintf(fp, "%s", _SL_); fprintf(fp, " %s_slave ", slave_kind); + snprintf(slave, sizeof(slave), "%s_slave", slave_kind); - slave_lu = get_link_slave_kind(slave_kind); + slave_lu = get_link_kind(slave); if (slave_lu && slave_lu->print_opt) { struct rtattr *attr[slave_lu->maxattr+1], **data = NULL; @@ -285,13 +294,24 @@ parse_rtattr_nested(tb, IFLA_INET6_MAX, inet6_attr); if (tb[IFLA_INET6_ADDR_GEN_MODE]) { - switch (rta_getattr_u8(tb[IFLA_INET6_ADDR_GEN_MODE])) { + __u8 mode = rta_getattr_u8(tb[IFLA_INET6_ADDR_GEN_MODE]); + + switch (mode) { case IN6_ADDR_GEN_MODE_EUI64: fprintf(fp, "addrgenmode eui64 "); break; case IN6_ADDR_GEN_MODE_NONE: fprintf(fp, "addrgenmode none "); break; + case IN6_ADDR_GEN_MODE_STABLE_PRIVACY: + fprintf(fp, "addrgenmode stable_secret "); + break; + case IN6_ADDR_GEN_MODE_RANDOM: + fprintf(fp, "addrgenmode random "); + break; + default: + fprintf(fp, "addrgenmode %#.2hhx ", mode); + break; } } } @@ -301,12 +321,9 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) { struct ifla_vf_mac *vf_mac; - struct ifla_vf_vlan *vf_vlan; struct ifla_vf_tx_rate *vf_tx_rate; - struct ifla_vf_spoofchk *vf_spoofchk; - struct ifla_vf_link_state *vf_linkstate; struct rtattr *vf[IFLA_VF_MAX + 1] = {}; - struct rtattr *tmp; + SPRINT_BUF(b1); if (vfinfo->rta_type != IFLA_VF_INFO) { @@ -317,41 +334,40 @@ parse_rtattr_nested(vf, IFLA_VF_MAX, vfinfo); vf_mac = RTA_DATA(vf[IFLA_VF_MAC]); - vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]); vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]); - /* Check if the spoof checking vf info type is supported by - * this kernel. - */ - tmp = (struct rtattr *)((char *)vf[IFLA_VF_TX_RATE] + - vf[IFLA_VF_TX_RATE]->rta_len); - - if (tmp->rta_type != IFLA_VF_SPOOFCHK) - vf_spoofchk = NULL; - else - vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]); - - if (vf_spoofchk) { - /* Check if the link state vf info type is supported by - * this kernel. - */ - tmp = (struct rtattr *)((char *)vf[IFLA_VF_SPOOFCHK] + - vf[IFLA_VF_SPOOFCHK]->rta_len); - - if (tmp->rta_type != IFLA_VF_LINK_STATE) - vf_linkstate = NULL; - else - vf_linkstate = RTA_DATA(vf[IFLA_VF_LINK_STATE]); - } else - vf_linkstate = NULL; - fprintf(fp, "%s vf %d MAC %s", _SL_, vf_mac->vf, ll_addr_n2a((unsigned char *)&vf_mac->mac, - ETH_ALEN, 0, b1, sizeof(b1))); - if (vf_vlan->vlan) - fprintf(fp, ", vlan %d", vf_vlan->vlan); - if (vf_vlan->qos) - fprintf(fp, ", qos %d", vf_vlan->qos); + ETH_ALEN, 0, b1, sizeof(b1))); + if (vf[IFLA_VF_VLAN_LIST]) { + struct rtattr *i, *vfvlanlist = vf[IFLA_VF_VLAN_LIST]; + int rem = RTA_PAYLOAD(vfvlanlist); + + for (i = RTA_DATA(vfvlanlist); + RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + struct ifla_vf_vlan_info *vf_vlan_info = + RTA_DATA(i); + SPRINT_BUF(b2); + + if (vf_vlan_info->vlan) + fprintf(fp, ", vlan %d", vf_vlan_info->vlan); + if (vf_vlan_info->qos) + fprintf(fp, ", qos %d", vf_vlan_info->qos); + if (vf_vlan_info->vlan_proto && + vf_vlan_info->vlan_proto != htons(ETH_P_8021Q)) + fprintf(fp, ", vlan protocol %s", + ll_proto_n2a(vf_vlan_info->vlan_proto, + b2, sizeof(b2))); + + } + } else { + struct ifla_vf_vlan *vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]); + + if (vf_vlan->vlan) + fprintf(fp, ", vlan %d", vf_vlan->vlan); + if (vf_vlan->qos) + fprintf(fp, ", qos %d", vf_vlan->qos); + } if (vf_tx_rate->rate) fprintf(fp, ", tx rate %d (Mbps)", vf_tx_rate->rate); @@ -363,14 +379,18 @@ if (vf_rate->min_tx_rate) fprintf(fp, ", min_tx_rate %dMbps", vf_rate->min_tx_rate); } + if (vf[IFLA_VF_SPOOFCHK]) { + struct ifla_vf_spoofchk *vf_spoofchk = + RTA_DATA(vf[IFLA_VF_SPOOFCHK]); + + if (vf_spoofchk->setting != -1) + fprintf(fp, ", spoof checking %s", + vf_spoofchk->setting ? "on" : "off"); + } + if (vf[IFLA_VF_LINK_STATE]) { + struct ifla_vf_link_state *vf_linkstate = + RTA_DATA(vf[IFLA_VF_LINK_STATE]); - if (vf_spoofchk && vf_spoofchk->setting != -1) { - if (vf_spoofchk->setting) - fprintf(fp, ", spoof checking on"); - else - fprintf(fp, ", spoof checking off"); - } - if (vf_linkstate) { if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO) fprintf(fp, ", link-state auto"); else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE) @@ -378,11 +398,34 @@ else fprintf(fp, ", link-state disable"); } + if (vf[IFLA_VF_TRUST]) { + struct ifla_vf_trust *vf_trust = RTA_DATA(vf[IFLA_VF_TRUST]); + + if (vf_trust->setting != -1) + fprintf(fp, ", trust %s", + vf_trust->setting ? "on" : "off"); + } + if (vf[IFLA_VF_RSS_QUERY_EN]) { + struct ifla_vf_rss_query_en *rss_query = + RTA_DATA(vf[IFLA_VF_RSS_QUERY_EN]); + + if (rss_query->setting != -1) + fprintf(fp, ", query_rss %s", + rss_query->setting ? "on" : "off"); + } + if (vf[IFLA_VF_RSS_QUERY_EN]) { + struct ifla_vf_rss_query_en *rss_query = + RTA_DATA(vf[IFLA_VF_RSS_QUERY_EN]); + + if (rss_query->setting != -1) + fprintf(fp, ", query_rss %s", + rss_query->setting ? "on" : "off"); + } if (vf[IFLA_VF_STATS] && show_stats) print_vf_stats64(fp, vf[IFLA_VF_STATS]); } -static void print_num(FILE *fp, unsigned width, uint64_t count) +static void print_num(FILE *fp, unsigned int width, uint64_t count) { const char *prefix = "kMGTPE"; const unsigned int base = use_iec ? 1024 : 1000; @@ -397,8 +440,9 @@ } /* increase value by a factor of 1000/1024 and print - * if result is something a human can read */ - for(;;) { + * if result is something a human can read + */ + for (;;) { powi *= base; if (count / base < powi) break; @@ -423,7 +467,7 @@ static void print_vf_stats64(FILE *fp, struct rtattr *vfstats) { - struct rtattr *vf[IFLA_VF_STATS_MAX + 1] = {}; + struct rtattr *vf[IFLA_VF_STATS_MAX + 1]; if (vfstats->rta_type != IFLA_VF_STATS) { fprintf(stderr, "BUG: rta type is %d\n", vfstats->rta_type); @@ -452,7 +496,7 @@ } static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s, - const struct rtattr *carrier_changes) + const struct rtattr *carrier_changes) { /* RX stats */ fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s", @@ -471,7 +515,8 @@ /* RX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " RX errors: length crc frame fifo missed%s", _SL_); + fprintf(fp, " RX errors: length crc frame fifo missed%s%s", + s->rx_nohandler ? " nohandler" : "", _SL_); fprintf(fp, " "); print_num(fp, 8, s->rx_length_errors); @@ -479,6 +524,9 @@ print_num(fp, 7, s->rx_frame_errors); print_num(fp, 7, s->rx_fifo_errors); print_num(fp, 7, s->rx_missed_errors); + if (s->rx_nohandler) + print_num(fp, 7, s->rx_nohandler); + } fprintf(fp, "%s", _SL_); @@ -486,7 +534,6 @@ fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s", s->tx_compressed ? "compressed" : "", _SL_); - fprintf(fp, " "); print_num(fp, 10, s->tx_bytes); print_num(fp, 8, s->tx_packets); @@ -501,7 +548,7 @@ if (show_stats > 1) { fprintf(fp, "%s", _SL_); fprintf(fp, " TX errors: aborted fifo window heartbeat"); - if (carrier_changes) + if (carrier_changes) fprintf(fp, " transns"); fprintf(fp, "%s", _SL_); @@ -511,7 +558,7 @@ print_num(fp, 7, s->tx_window_errors); print_num(fp, 7, s->tx_heartbeat_errors); if (carrier_changes) - print_num(fp, 7, *(uint32_t*)RTA_DATA(carrier_changes)); + print_num(fp, 7, *(uint32_t *)RTA_DATA(carrier_changes)); } } @@ -536,13 +583,16 @@ /* RX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " RX errors: length crc frame fifo missed%s", _SL_); + fprintf(fp, " RX errors: length crc frame fifo missed%s%s", + s->rx_nohandler ? " nohandler" : "", _SL_); fprintf(fp, " "); print_num(fp, 8, s->rx_length_errors); print_num(fp, 7, s->rx_crc_errors); print_num(fp, 7, s->rx_frame_errors); print_num(fp, 7, s->rx_fifo_errors); print_num(fp, 7, s->rx_missed_errors); + if (s->rx_nohandler) + print_num(fp, 7, s->rx_nohandler); } fprintf(fp, "%s", _SL_); @@ -564,7 +614,7 @@ if (show_stats > 1) { fprintf(fp, "%s", _SL_); fprintf(fp, " TX errors: aborted fifo window heartbeat"); - if (carrier_changes) + if (carrier_changes) fprintf(fp, " transns"); fprintf(fp, "%s", _SL_); @@ -574,24 +624,35 @@ print_num(fp, 7, s->tx_window_errors); print_num(fp, 7, s->tx_heartbeat_errors); if (carrier_changes) - print_num(fp, 7, *(uint32_t*)RTA_DATA(carrier_changes)); + print_num(fp, 7, *(uint32_t *)RTA_DATA(carrier_changes)); } } static void __print_link_stats(FILE *fp, struct rtattr **tb) { - if (tb[IFLA_STATS64]) - print_link_stats64(fp, RTA_DATA(tb[IFLA_STATS64]), - tb[IFLA_CARRIER_CHANGES]); - else if (tb[IFLA_STATS]) - print_link_stats32(fp, RTA_DATA(tb[IFLA_STATS]), - tb[IFLA_CARRIER_CHANGES]); + const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES]; + + if (tb[IFLA_STATS64]) { + struct rtnl_link_stats64 stats = { 0 }; + + memcpy(&stats, RTA_DATA(tb[IFLA_STATS64]), + MIN(RTA_PAYLOAD(tb[IFLA_STATS64]), sizeof(stats))); + + print_link_stats64(fp, &stats, carrier_changes); + } else if (tb[IFLA_STATS]) { + struct rtnl_link_stats stats = { 0 }; + + memcpy(&stats, RTA_DATA(tb[IFLA_STATS]), + MIN(RTA_PAYLOAD(tb[IFLA_STATS]), sizeof(stats))); + + print_link_stats32(fp, &stats, carrier_changes); + } } static void print_link_stats(FILE *fp, struct nlmsghdr *n) { struct ifinfomsg *ifi = NLMSG_DATA(n); - struct rtattr * tb[IFLA_MAX+1]; + struct rtattr *tb[IFLA_MAX+1]; parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi))); @@ -602,13 +663,13 @@ int print_linkinfo_brief(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct ifinfomsg *ifi = NLMSG_DATA(n); - struct rtattr * tb[IFLA_MAX+1]; + struct rtattr *tb[IFLA_MAX+1]; int len = n->nlmsg_len; char *name; char buf[32] = { 0, }; - unsigned m_flag = 0; + unsigned int m_flag = 0; if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) return -1; @@ -623,38 +684,34 @@ return -1; parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); - if (tb[IFLA_IFNAME] == NULL) { + if (tb[IFLA_IFNAME] == NULL) fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index); - } + if (filter.label && (!filter.family || filter.family == AF_PACKET) && fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)) return -1; if (tb[IFLA_GROUP]) { - int group = *(int*)RTA_DATA(tb[IFLA_GROUP]); + int group = *(int *)RTA_DATA(tb[IFLA_GROUP]); + if (filter.group != -1 && group != filter.group) return -1; } if (tb[IFLA_MASTER]) { - int master = *(int*)RTA_DATA(tb[IFLA_MASTER]); + int master = *(int *)RTA_DATA(tb[IFLA_MASTER]); + if (filter.master > 0 && master != filter.master) return -1; - } - else if (filter.master > 0) + } else if (filter.master > 0) return -1; - if (filter.kind) { - if (tb[IFLA_LINKINFO]) { - char *kind = parse_link_kind(tb[IFLA_LINKINFO]); + if (filter.kind && match_link_kind(tb, filter.kind, 0)) + return -1; - if (strcmp(kind, filter.kind)) - return -1; - } else { - return -1; - } - } + if (filter.slave_kind && match_link_kind(tb, filter.slave_kind, 1)) + return -1; if (n->nlmsg_type == RTM_DELLINK) fprintf(fp, "Deleted "); @@ -663,7 +720,8 @@ if (tb[IFLA_LINK]) { SPRINT_BUF(b1); - int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]); + int iflink = *(int *)RTA_DATA(tb[IFLA_LINK]); + if (iflink == 0) snprintf(buf, sizeof(buf), "%s@NONE", name); else { @@ -703,11 +761,11 @@ int print_linkinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct ifinfomsg *ifi = NLMSG_DATA(n); - struct rtattr * tb[IFLA_MAX+1]; + struct rtattr *tb[IFLA_MAX+1]; int len = n->nlmsg_len; - unsigned m_flag = 0; + unsigned int m_flag = 0; if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) return 0; @@ -722,49 +780,46 @@ return 0; parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); - if (tb[IFLA_IFNAME] == NULL) { + if (tb[IFLA_IFNAME] == NULL) fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index); - } + if (filter.label && (!filter.family || filter.family == AF_PACKET) && fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)) return 0; if (tb[IFLA_GROUP]) { - int group = *(int*)RTA_DATA(tb[IFLA_GROUP]); + int group = *(int *)RTA_DATA(tb[IFLA_GROUP]); + if (filter.group != -1 && group != filter.group) return -1; } if (tb[IFLA_MASTER]) { - int master = *(int*)RTA_DATA(tb[IFLA_MASTER]); + int master = *(int *)RTA_DATA(tb[IFLA_MASTER]); + if (filter.master > 0 && master != filter.master) return -1; - } - else if (filter.master > 0) + } else if (filter.master > 0) return -1; - if (filter.kind) { - if (tb[IFLA_LINKINFO]) { - char *kind = parse_link_kind(tb[IFLA_LINKINFO]); + if (filter.kind && match_link_kind(tb, filter.kind, 0)) + return -1; - if (strcmp(kind, filter.kind)) - return -1; - } else { - return -1; - } - } + if (filter.slave_kind && match_link_kind(tb, filter.slave_kind, 1)) + return -1; if (n->nlmsg_type == RTM_DELLINK) fprintf(fp, "Deleted "); fprintf(fp, "%d: ", ifi->ifi_index); color_fprintf(fp, COLOR_IFNAME, "%s", - tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : ""); + tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : ""); if (tb[IFLA_LINK]) { SPRINT_BUF(b1); - int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]); + int iflink = *(int *)RTA_DATA(tb[IFLA_LINK]); + if (iflink == 0) fprintf(fp, "@NONE: "); else { @@ -782,28 +837,12 @@ print_link_flags(fp, ifi->ifi_flags, m_flag); if (tb[IFLA_MTU]) - fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); + fprintf(fp, "mtu %u ", *(int *)RTA_DATA(tb[IFLA_MTU])); if (tb[IFLA_QDISC]) fprintf(fp, "qdisc %s ", rta_getattr_str(tb[IFLA_QDISC])); if (tb[IFLA_MASTER]) { SPRINT_BUF(b1); - fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); - } - - if (tb[IFLA_PHYS_PORT_ID]) { - SPRINT_BUF(b1); - fprintf(fp, "portid %s ", - hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_PORT_ID]), - RTA_PAYLOAD(tb[IFLA_PHYS_PORT_ID]), - b1, sizeof(b1))); - } - - if (tb[IFLA_PHYS_SWITCH_ID]) { - SPRINT_BUF(b1); - fprintf(fp, "switchid %s ", - hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_SWITCH_ID]), - RTA_PAYLOAD(tb[IFLA_PHYS_SWITCH_ID]), - b1, sizeof(b1))); + fprintf(fp, "master %s ", ll_idx_n2a(*(int *)RTA_DATA(tb[IFLA_MASTER]), b1)); } if (tb[IFLA_OPERSTATE]) @@ -814,7 +853,8 @@ if (tb[IFLA_GROUP]) { SPRINT_BUF(b1); - int group = *(int*)RTA_DATA(tb[IFLA_GROUP]); + int group = *(int *)RTA_DATA(tb[IFLA_GROUP]); + fprintf(fp, "group %s ", rtnl_group_n2a(group, b1, sizeof(b1))); } @@ -828,25 +868,26 @@ if (tb[IFLA_ADDRESS]) { color_fprintf(fp, COLOR_MAC, "%s", - ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), - RTA_PAYLOAD(tb[IFLA_ADDRESS]), - ifi->ifi_type, - b1, sizeof(b1))); + ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), + RTA_PAYLOAD(tb[IFLA_ADDRESS]), + ifi->ifi_type, + b1, sizeof(b1))); } if (tb[IFLA_BROADCAST]) { if (ifi->ifi_flags&IFF_POINTOPOINT) fprintf(fp, " peer "); else fprintf(fp, " brd "); - fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), - RTA_PAYLOAD(tb[IFLA_BROADCAST]), - ifi->ifi_type, - b1, sizeof(b1))); + color_fprintf(fp, COLOR_MAC, "%s", + ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), + RTA_PAYLOAD(tb[IFLA_BROADCAST]), + ifi->ifi_type, + b1, sizeof(b1))); } } if (tb[IFLA_LINK_NETNSID]) { - int id = *(int*)RTA_DATA(tb[IFLA_LINK_NETNSID]); + int id = *(int *)RTA_DATA(tb[IFLA_LINK_NETNSID]); if (id >= 0) fprintf(fp, " link-netnsid %d", id); @@ -859,15 +900,54 @@ fprintf(fp, " protodown on "); } - if (tb[IFLA_PROMISCUITY] && show_details) - fprintf(fp, " promiscuity %u ", - *(int*)RTA_DATA(tb[IFLA_PROMISCUITY])); - - if (tb[IFLA_LINKINFO] && show_details) - print_linktype(fp, tb[IFLA_LINKINFO]); + if (show_details) { + if (tb[IFLA_PROMISCUITY]) + fprintf(fp, " promiscuity %u ", + *(int *)RTA_DATA(tb[IFLA_PROMISCUITY])); + + if (tb[IFLA_LINKINFO]) + print_linktype(fp, tb[IFLA_LINKINFO]); + + if (do_link && tb[IFLA_AF_SPEC]) + print_af_spec(fp, tb[IFLA_AF_SPEC]); + + if (tb[IFLA_NUM_TX_QUEUES]) + fprintf(fp, "numtxqueues %u ", + rta_getattr_u32(tb[IFLA_NUM_TX_QUEUES])); + + if (tb[IFLA_NUM_RX_QUEUES]) + fprintf(fp, "numrxqueues %u ", + rta_getattr_u32(tb[IFLA_NUM_RX_QUEUES])); + + if (tb[IFLA_GSO_MAX_SIZE]) + fprintf(fp, "gso_max_size %u ", + rta_getattr_u32(tb[IFLA_GSO_MAX_SIZE])); + + if (tb[IFLA_GSO_MAX_SEGS]) + fprintf(fp, "gso_max_segs %u ", + rta_getattr_u32(tb[IFLA_GSO_MAX_SEGS])); + + if (tb[IFLA_PHYS_PORT_NAME]) + fprintf(fp, "portname %s ", + rta_getattr_str(tb[IFLA_PHYS_PORT_NAME])); + + if (tb[IFLA_PHYS_PORT_ID]) { + SPRINT_BUF(b1); + fprintf(fp, "portid %s ", + hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_PORT_ID]), + RTA_PAYLOAD(tb[IFLA_PHYS_PORT_ID]), + b1, sizeof(b1))); + } + + if (tb[IFLA_PHYS_SWITCH_ID]) { + SPRINT_BUF(b1); + fprintf(fp, "switchid %s ", + hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_SWITCH_ID]), + RTA_PAYLOAD(tb[IFLA_PHYS_SWITCH_ID]), + b1, sizeof(b1))); + } + } - if (do_link && tb[IFLA_AF_SPEC] && show_details) - print_af_spec(fp, tb[IFLA_AF_SPEC]); if ((do_link || show_details) && tb[IFLA_IFALIAS]) { fprintf(fp, "%s alias %s", _SL_, @@ -882,6 +962,7 @@ if ((do_link || show_details) && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) { struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST]; int rem = RTA_PAYLOAD(vflist); + for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) print_vfinfo(fp, i); } @@ -893,7 +974,17 @@ static int flush_update(void) { - if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) { + + /* + * Note that the kernel may delete multiple addresses for one + * delete request (e.g. if ipv4 address promotion is disabled). + * Since a flush operation is really a series of delete requests + * its possible that we may request an address delete that has + * already been done by the kernel. Therefore, ignore EADDRNOTAVAIL + * errors returned from a flush request + */ + if ((rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) && + (errno != EADDRNOTAVAIL)) { perror("Failed to send flush request"); return -1; } @@ -927,8 +1018,8 @@ int deprecated = 0; /* Use local copy of ifa_flags to not interfere with filtering code */ unsigned int ifa_flags; - struct rtattr * rta_tb[IFA_MAX+1]; - char abuf[256]; + struct rtattr *rta_tb[IFA_MAX+1]; + SPRINT_BUF(b1); if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) @@ -961,6 +1052,7 @@ if (filter.label) { SPRINT_BUF(b1); const char *label; + if (rta_tb[IFA_LABEL]) label = RTA_DATA(rta_tb[IFA_LABEL]); else @@ -970,9 +1062,8 @@ } if (filter.pfx.family) { if (rta_tb[IFA_LOCAL]) { - inet_prefix dst; - memset(&dst, 0, sizeof(dst)); - dst.family = ifa->ifa_family; + inet_prefix dst = { .family = ifa->ifa_family }; + memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL])); if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) return 0; @@ -984,16 +1075,17 @@ if (filter.flushb) { struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { if (flush_update()) return -1; } - fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp)); memcpy(fn, n, n->nlmsg_len); fn->nlmsg_type = RTM_DELADDR; fn->nlmsg_flags = NLM_F_REQUEST; fn->nlmsg_seq = ++rth.seq; - filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb; filter.flushed++; if (show_stats < 2) return 0; @@ -1018,52 +1110,35 @@ } if (rta_tb[IFA_LOCAL]) { - if (ifa->ifa_family == AF_INET) - color_fprintf(fp, COLOR_INET, "%s", format_host(ifa->ifa_family, - RTA_PAYLOAD(rta_tb[IFA_LOCAL]), - RTA_DATA(rta_tb[IFA_LOCAL]), - abuf, sizeof(abuf))); - else if (ifa->ifa_family == AF_INET6) - color_fprintf(fp, COLOR_INET6, "%s", format_host(ifa->ifa_family, - RTA_PAYLOAD(rta_tb[IFA_LOCAL]), - RTA_DATA(rta_tb[IFA_LOCAL]), - abuf, sizeof(abuf))); - else - fprintf(fp, "%s", format_host(ifa->ifa_family, - RTA_PAYLOAD(rta_tb[IFA_LOCAL]), - RTA_DATA(rta_tb[IFA_LOCAL]), - abuf, sizeof(abuf))); - - if (rta_tb[IFA_ADDRESS] == NULL || - memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), - ifa->ifa_family == AF_INET ? 4 : 16) == 0) { - fprintf(fp, "/%d ", ifa->ifa_prefixlen); - } else { - fprintf(fp, " peer %s/%d ", - format_host(ifa->ifa_family, - RTA_PAYLOAD(rta_tb[IFA_ADDRESS]), - RTA_DATA(rta_tb[IFA_ADDRESS]), - abuf, sizeof(abuf)), - ifa->ifa_prefixlen); + color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s", + format_host_rta(ifa->ifa_family, + rta_tb[IFA_LOCAL])); + if (rta_tb[IFA_ADDRESS] && + memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), + RTA_DATA(rta_tb[IFA_LOCAL]), + ifa->ifa_family == AF_INET ? 4 : 16)) { + fprintf(fp, " peer "); + color_fprintf(fp, ifa_family_color(ifa->ifa_family), + "%s", format_host_rta(ifa->ifa_family, + rta_tb[IFA_ADDRESS])); } + fprintf(fp, "/%d ", ifa->ifa_prefixlen); } if (brief) goto brief_exit; if (rta_tb[IFA_BROADCAST]) { - fprintf(fp, "brd %s ", - format_host(ifa->ifa_family, - RTA_PAYLOAD(rta_tb[IFA_BROADCAST]), - RTA_DATA(rta_tb[IFA_BROADCAST]), - abuf, sizeof(abuf))); + fprintf(fp, "brd "); + color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s ", + format_host_rta(ifa->ifa_family, + rta_tb[IFA_BROADCAST])); } if (rta_tb[IFA_ANYCAST]) { - fprintf(fp, "any %s ", - format_host(ifa->ifa_family, - RTA_PAYLOAD(rta_tb[IFA_ANYCAST]), - RTA_DATA(rta_tb[IFA_ANYCAST]), - abuf, sizeof(abuf))); + fprintf(fp, "any "); + color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s ", + format_host_rta(ifa->ifa_family, + rta_tb[IFA_ANYCAST])); } fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); if (ifa_flags & IFA_F_SECONDARY) { @@ -1102,9 +1177,9 @@ ifa_flags &= ~IFA_F_MCAUTOJOIN; fprintf(fp, "autojoin "); } - if (!(ifa_flags & IFA_F_PERMANENT)) { + if (!(ifa_flags & IFA_F_PERMANENT)) fprintf(fp, "dynamic "); - } else + else ifa_flags &= ~IFA_F_PERMANENT; if (ifa_flags & IFA_F_DADFAILED) { ifa_flags &= ~IFA_F_DADFAILED; @@ -1116,6 +1191,7 @@ fprintf(fp, "%s", rta_getattr_str(rta_tb[IFA_LABEL])); if (rta_tb[IFA_CACHEINFO]) { struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); + fprintf(fp, "%s", _SL_); fprintf(fp, " valid_lft "); if (ci->ifa_valid == INFINITY_LIFE_TIME) @@ -1138,36 +1214,12 @@ return 0; } -static int print_addrinfo_primary(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg) -{ - struct ifaddrmsg *ifa = NLMSG_DATA(n); - - if (ifa->ifa_flags & IFA_F_SECONDARY) - return 0; - - return print_addrinfo(who, n, arg); -} - -static int print_addrinfo_secondary(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg) -{ - struct ifaddrmsg *ifa = NLMSG_DATA(n); - - if (!(ifa->ifa_flags & IFA_F_SECONDARY)) - return 0; - - return print_addrinfo(who, n, arg); -} - -struct nlmsg_list -{ +struct nlmsg_list { struct nlmsg_list *next; struct nlmsghdr h; }; -struct nlmsg_chain -{ +struct nlmsg_chain { struct nlmsg_list *head; struct nlmsg_list *tail; }; @@ -1175,14 +1227,14 @@ static int print_selected_addrinfo(struct ifinfomsg *ifi, struct nlmsg_list *ainfo, FILE *fp) { - for ( ;ainfo ; ainfo = ainfo->next) { + for ( ; ainfo ; ainfo = ainfo->next) { struct nlmsghdr *n = &ainfo->h; struct ifaddrmsg *ifa = NLMSG_DATA(n); if (n->nlmsg_type != RTM_NEWADDR) continue; - if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa))) + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifa))) return -1; if (ifa->ifa_index != ifi->ifi_index || @@ -1208,7 +1260,7 @@ struct nlmsg_chain *lchain = (struct nlmsg_chain *)arg; struct nlmsg_list *h; - h = malloc(n->nlmsg_len+sizeof(void*)); + h = malloc(n->nlmsg_len+sizeof(void *)); if (h == NULL) return -1; @@ -1337,7 +1389,7 @@ struct nlmsg_list *l, **lp; lp = &linfo->head; - while ( (l = *lp) != NULL) { + while ((l = *lp) != NULL) { int ok = 0; int missing_net_address = 1; struct ifinfomsg *ifi = NLMSG_DATA(&l->h); @@ -1367,9 +1419,10 @@ tb[IFA_LOCAL] = tb[IFA_ADDRESS]; if (filter.pfx.family && tb[IFA_LOCAL]) { - inet_prefix dst; - memset(&dst, 0, sizeof(dst)); - dst.family = ifa->ifa_family; + inet_prefix dst = { + .family = ifa->ifa_family + }; + memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL])); if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) continue; @@ -1377,6 +1430,7 @@ if (filter.label) { SPRINT_BUF(b1); const char *label; + if (tb[IFA_LABEL]) label = RTA_DATA(tb[IFA_LABEL]); else @@ -1410,26 +1464,13 @@ filter.flushe = sizeof(flushb); while ((max_flush_loops == 0) || (round < max_flush_loops)) { - const struct rtnl_dump_filter_arg a[3] = { - { - .filter = print_addrinfo_secondary, - .arg1 = stdout, - }, - { - .filter = print_addrinfo_primary, - .arg1 = stdout, - }, - { - .filter = NULL, - .arg1 = NULL, - }, - }; if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { perror("Cannot send dump request"); exit(1); } filter.flushed = 0; - if (rtnl_dump_filter_l(&rth, a) < 0) { + if (rtnl_dump_filter_nc(&rth, print_addrinfo, + stdout, NLM_F_DUMP_INTR) < 0) { fprintf(stderr, "Flush terminated\n"); exit(1); } @@ -1439,7 +1480,7 @@ if (round == 0) printf("Nothing to flush.\n"); else - printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":""); } fflush(stdout); return 0; @@ -1466,6 +1507,36 @@ return 1; } +static int iplink_filter_req(struct nlmsghdr *nlh, int reqlen) +{ + int err; + + err = addattr32(nlh, reqlen, IFLA_EXT_MASK, RTEXT_FILTER_VF); + if (err) + return err; + + if (filter.master) { + err = addattr32(nlh, reqlen, IFLA_MASTER, filter.master); + if (err) + return err; + } + + if (filter.kind) { + struct rtattr *linkinfo; + + linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO); + + err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, filter.kind, + strlen(filter.kind)); + if (err) + return err; + + addattr_nest_end(nlh, linkinfo); + } + + return 0; +} + static int ipaddr_list_flush_or_save(int argc, char **argv, int action) { struct nlmsg_chain linfo = { NULL, NULL}; @@ -1476,10 +1547,7 @@ ipaddr_reset_filter(oneline, 0); filter.showqueue = 1; - - if (filter.family == AF_UNSPEC) - filter.family = preferred_family; - + filter.family = preferred_family; filter.group = -1; if (action == IPADD_FLUSH) { @@ -1501,7 +1569,8 @@ if (filter.family == AF_UNSPEC) filter.family = filter.pfx.family; } else if (strcmp(*argv, "scope") == 0) { - unsigned scope = 0; + unsigned int scope = 0; + NEXT_ARG(); filter.scopemask = -1; if (rtnl_rtscope_a2n(&scope, *argv)) { @@ -1568,18 +1637,36 @@ invarg("Invalid \"group\" value\n", *argv); } else if (strcmp(*argv, "master") == 0) { int ifindex; + NEXT_ARG(); ifindex = ll_name_to_index(*argv); if (!ifindex) invarg("Device does not exist\n", *argv); filter.master = ifindex; - } else if (do_link && strcmp(*argv, "type") == 0) { + } else if (strcmp(*argv, "vrf") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Not a valid VRF name\n", *argv); + if (!name_is_vrf(*argv)) + invarg("Not a valid VRF name\n", *argv); + filter.master = ifindex; + } else if (strcmp(*argv, "type") == 0) { + int soff; + NEXT_ARG(); - filter.kind = *argv; + soff = strlen(*argv) - strlen("_slave"); + if (!strcmp(*argv + soff, "_slave")) { + (*argv)[soff] = '\0'; + filter.slave_kind = *argv; + } else { + filter.kind = *argv; + } } else { - if (strcmp(*argv, "dev") == 0) { + if (strcmp(*argv, "dev") == 0) NEXT_ARG(); - } else if (matches(*argv, "help") == 0) usage(); if (filter_dev) @@ -1630,7 +1717,8 @@ exit(0); } - if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) { + if (rtnl_wilddump_req_filter_fn(&rth, preferred_family, RTM_GETLINK, + iplink_filter_req) < 0) { perror("Cannot send dump request"); exit(1); } @@ -1758,7 +1846,7 @@ static int default_scope(inet_prefix *lcl) { if (lcl->family == AF_INET) { - if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127) + if (lcl->bytelen >= 1 && *(__u8 *)&lcl->data == 127) return RT_SCOPE_HOST; } return 0; @@ -1780,7 +1868,12 @@ struct nlmsghdr n; struct ifaddrmsg ifa; char buf[256]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .ifa.ifa_family = preferred_family, + }; char *d = NULL; char *l = NULL; char *lcl_arg = NULL; @@ -1795,16 +1888,8 @@ int scoped = 0; __u32 preferred_lft = INFINITY_LIFE_TIME; __u32 valid_lft = INFINITY_LIFE_TIME; - struct ifa_cacheinfo cinfo; unsigned int ifa_flags = 0; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST | flags; - req.n.nlmsg_type = cmd; - req.ifa.ifa_family = preferred_family; - while (argc > 0) { if (strcmp(*argv, "peer") == 0 || strcmp(*argv, "remote") == 0) { @@ -1821,6 +1906,7 @@ } else if (matches(*argv, "broadcast") == 0 || strcmp(*argv, "brd") == 0) { inet_prefix addr; + NEXT_ARG(); if (brd_len) duparg("broadcast", *argv); @@ -1837,6 +1923,7 @@ } } else if (strcmp(*argv, "anycast") == 0) { inet_prefix addr; + NEXT_ARG(); if (any_len) duparg("anycast", *argv); @@ -1846,7 +1933,8 @@ addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen); any_len = addr.bytelen; } else if (strcmp(*argv, "scope") == 0) { - unsigned scope = 0; + unsigned int scope = 0; + NEXT_ARG(); if (rtnl_rtscope_a2n(&scope, *argv)) invarg("invalid scope value.", *argv); @@ -1884,9 +1972,8 @@ } else if (strcmp(*argv, "autojoin") == 0) { ifa_flags |= IFA_F_MCAUTOJOIN; } else { - if (strcmp(*argv, "local") == 0) { + if (strcmp(*argv, "local") == 0) NEXT_ARG(); - } if (matches(*argv, "help") == 0) usage(); if (local_len) @@ -1917,9 +2004,9 @@ if (peer_len == 0 && local_len) { if (cmd == RTM_DELADDR && lcl.family == AF_INET && !(lcl.flags & PREFIXLEN_SPECIFIED)) { fprintf(stderr, - "Warning: Executing wildcard deletion to stay compatible with old scripts.\n" \ - " Explicitly specify the prefix length (%s/%d) to avoid this warning.\n" \ - " This special behaviour is likely to disappear in further releases,\n" \ + "Warning: Executing wildcard deletion to stay compatible with old scripts.\n" + " Explicitly specify the prefix length (%s/%d) to avoid this warning.\n" + " This special behaviour is likely to disappear in further releases,\n" " fix your scripts!\n", lcl_arg, local_len*8); } else { peer = lcl; @@ -1932,6 +2019,7 @@ if (brd_len < 0 && cmd != RTM_DELADDR) { inet_prefix brd; int i; + if (req.ifa.ifa_family != AF_INET) { fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n"); return -1; @@ -1957,6 +2045,8 @@ } if (valid_lftp || preferred_lftp) { + struct ifa_cacheinfo cinfo = {}; + if (!valid_lft) { fprintf(stderr, "valid_lft is zero\n"); return -1; @@ -1966,7 +2056,6 @@ return -1; } - memset(&cinfo, 0, sizeof(cinfo)); cinfo.ifa_prefered = preferred_lft; cinfo.ifa_valid = valid_lft; addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo, diff -Nru iproute2-4.3.0/ip/ipaddrlabel.c iproute2-4.9.0/ip/ipaddrlabel.c --- iproute2-4.3.0/ip/ipaddrlabel.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipaddrlabel.c 2016-12-12 23:07:42.000000000 +0000 @@ -40,8 +40,8 @@ #include "utils.h" #include "ip_common.h" -#define IFAL_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrlblmsg)))) -#define IFAL_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrlblmsg)) +#define IFAL_RTA(r) ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrlblmsg)))) +#define IFAL_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ifaddrlblmsg)) extern struct rtnl_handle rth; @@ -49,17 +49,17 @@ static void usage(void) { - fprintf(stderr, "Usage: ip addrlabel [ list | add | del | flush ] prefix PREFIX [ dev DEV ] [ label LABEL ]\n"); + fprintf(stderr, "Usage: ip addrlabel { add | del } prefix PREFIX [ dev DEV ] [ label LABEL ]\n"); + fprintf(stderr, " ip addrlabel [ list | flush | help ]\n"); exit(-1); } int print_addrlabel(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct ifaddrlblmsg *ifal = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[IFAL_MAX+1]; - char abuf[256]; if (n->nlmsg_type != RTM_NEWADDRLABEL && n->nlmsg_type != RTM_DELADDRLABEL) return 0; @@ -75,10 +75,8 @@ if (tb[IFAL_ADDRESS]) { fprintf(fp, "prefix %s/%u ", - format_host(ifal->ifal_family, - RTA_PAYLOAD(tb[IFAL_ADDRESS]), - RTA_DATA(tb[IFAL_ADDRESS]), - abuf, sizeof(abuf)), + format_host_rta(ifal->ifal_family, + tb[IFAL_ADDRESS]), ifal->ifal_prefixlen); } @@ -87,6 +85,7 @@ if (tb[IFAL_LABEL] && RTA_PAYLOAD(tb[IFAL_LABEL]) == sizeof(uint32_t)) { uint32_t label; + memcpy(&label, RTA_DATA(tb[IFAL_LABEL]), sizeof(label)); fprintf(fp, "label %u ", label); } @@ -127,24 +126,19 @@ struct { struct nlmsghdr n; struct ifaddrlblmsg ifal; - char buf[1024]; - } req; + char buf[1024]; + } req = { + .n.nlmsg_type = cmd, + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrlblmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .ifal.ifal_family = preferred_family, + }; - inet_prefix prefix; + inet_prefix prefix = {}; uint32_t label = 0xffffffffUL; char *p = NULL; char *l = NULL; - memset(&req, 0, sizeof(req)); - memset(&prefix, 0, sizeof(prefix)); - - req.n.nlmsg_type = cmd; - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrlblmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.ifal.ifal_family = preferred_family; - req.ifal.ifal_prefixlen = 0; - req.ifal.ifal_index = 0; - if (cmd == RTM_NEWADDRLABEL) { req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; } @@ -194,7 +188,7 @@ struct rtnl_handle rth2; struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[IFAL_MAX+1]; + struct rtattr *tb[IFAL_MAX+1]; len -= NLMSG_LENGTH(sizeof(*r)); if (len < 0) diff -Nru iproute2-4.3.0/ip/ip.c iproute2-4.9.0/ip/ip.c --- iproute2-4.3.0/ip/ip.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ip.c 2016-12-12 23:07:42.000000000 +0000 @@ -49,9 +49,9 @@ fprintf(stderr, "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" " ip [ -force ] -batch filename\n" -"where OBJECT := { link | address | addrlabel | route | rule | neighbor | ntable |\n" +"where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |\n" " tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n" -" netns | l2tp | fou | tcp_metrics | token | netconf }\n" +" netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" " -h[uman-readable] | -iec |\n" " -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |\n" @@ -84,6 +84,8 @@ { "link", do_iplink }, { "l2tp", do_ipl2tp }, { "fou", do_ipfou }, + { "ila", do_ipila }, + { "macsec", do_ipmacsec }, { "tunnel", do_iptunnel }, { "tunl", do_iptunnel }, { "tuntap", do_iptuntap }, diff -Nru iproute2-4.3.0/ip/ip_common.h iproute2-4.9.0/ip/ip_common.h --- iproute2-4.3.0/ip/ip_common.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ip_common.h 2016-12-12 23:07:42.000000000 +0000 @@ -1,72 +1,68 @@ -extern int get_operstate(const char *name); -extern int print_linkinfo(const struct sockaddr_nl *who, - struct nlmsghdr *n, - void *arg); -extern int print_linkinfo_brief(const struct sockaddr_nl *who, - struct nlmsghdr *n, - void *arg); -extern int print_addrinfo(const struct sockaddr_nl *who, - struct nlmsghdr *n, - void *arg); -extern int print_addrlabel(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg); -extern int print_neigh(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg); -extern int print_ntable(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg); -extern int ipaddr_list(int argc, char **argv); -extern int ipaddr_list_link(int argc, char **argv); +int get_operstate(const char *name); +int print_linkinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int print_linkinfo_brief(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int print_addrinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int print_addrlabel(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int print_neigh(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int ipaddr_list_link(int argc, char **argv); void ipaddr_get_vf_rate(int, int *, int *, int); -extern int iproute_monitor(int argc, char **argv); -extern void iplink_usage(void) __attribute__((noreturn)); +void iplink_usage(void) __attribute__((noreturn)); -extern void iproute_reset_filter(int ifindex); -extern void ipmroute_reset_filter(int ifindex); -extern void ipaddr_reset_filter(int oneline, int ifindex); -extern void ipneigh_reset_filter(int ifindex); -extern void ipntable_reset_filter(void); -extern void ipnetconf_reset_filter(int ifindex); - -extern int print_route(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg); -extern int print_mroute(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg); -extern int print_prefix(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg); -extern int print_rule(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg); -extern int print_netconf(const struct sockaddr_nl *who, - struct rtnl_ctrl_data *ctrl, - struct nlmsghdr *n, void *arg); -extern void netns_map_init(void); -extern int print_nsid(const struct sockaddr_nl *who, - struct nlmsghdr *n, void *arg); -extern int do_ipaddr(int argc, char **argv); -extern int do_ipaddrlabel(int argc, char **argv); -extern int do_iproute(int argc, char **argv); -extern int do_iprule(int argc, char **argv); -extern int do_ipneigh(int argc, char **argv); -extern int do_ipntable(int argc, char **argv); -extern int do_iptunnel(int argc, char **argv); -extern int do_ip6tunnel(int argc, char **argv); -extern int do_iptuntap(int argc, char **argv); -extern int do_iplink(int argc, char **argv); -extern int do_ipmonitor(int argc, char **argv); -extern int do_multiaddr(int argc, char **argv); -extern int do_multiroute(int argc, char **argv); -extern int do_multirule(int argc, char **argv); -extern int do_netns(int argc, char **argv); -extern int do_xfrm(int argc, char **argv); -extern int do_ipl2tp(int argc, char **argv); -extern int do_ipfou(int argc, char **argv); -extern int do_tcp_metrics(int argc, char **argv); -extern int do_ipnetconf(int argc, char **argv); -extern int do_iptoken(int argc, char **argv); -extern int iplink_get(unsigned int flags, char *name, __u32 filt_mask); +void iproute_reset_filter(int ifindex); +void ipmroute_reset_filter(int ifindex); +void ipaddr_reset_filter(int oneline, int ifindex); +void ipneigh_reset_filter(int ifindex); +void ipnetconf_reset_filter(int ifindex); + +int print_route(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int print_mroute(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int print_prefix(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int print_rule(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int print_netconf(const struct sockaddr_nl *who, + struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg); +void netns_map_init(void); +void netns_nsid_socket_init(void); +int print_nsid(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +int do_ipaddr(int argc, char **argv); +int do_ipaddrlabel(int argc, char **argv); +int do_iproute(int argc, char **argv); +int do_iprule(int argc, char **argv); +int do_ipneigh(int argc, char **argv); +int do_ipntable(int argc, char **argv); +int do_iptunnel(int argc, char **argv); +int do_ip6tunnel(int argc, char **argv); +int do_iptuntap(int argc, char **argv); +int do_iplink(int argc, char **argv); +int do_ipmacsec(int argc, char **argv); +int do_ipmonitor(int argc, char **argv); +int do_multiaddr(int argc, char **argv); +int do_multiroute(int argc, char **argv); +int do_multirule(int argc, char **argv); +int do_netns(int argc, char **argv); +int do_xfrm(int argc, char **argv); +int do_ipl2tp(int argc, char **argv); +int do_ipfou(int argc, char **argv); +extern int do_ipila(int argc, char **argv); +int do_tcp_metrics(int argc, char **argv); +int do_ipnetconf(int argc, char **argv); +int do_iptoken(int argc, char **argv); +int iplink_get(unsigned int flags, char *name, __u32 filt_mask); static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb) { __u32 table = r->rtm_table; + if (tb[RTA_TABLE]) table = rta_getattr_u32(tb[RTA_TABLE]); return table; @@ -76,8 +72,7 @@ #include -struct link_util -{ +struct link_util { struct link_util *next; const char *id; int maxattr; @@ -89,12 +84,19 @@ struct rtattr *); void (*print_help)(struct link_util *, int, char **, FILE *); - bool slave; }; struct link_util *get_link_kind(const char *kind); -struct link_util *get_link_slave_kind(const char *slave_kind); + +void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len); + +__u32 ipvrf_get_table(const char *name); +bool name_is_vrf(const char *name); #ifndef INFINITY_LIFE_TIME #define INFINITY_LIFE_TIME 0xFFFFFFFFU #endif + +#ifndef LABEL_MAX_MASK +#define LABEL_MAX_MASK 0xFFFFFU +#endif diff -Nru iproute2-4.3.0/ip/ipfou.c iproute2-4.9.0/ip/ipfou.c --- iproute2-4.3.0/ip/ipfou.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipfou.c 2016-12-12 23:07:42.000000000 +0000 @@ -25,8 +25,9 @@ static void usage(void) { - fprintf(stderr, "Usage: ip fou add port PORT { ipproto PROTO | gue }\n"); - fprintf(stderr, " ip fou del port PORT\n"); + fprintf(stderr, "Usage: ip fou add port PORT " + "{ ipproto PROTO | gue } [ -6 ]\n"); + fprintf(stderr, " ip fou del port PORT [ -6 ]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where: PROTO { ipproto-name | 1..255 }\n"); fprintf(stderr, " PORT { 1..65535 }\n"); @@ -50,14 +51,14 @@ __u8 ipproto, type; bool gue_set = false; int ipproto_set = 0; + unsigned short family = AF_INET; while (argc > 0) { if (!matches(*argv, "port")) { NEXT_ARG(); - if (get_u16(&port, *argv, 0) || port == 0) + if (get_be16(&port, *argv, 0) || port == 0) invarg("invalid port", *argv); - port = htons(port); port_set = 1; } else if (!matches(*argv, "ipproto")) { struct protoent *servptr; @@ -72,6 +73,8 @@ ipproto_set = 1; } else if (!matches(*argv, "gue")) { gue_set = true; + } else if (!matches(*argv, "-6")) { + family = AF_INET6; } else { fprintf(stderr, "fou: unknown command \"%s\"?\n", *argv); usage(); @@ -99,6 +102,7 @@ addattr16(n, 1024, FOU_ATTR_PORT, port); addattr8(n, 1024, FOU_ATTR_TYPE, type); + addattr16(n, 1024, FOU_ATTR_AF, family); if (ipproto_set) addattr8(n, 1024, FOU_ATTR_IPPROTO, ipproto); @@ -132,27 +136,19 @@ int do_ipfou(int argc, char **argv) { - if (genl_family < 0) { - if (rtnl_open_byproto(&genl_rth, 0, NETLINK_GENERIC) < 0) { - fprintf(stderr, "Cannot open generic netlink socket\n"); - exit(1); - } - - genl_family = genl_resolve_family(&genl_rth, FOU_GENL_NAME); - if (genl_family < 0) - exit(1); - } - if (argc < 1) usage(); + if (matches(*argv, "help") == 0) + usage(); + + if (genl_init_handle(&genl_rth, FOU_GENL_NAME, &genl_family)) + exit(1); + if (matches(*argv, "add") == 0) return do_add(argc-1, argv+1); if (matches(*argv, "delete") == 0) return do_del(argc-1, argv+1); - if (matches(*argv, "help") == 0) - usage(); - fprintf(stderr, "Command \"%s\" is unknown, try \"ip fou help\".\n", *argv); exit(-1); } diff -Nru iproute2-4.3.0/ip/ipila.c iproute2-4.9.0/ip/ipila.c --- iproute2-4.3.0/ip/ipila.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/ip/ipila.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,262 @@ +/* + * ipila.c ILA (Identifier Locator Addressing) support + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Tom Herbert + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libgenl.h" +#include "utils.h" +#include "ip_common.h" + +static void usage(void) +{ + fprintf(stderr, "Usage: ip ila add loc_match LOCATOR_MATCH " + "loc LOCATOR [ dev DEV ]\n"); + fprintf(stderr, " ip ila del loc_match LOCATOR_MATCH " + "[ loc LOCATOR ] [ dev DEV ]\n"); + fprintf(stderr, " ip ila list\n"); + fprintf(stderr, "\n"); + + exit(-1); +} + +/* netlink socket */ +static struct rtnl_handle genl_rth = { .fd = -1 }; +static int genl_family = -1; + +#define ILA_REQUEST(_req, _bufsiz, _cmd, _flags) \ + GENL_REQUEST(_req, _bufsiz, genl_family, 0, \ + ILA_GENL_VERSION, _cmd, _flags) + +#define ILA_RTA(g) ((struct rtattr *)(((char *)(g)) + \ + NLMSG_ALIGN(sizeof(struct genlmsghdr)))) + +#define ADDR_BUF_SIZE sizeof("xxxx:xxxx:xxxx:xxxx") + +static int print_addr64(__u64 addr, char *buff, size_t len) +{ + __u16 *words = (__u16 *)&addr; + __u16 v; + int i, ret; + size_t written = 0; + char *sep = ":"; + + for (i = 0; i < 4; i++) { + v = ntohs(words[i]); + + if (i == 3) + sep = ""; + + ret = snprintf(&buff[written], len - written, "%x%s", v, sep); + if (ret < 0) + return ret; + + written += ret; + } + + return written; +} + +static void print_ila_locid(FILE *fp, int attr, struct rtattr *tb[], int space) +{ + char abuf[256]; + size_t blen; + int i; + + if (tb[attr]) { + blen = print_addr64(rta_getattr_u32(tb[attr]), + abuf, sizeof(abuf)); + fprintf(fp, "%s", abuf); + } else { + fprintf(fp, "-"); + blen = 1; + } + + for (i = 0; i < space - blen; i++) + fprintf(fp, " "); +} + +static int print_ila_mapping(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct genlmsghdr *ghdr; + struct rtattr *tb[ILA_ATTR_MAX + 1]; + int len = n->nlmsg_len; + + if (n->nlmsg_type != genl_family) + return 0; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) + return -1; + + ghdr = NLMSG_DATA(n); + parse_rtattr(tb, ILA_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len); + + print_ila_locid(fp, ILA_ATTR_LOCATOR_MATCH, tb, ADDR_BUF_SIZE); + print_ila_locid(fp, ILA_ATTR_LOCATOR, tb, ADDR_BUF_SIZE); + + if (tb[ILA_ATTR_IFINDEX]) + fprintf(fp, "%s", ll_index_to_name(rta_getattr_u32(tb[ILA_ATTR_IFINDEX]))); + else + fprintf(fp, "-"); + fprintf(fp, "\n"); + + return 0; +} + +#define NLMSG_BUF_SIZE 4096 + +static int do_list(int argc, char **argv) +{ + ILA_REQUEST(req, 1024, ILA_CMD_GET, NLM_F_REQUEST | NLM_F_DUMP); + + if (argc > 0) { + fprintf(stderr, "\"ip ila show\" does not take " + "any arguments.\n"); + return -1; + } + + if (rtnl_send(&genl_rth, (void *)&req, req.n.nlmsg_len) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&genl_rth, print_ila_mapping, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +static int ila_parse_opt(int argc, char **argv, struct nlmsghdr *n, + bool adding) +{ + __u64 locator = 0; + __u64 locator_match = 0; + int ifindex = 0; + bool loc_set = false; + bool loc_match_set = false; + bool ifindex_set = false; + + while (argc > 0) { + if (!matches(*argv, "loc")) { + NEXT_ARG(); + + if (get_addr64(&locator, *argv) < 0) { + fprintf(stderr, "Bad locator: %s\n", *argv); + return -1; + } + loc_set = true; + } else if (!matches(*argv, "loc_match")) { + NEXT_ARG(); + + if (get_addr64(&locator_match, *argv) < 0) { + fprintf(stderr, "Bad locator to match: %s\n", + *argv); + return -1; + } + loc_match_set = true; + } else if (!matches(*argv, "dev")) { + NEXT_ARG(); + + ifindex = ll_name_to_index(*argv); + if (ifindex == 0) { + fprintf(stderr, "No such interface: %s\n", + *argv); + return -1; + } + ifindex_set = true; + } else { + usage(); + return -1; + } + argc--, argv++; + } + + if (adding) { + if (!loc_set) { + fprintf(stderr, "ila: missing locator\n"); + return -1; + } + if (!loc_match_set) { + fprintf(stderr, "ila: missing locator0match\n"); + return -1; + } + } + + if (loc_match_set) + addattr64(n, 1024, ILA_ATTR_LOCATOR_MATCH, locator_match); + + if (loc_set) + addattr64(n, 1024, ILA_ATTR_LOCATOR, locator); + + if (ifindex_set) + addattr32(n, 1024, ILA_ATTR_IFINDEX, ifindex); + + return 0; +} + +static int do_add(int argc, char **argv) +{ + ILA_REQUEST(req, 1024, ILA_CMD_ADD, NLM_F_REQUEST); + + ila_parse_opt(argc, argv, &req.n, true); + + if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0) + return -2; + + return 0; +} + +static int do_del(int argc, char **argv) +{ + ILA_REQUEST(req, 1024, ILA_CMD_DEL, NLM_F_REQUEST); + + ila_parse_opt(argc, argv, &req.n, false); + + if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0) + return -2; + + return 0; +} + +int do_ipila(int argc, char **argv) +{ + if (argc < 1) + usage(); + + if (matches(*argv, "help") == 0) + usage(); + + if (genl_init_handle(&genl_rth, ILA_GENL_NAME, &genl_family)) + exit(1); + + if (matches(*argv, "add") == 0) + return do_add(argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return do_del(argc-1, argv+1); + if (matches(*argv, "list") == 0) + return do_list(argc-1, argv+1); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip ila help\".\n", + *argv); + exit(-1); +} diff -Nru iproute2-4.3.0/ip/ipl2tp.c iproute2-4.9.0/ip/ipl2tp.c --- iproute2-4.3.0/ip/ipl2tp.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipl2tp.c 2016-12-12 23:07:42.000000000 +0000 @@ -56,13 +56,15 @@ uint16_t pw_type; uint16_t mtu; - int udp_csum:1; - int recv_seq:1; - int send_seq:1; - int lns_mode:1; - int data_seq:2; - int tunnel:1; - int session:1; + unsigned int udp6_csum_tx:1; + unsigned int udp6_csum_rx:1; + unsigned int udp_csum:1; + unsigned int recv_seq:1; + unsigned int send_seq:1; + unsigned int lns_mode:1; + unsigned int data_seq:2; + unsigned int tunnel:1; + unsigned int session:1; int reorder_timeout; const char *ifname; uint8_t l2spec_type; @@ -108,15 +110,23 @@ if (p->local_ip.family == AF_INET6) local_attr = L2TP_ATTR_IP6_SADDR; - addattr_l(&req.n, 1024, local_attr, &p->local_ip.data, p->local_ip.bytelen); + addattr_l(&req.n, 1024, local_attr, &p->local_ip.data, + p->local_ip.bytelen); if (p->peer_ip.family == AF_INET6) peer_attr = L2TP_ATTR_IP6_DADDR; - addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data, p->peer_ip.bytelen); + addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data, + p->peer_ip.bytelen); if (p->encap == L2TP_ENCAPTYPE_UDP) { addattr16(&req.n, 1024, L2TP_ATTR_UDP_SPORT, p->local_udp_port); addattr16(&req.n, 1024, L2TP_ATTR_UDP_DPORT, p->peer_udp_port); + if (p->udp_csum) + addattr8(&req.n, 1024, L2TP_ATTR_UDP_CSUM, 1); + if (!p->udp6_csum_tx) + addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_TX); + if (!p->udp6_csum_rx) + addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_RX); } if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0) @@ -151,18 +161,27 @@ addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_TYPE, p->l2spec_type); addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_LEN, p->l2spec_len); - if (p->mtu) addattr16(&req.n, 1024, L2TP_ATTR_MTU, p->mtu); - if (p->recv_seq) addattr(&req.n, 1024, L2TP_ATTR_RECV_SEQ); - if (p->send_seq) addattr(&req.n, 1024, L2TP_ATTR_SEND_SEQ); - if (p->lns_mode) addattr(&req.n, 1024, L2TP_ATTR_LNS_MODE); - if (p->data_seq) addattr8(&req.n, 1024, L2TP_ATTR_DATA_SEQ, p->data_seq); - if (p->reorder_timeout) addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT, + if (p->mtu) + addattr16(&req.n, 1024, L2TP_ATTR_MTU, p->mtu); + if (p->recv_seq) + addattr8(&req.n, 1024, L2TP_ATTR_RECV_SEQ, 1); + if (p->send_seq) + addattr8(&req.n, 1024, L2TP_ATTR_SEND_SEQ, 1); + if (p->lns_mode) + addattr(&req.n, 1024, L2TP_ATTR_LNS_MODE); + if (p->data_seq) + addattr8(&req.n, 1024, L2TP_ATTR_DATA_SEQ, p->data_seq); + if (p->reorder_timeout) + addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT, p->reorder_timeout); - if (p->offset) addattr16(&req.n, 1024, L2TP_ATTR_OFFSET, p->offset); - if (p->cookie_len) addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE, - p->cookie, p->cookie_len); - if (p->peer_cookie_len) addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE, - p->peer_cookie, p->peer_cookie_len); + if (p->offset) + addattr16(&req.n, 1024, L2TP_ATTR_OFFSET, p->offset); + if (p->cookie_len) + addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE, + p->cookie, p->cookie_len); + if (p->peer_cookie_len) + addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE, + p->peer_cookie, p->peer_cookie_len); if (p->ifname && p->ifname[0]) addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname); @@ -205,14 +224,37 @@ p->tunnel_id, p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" : p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??"); - printf(" From %s ", inet_ntop(p->local_ip.family, p->local_ip.data, buf, sizeof(buf))); - printf("to %s\n", inet_ntop(p->peer_ip.family, p->peer_ip.data, buf, sizeof(buf))); + printf(" From %s ", + inet_ntop(p->local_ip.family, p->local_ip.data, + buf, sizeof(buf))); + printf("to %s\n", + inet_ntop(p->peer_ip.family, p->peer_ip.data, + buf, sizeof(buf))); printf(" Peer tunnel %u\n", p->peer_tunnel_id); - if (p->encap == L2TP_ENCAPTYPE_UDP) + if (p->encap == L2TP_ENCAPTYPE_UDP) { printf(" UDP source / dest ports: %hu/%hu\n", p->local_udp_port, p->peer_udp_port); + + switch (p->local_ip.family) { + case AF_INET: + printf(" UDP checksum: %s\n", + p->udp_csum ? "enabled" : "disabled"); + break; + case AF_INET6: + printf(" UDP checksum: %s%s%s%s\n", + p->udp6_csum_tx && p->udp6_csum_rx + ? "enabled" : "", + p->udp6_csum_tx && !p->udp6_csum_rx + ? "tx" : "", + !p->udp6_csum_tx && p->udp6_csum_rx + ? "rx" : "", + !p->udp6_csum_tx && !p->udp6_csum_rx + ? "disabled" : ""); + break; + } + } } static void print_session(struct l2tp_data *data) @@ -224,9 +266,9 @@ printf(" Peer session %u, tunnel %u\n", p->peer_session_id, p->peer_tunnel_id); - if (p->ifname != NULL) { + if (p->ifname != NULL) printf(" interface name: %s\n", p->ifname); - } + printf(" offset %u, peer offset %u\n", p->offset, p->peer_offset); if (p->cookie_len > 0) @@ -238,6 +280,14 @@ printf(" reorder timeout: %u\n", p->reorder_timeout); else printf("\n"); + if (p->send_seq || p->recv_seq) { + printf(" sequence numbering:"); + if (p->send_seq) + printf(" send"); + if (p->recv_seq) + printf(" recv"); + printf("\n"); + } } static int get_response(struct nlmsghdr *n, void *arg) @@ -281,7 +331,12 @@ if (attrs[L2TP_ATTR_L2SPEC_LEN]) p->l2spec_len = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_LEN]); - p->udp_csum = !!attrs[L2TP_ATTR_UDP_CSUM]; + if (attrs[L2TP_ATTR_UDP_CSUM]) + p->udp_csum = !!rta_getattr_u8(attrs[L2TP_ATTR_UDP_CSUM]); + + p->udp6_csum_tx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]; + p->udp6_csum_rx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]; + if (attrs[L2TP_ATTR_COOKIE]) memcpy(p->cookie, RTA_DATA(attrs[L2TP_ATTR_COOKIE]), p->cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_COOKIE])); @@ -290,8 +345,10 @@ memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]), p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE])); - p->recv_seq = !!attrs[L2TP_ATTR_RECV_SEQ]; - p->send_seq = !!attrs[L2TP_ATTR_SEND_SEQ]; + if (attrs[L2TP_ATTR_RECV_SEQ]) + p->recv_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_RECV_SEQ]); + if (attrs[L2TP_ATTR_SEND_SEQ]) + p->send_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_SEND_SEQ]); if (attrs[L2TP_ATTR_RECV_TIMEOUT]) p->reorder_timeout = rta_getattr_u64(attrs[L2TP_ATTR_RECV_TIMEOUT]); @@ -355,7 +412,8 @@ return 0; } -static int session_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +static int session_nlmsg(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) { int ret = get_response(n, arg); @@ -375,7 +433,8 @@ if (p->config.tunnel_id && p->config.session_id) { addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->config.tunnel_id); - addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID, p->config.session_id); + addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID, + p->config.session_id); } if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0) @@ -389,7 +448,8 @@ return 0; } -static int tunnel_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +static int tunnel_nlmsg(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) { int ret = get_response(n, arg); @@ -425,30 +485,19 @@ * Command parser *****************************************************************************/ -static int hex(char ch) -{ - if ((ch >= 'a') && (ch <= 'f')) - return ch - 'a' + 10; - if ((ch >= '0') && (ch <= '9')) - return ch - '0'; - if ((ch >= 'A') && (ch <= 'F')) - return ch - 'A' + 10; - return -1; -} - static int hex2mem(const char *buf, uint8_t *mem, int count) { int i, j; int c; for (i = 0, j = 0; i < count; i++, j += 2) { - c = hex(buf[j]); + c = get_hex(buf[j]); if (c < 0) goto err; mem[i] = c << 4; - c = hex(buf[j + 1]); + c = get_hex(buf[j + 1]); if (c < 0) goto err; @@ -465,28 +514,33 @@ static void usage(void) { - fprintf(stderr, "Usage: ip l2tp add tunnel\n"); - fprintf(stderr, " remote ADDR local ADDR\n"); - fprintf(stderr, " tunnel_id ID peer_tunnel_id ID\n"); - fprintf(stderr, " [ encap { ip | udp } ]\n"); - fprintf(stderr, " [ udp_sport PORT ] [ udp_dport PORT ]\n"); - fprintf(stderr, "Usage: ip l2tp add session [ name NAME ]\n"); - fprintf(stderr, " tunnel_id ID\n"); - fprintf(stderr, " session_id ID peer_session_id ID\n"); - fprintf(stderr, " [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n"); - fprintf(stderr, " [ offset OFFSET ] [ peer_offset OFFSET ]\n"); - fprintf(stderr, " [ l2spec_type L2SPEC ]\n"); - fprintf(stderr, " ip l2tp del tunnel tunnel_id ID\n"); - fprintf(stderr, " ip l2tp del session tunnel_id ID session_id ID\n"); - fprintf(stderr, " ip l2tp show tunnel [ tunnel_id ID ]\n"); - fprintf(stderr, " ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Where: NAME := STRING\n"); - fprintf(stderr, " ADDR := { IP_ADDRESS | any }\n"); - fprintf(stderr, " PORT := { 0..65535 }\n"); - fprintf(stderr, " ID := { 1..4294967295 }\n"); - fprintf(stderr, " HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n"); - fprintf(stderr, " L2SPEC := { none | default }\n"); + fprintf(stderr, "Usage: ip l2tp add tunnel\n" + " remote ADDR local ADDR\n" + " tunnel_id ID peer_tunnel_id ID\n" + " [ encap { ip | udp } ]\n" + " [ udp_sport PORT ] [ udp_dport PORT ]\n" + " [ udp_csum { on | off } ]\n" + " [ udp6_csum_tx { on | off } ]\n" + " [ udp6_csum_rx { on | off } ]\n" + "Usage: ip l2tp add session [ name NAME ]\n" + " tunnel_id ID\n" + " session_id ID peer_session_id ID\n" + " [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n" + " [ offset OFFSET ] [ peer_offset OFFSET ]\n" + " [ seq { none | send | recv | both } ]\n" + " [ l2spec_type L2SPEC ]\n" + " ip l2tp del tunnel tunnel_id ID\n" + " ip l2tp del session tunnel_id ID session_id ID\n" + " ip l2tp show tunnel [ tunnel_id ID ]\n" + " ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n" + "\n" + "Where: NAME := STRING\n" + " ADDR := { IP_ADDRESS | any }\n" + " PORT := { 0..65535 }\n" + " ID := { 1..4294967295 }\n" + " HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n" + " L2SPEC := { none | default }\n"); + exit(-1); } @@ -500,6 +554,8 @@ /* Defaults */ p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT; p->l2spec_len = 4; + p->udp6_csum_rx = 1; + p->udp6_csum_tx = 1; while (argc > 0) { if (strcmp(*argv, "encap") == 0) { @@ -526,6 +582,7 @@ } else if ((strcmp(*argv, "tunnel_id") == 0) || (strcmp(*argv, "tid") == 0)) { __u32 uval; + NEXT_ARG(); if (get_u32(&uval, *argv, 0)) invarg("invalid ID\n", *argv); @@ -533,6 +590,7 @@ } else if ((strcmp(*argv, "peer_tunnel_id") == 0) || (strcmp(*argv, "ptid") == 0)) { __u32 uval; + NEXT_ARG(); if (get_u32(&uval, *argv, 0)) invarg("invalid ID\n", *argv); @@ -540,6 +598,7 @@ } else if ((strcmp(*argv, "session_id") == 0) || (strcmp(*argv, "sid") == 0)) { __u32 uval; + NEXT_ARG(); if (get_u32(&uval, *argv, 0)) invarg("invalid ID\n", *argv); @@ -547,36 +606,68 @@ } else if ((strcmp(*argv, "peer_session_id") == 0) || (strcmp(*argv, "psid") == 0)) { __u32 uval; + NEXT_ARG(); if (get_u32(&uval, *argv, 0)) invarg("invalid ID\n", *argv); p->peer_session_id = uval; } else if (strcmp(*argv, "udp_sport") == 0) { __u16 uval; + NEXT_ARG(); if (get_u16(&uval, *argv, 0)) invarg("invalid port\n", *argv); p->local_udp_port = uval; } else if (strcmp(*argv, "udp_dport") == 0) { __u16 uval; + NEXT_ARG(); if (get_u16(&uval, *argv, 0)) invarg("invalid port\n", *argv); p->peer_udp_port = uval; + } else if (strcmp(*argv, "udp_csum") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "on") == 0) + p->udp_csum = 1; + else if (strcmp(*argv, "off") == 0) + p->udp_csum = 0; + else + invarg("invalid option for udp_csum\n", *argv); + } else if (strcmp(*argv, "udp6_csum_rx") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "on") == 0) + p->udp6_csum_rx = 1; + else if (strcmp(*argv, "off") == 0) + p->udp6_csum_rx = 0; + else + invarg("invalid option for udp6_csum_rx\n" + , *argv); + } else if (strcmp(*argv, "udp6_csum_tx") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "on") == 0) + p->udp6_csum_tx = 1; + else if (strcmp(*argv, "off") == 0) + p->udp6_csum_tx = 0; + else + invarg("invalid option for udp6_csum_tx\n" + , *argv); } else if (strcmp(*argv, "offset") == 0) { __u8 uval; + NEXT_ARG(); if (get_u8(&uval, *argv, 0)) invarg("invalid offset\n", *argv); p->offset = uval; } else if (strcmp(*argv, "peer_offset") == 0) { __u8 uval; + NEXT_ARG(); if (get_u8(&uval, *argv, 0)) invarg("invalid offset\n", *argv); p->peer_offset = uval; } else if (strcmp(*argv, "cookie") == 0) { int slen; + NEXT_ARG(); slen = strlen(*argv); if ((slen != 8) && (slen != 16)) @@ -587,6 +678,7 @@ invarg("cookie must be a hex string\n", *argv); } else if (strcmp(*argv, "peer_cookie") == 0) { int slen; + NEXT_ARG(); slen = strlen(*argv); if ((slen != 8) && (slen != 16)) @@ -604,7 +696,26 @@ p->l2spec_type = L2TP_L2SPECTYPE_NONE; p->l2spec_len = 0; } else { - fprintf(stderr, "Unknown layer2specific header type \"%s\"\n", *argv); + fprintf(stderr, + "Unknown layer2specific header type \"%s\"\n", + *argv); + exit(-1); + } + } else if (strcmp(*argv, "seq") == 0) { + NEXT_ARG(); + if (strcasecmp(*argv, "both") == 0) { + p->recv_seq = 1; + p->send_seq = 1; + } else if (strcasecmp(*argv, "recv") == 0) { + p->recv_seq = 1; + } else if (strcasecmp(*argv, "send") == 0) { + p->send_seq = 1; + } else if (strcasecmp(*argv, "none") == 0) { + p->recv_seq = 0; + p->send_seq = 0; + } else { + fprintf(stderr, + "Unknown seq value \"%s\"\n", *argv); exit(-1); } } else if (strcmp(*argv, "tunnel") == 0) { @@ -720,20 +831,12 @@ int do_ipl2tp(int argc, char **argv) { - if (genl_family < 0) { - if (rtnl_open_byproto(&genl_rth, 0, NETLINK_GENERIC) < 0) { - fprintf(stderr, "Cannot open generic netlink socket\n"); - exit(1); - } - - genl_family = genl_resolve_family(&genl_rth, L2TP_GENL_NAME); - if (genl_family < 0) - exit(1); - } - - if (argc < 1) + if (argc < 1 || !matches(*argv, "help")) usage(); + if (genl_init_handle(&genl_rth, L2TP_GENL_NAME, &genl_family)) + exit(1); + if (matches(*argv, "add") == 0) return do_add(argc-1, argv+1); if (matches(*argv, "delete") == 0) @@ -742,9 +845,8 @@ matches(*argv, "lst") == 0 || matches(*argv, "list") == 0) return do_show(argc-1, argv+1); - if (matches(*argv, "help") == 0) - usage(); - fprintf(stderr, "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv); + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv); exit(-1); } diff -Nru iproute2-4.3.0/ip/iplink_bond.c iproute2-4.9.0/ip/iplink_bond.c --- iproute2-4.3.0/ip/iplink_bond.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_bond.c 2016-12-12 23:07:42.000000000 +0000 @@ -166,7 +166,7 @@ __u32 miimon, updelay, downdelay, arp_interval, arp_validate; __u32 arp_all_targets, resend_igmp, min_links, lp_interval; __u32 packets_per_slave; - unsigned ifindex; + unsigned int ifindex; while (argc > 0) { if (matches(*argv, "mode") == 0) { @@ -209,7 +209,7 @@ invarg("invalid arp_interval", *argv); addattr32(n, 1024, IFLA_BOND_ARP_INTERVAL, arp_interval); } else if (matches(*argv, "arp_ip_target") == 0) { - struct rtattr * nest = addattr_nest(n, 1024, + struct rtattr *nest = addattr_nest(n, 1024, IFLA_BOND_ARP_IP_TARGET); if (NEXT_ARG_OK()) { NEXT_ARG(); @@ -217,8 +217,9 @@ char *target = strtok(targets, ","); int i; - for(i = 0; target && i < BOND_MAX_ARP_TARGETS; i++) { + for (i = 0; target && i < BOND_MAX_ARP_TARGETS; i++) { __u32 addr = get_addr32(target); + addattr32(n, 1024, i, addr); target = strtok(NULL, ","); } @@ -368,7 +369,7 @@ static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { - unsigned ifindex; + unsigned int ifindex; if (!tb) return; @@ -410,7 +411,6 @@ if (tb[IFLA_BOND_ARP_IP_TARGET]) { struct rtattr *iptb[BOND_MAX_ARP_TARGETS + 1]; - char buf[INET_ADDRSTRLEN]; int i; parse_rtattr_nested(iptb, BOND_MAX_ARP_TARGETS, @@ -422,11 +422,7 @@ for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) { if (iptb[i]) fprintf(f, "%s", - rt_addr_n2a(AF_INET, - RTA_PAYLOAD(iptb[i]), - RTA_DATA(iptb[i]), - buf, - INET_ADDRSTRLEN)); + rt_addr_n2a_rta(AF_INET, iptb[i])); if (i < BOND_MAX_ARP_TARGETS-1 && iptb[i+1]) fprintf(f, ","); } diff -Nru iproute2-4.3.0/ip/iplink_bond_slave.c iproute2-4.9.0/ip/iplink_bond_slave.c --- iproute2-4.3.0/ip/iplink_bond_slave.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_bond_slave.c 2016-12-12 23:07:42.000000000 +0000 @@ -17,6 +17,16 @@ #include "utils.h" #include "ip_common.h" +static void print_explain(FILE *f) +{ + fprintf(f, "Usage: ... bond_slave [ queue_id ID ]\n"); +} + +static void explain(void) +{ + print_explain(stderr); +} + static const char *slave_states[] = { [BOND_STATE_ACTIVE] = "ACTIVE", [BOND_STATE_BACKUP] = "BACKUP", @@ -26,7 +36,7 @@ { unsigned int state = rta_getattr_u8(tb); - if (state >= sizeof(slave_states) / sizeof(slave_states[0])) + if (state >= ARRAY_SIZE(slave_states)) fprintf(f, "state %d ", state); else fprintf(f, "state %s ", slave_states[state]); @@ -43,7 +53,7 @@ { unsigned int status = rta_getattr_u8(tb); - if (status >= sizeof(slave_mii_status) / sizeof(slave_mii_status[0])) + if (status >= ARRAY_SIZE(slave_mii_status)) fprintf(f, "mii_status %d ", status); else fprintf(f, "mii_status %s ", slave_mii_status[status]); @@ -80,11 +90,11 @@ rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID])); if (tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE]) - fprintf(f, "ad_actor_oper_port_state %d\n", + fprintf(f, "ad_actor_oper_port_state %d ", rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE])); if (tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE]) - fprintf(f, "ad_partner_oper_port_state %d\n", + fprintf(f, "ad_partner_oper_port_state %d ", rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE])); } @@ -99,6 +109,13 @@ if (get_u16(&queue_id, *argv, 0)) invarg("queue_id is invalid", *argv); addattr16(n, 1024, IFLA_BOND_SLAVE_QUEUE_ID, queue_id); + } else { + if (matches(*argv, "help") != 0) + fprintf(stderr, + "bond_slave: unknown option \"%s\"?\n", + *argv); + explain(); + return -1; } argc--, argv++; } @@ -106,10 +123,16 @@ return 0; } +static void bond_slave_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + struct link_util bond_slave_link_util = { - .id = "bond", + .id = "bond_slave", .maxattr = IFLA_BOND_SLAVE_MAX, .print_opt = bond_slave_print_opt, .parse_opt = bond_slave_parse_opt, - .slave = true, + .print_help = bond_slave_print_help, }; diff -Nru iproute2-4.3.0/ip/iplink_bridge.c iproute2-4.9.0/ip/iplink_bridge.c --- iproute2-4.3.0/ip/iplink_bridge.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_bridge.c 2016-12-12 23:07:42.000000000 +0000 @@ -13,6 +13,7 @@ #include #include #include +#include #include "rt_names.h" #include "utils.h" @@ -27,8 +28,28 @@ " [ ageing_time AGEING_TIME ]\n" " [ stp_state STP_STATE ]\n" " [ priority PRIORITY ]\n" + " [ group_fwd_mask MASK ]\n" + " [ group_address ADDRESS ]\n" " [ vlan_filtering VLAN_FILTERING ]\n" " [ vlan_protocol VLAN_PROTOCOL ]\n" + " [ vlan_default_pvid VLAN_DEFAULT_PVID ]\n" + " [ mcast_snooping MULTICAST_SNOOPING ]\n" + " [ mcast_router MULTICAST_ROUTER ]\n" + " [ mcast_query_use_ifaddr MCAST_QUERY_USE_IFADDR ]\n" + " [ mcast_querier MULTICAST_QUERIER ]\n" + " [ mcast_hash_elasticity HASH_ELASTICITY ]\n" + " [ mcast_hash_max HASH_MAX ]\n" + " [ mcast_last_member_count LAST_MEMBER_COUNT ]\n" + " [ mcast_startup_query_count STARTUP_QUERY_COUNT ]\n" + " [ mcast_last_member_interval LAST_MEMBER_INTERVAL ]\n" + " [ mcast_membership_interval MEMBERSHIP_INTERVAL ]\n" + " [ mcast_querier_interval QUERIER_INTERVAL ]\n" + " [ mcast_query_interval QUERY_INTERVAL ]\n" + " [ mcast_query_response_interval QUERY_RESPONSE_INTERVAL ]\n" + " [ mcast_startup_query_interval STARTUP_QUERY_INTERVAL ]\n" + " [ nf_call_iptables NF_CALL_IPTABLES ]\n" + " [ nf_call_ip6tables NF_CALL_IP6TABLES ]\n" + " [ nf_call_arptables NF_CALL_ARPTABLES ]\n" "\n" "Where: VLAN_PROTOCOL := { 802.1Q | 802.1ad }\n" ); @@ -39,6 +60,14 @@ print_explain(stderr); } +void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len) +{ + char eaddr[32]; + + ether_ntoa_r((const struct ether_addr *)id->addr, eaddr); + snprintf(buf, len, "%.2x%.2x.%s", id->prio[0], id->prio[1], eaddr); +} + static int bridge_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { @@ -87,20 +116,204 @@ __u8 vlan_filter; NEXT_ARG(); - if (get_u8(&vlan_filter, *argv, 0)) { + if (get_u8(&vlan_filter, *argv, 0)) invarg("invalid vlan_filtering", *argv); - return -1; - } + addattr8(n, 1024, IFLA_BR_VLAN_FILTERING, vlan_filter); } else if (matches(*argv, "vlan_protocol") == 0) { __u16 vlan_proto; NEXT_ARG(); - if (ll_proto_a2n(&vlan_proto, *argv)) { + if (ll_proto_a2n(&vlan_proto, *argv)) invarg("invalid vlan_protocol", *argv); - return -1; - } + addattr16(n, 1024, IFLA_BR_VLAN_PROTOCOL, vlan_proto); + } else if (matches(*argv, "group_fwd_mask") == 0) { + __u16 fwd_mask; + + NEXT_ARG(); + if (get_u16(&fwd_mask, *argv, 0)) + invarg("invalid group_fwd_mask", *argv); + + addattr16(n, 1024, IFLA_BR_GROUP_FWD_MASK, fwd_mask); + } else if (matches(*argv, "group_address") == 0) { + char llabuf[32]; + int len; + + NEXT_ARG(); + len = ll_addr_a2n(llabuf, sizeof(llabuf), *argv); + if (len < 0) + return -1; + addattr_l(n, 1024, IFLA_BR_GROUP_ADDR, llabuf, len); + } else if (matches(*argv, "vlan_default_pvid") == 0) { + __u16 default_pvid; + + NEXT_ARG(); + if (get_u16(&default_pvid, *argv, 0)) + invarg("invalid vlan_default_pvid", *argv); + + addattr16(n, 1024, IFLA_BR_VLAN_DEFAULT_PVID, + default_pvid); + } else if (matches(*argv, "mcast_router") == 0) { + __u8 mcast_router; + + NEXT_ARG(); + if (get_u8(&mcast_router, *argv, 0)) + invarg("invalid mcast_router", *argv); + + addattr8(n, 1024, IFLA_BR_MCAST_ROUTER, mcast_router); + } else if (matches(*argv, "mcast_snooping") == 0) { + __u8 mcast_snoop; + + NEXT_ARG(); + if (get_u8(&mcast_snoop, *argv, 0)) + invarg("invalid mcast_snooping", *argv); + + addattr8(n, 1024, IFLA_BR_MCAST_SNOOPING, mcast_snoop); + } else if (matches(*argv, "mcast_query_use_ifaddr") == 0) { + __u8 mcast_qui; + + NEXT_ARG(); + if (get_u8(&mcast_qui, *argv, 0)) + invarg("invalid mcast_query_use_ifaddr", + *argv); + + addattr8(n, 1024, IFLA_BR_MCAST_QUERY_USE_IFADDR, + mcast_qui); + } else if (matches(*argv, "mcast_querier") == 0) { + __u8 mcast_querier; + + NEXT_ARG(); + if (get_u8(&mcast_querier, *argv, 0)) + invarg("invalid mcast_querier", *argv); + + addattr8(n, 1024, IFLA_BR_MCAST_QUERIER, mcast_querier); + } else if (matches(*argv, "mcast_hash_elasticity") == 0) { + __u32 mcast_hash_el; + + NEXT_ARG(); + if (get_u32(&mcast_hash_el, *argv, 0)) + invarg("invalid mcast_hash_elasticity", + *argv); + + addattr32(n, 1024, IFLA_BR_MCAST_HASH_ELASTICITY, + mcast_hash_el); + } else if (matches(*argv, "mcast_hash_max") == 0) { + __u32 mcast_hash_max; + + NEXT_ARG(); + if (get_u32(&mcast_hash_max, *argv, 0)) + invarg("invalid mcast_hash_max", *argv); + + addattr32(n, 1024, IFLA_BR_MCAST_HASH_MAX, + mcast_hash_max); + } else if (matches(*argv, "mcast_last_member_count") == 0) { + __u32 mcast_lmc; + + NEXT_ARG(); + if (get_u32(&mcast_lmc, *argv, 0)) + invarg("invalid mcast_last_member_count", + *argv); + + addattr32(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_CNT, + mcast_lmc); + } else if (matches(*argv, "mcast_startup_query_count") == 0) { + __u32 mcast_sqc; + + NEXT_ARG(); + if (get_u32(&mcast_sqc, *argv, 0)) + invarg("invalid mcast_startup_query_count", + *argv); + + addattr32(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_CNT, + mcast_sqc); + } else if (matches(*argv, "mcast_last_member_interval") == 0) { + __u64 mcast_last_member_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_last_member_intvl, *argv, 0)) + invarg("invalid mcast_last_member_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_INTVL, + mcast_last_member_intvl); + } else if (matches(*argv, "mcast_membership_interval") == 0) { + __u64 mcast_membership_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_membership_intvl, *argv, 0)) + invarg("invalid mcast_membership_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_MEMBERSHIP_INTVL, + mcast_membership_intvl); + } else if (matches(*argv, "mcast_querier_interval") == 0) { + __u64 mcast_querier_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_querier_intvl, *argv, 0)) + invarg("invalid mcast_querier_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_QUERIER_INTVL, + mcast_querier_intvl); + } else if (matches(*argv, "mcast_query_interval") == 0) { + __u64 mcast_query_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_query_intvl, *argv, 0)) + invarg("invalid mcast_query_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_QUERY_INTVL, + mcast_query_intvl); + } else if (!matches(*argv, "mcast_query_response_interval")) { + __u64 mcast_query_resp_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_query_resp_intvl, *argv, 0)) + invarg("invalid mcast_query_response_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + mcast_query_resp_intvl); + } else if (!matches(*argv, "mcast_startup_query_interval")) { + __u64 mcast_startup_query_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_startup_query_intvl, *argv, 0)) + invarg("invalid mcast_startup_query_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, + mcast_startup_query_intvl); + } else if (matches(*argv, "nf_call_iptables") == 0) { + __u8 nf_call_ipt; + + NEXT_ARG(); + if (get_u8(&nf_call_ipt, *argv, 0)) + invarg("invalid nf_call_iptables", *argv); + + addattr8(n, 1024, IFLA_BR_NF_CALL_IPTABLES, + nf_call_ipt); + } else if (matches(*argv, "nf_call_ip6tables") == 0) { + __u8 nf_call_ip6t; + + NEXT_ARG(); + if (get_u8(&nf_call_ip6t, *argv, 0)) + invarg("invalid nf_call_ip6tables", *argv); + + addattr8(n, 1024, IFLA_BR_NF_CALL_IP6TABLES, + nf_call_ip6t); + } else if (matches(*argv, "nf_call_arptables") == 0) { + __u8 nf_call_arpt; + + NEXT_ARG(); + if (get_u8(&nf_call_arpt, *argv, 0)) + invarg("invalid nf_call_arptables", *argv); + + addattr8(n, 1024, IFLA_BR_NF_CALL_ARPTABLES, + nf_call_arpt); } else if (matches(*argv, "help") == 0) { explain(); return -1; @@ -155,6 +368,157 @@ ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]), b1, sizeof(b1))); } + + if (tb[IFLA_BR_BRIDGE_ID]) { + char bridge_id[32]; + + br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), bridge_id, + sizeof(bridge_id)); + fprintf(f, "bridge_id %s ", bridge_id); + } + + if (tb[IFLA_BR_ROOT_ID]) { + char root_id[32]; + + br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), root_id, + sizeof(root_id)); + fprintf(f, "designated_root %s ", root_id); + } + + if (tb[IFLA_BR_ROOT_PORT]) + fprintf(f, "root_port %u ", + rta_getattr_u16(tb[IFLA_BR_ROOT_PORT])); + + if (tb[IFLA_BR_ROOT_PATH_COST]) + fprintf(f, "root_path_cost %u ", + rta_getattr_u32(tb[IFLA_BR_ROOT_PATH_COST])); + + if (tb[IFLA_BR_TOPOLOGY_CHANGE]) + fprintf(f, "topology_change %u ", + rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE])); + + if (tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED]) + fprintf(f, "topology_change_detected %u ", + rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED])); + + if (tb[IFLA_BR_HELLO_TIMER]) { + struct timeval tv; + + __jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_HELLO_TIMER])); + fprintf(f, "hello_timer %4i.%.2i ", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } + + if (tb[IFLA_BR_TCN_TIMER]) { + struct timeval tv; + + __jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_TCN_TIMER])); + fprintf(f, "tcn_timer %4i.%.2i ", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } + + if (tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]) { + unsigned long jiffies; + struct timeval tv; + + jiffies = rta_getattr_u64(tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]); + __jiffies_to_tv(&tv, jiffies); + fprintf(f, "topology_change_timer %4i.%.2i ", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } + + if (tb[IFLA_BR_GC_TIMER]) { + struct timeval tv; + + __jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_GC_TIMER])); + fprintf(f, "gc_timer %4i.%.2i ", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } + + if (tb[IFLA_BR_VLAN_DEFAULT_PVID]) + fprintf(f, "vlan_default_pvid %u ", + rta_getattr_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID])); + + if (tb[IFLA_BR_GROUP_FWD_MASK]) + fprintf(f, "group_fwd_mask %#x ", + rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK])); + + if (tb[IFLA_BR_GROUP_ADDR]) { + SPRINT_BUF(mac); + + fprintf(f, "group_address %s ", + ll_addr_n2a(RTA_DATA(tb[IFLA_BR_GROUP_ADDR]), + RTA_PAYLOAD(tb[IFLA_BR_GROUP_ADDR]), + 1 /*ARPHDR_ETHER*/, mac, sizeof(mac))); + } + + if (tb[IFLA_BR_MCAST_SNOOPING]) + fprintf(f, "mcast_snooping %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING])); + + if (tb[IFLA_BR_MCAST_ROUTER]) + fprintf(f, "mcast_router %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_ROUTER])); + + if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]) + fprintf(f, "mcast_query_use_ifaddr %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR])); + + if (tb[IFLA_BR_MCAST_QUERIER]) + fprintf(f, "mcast_querier %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER])); + + if (tb[IFLA_BR_MCAST_HASH_ELASTICITY]) + fprintf(f, "mcast_hash_elasticity %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_ELASTICITY])); + + if (tb[IFLA_BR_MCAST_HASH_MAX]) + fprintf(f, "mcast_hash_max %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_MAX])); + + if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]) + fprintf(f, "mcast_last_member_count %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT])); + + if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) + fprintf(f, "mcast_startup_query_count %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT])); + + if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) + fprintf(f, "mcast_last_member_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL])); + + if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) + fprintf(f, "mcast_membership_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL])); + + if (tb[IFLA_BR_MCAST_QUERIER_INTVL]) + fprintf(f, "mcast_querier_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL])); + + if (tb[IFLA_BR_MCAST_QUERY_INTVL]) + fprintf(f, "mcast_query_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_INTVL])); + + if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) + fprintf(f, "mcast_query_response_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL])); + + if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) + fprintf(f, "mcast_startup_query_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL])); + + if (tb[IFLA_BR_NF_CALL_IPTABLES]) + fprintf(f, "nf_call_iptables %u ", + rta_getattr_u8(tb[IFLA_BR_NF_CALL_IPTABLES])); + + if (tb[IFLA_BR_NF_CALL_IP6TABLES]) + fprintf(f, "nf_call_ip6tables %u ", + rta_getattr_u8(tb[IFLA_BR_NF_CALL_IP6TABLES])); + + if (tb[IFLA_BR_NF_CALL_ARPTABLES]) + fprintf(f, "nf_call_arptables %u ", + rta_getattr_u8(tb[IFLA_BR_NF_CALL_ARPTABLES])); } static void bridge_print_help(struct link_util *lu, int argc, char **argv, diff -Nru iproute2-4.3.0/ip/iplink_bridge_slave.c iproute2-4.9.0/ip/iplink_bridge_slave.c --- iproute2-4.3.0/ip/iplink_bridge_slave.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_bridge_slave.c 2016-12-12 23:07:42.000000000 +0000 @@ -24,11 +24,16 @@ fprintf(f, "Usage: ... bridge_slave [ state STATE ] [ priority PRIO ] [cost COST ]\n" " [ guard {on | off} ]\n" - " [ hairpin {on | off} ] \n" + " [ hairpin {on | off} ]\n" " [ fastleave {on | off} ]\n" " [ root_block {on | off} ]\n" " [ learning {on | off} ]\n" " [ flood {on | off} ]\n" + " [ proxy_arp {on | off} ]\n" + " [ proxy_arp_wifi {on | off} ]\n" + " [ mcast_router MULTICAST_ROUTER ]\n" + " [ mcast_fast_leave {on | off} ]\n" + " [ mcast_flood {on | off} ]\n" ); } @@ -98,6 +103,95 @@ if (tb[IFLA_BRPORT_UNICAST_FLOOD]) print_onoff(f, "flood", rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD])); + + if (tb[IFLA_BRPORT_ID]) + fprintf(f, "port_id 0x%x ", + rta_getattr_u16(tb[IFLA_BRPORT_ID])); + + if (tb[IFLA_BRPORT_NO]) + fprintf(f, "port_no 0x%x ", + rta_getattr_u16(tb[IFLA_BRPORT_NO])); + + if (tb[IFLA_BRPORT_DESIGNATED_PORT]) + fprintf(f, "designated_port %u ", + rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_PORT])); + + if (tb[IFLA_BRPORT_DESIGNATED_COST]) + fprintf(f, "designated_cost %u ", + rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_COST])); + + if (tb[IFLA_BRPORT_BRIDGE_ID]) { + char bridge_id[32]; + + br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_BRIDGE_ID]), + bridge_id, sizeof(bridge_id)); + fprintf(f, "designated_bridge %s ", bridge_id); + } + + if (tb[IFLA_BRPORT_ROOT_ID]) { + char root_id[32]; + + br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_ROOT_ID]), + root_id, sizeof(root_id)); + fprintf(f, "designated_root %s ", root_id); + } + + if (tb[IFLA_BRPORT_HOLD_TIMER]) { + struct timeval tv; + __u64 htimer; + + htimer = rta_getattr_u64(tb[IFLA_BRPORT_HOLD_TIMER]); + __jiffies_to_tv(&tv, htimer); + fprintf(f, "hold_timer %4i.%.2i ", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } + + if (tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]) { + struct timeval tv; + __u64 agetimer; + + agetimer = rta_getattr_u64(tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]); + __jiffies_to_tv(&tv, agetimer); + fprintf(f, "message_age_timer %4i.%.2i ", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } + + if (tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]) { + struct timeval tv; + __u64 fwdtimer; + + fwdtimer = rta_getattr_u64(tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]); + __jiffies_to_tv(&tv, fwdtimer); + fprintf(f, "forward_delay_timer %4i.%.2i ", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } + + if (tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK]) + fprintf(f, "topology_change_ack %u ", + rta_getattr_u8(tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK])); + + if (tb[IFLA_BRPORT_CONFIG_PENDING]) + fprintf(f, "config_pending %u ", + rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING])); + if (tb[IFLA_BRPORT_PROXYARP]) + print_onoff(f, "proxy_arp", + rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP])); + + if (tb[IFLA_BRPORT_PROXYARP_WIFI]) + print_onoff(f, "proxy_arp_wifi", + rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI])); + + if (tb[IFLA_BRPORT_MULTICAST_ROUTER]) + fprintf(f, "mcast_router %u ", + rta_getattr_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER])); + + if (tb[IFLA_BRPORT_FAST_LEAVE]) + print_onoff(f, "mcast_fast_leave", + rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE])); + + if (tb[IFLA_BRPORT_MCAST_FLOOD]) + print_onoff(f, "mcast_flood", + rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD])); } static void bridge_slave_parse_on_off(char *arg_name, char *arg_val, @@ -162,6 +256,30 @@ NEXT_ARG(); bridge_slave_parse_on_off("flood", *argv, n, IFLA_BRPORT_UNICAST_FLOOD); + } else if (matches(*argv, "mcast_flood") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("mcast_flood", *argv, n, + IFLA_BRPORT_MCAST_FLOOD); + } else if (matches(*argv, "proxy_arp") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("proxy_arp", *argv, n, + IFLA_BRPORT_PROXYARP); + } else if (matches(*argv, "proxy_arp_wifi") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("proxy_arp_wifi", *argv, n, + IFLA_BRPORT_PROXYARP_WIFI); + } else if (matches(*argv, "mcast_router") == 0) { + __u8 mcast_router; + + NEXT_ARG(); + if (get_u8(&mcast_router, *argv, 0)) + invarg("invalid mcast_router", *argv); + addattr8(n, 1024, IFLA_BRPORT_MULTICAST_ROUTER, + mcast_router); + } else if (matches(*argv, "mcast_fast_leave") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("mcast_fast_leave", *argv, n, + IFLA_BRPORT_FAST_LEAVE); } else if (matches(*argv, "help") == 0) { explain(); return -1; @@ -184,10 +302,9 @@ } struct link_util bridge_slave_link_util = { - .id = "bridge", + .id = "bridge_slave", .maxattr = IFLA_BRPORT_MAX, .print_opt = bridge_slave_print_opt, .parse_opt = bridge_slave_parse_opt, .print_help = bridge_slave_print_help, - .slave = true, }; diff -Nru iproute2-4.3.0/ip/iplink.c iproute2-4.9.0/ip/iplink.c --- iproute2-4.3.0/ip/iplink.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink.c 2016-12-12 23:07:42.000000000 +0000 @@ -45,56 +45,62 @@ void iplink_usage(void) { if (iplink_have_newlink()) { - fprintf(stderr, "Usage: ip link add [link DEV] [ name ] NAME\n"); - fprintf(stderr, " [ txqueuelen PACKETS ]\n"); - fprintf(stderr, " [ address LLADDR ]\n"); - fprintf(stderr, " [ broadcast LLADDR ]\n"); - fprintf(stderr, " [ mtu MTU ] [index IDX ]\n"); - fprintf(stderr, " [ numtxqueues QUEUE_COUNT ]\n"); - fprintf(stderr, " [ numrxqueues QUEUE_COUNT ]\n"); - fprintf(stderr, " type TYPE [ ARGS ]\n"); - fprintf(stderr, " ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " ip link set { DEVICE | dev DEVICE | group DEVGROUP } [ { up | down } ]\n"); + fprintf(stderr, + "Usage: ip link add [link DEV] [ name ] NAME\n" + " [ txqueuelen PACKETS ]\n" + " [ address LLADDR ]\n" + " [ broadcast LLADDR ]\n" + " [ mtu MTU ] [index IDX ]\n" + " [ numtxqueues QUEUE_COUNT ]\n" + " [ numrxqueues QUEUE_COUNT ]\n" + " type TYPE [ ARGS ]\n" + " ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n" + "\n" + " ip link set { DEVICE | dev DEVICE | group DEVGROUP }\n" + " [ { up | down } ]\n" + " [ type TYPE ARGS ]\n"); } else fprintf(stderr, "Usage: ip link set DEVICE [ { up | down } ]\n"); - fprintf(stderr, " [ arp { on | off } ]\n"); - fprintf(stderr, " [ dynamic { on | off } ]\n"); - fprintf(stderr, " [ multicast { on | off } ]\n"); - fprintf(stderr, " [ allmulticast { on | off } ]\n"); - fprintf(stderr, " [ promisc { on | off } ]\n"); - fprintf(stderr, " [ trailers { on | off } ]\n"); - fprintf(stderr, " [ txqueuelen PACKETS ]\n"); - fprintf(stderr, " [ name NEWNAME ]\n"); - fprintf(stderr, " [ address LLADDR ]\n"); - fprintf(stderr, " [ broadcast LLADDR ]\n"); - fprintf(stderr, " [ mtu MTU ]\n"); - fprintf(stderr, " [ netns PID ]\n"); - fprintf(stderr, " [ netns NAME ]\n"); - fprintf(stderr, " [ link-netnsid ID ]\n"); - fprintf(stderr, " [ alias NAME ]\n"); - fprintf(stderr, " [ vf NUM [ mac LLADDR ]\n"); - fprintf(stderr, " [ vlan VLANID [ qos VLAN-QOS ] ]\n"); - - fprintf(stderr, " [ rate TXRATE ] ]\n"); - - fprintf(stderr, " [ spoofchk { on | off} ] ]\n"); - fprintf(stderr, " [ query_rss { on | off} ] ]\n"); - fprintf(stderr, " [ state { auto | enable | disable} ] ]\n"); - fprintf(stderr, " [ master DEVICE ]\n"); - fprintf(stderr, " [ nomaster ]\n"); - fprintf(stderr, " [ addrgenmode { eui64 | none } ]\n"); - fprintf(stderr, " [ protodown { on | off } ]\n"); - fprintf(stderr, " ip link show [ DEVICE | group GROUP ] [up] [master DEV] [type TYPE]\n"); + fprintf(stderr, + " [ arp { on | off } ]\n" + " [ dynamic { on | off } ]\n" + " [ multicast { on | off } ]\n" + " [ allmulticast { on | off } ]\n" + " [ promisc { on | off } ]\n" + " [ trailers { on | off } ]\n" + " [ txqueuelen PACKETS ]\n" + " [ name NEWNAME ]\n" + " [ address LLADDR ]\n" + " [ broadcast LLADDR ]\n" + " [ mtu MTU ]\n" + " [ netns { PID | NAME } ]\n" + " [ link-netnsid ID ]\n" + " [ alias NAME ]\n" + " [ vf NUM [ mac LLADDR ]\n" + " [ vlan VLANID [ qos VLAN-QOS ] [ proto VLAN-PROTO ] ]\n" + + " [ rate TXRATE ]\n" + " [ max_tx_rate TXRATE ]\n" + " [ min_tx_rate TXRATE ]\n" + + " [ spoofchk { on | off} ]\n" + " [ query_rss { on | off} ]\n" + " [ state { auto | enable | disable} ] ]\n" + " [ trust { on | off} ] ]\n" + " [ master DEVICE ][ vrf NAME ]\n" + " [ nomaster ]\n" + " [ addrgenmode { eui64 | none | stable_secret | random } ]\n" + " [ protodown { on | off } ]\n" + " ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n"); if (iplink_have_newlink()) { - fprintf(stderr, " ip link help [ TYPE ]\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n"); - fprintf(stderr, " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |\n"); - fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti | nlmon |\n"); - fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf }\n"); + fprintf(stderr, + " ip link help [ TYPE ]\n\n" + "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n" + " bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n" + " gre | gretap | ip6gre | ip6gretap | vti | nlmon | team_slave |\n" + " bond_slave | ipvlan | geneve | bridge_slave | vrf | macsec }\n"); } exit(-1); } @@ -115,15 +121,14 @@ static void *BODY; /* cached dlopen(NULL) handle */ static struct link_util *linkutil_list; -static struct link_util *__get_link_kind(const char *id, bool slave) +struct link_util *get_link_kind(const char *id) { void *dlh; char buf[256]; struct link_util *l; for (l = linkutil_list; l; l = l->next) - if (strcmp(l->id, id) == 0 && - l->slave == slave) + if (strcmp(l->id, id) == 0) return l; snprintf(buf, sizeof(buf), LIBDIR "/ip/link_%s.so", id); @@ -138,10 +143,7 @@ } } - if (slave) - snprintf(buf, sizeof(buf), "%s_slave_link_util", id); - else - snprintf(buf, sizeof(buf), "%s_link_util", id); + snprintf(buf, sizeof(buf), "%s_link_util", id); l = dlsym(dlh, buf); if (l == NULL) return NULL; @@ -151,16 +153,6 @@ return l; } -struct link_util *get_link_kind(const char *id) -{ - return __get_link_kind(id, false); -} - -struct link_util *get_link_slave_kind(const char *id) -{ - return __get_link_kind(id, true); -} - static int get_link_mode(const char *mode) { if (strcasecmp(mode, "default") == 0) @@ -176,6 +168,10 @@ return IN6_ADDR_GEN_MODE_EUI64; if (strcasecmp(mode, "none") == 0) return IN6_ADDR_GEN_MODE_NONE; + if (strcasecmp(mode, "stable_secret") == 0) + return IN6_ADDR_GEN_MODE_STABLE_PRIVACY; + if (strcasecmp(mode, "random") == 0) + return IN6_ADDR_GEN_MODE_RANDOM; return -1; } @@ -202,16 +198,14 @@ struct nlmsghdr n; struct ifinfomsg i; char buf[1024]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .n.nlmsg_type = RTM_NEWLINK, + .i.ifi_family = AF_UNSPEC, + }; if (have_rtnl_newlink < 0) { - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; - req.n.nlmsg_type = RTM_NEWLINK; - req.i.ifi_family = AF_UNSPEC; - if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) { perror("request send failed"); exit(1); @@ -233,6 +227,89 @@ char buf[1024]; }; +static int nl_get_ll_addr_len(unsigned int dev_index) +{ + int len; + struct iplink_req req = { + .n = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_GETLINK, + .nlmsg_flags = NLM_F_REQUEST + }, + .i = { + .ifi_family = preferred_family, + .ifi_index = dev_index, + } + }; + struct rtattr *tb[IFLA_MAX+1]; + + if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) + return -1; + + len = req.n.nlmsg_len - NLMSG_LENGTH(sizeof(req.i)); + if (len < 0) + return -1; + + parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(&req.i), len, NLA_F_NESTED); + if (!tb[IFLA_ADDRESS]) + return -1; + + return RTA_PAYLOAD(tb[IFLA_ADDRESS]); +} + +static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp, + struct ifla_vf_vlan_info *ivvip) +{ + int argc = *argcp; + char **argv = *argvp; + + NEXT_ARG(); + if (get_unsigned(&ivvip->vlan, *argv, 0)) + invarg("Invalid \"vlan\" value\n", *argv); + + ivvip->vf = vf; + ivvip->qos = 0; + ivvip->vlan_proto = htons(ETH_P_8021Q); + if (NEXT_ARG_OK()) { + NEXT_ARG(); + if (matches(*argv, "qos") == 0) { + NEXT_ARG(); + if (get_unsigned(&ivvip->qos, *argv, 0)) + invarg("Invalid \"qos\" value\n", *argv); + } else { + /* rewind arg */ + PREV_ARG(); + } + } + if (NEXT_ARG_OK()) { + NEXT_ARG(); + if (matches(*argv, "proto") == 0) { + NEXT_ARG(); + if (ll_proto_a2n(&ivvip->vlan_proto, *argv)) + invarg("protocol is invalid\n", *argv); + if (ivvip->vlan_proto != htons(ETH_P_8021AD) && + ivvip->vlan_proto != htons(ETH_P_8021Q)) { + SPRINT_BUF(b1); + SPRINT_BUF(b2); + char msg[64 + sizeof(b1) + sizeof(b2)]; + + sprintf(msg, "Invalid \"vlan protocol\" value - supported %s, %s\n", + ll_proto_n2a(htons(ETH_P_8021Q), + b1, sizeof(b1)), + ll_proto_n2a(htons(ETH_P_8021AD), + b2, sizeof(b2))); + invarg(msg, *argv); + } + } else { + /* rewind arg */ + PREV_ARG(); + } + } + + *argcp = argc; + *argvp = argv; +} + static int iplink_parse_vf(int vf, int *argcp, char ***argvp, struct iplink_req *req, int dev_index) { @@ -269,35 +346,58 @@ while (NEXT_ARG_OK()) { NEXT_ARG(); if (matches(*argv, "mac") == 0) { - struct ifla_vf_mac ivm; + struct ifla_vf_mac ivm = { 0 }; + int halen = nl_get_ll_addr_len(dev_index); NEXT_ARG(); ivm.vf = vf; len = ll_addr_a2n((char *)ivm.mac, 32, *argv); if (len < 0) return -1; - addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, &ivm, sizeof(ivm)); + if (halen > 0 && len != halen) { + fprintf(stderr, + "Invalid address length %d - must be %d bytes\n", + len, halen); + return -1; + } + addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, + &ivm, sizeof(ivm)); } else if (matches(*argv, "vlan") == 0) { - struct ifla_vf_vlan ivv; + struct ifla_vf_vlan_info ivvi; - NEXT_ARG(); - if (get_unsigned(&ivv.vlan, *argv, 0)) - invarg("Invalid \"vlan\" value\n", *argv); + iplink_parse_vf_vlan_info(vf, &argc, &argv, &ivvi); + /* support the old interface in case of older kernel*/ + if (ivvi.vlan_proto == htons(ETH_P_8021Q)) { + struct ifla_vf_vlan ivv; + + ivv.vf = ivvi.vf; + ivv.vlan = ivvi.vlan; + ivv.qos = ivvi.qos; + addattr_l(&req->n, sizeof(*req), + IFLA_VF_VLAN, &ivv, sizeof(ivv)); + } else { + struct rtattr *vfvlanlist; - ivv.vf = vf; - ivv.qos = 0; - if (NEXT_ARG_OK()) { - NEXT_ARG(); - if (matches(*argv, "qos") == 0) { + vfvlanlist = addattr_nest(&req->n, sizeof(*req), + IFLA_VF_VLAN_LIST); + addattr_l(&req->n, sizeof(*req), + IFLA_VF_VLAN_INFO, &ivvi, + sizeof(ivvi)); + + while (NEXT_ARG_OK()) { NEXT_ARG(); - if (get_unsigned(&ivv.qos, *argv, 0)) - invarg("Invalid \"qos\" value\n", *argv); - } else { - /* rewind arg */ - PREV_ARG(); + if (matches(*argv, "vlan") != 0) { + PREV_ARG(); + break; + } + iplink_parse_vf_vlan_info(vf, &argc, + &argv, &ivvi); + addattr_l(&req->n, sizeof(*req), + IFLA_VF_VLAN_INFO, &ivvi, + sizeof(ivvi)); } + addattr_nest_end(&req->n, vfvlanlist); } - addattr_l(&req->n, sizeof(*req), IFLA_VF_VLAN, &ivv, sizeof(ivv)); } else if (matches(*argv, "rate") == 0) { struct ifla_vf_tx_rate ivt; @@ -337,7 +437,8 @@ else return on_off("spoofchk", *argv); ivs.vf = vf; - addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs)); + addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, + &ivs, sizeof(ivs)); } else if (matches(*argv, "query_rss") == 0) { struct ifla_vf_rss_query_en ivs; @@ -350,7 +451,22 @@ else return on_off("query_rss", *argv); ivs.vf = vf; - addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(ivs)); + addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, + &ivs, sizeof(ivs)); + + } else if (matches(*argv, "trust") == 0) { + struct ifla_vf_trust ivt; + + NEXT_ARG(); + if (matches(*argv, "on") == 0) + ivt.setting = 1; + else if (matches(*argv, "off") == 0) + ivt.setting = 0; + else + invarg("Invalid \"trust\" value\n", *argv); + ivt.vf = vf; + addattr_l(&req->n, sizeof(*req), IFLA_VF_TRUST, + &ivt, sizeof(ivt)); } else if (matches(*argv, "state") == 0) { struct ifla_vf_link_state ivl; @@ -365,7 +481,30 @@ else invarg("Invalid \"state\" value\n", *argv); ivl.vf = vf; - addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl)); + addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, + &ivl, sizeof(ivl)); + } else if (matches(*argv, "node_guid") == 0) { + struct ifla_vf_guid ivg; + + NEXT_ARG(); + ivg.vf = vf; + if (get_guid(&ivg.guid, *argv)) { + invarg("Invalid GUID format\n", *argv); + return -1; + } + addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_NODE_GUID, + &ivg, sizeof(ivg)); + } else if (matches(*argv, "port_guid") == 0) { + struct ifla_vf_guid ivg; + + NEXT_ARG(); + ivg.vf = vf; + if (get_guid(&ivg.guid, *argv)) { + invarg("Invalid GUID format\n", *argv); + return -1; + } + addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_PORT_GUID, + &ivg, sizeof(ivg)); } else { /* rewind arg */ PREV_ARG(); @@ -411,6 +550,7 @@ int numrxqueues = -1; int dev_index = 0; int link_netnsid = -1; + int addr_len = 0; *group = -1; ret = argc; @@ -435,10 +575,10 @@ *link = *argv; } else if (matches(*argv, "address") == 0) { NEXT_ARG(); - len = ll_addr_a2n(abuf, sizeof(abuf), *argv); - if (len < 0) + addr_len = ll_addr_a2n(abuf, sizeof(abuf), *argv); + if (addr_len < 0) return -1; - addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, len); + addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, addr_len); } else if (matches(*argv, "broadcast") == 0 || strcmp(*argv, "brd") == 0) { NEXT_ARG(); @@ -468,9 +608,11 @@ duparg("netns", *argv); netns = netns_get_fd(*argv); if (netns >= 0) - addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4); + addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, + &netns, 4); else if (get_integer(&netns, *argv, 0) == 0) - addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4); + addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, + &netns, 4); else invarg("Invalid \"netns\" value\n", *argv); } else if (strcmp(*argv, "multicast") == 0) { @@ -548,6 +690,17 @@ invarg("Device does not exist\n", *argv); addattr_l(&req->n, sizeof(*req), IFLA_MASTER, &ifindex, 4); + } else if (strcmp(*argv, "vrf") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Not a valid VRF name\n", *argv); + if (!name_is_vrf(*argv)) + invarg("Not a valid VRF name\n", *argv); + addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + &ifindex, sizeof(ifindex)); } else if (matches(*argv, "nomaster") == 0) { int ifindex = 0; @@ -623,7 +776,8 @@ invarg("Invalid address generation mode\n", *argv); afs = addattr_nest(&req->n, sizeof(*req), IFLA_AF_SPEC); afs6 = addattr_nest(&req->n, sizeof(*req), AF_INET6); - addattr8(&req->n, sizeof(*req), IFLA_INET6_ADDR_GEN_MODE, mode); + addattr8(&req->n, sizeof(*req), + IFLA_INET6_ADDR_GEN_MODE, mode); addattr_nest_end(&req->n, afs6); addattr_nest_end(&req->n, afs); } else if (matches(*argv, "link-netnsid") == 0) { @@ -660,6 +814,17 @@ argc--; argv++; } + if (dev_index && addr_len) { + int halen = nl_get_ll_addr_len(dev_index); + + if (halen >= 0 && halen != addr_len) { + fprintf(stderr, + "Invalid address length %d - must be %d bytes\n", + addr_len, halen); + return -1; + } + } + return ret - argc; } @@ -673,17 +838,16 @@ int index = -1; int group; struct link_util *lu = NULL; - struct iplink_req req; + struct iplink_req req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .i.ifi_family = preferred_family, + }; int ret; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.i.ifi_family = preferred_family; - - ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev, &group, &index); + ret = iplink_parse(argc, argv, + &req, &name, &type, &link, &dev, &group, &index); if (ret < 0) return ret; @@ -696,34 +860,31 @@ &group, sizeof(group)); else { if (argc) { - fprintf(stderr, "Garbage instead of arguments " - "\"%s ...\". Try \"ip link " - "help\".\n", *argv); + fprintf(stderr, + "Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n", + *argv); return -1; } if (flags & NLM_F_CREATE) { - fprintf(stderr, "group cannot be used when " - "creating devices.\n"); + fprintf(stderr, "group cannot be used when creating devices.\n"); return -1; } req.i.ifi_index = 0; addattr32(&req.n, sizeof(req), IFLA_GROUP, group); if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) - exit(2); + return -2; return 0; } } if (!(flags & NLM_F_CREATE)) { if (!dev) { - fprintf(stderr, "Not enough information: \"dev\" " - "argument is required.\n"); + fprintf(stderr, "Not enough information: \"dev\" argument is required.\n"); exit(-1); } if (cmd == RTM_NEWLINK && index != -1) { - fprintf(stderr, "index can be used only when " - "creating devices.\n"); + fprintf(stderr, "index can be used only when creating devices.\n"); exit(-1); } @@ -758,7 +919,8 @@ if (name) { len = strlen(name) + 1; if (len == 1) - invarg("\"\" is not a valid device identifier\n", "name"); + invarg("\"\" is not a valid device identifier\n", + "name"); if (len > IFNAMSIZ) invarg("\"name\" too long\n", name); addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len); @@ -766,28 +928,21 @@ if (type) { struct rtattr *linkinfo; - char slavebuf[128], *ulinep = strchr(type, '_'); + char *ulinep = strchr(type, '_'); int iflatype; linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO); addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type, strlen(type)); - if (ulinep && !strcmp(ulinep, "_slave")) { - strncpy(slavebuf, type, sizeof(slavebuf)); - slavebuf[sizeof(slavebuf) - 1] = '\0'; - ulinep = strchr(slavebuf, '_'); - /* check in case it was after sizeof(slavebuf) - 1*/ - if (ulinep) - *ulinep = '\0'; - lu = get_link_slave_kind(slavebuf); + lu = get_link_kind(type); + if (ulinep && !strcmp(ulinep, "_slave")) iflatype = IFLA_INFO_SLAVE_DATA; - } else { - lu = get_link_kind(type); + else iflatype = IFLA_INFO_DATA; - } if (lu && argc) { - struct rtattr *data = addattr_nest(&req.n, sizeof(req), iflatype); + struct rtattr *data = addattr_nest(&req.n, + sizeof(req), iflatype); if (lu->parse_opt && lu->parse_opt(lu, argc, argv, &req.n)) @@ -797,19 +952,19 @@ } else if (argc) { if (matches(*argv, "help") == 0) usage(); - fprintf(stderr, "Garbage instead of arguments \"%s ...\". " - "Try \"ip link help\".\n", *argv); + fprintf(stderr, + "Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n", + *argv); return -1; } addattr_nest_end(&req.n, linkinfo); } else if (flags & NLM_F_CREATE) { - fprintf(stderr, "Not enough information: \"type\" argument " - "is required\n"); + fprintf(stderr, "Not enough information: \"type\" argument is required\n"); return -1; } if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) - exit(2); + return -2; return 0; } @@ -817,19 +972,17 @@ int iplink_get(unsigned int flags, char *name, __u32 filt_mask) { int len; - struct iplink_req req; + struct iplink_req req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + }; struct { struct nlmsghdr n; char buf[16384]; } answer; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = RTM_GETLINK; - req.i.ifi_family = preferred_family; - if (name) { len = strlen(name) + 1; if (len == 1) @@ -923,16 +1076,14 @@ static int set_qlen(const char *dev, int qlen) { - struct ifreq ifr; + struct ifreq ifr = { .ifr_qlen = qlen }; int s; s = get_ctl_fd(); if (s < 0) return -1; - memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, dev, IFNAMSIZ); - ifr.ifr_qlen = qlen; if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { perror("SIOCSIFXQLEN"); close(s); @@ -945,16 +1096,14 @@ static int set_mtu(const char *dev, int mtu) { - struct ifreq ifr; + struct ifreq ifr = { .ifr_mtu = mtu }; int s; s = get_ctl_fd(); if (s < 0) return -1; - memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, dev, IFNAMSIZ); - ifr.ifr_mtu = mtu; if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { perror("SIOCSIFMTU"); close(s); @@ -967,8 +1116,11 @@ static int get_address(const char *dev, int *htype) { - struct ifreq ifr; - struct sockaddr_ll me; + struct ifreq ifr = {}; + struct sockaddr_ll me = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_LOOP), + }; socklen_t alen; int s; @@ -978,7 +1130,6 @@ return -1; } - memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, dev, IFNAMSIZ); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { perror("SIOCGIFINDEX"); @@ -986,10 +1137,7 @@ return -1; } - memset(&me, 0, sizeof(me)); - me.sll_family = AF_PACKET; me.sll_ifindex = ifr.ifr_ifindex; - me.sll_protocol = htons(ETH_P_LOOP); if (bind(s, (struct sockaddr *)&me, sizeof(me)) == -1) { perror("bind"); close(s); @@ -1019,7 +1167,8 @@ if (alen < 0) return -1; if (alen != halen) { - fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); + fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", + lla, halen); return -1; } return 0; @@ -1159,7 +1308,8 @@ } if (!dev) { - fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); + fprintf(stderr, + "Not enough of information: \"dev\" argument is required.\n"); exit(-1); } diff -Nru iproute2-4.3.0/ip/iplink_can.c iproute2-4.9.0/ip/iplink_can.c --- iproute2-4.3.0/ip/iplink_can.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_can.c 2016-12-12 23:07:42.000000000 +0000 @@ -23,13 +23,11 @@ { fprintf(f, "Usage: ip link set DEVICE type can\n" - "\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] | \n" - "\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n " - "\t phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n" + "\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] |\n" + "\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n \t phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n" "\n" - "\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] | \n" - "\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n " - "\t dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n" + "\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] |\n" + "\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n \t dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n" "\n" "\t[ loopback { on | off } ]\n" "\t[ listen-only { on | off } ]\n" @@ -73,7 +71,7 @@ return 0; } -static void set_ctrlmode(char* name, char *arg, +static void set_ctrlmode(char *name, char *arg, struct can_ctrlmode *cm, __u32 flags) { if (strcmp(arg, "on") == 0) { @@ -112,11 +110,9 @@ static int can_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { - struct can_bittiming bt, dbt; + struct can_bittiming bt = {}, dbt = {}; struct can_ctrlmode cm = {0, 0}; - memset(&bt, 0, sizeof(bt)); - memset(&dbt, 0, sizeof(dbt)); while (argc > 0) { if (matches(*argv, "bitrate") == 0) { NEXT_ARG(); @@ -289,11 +285,9 @@ if (tb[IFLA_CAN_BITTIMING]) { struct can_bittiming *bt = RTA_DATA(tb[IFLA_CAN_BITTIMING]); - fprintf(f, "\n " - "bitrate %d sample-point %.3f ", + fprintf(f, "\n bitrate %d sample-point %.3f ", bt->bitrate, (float)bt->sample_point / 1000.); - fprintf(f, "\n " - "tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d", + fprintf(f, "\n tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d", bt->tq, bt->prop_seg, bt->phase_seg1, bt->phase_seg2, bt->sjw); } @@ -302,8 +296,7 @@ struct can_bittiming_const *btc = RTA_DATA(tb[IFLA_CAN_BITTIMING_CONST]); - fprintf(f, "\n " - "%s: tseg1 %d..%d tseg2 %d..%d " + fprintf(f, "\n %s: tseg1 %d..%d tseg2 %d..%d " "sjw 1..%d brp %d..%d brp-inc %d", btc->name, btc->tseg1_min, btc->tseg1_max, btc->tseg2_min, btc->tseg2_max, btc->sjw_max, @@ -314,11 +307,9 @@ struct can_bittiming *dbt = RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]); - fprintf(f, "\n " - "dbitrate %d dsample-point %.3f ", + fprintf(f, "\n dbitrate %d dsample-point %.3f ", dbt->bitrate, (float)dbt->sample_point / 1000.); - fprintf(f, "\n " - "dtq %d dprop-seg %d dphase-seg1 %d " + fprintf(f, "\n dtq %d dprop-seg %d dphase-seg1 %d " "dphase-seg2 %d dsjw %d", dbt->tq, dbt->prop_seg, dbt->phase_seg1, dbt->phase_seg2, dbt->sjw); @@ -328,8 +319,7 @@ struct can_bittiming_const *dbtc = RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING_CONST]); - fprintf(f, "\n " - "%s: dtseg1 %d..%d dtseg2 %d..%d " + fprintf(f, "\n %s: dtseg1 %d..%d dtseg2 %d..%d " "dsjw 1..%d dbrp %d..%d dbrp-inc %d", dbtc->name, dbtc->tseg1_min, dbtc->tseg1_max, dbtc->tseg2_min, dbtc->tseg2_max, dbtc->sjw_max, @@ -351,8 +341,7 @@ if (xstats && RTA_PAYLOAD(xstats) == sizeof(*stats)) { stats = RTA_DATA(xstats); - fprintf(f, "\n " - "re-started bus-errors arbit-lost " + fprintf(f, "\n re-started bus-errors arbit-lost " "error-warn error-pass bus-off"); fprintf(f, "\n %-10d %-10d %-10d %-10d %-10d %-10d", stats->restarts, stats->bus_error, diff -Nru iproute2-4.3.0/ip/iplink_geneve.c iproute2-4.9.0/ip/iplink_geneve.c --- iproute2-4.3.0/ip/iplink_geneve.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_geneve.c 2016-12-12 23:07:42.000000000 +0000 @@ -18,12 +18,15 @@ static void print_explain(FILE *f) { fprintf(f, "Usage: ... geneve id VNI remote ADDR\n"); - fprintf(f, " [ ttl TTL ] [ tos TOS ]\n"); + fprintf(f, " [ ttl TTL ] [ tos TOS ] [ flowlabel LABEL ]\n"); + fprintf(f, " [ dstport PORT ] [ [no]external ]\n"); + fprintf(f, " [ [no]udpcsum ] [ [no]udp6zerocsumtx ] [ [no]udp6zerocsumrx ]\n"); fprintf(f, "\n"); - fprintf(f, "Where: VNI := 0-16777215\n"); - fprintf(f, " ADDR := IP_ADDRESS\n"); - fprintf(f, " TOS := { NUMBER | inherit }\n"); - fprintf(f, " TTL := { 1..255 | inherit }\n"); + fprintf(f, "Where: VNI := 0-16777215\n"); + fprintf(f, " ADDR := IP_ADDRESS\n"); + fprintf(f, " TOS := { NUMBER | inherit }\n"); + fprintf(f, " TTL := { 1..255 | inherit }\n"); + fprintf(f, " LABEL := 0-1048575\n"); } static void explain(void) @@ -38,8 +41,17 @@ int vni_set = 0; __u32 daddr = 0; struct in6_addr daddr6 = IN6ADDR_ANY_INIT; + __u32 label = 0; __u8 ttl = 0; __u8 tos = 0; + __u16 dstport = 0; + bool metadata = 0; + __u8 udpcsum = 0; + bool udpcsum_set = false; + __u8 udp6zerocsumtx = 0; + bool udp6zerocsumtx_set = false; + __u8 udp6zerocsumrx = 0; + bool udp6zerocsumrx_set = false; while (argc > 0) { if (!matches(*argv, "id") || @@ -55,11 +67,11 @@ fprintf(stderr, "Invalid address \"%s\"\n", *argv); return -1; } - if (IN_MULTICAST(ntohl(daddr))) + if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr))) invarg("invalid remote address", *argv); } else if (!matches(*argv, "ttl") || !matches(*argv, "hoplimit")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); if (strcmp(*argv, "inherit") != 0) { @@ -80,6 +92,41 @@ tos = uval; } else tos = 1; + } else if (!matches(*argv, "label") || + !matches(*argv, "flowlabel")) { + __u32 uval; + + NEXT_ARG(); + if (get_u32(&uval, *argv, 0) || + (uval & ~LABEL_MAX_MASK)) + invarg("invalid flowlabel", *argv); + label = htonl(uval); + } else if (!matches(*argv, "dstport")) { + NEXT_ARG(); + if (get_u16(&dstport, *argv, 0)) + invarg("dstport", *argv); + } else if (!matches(*argv, "external")) { + metadata = true; + } else if (!matches(*argv, "noexternal")) { + metadata = false; + } else if (!matches(*argv, "udpcsum")) { + udpcsum = 1; + udpcsum_set = true; + } else if (!matches(*argv, "noudpcsum")) { + udpcsum = 0; + udpcsum_set = true; + } else if (!matches(*argv, "udp6zerocsumtx")) { + udp6zerocsumtx = 1; + udp6zerocsumtx_set = true; + } else if (!matches(*argv, "noudp6zerocsumtx")) { + udp6zerocsumtx = 0; + udp6zerocsumtx_set = true; + } else if (!matches(*argv, "udp6zerocsumrx")) { + udp6zerocsumrx = 1; + udp6zerocsumrx_set = true; + } else if (!matches(*argv, "noudp6zerocsumrx")) { + udp6zerocsumrx = 0; + udp6zerocsumrx_set = true; } else if (matches(*argv, "help") == 0) { explain(); return -1; @@ -91,25 +138,42 @@ argc--, argv++; } - if (!vni_set) { - fprintf(stderr, "geneve: missing virtual network identifier\n"); + if (metadata && vni_set) { + fprintf(stderr, "geneve: both 'external' and vni cannot be specified\n"); return -1; } - if (!daddr) { - fprintf(stderr, "geneve: remove link partner not specified\n"); - return -1; - } - if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0) { - fprintf(stderr, "geneve: remove link over IPv6 not supported\n"); - return -1; + if (!metadata) { + /* parameter checking make sense only for full geneve tunnels */ + if (!vni_set) { + fprintf(stderr, "geneve: missing virtual network identifier\n"); + return -1; + } + + if (!daddr && IN6_IS_ADDR_UNSPECIFIED(&daddr6)) { + fprintf(stderr, "geneve: remote link partner not specified\n"); + return -1; + } } addattr32(n, 1024, IFLA_GENEVE_ID, vni); if (daddr) addattr_l(n, 1024, IFLA_GENEVE_REMOTE, &daddr, 4); + if (!IN6_IS_ADDR_UNSPECIFIED(&daddr6)) + addattr_l(n, 1024, IFLA_GENEVE_REMOTE6, &daddr6, sizeof(struct in6_addr)); + addattr32(n, 1024, IFLA_GENEVE_LABEL, label); addattr8(n, 1024, IFLA_GENEVE_TTL, ttl); addattr8(n, 1024, IFLA_GENEVE_TOS, tos); + if (dstport) + addattr16(n, 1024, IFLA_GENEVE_PORT, htons(dstport)); + if (metadata) + addattr(n, 1024, IFLA_GENEVE_COLLECT_METADATA); + if (udpcsum_set) + addattr8(n, 1024, IFLA_GENEVE_UDP_CSUM, udpcsum); + if (udp6zerocsumtx_set) + addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, udp6zerocsumtx); + if (udp6zerocsumrx_set) + addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, udp6zerocsumrx); return 0; } @@ -117,7 +181,6 @@ static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { __u32 vni; - char s1[1024]; __u8 tos; if (!tb) @@ -132,13 +195,24 @@ if (tb[IFLA_GENEVE_REMOTE]) { __be32 addr = rta_getattr_u32(tb[IFLA_GENEVE_REMOTE]); + if (addr) fprintf(f, "remote %s ", - format_host(AF_INET, 4, &addr, s1, sizeof(s1))); + format_host(AF_INET, 4, &addr)); + } else if (tb[IFLA_GENEVE_REMOTE6]) { + struct in6_addr addr; + + memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr)); + if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) { + if (!IN6_IS_ADDR_MULTICAST(&addr)) + fprintf(f, "remote %s ", + format_host(AF_INET6, sizeof(struct in6_addr), &addr)); + } } if (tb[IFLA_GENEVE_TTL]) { __u8 ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]); + if (ttl) fprintf(f, "ttl %d ", ttl); } @@ -150,6 +224,38 @@ else fprintf(f, "tos %#x ", tos); } + + if (tb[IFLA_GENEVE_LABEL]) { + __u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]); + + if (label) + fprintf(f, "flowlabel %#x ", ntohl(label)); + } + + if (tb[IFLA_GENEVE_PORT]) + fprintf(f, "dstport %u ", + ntohs(rta_getattr_u16(tb[IFLA_GENEVE_PORT]))); + + if (tb[IFLA_GENEVE_COLLECT_METADATA]) + fputs("external ", f); + + if (tb[IFLA_GENEVE_UDP_CSUM]) { + if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])) + fputs("no", f); + fputs("udpcsum ", f); + } + + if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { + if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) + fputs("no", f); + fputs("udp6zerocsumtx ", f); + } + + if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { + if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) + fputs("no", f); + fputs("udp6zerocsumrx ", f); + } } static void geneve_print_help(struct link_util *lu, int argc, char **argv, diff -Nru iproute2-4.3.0/ip/iplink_hsr.c iproute2-4.9.0/ip/iplink_hsr.c --- iproute2-4.3.0/ip/iplink_hsr.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_hsr.c 2016-12-12 23:07:42.000000000 +0000 @@ -25,7 +25,7 @@ { fprintf(f, "Usage:\tip link add name NAME type hsr slave1 SLAVE1-IF slave2 SLAVE2-IF\n" -"\t[ supervision ADDR-BYTE ]\n" +"\t[ supervision ADDR-BYTE ] [version VERSION]\n" "\n" "NAME\n" " name of new hsr device (e.g. hsr0)\n" @@ -33,7 +33,9 @@ " the two slave devices bound to the HSR device\n" "ADDR-BYTE\n" " 0-255; the last byte of the multicast address used for HSR supervision\n" -" frames (default = 0)\n"); +" frames (default = 0)\n" +"VERSION\n" +" 0,1; the protocol version to be used. (default = 0)\n"); } static void usage(void) @@ -46,6 +48,7 @@ { int ifindex; unsigned char multicast_spec; + unsigned char protocol_version; while (argc > 0) { if (matches(*argv, "supervision") == 0) { @@ -54,6 +57,13 @@ invarg("ADDR-BYTE is invalid", *argv); addattr_l(n, 1024, IFLA_HSR_MULTICAST_SPEC, &multicast_spec, 1); + } else if (matches(*argv, "version") == 0) { + NEXT_ARG(); + if (!(get_u8(&protocol_version, *argv, 0) == 0 || + get_u8(&protocol_version, *argv, 0) == 1)) + invarg("version is invalid", *argv); + addattr_l(n, 1024, IFLA_HSR_VERSION, + &protocol_version, 1); } else if (matches(*argv, "slave1") == 0) { NEXT_ARG(); ifindex = ll_name_to_index(*argv); diff -Nru iproute2-4.3.0/ip/iplink_ipoib.c iproute2-4.9.0/ip/iplink_ipoib.c --- iproute2-4.3.0/ip/iplink_ipoib.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_ipoib.c 2016-12-12 23:07:42.000000000 +0000 @@ -22,8 +22,7 @@ static void print_explain(FILE *f) { fprintf(f, - "Usage: ... ipoib [pkey PKEY] [mode {datagram | connected}]" - "[umcast {0|1}]\n" + "Usage: ... ipoib [pkey PKEY] [mode {datagram | connected}][umcast {0|1}]\n" "\n" "PKEY := 0x8001-0xffff\n" ); @@ -36,8 +35,7 @@ static int mode_arg(void) { - fprintf(stderr, "Error: argument of \"mode\" must be \"datagram\"" - "or \"connected\"\n"); + fprintf(stderr, "Error: argument of \"mode\" must be \"datagram\"or \"connected\"\n"); return -1; } diff -Nru iproute2-4.3.0/ip/iplink_ipvlan.c iproute2-4.9.0/ip/iplink_ipvlan.c --- iproute2-4.3.0/ip/iplink_ipvlan.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_ipvlan.c 2016-12-12 23:07:42.000000000 +0000 @@ -20,19 +20,7 @@ static void ipvlan_explain(FILE *f) { - fprintf(f, "Usage: ... ipvlan [ mode { l2 | l3 } ]\n"); -} - -static void explain(void) -{ - ipvlan_explain(stderr); -} - -static int mode_arg(void) -{ - fprintf(stderr, "Error: argument of \"mode\" must be either \"l2\", " - "or \"l3\"\n"); - return -1; + fprintf(f, "Usage: ... ipvlan [ mode { l2 | l3 | l3s } ]\n"); } static int ipvlan_parse_opt(struct link_util *lu, int argc, char **argv, @@ -41,26 +29,31 @@ while (argc > 0) { if (matches(*argv, "mode") == 0) { __u16 mode = 0; + NEXT_ARG(); if (strcmp(*argv, "l2") == 0) mode = IPVLAN_MODE_L2; else if (strcmp(*argv, "l3") == 0) mode = IPVLAN_MODE_L3; - else - return mode_arg(); - + else if (strcmp(*argv, "l3s") == 0) + mode = IPVLAN_MODE_L3S; + else { + fprintf(stderr, "Error: argument of \"mode\" must be either \"l2\", \"l3\" or \"l3s\"\n"); + return -1; + } addattr16(n, 1024, IFLA_IPVLAN_MODE, mode); } else if (matches(*argv, "help") == 0) { - explain(); + ipvlan_explain(stderr); return -1; } else { fprintf(stderr, "ipvlan: unknown option \"%s\"?\n", *argv); - explain(); + ipvlan_explain(stderr); return -1; } - argc--, argv++; + argc--; + argv++; } return 0; @@ -78,7 +71,8 @@ fprintf(f, " mode %s ", mode == IPVLAN_MODE_L2 ? "l2" : - mode == IPVLAN_MODE_L3 ? "l3" : "unknown"); + mode == IPVLAN_MODE_L3 ? "l3" : + mode == IPVLAN_MODE_L3S ? "l3s" : "unknown"); } } } diff -Nru iproute2-4.3.0/ip/iplink_macvlan.c iproute2-4.9.0/ip/iplink_macvlan.c --- iproute2-4.3.0/ip/iplink_macvlan.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_macvlan.c 2016-12-12 23:07:42.000000000 +0000 @@ -15,6 +15,7 @@ #include #include #include +#include #include "rt_names.h" #include "utils.h" @@ -29,7 +30,11 @@ static void print_explain(struct link_util *lu, FILE *f) { fprintf(f, - "Usage: ... %s mode { private | vepa | bridge | passthru [nopromisc] }\n", + "Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS\n" + "MODE: private | vepa | bridge | passthru | source\n" + "MODE_FLAG: null | nopromisc\n" + "MODE_OPTS: for mode \"source\":\n" + "\tmacaddr { { add | del } | set [ [ ... ] ] | flush }\n", lu->id ); } @@ -39,11 +44,21 @@ print_explain(lu, stderr); } + static int mode_arg(const char *arg) { - fprintf(stderr, "Error: argument of \"mode\" must be \"private\", " - "\"vepa\", \"bridge\" or \"passthru\", not \"%s\"\n", arg); - return -1; + fprintf(stderr, + "Error: argument of \"mode\" must be \"private\", \"vepa\", \"bridge\", \"passthru\" or \"source\", not \"%s\"\n", + arg); + return -1; +} + +static int flag_arg(const char *arg) +{ + fprintf(stderr, + "Error: argument of \"flag\" must be \"nopromisc\" or \"null\", not \"%s\"\n", + arg); + return -1; } static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv, @@ -51,6 +66,10 @@ { __u32 mode = 0; __u16 flags = 0; + __u32 mac_mode = 0; + int has_flags = 0; + char mac[ETH_ALEN]; + struct rtattr *nmac; while (argc > 0) { if (matches(*argv, "mode") == 0) { @@ -64,10 +83,72 @@ mode = MACVLAN_MODE_BRIDGE; else if (strcmp(*argv, "passthru") == 0) mode = MACVLAN_MODE_PASSTHRU; + else if (strcmp(*argv, "source") == 0) + mode = MACVLAN_MODE_SOURCE; else return mode_arg(*argv); + } else if (matches(*argv, "flag") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "nopromisc") == 0) + flags |= MACVLAN_FLAG_NOPROMISC; + else if (strcmp(*argv, "null") == 0) + flags |= 0; + else + return flag_arg(*argv); + + has_flags = 1; + + } else if (matches(*argv, "macaddr") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "add") == 0) { + mac_mode = MACVLAN_MACADDR_ADD; + } else if (strcmp(*argv, "del") == 0) { + mac_mode = MACVLAN_MACADDR_DEL; + } else if (strcmp(*argv, "set") == 0) { + mac_mode = MACVLAN_MACADDR_SET; + } else if (strcmp(*argv, "flush") == 0) { + mac_mode = MACVLAN_MACADDR_FLUSH; + } else { + explain(lu); + return -1; + } + + addattr32(n, 1024, IFLA_MACVLAN_MACADDR_MODE, mac_mode); + + if (mac_mode == MACVLAN_MACADDR_ADD || + mac_mode == MACVLAN_MACADDR_DEL) { + NEXT_ARG(); + + if (ll_addr_a2n(mac, sizeof(mac), + *argv) != ETH_ALEN) + return -1; + + addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, &mac, + ETH_ALEN); + } + + if (mac_mode == MACVLAN_MACADDR_SET) { + nmac = addattr_nest(n, 1024, + IFLA_MACVLAN_MACADDR_DATA); + while (NEXT_ARG_OK()) { + NEXT_ARG_FWD(); + + if (ll_addr_a2n(mac, sizeof(mac), + *argv) != ETH_ALEN) { + PREV_ARG(); + break; + } + + addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, + &mac, ETH_ALEN); + } + addattr_nest_end(n, nmac); + } } else if (matches(*argv, "nopromisc") == 0) { flags |= MACVLAN_FLAG_NOPROMISC; + has_flags = 1; } else if (matches(*argv, "help") == 0) { explain(lu); return -1; @@ -82,7 +163,7 @@ if (mode) addattr32(n, 1024, IFLA_MACVLAN_MODE, mode); - if (flags) { + if (has_flags) { if (flags & MACVLAN_FLAG_NOPROMISC && mode != MACVLAN_MODE_PASSTHRU) { pfx_err(lu, "nopromisc flag only valid in passthru mode"); @@ -98,6 +179,10 @@ { __u32 mode; __u16 flags; + __u32 count; + unsigned char *addr; + int len; + struct rtattr *rta; if (!tb) return; @@ -107,20 +192,49 @@ return; mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]); - fprintf(f, " mode %s ", + fprintf(f, "mode %s ", mode == MACVLAN_MODE_PRIVATE ? "private" : mode == MACVLAN_MODE_VEPA ? "vepa" : mode == MACVLAN_MODE_BRIDGE ? "bridge" : mode == MACVLAN_MODE_PASSTHRU ? "passthru" + : mode == MACVLAN_MODE_SOURCE ? "source" : "unknown"); if (!tb[IFLA_MACVLAN_FLAGS] || RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16)) - return; + flags = 0; + else + flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]); - flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]); if (flags & MACVLAN_FLAG_NOPROMISC) fprintf(f, "nopromisc "); + + /* in source mode, there are more options to print */ + + if (mode != MACVLAN_MODE_SOURCE) + return; + + if (!tb[IFLA_MACVLAN_MACADDR_COUNT] || + RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_COUNT]) < sizeof(__u32)) + return; + + count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]); + fprintf(f, "remotes (%d) ", count); + + if (!tb[IFLA_MACVLAN_MACADDR_DATA]) + return; + + rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]); + len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]); + + for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + if (rta->rta_type != IFLA_MACVLAN_MACADDR || + RTA_PAYLOAD(rta) < 6) + continue; + addr = RTA_DATA(rta); + fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0], + addr[1], addr[2], addr[3], addr[4], addr[5]); + } } static void macvlan_print_help(struct link_util *lu, int argc, char **argv, diff -Nru iproute2-4.3.0/ip/iplink_vlan.c iproute2-4.9.0/ip/iplink_vlan.c --- iproute2-4.3.0/ip/iplink_vlan.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_vlan.c 2016-12-12 23:07:42.000000000 +0000 @@ -21,8 +21,7 @@ static void print_explain(FILE *f) { fprintf(f, - "Usage: ... vlan [ protocol VLANPROTO ] id VLANID" - " [ FLAG-LIST ]\n" + "Usage: ... vlan [ protocol VLANPROTO ] id VLANID [ FLAG-LIST ]\n" " [ ingress-qos-map QOS-MAP ] [ egress-qos-map QOS-MAP ]\n" "\n" "VLANPROTO: [ 802.1Q / 802.1ad ]\n" @@ -182,7 +181,7 @@ { fprintf(fp, "<"); #define _PF(f) if (flags & VLAN_FLAG_##f) { \ - flags &= ~ VLAN_FLAG_##f; \ + flags &= ~VLAN_FLAG_##f; \ fprintf(fp, #f "%s", flags ? "," : ""); \ } _PF(REORDER_HDR); @@ -198,6 +197,7 @@ static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { struct ifla_vlan_flags *flags; + SPRINT_BUF(b1); if (!tb) diff -Nru iproute2-4.3.0/ip/iplink_vrf.c iproute2-4.9.0/ip/iplink_vrf.c --- iproute2-4.3.0/ip/iplink_vrf.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_vrf.c 2016-12-12 23:07:42.000000000 +0000 @@ -20,7 +20,7 @@ static void vrf_explain(FILE *f) { - fprintf(f, "Usage: ... vrf table TABLEID \n"); + fprintf(f, "Usage: ... vrf table TABLEID\n"); } static void explain(void) @@ -28,12 +28,6 @@ vrf_explain(stderr); } -static int table_arg(void) -{ - fprintf(stderr,"Error: argument of \"table\" must be 0-32767 and currently unused\n"); - return -1; -} - static int vrf_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { @@ -43,9 +37,8 @@ NEXT_ARG(); - table = atoi(*argv); - if (table > 32767) - return table_arg(); + if (rtnl_rttable_a2n(&table, *argv)) + invarg("invalid table ID\n", *argv); addattr32(n, 1024, IFLA_VRF_TABLE, table); } else if (matches(*argv, "help") == 0) { explain(); @@ -71,6 +64,18 @@ fprintf(f, "table %u ", rta_getattr_u32(tb[IFLA_VRF_TABLE])); } +static void vrf_slave_print_opt(struct link_util *lu, FILE *f, + struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_VRF_PORT_TABLE]) { + fprintf(f, "table %u ", + rta_getattr_u32(tb[IFLA_VRF_PORT_TABLE])); + } +} + static void vrf_print_help(struct link_util *lu, int argc, char **argv, FILE *f) { @@ -84,3 +89,122 @@ .print_opt = vrf_print_opt, .print_help = vrf_print_help, }; + +struct link_util vrf_slave_link_util = { + .id = "vrf_slave", + .maxattr = IFLA_VRF_PORT_MAX, + .print_opt = vrf_slave_print_opt, +}; + +/* returns table id if name is a VRF device */ +__u32 ipvrf_get_table(const char *name) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req = { + .n = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_type = RTM_GETLINK, + }, + .i = { + .ifi_family = preferred_family, + }, + }; + struct { + struct nlmsghdr n; + char buf[8192]; + } answer; + struct rtattr *tb[IFLA_MAX+1]; + struct rtattr *li[IFLA_INFO_MAX+1]; + struct rtattr *vrf_attr[IFLA_VRF_MAX + 1]; + struct ifinfomsg *ifi; + __u32 tb_id = 0; + int len; + + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); + + if (rtnl_talk(&rth, &req.n, &answer.n, sizeof(answer)) < 0) + return 0; + + ifi = NLMSG_DATA(&answer.n); + len = answer.n.nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) { + fprintf(stderr, "BUG: Invalid response to link query.\n"); + return 0; + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + if (!tb[IFLA_LINKINFO]) + return 0; + + parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!li[IFLA_INFO_KIND] || !li[IFLA_INFO_DATA]) + return 0; + + if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf")) + return 0; + + parse_rtattr_nested(vrf_attr, IFLA_VRF_MAX, li[IFLA_INFO_DATA]); + if (vrf_attr[IFLA_VRF_TABLE]) + tb_id = rta_getattr_u32(vrf_attr[IFLA_VRF_TABLE]); + + if (!tb_id) + fprintf(stderr, "BUG: VRF %s is missing table id\n", name); + + return tb_id; +} + +bool name_is_vrf(const char *name) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req = { + .n = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_type = RTM_GETLINK, + }, + .i = { + .ifi_family = preferred_family, + }, + }; + struct { + struct nlmsghdr n; + char buf[8192]; + } answer; + struct rtattr *tb[IFLA_MAX+1]; + struct rtattr *li[IFLA_INFO_MAX+1]; + struct ifinfomsg *ifi; + int len; + + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); + + if (rtnl_talk(&rth, &req.n, &answer.n, sizeof(answer)) < 0) + return false; + + ifi = NLMSG_DATA(&answer.n); + len = answer.n.nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) { + fprintf(stderr, "BUG: Invalid response to link query.\n"); + return false; + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + if (!tb[IFLA_LINKINFO]) + return false; + + parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!li[IFLA_INFO_KIND]) + return false; + + return strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf") == 0; +} diff -Nru iproute2-4.3.0/ip/iplink_vxlan.c iproute2-4.9.0/ip/iplink_vxlan.c --- iproute2-4.3.0/ip/iplink_vxlan.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iplink_vxlan.c 2016-12-12 23:07:42.000000000 +0000 @@ -23,19 +23,21 @@ static void print_explain(FILE *f) { - fprintf(f, "Usage: ... vxlan id VNI [ { group | remote } ADDR ] [ local ADDR ]\n"); - fprintf(f, " [ ttl TTL ] [ tos TOS ] [ dev PHYS_DEV ]\n"); + fprintf(f, "Usage: ... vxlan id VNI [ { group | remote } IP_ADDRESS ] [ local ADDR ]\n"); + fprintf(f, " [ ttl TTL ] [ tos TOS ] [ flowlabel LABEL ] [ dev PHYS_DEV ]\n"); fprintf(f, " [ dstport PORT ] [ srcport MIN MAX ]\n"); fprintf(f, " [ [no]learning ] [ [no]proxy ] [ [no]rsc ]\n"); fprintf(f, " [ [no]l2miss ] [ [no]l3miss ]\n"); fprintf(f, " [ ageing SECONDS ] [ maxaddress NUMBER ]\n"); fprintf(f, " [ [no]udpcsum ] [ [no]udp6zerocsumtx ] [ [no]udp6zerocsumrx ]\n"); - fprintf(f, " [ gbp ]\n"); + fprintf(f, " [ [no]remcsumtx ] [ [no]remcsumrx ]\n"); + fprintf(f, " [ [no]external ] [ gbp ] [ gpe ]\n"); fprintf(f, "\n"); - fprintf(f, "Where: VNI := 0-16777215\n"); - fprintf(f, " ADDR := { IP_ADDRESS | any }\n"); - fprintf(f, " TOS := { NUMBER | inherit }\n"); - fprintf(f, " TTL := { 1..255 | inherit }\n"); + fprintf(f, "Where: VNI := 0-16777215\n"); + fprintf(f, " ADDR := { IP_ADDRESS | any }\n"); + fprintf(f, " TOS := { NUMBER | inherit }\n"); + fprintf(f, " TTL := { 1..255 | inherit }\n"); + fprintf(f, " LABEL := 0-1048575\n"); } static void explain(void) @@ -54,9 +56,10 @@ struct in6_addr saddr6 = IN6ADDR_ANY_INIT; struct in6_addr gaddr6 = IN6ADDR_ANY_INIT; struct in6_addr daddr6 = IN6ADDR_ANY_INIT; - unsigned link = 0; + unsigned int link = 0; __u8 tos = 0; __u8 ttl = 0; + __u32 label = 0; __u8 learning = 1; __u8 proxy = 0; __u8 rsc = 0; @@ -67,9 +70,16 @@ __u32 maxaddr = 0; __u16 dstport = 0; __u8 udpcsum = 0; + bool udpcsum_set = false; __u8 udp6zerocsumtx = 0; + bool udp6zerocsumtx_set = false; __u8 udp6zerocsumrx = 0; + bool udp6zerocsumrx_set = false; + __u8 remcsumtx = 0; + __u8 remcsumrx = 0; + __u8 metadata = 0; __u8 gbp = 0; + __u8 gpe = 0; int dst_port_set = 0; struct ifla_vxlan_port_range range = { 0, 0 }; @@ -118,7 +128,7 @@ } } else if (!matches(*argv, "ttl") || !matches(*argv, "hoplimit")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); if (strcmp(*argv, "inherit") != 0) { @@ -139,6 +149,15 @@ tos = uval; } else tos = 1; + } else if (!matches(*argv, "label") || + !matches(*argv, "flowlabel")) { + __u32 uval; + + NEXT_ARG(); + if (get_u32(&uval, *argv, 0) || + (uval & ~LABEL_MAX_MASK)) + invarg("invalid flowlabel", *argv); + label = htonl(uval); } else if (!matches(*argv, "ageing")) { NEXT_ARG(); if (strcmp(*argv, "none") == 0) @@ -153,16 +172,13 @@ invarg("max addresses", *argv); } else if (!matches(*argv, "port") || !matches(*argv, "srcport")) { - __u16 minport, maxport; NEXT_ARG(); - if (get_u16(&minport, *argv, 0)) + if (get_be16(&range.low, *argv, 0)) invarg("min port", *argv); NEXT_ARG(); - if (get_u16(&maxport, *argv, 0)) + if (get_be16(&range.high, *argv, 0)) invarg("max port", *argv); - range.low = htons(minport); - range.high = htons(maxport); - } else if (!matches(*argv, "dstport")){ + } else if (!matches(*argv, "dstport")) { NEXT_ARG(); if (get_u16(&dstport, *argv, 0)) invarg("dst port", *argv); @@ -189,18 +205,39 @@ l3miss = 1; } else if (!matches(*argv, "udpcsum")) { udpcsum = 1; + udpcsum_set = true; } else if (!matches(*argv, "noudpcsum")) { udpcsum = 0; + udpcsum_set = true; } else if (!matches(*argv, "udp6zerocsumtx")) { udp6zerocsumtx = 1; + udp6zerocsumtx_set = true; } else if (!matches(*argv, "noudp6zerocsumtx")) { udp6zerocsumtx = 0; + udp6zerocsumtx_set = true; } else if (!matches(*argv, "udp6zerocsumrx")) { udp6zerocsumrx = 1; + udp6zerocsumrx_set = true; } else if (!matches(*argv, "noudp6zerocsumrx")) { udp6zerocsumrx = 0; + udp6zerocsumrx_set = true; + } else if (!matches(*argv, "remcsumtx")) { + remcsumtx = 1; + } else if (!matches(*argv, "noremcsumtx")) { + remcsumtx = 0; + } else if (!matches(*argv, "remcsumrx")) { + remcsumrx = 1; + } else if (!matches(*argv, "noremcsumrx")) { + remcsumrx = 0; + } else if (!matches(*argv, "external")) { + metadata = 1; + learning = 0; + } else if (!matches(*argv, "noexternal")) { + metadata = 0; } else if (!matches(*argv, "gbp")) { gbp = 1; + } else if (!matches(*argv, "gpe")) { + gpe = 1; } else if (matches(*argv, "help") == 0) { explain(); return -1; @@ -212,19 +249,31 @@ argc--, argv++; } - if (!vni_set) { + if (metadata && vni_set) { + fprintf(stderr, "vxlan: both 'external' and vni cannot be specified\n"); + return -1; + } + + if (!metadata && !vni_set) { fprintf(stderr, "vxlan: missing virtual network identifier\n"); return -1; } if ((gaddr && daddr) || - (memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) && - memcmp(&daddr6, &in6addr_any, sizeof(daddr6)))) { + (!IN6_IS_ADDR_UNSPECIFIED(&gaddr6) && + !IN6_IS_ADDR_UNSPECIFIED(&daddr6))) { fprintf(stderr, "vxlan: both group and remote cannot be specified\n"); return -1; } - if (!dst_port_set) { + if ((gaddr || !IN6_IS_ADDR_UNSPECIFIED(&gaddr6)) && !link) { + fprintf(stderr, "vxlan: 'group' requires 'dev' to be specified\n"); + return -1; + } + + if (!dst_port_set && gpe) { + dstport = 4790; + } else if (!dst_port_set) { fprintf(stderr, "vxlan: destination port not specified\n" "Will use Linux kernel default (non-standard value)\n"); fprintf(stderr, @@ -237,18 +286,19 @@ addattr_l(n, 1024, IFLA_VXLAN_GROUP, &gaddr, 4); else if (daddr) addattr_l(n, 1024, IFLA_VXLAN_GROUP, &daddr, 4); - if (memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) != 0) + if (!IN6_IS_ADDR_UNSPECIFIED(&gaddr6)) addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &gaddr6, sizeof(struct in6_addr)); - else if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0) + else if (!IN6_IS_ADDR_UNSPECIFIED(&daddr6)) addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &daddr6, sizeof(struct in6_addr)); if (saddr) addattr_l(n, 1024, IFLA_VXLAN_LOCAL, &saddr, 4); - else if (memcmp(&saddr6, &in6addr_any, sizeof(saddr6)) != 0) + else if (!IN6_IS_ADDR_UNSPECIFIED(&saddr6)) addattr_l(n, 1024, IFLA_VXLAN_LOCAL6, &saddr6, sizeof(struct in6_addr)); if (link) addattr32(n, 1024, IFLA_VXLAN_LINK, link); + addattr32(n, 1024, IFLA_VXLAN_LABEL, label); addattr8(n, 1024, IFLA_VXLAN_TTL, ttl); addattr8(n, 1024, IFLA_VXLAN_TOS, tos); addattr8(n, 1024, IFLA_VXLAN_LEARNING, learning); @@ -256,10 +306,16 @@ addattr8(n, 1024, IFLA_VXLAN_RSC, rsc); addattr8(n, 1024, IFLA_VXLAN_L2MISS, l2miss); addattr8(n, 1024, IFLA_VXLAN_L3MISS, l3miss); - addattr8(n, 1024, IFLA_VXLAN_UDP_CSUM, udpcsum); - addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, udp6zerocsumtx); - addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, udp6zerocsumrx); - + addattr8(n, 1024, IFLA_VXLAN_REMCSUM_TX, remcsumtx); + addattr8(n, 1024, IFLA_VXLAN_REMCSUM_RX, remcsumrx); + addattr8(n, 1024, IFLA_VXLAN_COLLECT_METADATA, metadata); + + if (udpcsum_set) + addattr8(n, 1024, IFLA_VXLAN_UDP_CSUM, udpcsum); + if (udp6zerocsumtx_set) + addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, udp6zerocsumtx); + if (udp6zerocsumrx_set) + addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, udp6zerocsumrx); if (noage) addattr32(n, 1024, IFLA_VXLAN_AGEING, 0); else if (age) @@ -274,6 +330,8 @@ if (gbp) addattr_l(n, 1024, IFLA_VXLAN_GBP, NULL, 0); + if (gpe) + addattr_l(n, 1024, IFLA_VXLAN_GPE, NULL, 0); return 0; @@ -282,10 +340,9 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { __u32 vni; - unsigned link; + unsigned int link; __u8 tos; __u32 maxaddr; - char s1[1024]; char s2[64]; if (!tb) @@ -300,38 +357,42 @@ if (tb[IFLA_VXLAN_GROUP]) { __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_GROUP]); + if (addr) { if (IN_MULTICAST(ntohl(addr))) fprintf(f, "group %s ", - format_host(AF_INET, 4, &addr, s1, sizeof(s1))); + format_host(AF_INET, 4, &addr)); else fprintf(f, "remote %s ", - format_host(AF_INET, 4, &addr, s1, sizeof(s1))); + format_host(AF_INET, 4, &addr)); } } else if (tb[IFLA_VXLAN_GROUP6]) { struct in6_addr addr; + memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_GROUP6]), sizeof(struct in6_addr)); - if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) { + if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) { if (IN6_IS_ADDR_MULTICAST(&addr)) fprintf(f, "group %s ", - format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1))); + format_host(AF_INET6, sizeof(struct in6_addr), &addr)); else fprintf(f, "remote %s ", - format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1))); + format_host(AF_INET6, sizeof(struct in6_addr), &addr)); } } if (tb[IFLA_VXLAN_LOCAL]) { __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]); + if (addr) fprintf(f, "local %s ", - format_host(AF_INET, 4, &addr, s1, sizeof(s1))); + format_host(AF_INET, 4, &addr)); } else if (tb[IFLA_VXLAN_LOCAL6]) { struct in6_addr addr; + memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_LOCAL6]), sizeof(struct in6_addr)); - if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) + if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) fprintf(f, "local %s ", - format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1))); + format_host(AF_INET6, sizeof(struct in6_addr), &addr)); } if (tb[IFLA_VXLAN_LINK] && @@ -380,12 +441,21 @@ if (tb[IFLA_VXLAN_TTL]) { __u8 ttl = rta_getattr_u8(tb[IFLA_VXLAN_TTL]); + if (ttl) fprintf(f, "ttl %d ", ttl); } + if (tb[IFLA_VXLAN_LABEL]) { + __u32 label = rta_getattr_u32(tb[IFLA_VXLAN_LABEL]); + + if (label) + fprintf(f, "flowlabel %#x ", ntohl(label)); + } + if (tb[IFLA_VXLAN_AGEING]) { __u32 age = rta_getattr_u32(tb[IFLA_VXLAN_AGEING]); + if (age == 0) fprintf(f, "ageing none "); else @@ -396,19 +466,40 @@ ((maxaddr = rta_getattr_u32(tb[IFLA_VXLAN_LIMIT])) != 0)) fprintf(f, "maxaddr %u ", maxaddr); - if (tb[IFLA_VXLAN_UDP_CSUM] && rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM])) + if (tb[IFLA_VXLAN_UDP_CSUM]) { + if (!rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM])) + fputs("no", f); fputs("udpcsum ", f); + } - if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] && - rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX])) + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) { + if (!rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX])) + fputs("no", f); fputs("udp6zerocsumtx ", f); + } - if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] && - rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])) + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) { + if (!rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])) + fputs("no", f); fputs("udp6zerocsumrx ", f); + } + + if (tb[IFLA_VXLAN_REMCSUM_TX] && + rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_TX])) + fputs("remcsumtx ", f); + + if (tb[IFLA_VXLAN_REMCSUM_RX] && + rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_RX])) + fputs("remcsumrx ", f); + + if (tb[IFLA_VXLAN_COLLECT_METADATA] && + rta_getattr_u8(tb[IFLA_VXLAN_COLLECT_METADATA])) + fputs("external ", f); if (tb[IFLA_VXLAN_GBP]) fputs("gbp ", f); + if (tb[IFLA_VXLAN_GPE]) + fputs("gpe ", f); } static void vxlan_print_help(struct link_util *lu, int argc, char **argv, diff -Nru iproute2-4.3.0/ip/ipmacsec.c iproute2-4.9.0/ip/ipmacsec.c --- iproute2-4.3.0/ip/ipmacsec.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/ip/ipmacsec.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,1268 @@ +/* + * ipmacsec.c "ip macsec". + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Sabrina Dubroca + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "ll_map.h" +#include "libgenl.h" + +static const char *values_on_off[] = { "off", "on" }; + +static const char *VALIDATE_STR[] = { + [MACSEC_VALIDATE_DISABLED] = "disabled", + [MACSEC_VALIDATE_CHECK] = "check", + [MACSEC_VALIDATE_STRICT] = "strict", +}; + +struct sci { + __u64 sci; + __u16 port; + char abuf[6]; +}; + +struct sa_desc { + __u8 an; + __u32 pn; + __u8 key_id[MACSEC_KEYID_LEN]; + __u32 key_len; + __u8 key[MACSEC_MAX_KEY_LEN]; + __u8 active; +}; + +struct cipher_args { + __u64 id; + __u8 icv_len; +}; + +struct txsc_desc { + int ifindex; + __u64 sci; + __be16 port; + struct cipher_args cipher; + __u32 window; + enum macsec_validation_type validate; + __u8 encoding_sa; +}; + +struct rxsc_desc { + int ifindex; + __u64 sci; + __u8 active; +}; + +#define MACSEC_BUFLEN 1024 + + +/* netlink socket */ +static struct rtnl_handle genl_rth; +static int genl_family = -1; + +#define MACSEC_GENL_REQ(_req, _bufsiz, _cmd, _flags) \ + GENL_REQUEST(_req, _bufsiz, genl_family, 0, MACSEC_GENL_VERSION, \ + _cmd, _flags) + + +static void ipmacsec_usage(void) +{ + fprintf(stderr, "Usage: ip macsec add DEV tx sa { 0..3 } [ OPTS ] key ID KEY\n"); + fprintf(stderr, " ip macsec set DEV tx sa { 0..3 } [ OPTS ]\n"); + fprintf(stderr, " ip macsec del DEV tx sa { 0..3 }\n"); + fprintf(stderr, " ip macsec add DEV rx SCI [ on | off ]\n"); + fprintf(stderr, " ip macsec set DEV rx SCI [ on | off ]\n"); + fprintf(stderr, " ip macsec del DEV rx SCI\n"); + fprintf(stderr, " ip macsec add DEV rx SCI sa { 0..3 } [ OPTS ] key ID KEY\n"); + fprintf(stderr, " ip macsec set DEV rx SCI sa { 0..3 } [ OPTS ]\n"); + fprintf(stderr, " ip macsec del DEV rx SCI sa { 0..3 }\n"); + fprintf(stderr, " ip macsec show\n"); + fprintf(stderr, " ip macsec show DEV\n"); + fprintf(stderr, "where OPTS := [ pn ] [ on | off ]\n"); + fprintf(stderr, " ID := 128-bit hex string\n"); + fprintf(stderr, " KEY := 128-bit hex string\n"); + fprintf(stderr, " SCI := { sci | port { 1..2^16-1 } address }\n"); + + exit(-1); +} + +static int one_of(const char *msg, const char *realval, const char **list, + size_t len, int *index) +{ + int i; + + for (i = 0; i < len; i++) { + if (matches(realval, list[i]) == 0) { + *index = i; + return 0; + } + } + + fprintf(stderr, "Error: argument of \"%s\" must be one of ", msg); + for (i = 0; i < len; i++) + fprintf(stderr, "\"%s\", ", list[i]); + fprintf(stderr, "not \"%s\"\n", realval); + return -1; +} + +static int get_an(__u8 *val, const char *arg) +{ + int ret = get_u8(val, arg, 0); + + if (ret) + return ret; + + if (*val > 3) + return -1; + + return 0; +} + +static int get_sci(__u64 *sci, const char *arg) +{ + return get_be64(sci, arg, 16); +} + +static int get_port(__be16 *port, const char *arg) +{ + return get_be16(port, arg, 0); +} + +#define _STR(a) #a +#define STR(a) _STR(a) + +static void get_icvlen(__u8 *icvlen, char *arg) +{ + int ret = get_u8(icvlen, arg, 10); + + if (ret) + invarg("expected ICV length", arg); + + if (*icvlen < MACSEC_MIN_ICV_LEN || *icvlen > MACSEC_STD_ICV_LEN) + invarg("ICV length must be in the range {" + STR(MACSEC_MIN_ICV_LEN) ".." STR(MACSEC_STD_ICV_LEN) + "}", arg); +} + +static bool get_sa(int *argcp, char ***argvp, __u8 *an) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + + if (argc <= 0 || strcmp(*argv, "sa") != 0) + return false; + + NEXT_ARG(); + ret = get_an(an, *argv); + if (ret) + invarg("expected an { 0..3 }", *argv); + argc--; argv++; + + *argvp = argv; + *argcp = argc; + return true; +} + +static int parse_sa_args(int *argcp, char ***argvp, struct sa_desc *sa) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + bool active_set = false; + + while (argc > 0) { + if (strcmp(*argv, "pn") == 0) { + if (sa->pn != 0) + duparg2("pn", "pn"); + NEXT_ARG(); + ret = get_u32(&sa->pn, *argv, 0); + if (ret) + invarg("expected pn", *argv); + if (sa->pn == 0) + invarg("expected pn != 0", *argv); + } else if (strcmp(*argv, "key") == 0) { + unsigned int len; + + NEXT_ARG(); + if (!hexstring_a2n(*argv, sa->key_id, MACSEC_KEYID_LEN, + &len)) + invarg("expected key id", *argv); + NEXT_ARG(); + if (!hexstring_a2n(*argv, sa->key, MACSEC_MAX_KEY_LEN, + &sa->key_len)) + invarg("expected key", *argv); + } else if (strcmp(*argv, "on") == 0) { + if (active_set) + duparg2("on/off", "on"); + sa->active = true; + active_set = true; + } else if (strcmp(*argv, "off") == 0) { + if (active_set) + duparg2("on/off", "off"); + sa->active = false; + active_set = true; + } else { + fprintf(stderr, "macsec: unknown command \"%s\"?\n", + *argv); + ipmacsec_usage(); + } + + argv++; argc--; + } + + *argvp = argv; + *argcp = argc; + return 0; +} + +static __u64 make_sci(char *addr, __be16 port) +{ + __u64 sci; + + memcpy(&sci, addr, ETH_ALEN); + memcpy(((char *)&sci) + ETH_ALEN, &port, sizeof(port)); + + return sci; +} + +static bool sci_complete(bool sci, bool port, bool addr, bool port_only) +{ + return sci || (port && (addr || port_only)); +} + +static int get_sci_portaddr(struct sci *sci, int *argcp, char ***argvp, + bool port_only, bool optional) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + bool p = false, a = false, s = false; + + while (argc > 0) { + if (strcmp(*argv, "sci") == 0) { + if (p) + invarg("expected address", *argv); + if (a) + invarg("expected port", *argv); + NEXT_ARG(); + ret = get_sci(&sci->sci, *argv); + if (ret) + invarg("expected sci", *argv); + s = true; + } else if (strcmp(*argv, "port") == 0) { + NEXT_ARG(); + ret = get_port(&sci->port, *argv); + if (ret) + invarg("expected port", *argv); + if (sci->port == 0) + invarg("expected port != 0", *argv); + p = true; + } else if (strcmp(*argv, "address") == 0) { + NEXT_ARG(); + ret = ll_addr_a2n(sci->abuf, sizeof(sci->abuf), *argv); + if (ret < 0) + invarg("expected lladdr", *argv); + a = true; + } else if (optional) { + break; + } else { + invarg("expected sci, port, or address", *argv); + } + + argv++; argc--; + + if (sci_complete(s, p, a, port_only)) + break; + } + + if (!optional && !sci_complete(s, p, a, port_only)) + return -1; + + if (p && a) + sci->sci = make_sci(sci->abuf, sci->port); + + *argvp = argv; + *argcp = argc; + + return p || a || s; +} + +static bool parse_rxsci(int *argcp, char ***argvp, struct rxsc_desc *rxsc, + struct sa_desc *rxsa) +{ + struct sci sci = { 0 }; + + if (*argcp == 0 || + get_sci_portaddr(&sci, argcp, argvp, false, false) < 0) { + fprintf(stderr, "expected sci\n"); + ipmacsec_usage(); + } + + rxsc->sci = sci.sci; + + return get_sa(argcp, argvp, &rxsa->an); +} + +static int parse_rxsci_args(int *argcp, char ***argvp, struct rxsc_desc *rxsc) +{ + int argc = *argcp; + char **argv = *argvp; + bool active_set = false; + + while (argc > 0) { + if (strcmp(*argv, "on") == 0) { + if (active_set) + duparg2("on/off", "on"); + rxsc->active = true; + active_set = true; + } else if (strcmp(*argv, "off") == 0) { + if (active_set) + duparg2("on/off", "off"); + rxsc->active = false; + active_set = true; + } else { + fprintf(stderr, "macsec: unknown command \"%s\"?\n", + *argv); + ipmacsec_usage(); + } + + argv++; argc--; + } + + *argvp = argv; + *argcp = argc; + return 0; +} + +enum cmd { + CMD_ADD, + CMD_DEL, + CMD_UPD, + __CMD_MAX +}; + +static const enum macsec_nl_commands macsec_commands[__CMD_MAX][2][2] = { + [CMD_ADD] = { + [0] = {-1, MACSEC_CMD_ADD_RXSC}, + [1] = {MACSEC_CMD_ADD_TXSA, MACSEC_CMD_ADD_RXSA}, + }, + [CMD_UPD] = { + [0] = {-1, MACSEC_CMD_UPD_RXSC}, + [1] = {MACSEC_CMD_UPD_TXSA, MACSEC_CMD_UPD_RXSA}, + }, + [CMD_DEL] = { + [0] = {-1, MACSEC_CMD_DEL_RXSC}, + [1] = {MACSEC_CMD_DEL_TXSA, MACSEC_CMD_DEL_RXSA}, + }, +}; + +static int do_modify_nl(enum cmd c, enum macsec_nl_commands cmd, int ifindex, + struct rxsc_desc *rxsc, struct sa_desc *sa) +{ + struct rtattr *attr_sa; + + MACSEC_GENL_REQ(req, MACSEC_BUFLEN, cmd, NLM_F_REQUEST); + + addattr32(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_IFINDEX, ifindex); + if (rxsc) { + struct rtattr *attr_rxsc; + + attr_rxsc = addattr_nest(&req.n, MACSEC_BUFLEN, + MACSEC_ATTR_RXSC_CONFIG); + addattr64(&req.n, MACSEC_BUFLEN, + MACSEC_RXSC_ATTR_SCI, rxsc->sci); + if (c != CMD_DEL && rxsc->active != 0xff) + addattr8(&req.n, MACSEC_BUFLEN, + MACSEC_RXSC_ATTR_ACTIVE, rxsc->active); + + addattr_nest_end(&req.n, attr_rxsc); + } + + if (sa->an == 0xff) + goto talk; + + attr_sa = addattr_nest(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_SA_CONFIG); + + addattr8(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_AN, sa->an); + + if (c != CMD_DEL) { + if (sa->pn) + addattr32(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_PN, + sa->pn); + + if (sa->key_len) { + addattr_l(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_KEYID, + sa->key_id, MACSEC_KEYID_LEN); + addattr_l(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_KEY, + sa->key, sa->key_len); + } + + if (sa->active != 0xff) { + addattr8(&req.n, MACSEC_BUFLEN, + MACSEC_SA_ATTR_ACTIVE, sa->active); + } + } + + addattr_nest_end(&req.n, attr_sa); + +talk: + if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0) + return -2; + + return 0; +} + +static bool check_sa_args(enum cmd c, struct sa_desc *sa) +{ + if (c == CMD_ADD) { + if (!sa->key_len) { + fprintf(stderr, "cannot create SA without key\n"); + return -1; + } + + if (sa->pn == 0) { + fprintf(stderr, "must specify a packet number != 0\n"); + return -1; + } + } else if (c == CMD_UPD) { + if (sa->key_len) { + fprintf(stderr, "cannot change key on SA\n"); + return -1; + } + } + + return 0; +} + +static int do_modify_txsa(enum cmd c, int argc, char **argv, int ifindex) +{ + struct sa_desc txsa = {0}; + enum macsec_nl_commands cmd; + + txsa.an = 0xff; + txsa.active = 0xff; + + if (argc == 0 || !get_sa(&argc, &argv, &txsa.an)) + ipmacsec_usage(); + + if (c == CMD_DEL) + goto modify; + + if (parse_sa_args(&argc, &argv, &txsa)) + return -1; + + if (check_sa_args(c, &txsa)) + return -1; + +modify: + cmd = macsec_commands[c][1][0]; + return do_modify_nl(c, cmd, ifindex, NULL, &txsa); +} + +static int do_modify_rxsci(enum cmd c, int argc, char **argv, int ifindex) +{ + struct rxsc_desc rxsc = {0}; + struct sa_desc rxsa = {0}; + bool sa_set; + enum macsec_nl_commands cmd; + + rxsc.ifindex = ifindex; + rxsc.active = 0xff; + rxsa.an = 0xff; + rxsa.active = 0xff; + + sa_set = parse_rxsci(&argc, &argv, &rxsc, &rxsa); + + if (c == CMD_DEL) + goto modify; + + if (sa_set && (parse_sa_args(&argc, &argv, &rxsa) || + check_sa_args(c, &rxsa))) + return -1; + if (!sa_set && parse_rxsci_args(&argc, &argv, &rxsc)) + return -1; + +modify: + cmd = macsec_commands[c][sa_set][1]; + return do_modify_nl(c, cmd, rxsc.ifindex, &rxsc, &rxsa); +} + +static int do_modify(enum cmd c, int argc, char **argv) +{ + int ifindex; + + if (argc == 0) + ipmacsec_usage(); + + ifindex = ll_name_to_index(*argv); + if (!ifindex) { + fprintf(stderr, "Device \"%s\" does not exist.\n", *argv); + return -1; + } + argc--; argv++; + + if (argc == 0) + ipmacsec_usage(); + + if (strcmp(*argv, "tx") == 0) + return do_modify_txsa(c, argc-1, argv+1, ifindex); + if (strcmp(*argv, "rx") == 0) + return do_modify_rxsci(c, argc-1, argv+1, ifindex); + + ipmacsec_usage(); + return -1; +} + +/* dump/show */ +static struct { + int ifindex; + __u64 sci; +} filter; + +static int validate_dump(struct rtattr **attrs) +{ + return attrs[MACSEC_ATTR_IFINDEX] && attrs[MACSEC_ATTR_SECY] && + attrs[MACSEC_ATTR_TXSA_LIST] && attrs[MACSEC_ATTR_RXSC_LIST] && + attrs[MACSEC_ATTR_TXSC_STATS] && attrs[MACSEC_ATTR_SECY_STATS]; + +} + +static int validate_secy_dump(struct rtattr **attrs) +{ + return attrs[MACSEC_SECY_ATTR_SCI] && + attrs[MACSEC_SECY_ATTR_ENCODING_SA] && + attrs[MACSEC_SECY_ATTR_CIPHER_SUITE] && + attrs[MACSEC_SECY_ATTR_ICV_LEN] && + attrs[MACSEC_SECY_ATTR_PROTECT] && + attrs[MACSEC_SECY_ATTR_REPLAY] && + attrs[MACSEC_SECY_ATTR_OPER] && + attrs[MACSEC_SECY_ATTR_VALIDATE] && + attrs[MACSEC_SECY_ATTR_ENCRYPT] && + attrs[MACSEC_SECY_ATTR_INC_SCI] && + attrs[MACSEC_SECY_ATTR_ES] && + attrs[MACSEC_SECY_ATTR_SCB]; +} + +static void print_flag(FILE *f, struct rtattr *attrs[], const char *desc, + int field) +{ + if (attrs[field]) + fprintf(f, "%s %s ", desc, + values_on_off[!!rta_getattr_u8(attrs[field])]); +} + +#define DEFAULT_CIPHER_NAME "GCM-AES-128" + +static const char *cs_id_to_name(__u64 cid) +{ + switch (cid) { + case MACSEC_DEFAULT_CIPHER_ID: + case MACSEC_DEFAULT_CIPHER_ALT: + return DEFAULT_CIPHER_NAME; + default: + return "(unknown)"; + } +} + +static void print_cipher_suite(const char *prefix, __u64 cid, __u8 icv_len) +{ + printf("%scipher suite: %s, using ICV length %d\n", prefix, + cs_id_to_name(cid), icv_len); +} + +static void print_attrs(const char *prefix, struct rtattr *attrs[]) +{ + print_flag(stdout, attrs, "protect", MACSEC_SECY_ATTR_PROTECT); + + if (attrs[MACSEC_SECY_ATTR_VALIDATE]) { + __u8 val = rta_getattr_u8(attrs[MACSEC_SECY_ATTR_VALIDATE]); + + printf("validate %s ", VALIDATE_STR[val]); + } + + print_flag(stdout, attrs, "sc", MACSEC_RXSC_ATTR_ACTIVE); + print_flag(stdout, attrs, "sa", MACSEC_SA_ATTR_ACTIVE); + print_flag(stdout, attrs, "encrypt", MACSEC_SECY_ATTR_ENCRYPT); + print_flag(stdout, attrs, "send_sci", MACSEC_SECY_ATTR_INC_SCI); + print_flag(stdout, attrs, "end_station", MACSEC_SECY_ATTR_ES); + print_flag(stdout, attrs, "scb", MACSEC_SECY_ATTR_SCB); + + print_flag(stdout, attrs, "replay", MACSEC_SECY_ATTR_REPLAY); + if (attrs[MACSEC_SECY_ATTR_WINDOW]) { + printf("window %d ", + rta_getattr_u32(attrs[MACSEC_SECY_ATTR_WINDOW])); + } + + if (attrs[MACSEC_SECY_ATTR_CIPHER_SUITE] && + attrs[MACSEC_SECY_ATTR_ICV_LEN]) { + printf("\n"); + print_cipher_suite(prefix, + rta_getattr_u64(attrs[MACSEC_SECY_ATTR_CIPHER_SUITE]), + rta_getattr_u8(attrs[MACSEC_SECY_ATTR_ICV_LEN])); + } + +} + +static void print_one_stat(const char **names, struct rtattr **attr, int idx, + bool long_stat) +{ + int pad = strlen(names[idx]) + 1; + + if (attr[idx]) { + if (long_stat) + printf("%*llu", pad, rta_getattr_u64(attr[idx])); + else + printf("%*u", pad, rta_getattr_u32(attr[idx])); + } else { + printf("%*c", pad, '-'); + } +} + +static const char *txsc_stats_names[NUM_MACSEC_TXSC_STATS_ATTR] = { + [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutPktsProtected", + [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutPktsEncrypted", + [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED] = "OutOctetsProtected", + [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED] = "OutOctetsEncrypted", +}; + +static void print_txsc_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_TXSC_STATS_ATTR_MAX + 1]; + int i; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_TXSC_STATS_ATTR_MAX + 1, attr); + printf("%sstats:", prefix); + + for (i = 1; i < NUM_MACSEC_TXSC_STATS_ATTR; i++) { + if (!txsc_stats_names[i]) + continue; + printf(" %s", txsc_stats_names[i]); + } + + printf("\n%s ", prefix); + + for (i = 1; i < NUM_MACSEC_TXSC_STATS_ATTR; i++) { + if (!txsc_stats_names[i]) + continue; + print_one_stat(txsc_stats_names, stats, i, true); + } + + printf("\n"); +} + +static const char *secy_stats_names[NUM_MACSEC_SECY_STATS_ATTR] = { + [MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED] = "OutPktsUntagged", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED] = "InPktsUntagged", + [MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG] = "OutPktsTooLong", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG] = "InPktsNoTag", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG] = "InPktsBadTag", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI] = "InPktsUnknownSCI", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI] = "InPktsNoSCI", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN] = "InPktsOverrun", +}; + +static void print_secy_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_SECY_STATS_ATTR_MAX + 1]; + int i; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_SECY_STATS_ATTR_MAX + 1, attr); + printf("%sstats:", prefix); + + for (i = 1; i < NUM_MACSEC_SECY_STATS_ATTR; i++) { + if (!secy_stats_names[i]) + continue; + printf(" %s", secy_stats_names[i]); + } + + printf("\n%s ", prefix); + + for (i = 1; i < NUM_MACSEC_SECY_STATS_ATTR; i++) { + if (!secy_stats_names[i]) + continue; + print_one_stat(secy_stats_names, stats, i, true); + } + + printf("\n"); +} + +static const char *rxsa_stats_names[NUM_MACSEC_SA_STATS_ATTR] = { + [MACSEC_SA_STATS_ATTR_IN_PKTS_OK] = "InPktsOK", + [MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID] = "InPktsInvalid", + [MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID] = "InPktsNotValid", + [MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA] = "InPktsNotUsingSA", + [MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA] = "InPktsUnusedSA", +}; + +static void print_rxsa_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_SA_STATS_ATTR_MAX + 1]; + int i; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_SA_STATS_ATTR_MAX + 1, attr); + printf("%s%s ", prefix, prefix); + + for (i = 1; i < NUM_MACSEC_SA_STATS_ATTR; i++) { + if (!rxsa_stats_names[i]) + continue; + printf(" %s", rxsa_stats_names[i]); + } + + printf("\n%s%s ", prefix, prefix); + + for (i = 1; i < NUM_MACSEC_SA_STATS_ATTR; i++) { + if (!rxsa_stats_names[i]) + continue; + print_one_stat(rxsa_stats_names, stats, i, false); + } + + printf("\n"); +} + +static const char *txsa_stats_names[NUM_MACSEC_SA_STATS_ATTR] = { + [MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutPktsProtected", + [MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutPktsEncrypted", +}; + +static void print_txsa_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_SA_STATS_ATTR_MAX + 1]; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_SA_STATS_ATTR_MAX + 1, attr); + printf("%s%s %s %s\n", prefix, prefix, + txsa_stats_names[MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED], + txsa_stats_names[MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED]); + printf("%s%s ", prefix, prefix); + + print_one_stat(txsa_stats_names, stats, + MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, false); + print_one_stat(txsa_stats_names, stats, + MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, false); + printf("\n"); +} + +static void print_tx_sc(const char *prefix, __u64 sci, __u8 encoding_sa, + struct rtattr *txsc_stats, struct rtattr *secy_stats, + struct rtattr *sa) +{ + struct rtattr *sa_attr[MACSEC_SA_ATTR_MAX + 1]; + struct rtattr *a; + int rem; + + printf("%sTXSC: %016llx on SA %d\n", prefix, ntohll(sci), encoding_sa); + print_secy_stats(prefix, secy_stats); + print_txsc_stats(prefix, txsc_stats); + + rem = RTA_PAYLOAD(sa); + for (a = RTA_DATA(sa); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) { + SPRINT_BUF(keyid); + bool state; + + parse_rtattr_nested(sa_attr, MACSEC_SA_ATTR_MAX + 1, a); + state = rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_ACTIVE]); + printf("%s%s%d: PN %u, state %s, key %s\n", prefix, prefix, + rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_AN]), + rta_getattr_u32(sa_attr[MACSEC_SA_ATTR_PN]), + values_on_off[state], + hexstring_n2a(RTA_DATA(sa_attr[MACSEC_SA_ATTR_KEYID]), + RTA_PAYLOAD(sa_attr[MACSEC_SA_ATTR_KEYID]), + keyid, sizeof(keyid))); + print_txsa_stats(prefix, sa_attr[MACSEC_SA_ATTR_STATS]); + } +} + +static const char *rxsc_stats_names[NUM_MACSEC_RXSC_STATS_ATTR] = { + [MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED] = "InOctetsValidated", + [MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED] = "InOctetsDecrypted", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED] = "InPktsUnchecked", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED] = "InPktsDelayed", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK] = "InPktsOK", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID] = "InPktsInvalid", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE] = "InPktsLate", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID] = "InPktsNotValid", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA] = "InPktsNotUsingSA", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA] = "InPktsUnusedSA", +}; + +static void print_rxsc_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_RXSC_STATS_ATTR_MAX + 1]; + int i; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_RXSC_STATS_ATTR_MAX + 1, attr); + printf("%sstats:", prefix); + for (i = 1; i < NUM_MACSEC_RXSC_STATS_ATTR; i++) { + if (!rxsc_stats_names[i]) + continue; + printf(" %s", rxsc_stats_names[i]); + } + + printf("\n%s ", prefix); + + for (i = 1; i < NUM_MACSEC_RXSC_STATS_ATTR; i++) { + if (!rxsc_stats_names[i]) + continue; + print_one_stat(rxsc_stats_names, stats, i, true); + } + + printf("\n"); +} + +static void print_rx_sc(const char *prefix, __u64 sci, __u8 active, + struct rtattr *rxsc_stats, struct rtattr *sa) +{ + struct rtattr *sa_attr[MACSEC_SA_ATTR_MAX + 1]; + struct rtattr *a; + int rem; + + printf("%sRXSC: %016llx, state %s\n", prefix, ntohll(sci), + values_on_off[!!active]); + print_rxsc_stats(prefix, rxsc_stats); + + rem = RTA_PAYLOAD(sa); + for (a = RTA_DATA(sa); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) { + SPRINT_BUF(keyid); + bool state; + + parse_rtattr_nested(sa_attr, MACSEC_SA_ATTR_MAX + 1, a); + state = rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_ACTIVE]); + printf("%s%s%d: PN %u, state %s, key %s\n", prefix, prefix, + rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_AN]), + rta_getattr_u32(sa_attr[MACSEC_SA_ATTR_PN]), + values_on_off[state], + hexstring_n2a(RTA_DATA(sa_attr[MACSEC_SA_ATTR_KEYID]), + RTA_PAYLOAD(sa_attr[MACSEC_SA_ATTR_KEYID]), + keyid, sizeof(keyid))); + print_rxsa_stats(prefix, sa_attr[MACSEC_SA_ATTR_STATS]); + } +} + +static int process(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + struct genlmsghdr *ghdr; + struct rtattr *attrs[MACSEC_ATTR_MAX + 1], *sc, *c; + struct rtattr *attrs_secy[MACSEC_SECY_ATTR_MAX + 1]; + int len = n->nlmsg_len; + int ifindex; + __u64 sci; + __u8 encoding_sa; + int rem; + + if (n->nlmsg_type != genl_family) + return -1; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) + return -1; + + ghdr = NLMSG_DATA(n); + if (ghdr->cmd != MACSEC_CMD_GET_TXSC) + return 0; + + parse_rtattr(attrs, MACSEC_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len); + if (!validate_dump(attrs)) { + printf("incomplete dump message\n"); + return -1; + } + + ifindex = rta_getattr_u32(attrs[MACSEC_ATTR_IFINDEX]); + parse_rtattr_nested(attrs_secy, MACSEC_SECY_ATTR_MAX + 1, + attrs[MACSEC_ATTR_SECY]); + + if (!validate_secy_dump(attrs_secy)) { + printf("incomplete dump message\n"); + return -1; + } + + sci = rta_getattr_u64(attrs_secy[MACSEC_SECY_ATTR_SCI]); + encoding_sa = rta_getattr_u8(attrs_secy[MACSEC_SECY_ATTR_ENCODING_SA]); + + if (filter.ifindex && ifindex != filter.ifindex) + return 0; + + if (filter.sci && sci != filter.sci) + return 0; + + printf("%d: %s: ", ifindex, ll_index_to_name(ifindex)); + print_attrs(" ", attrs_secy); + + print_tx_sc(" ", sci, encoding_sa, + attrs[MACSEC_ATTR_TXSC_STATS], + attrs[MACSEC_ATTR_SECY_STATS], + attrs[MACSEC_ATTR_TXSA_LIST]); + + if (!attrs[MACSEC_ATTR_RXSC_LIST]) + return 0; + + sc = attrs[MACSEC_ATTR_RXSC_LIST]; + rem = RTA_PAYLOAD(sc); + for (c = RTA_DATA(sc); RTA_OK(c, rem); c = RTA_NEXT(c, rem)) { + struct rtattr *sc_attr[MACSEC_RXSC_ATTR_MAX + 1]; + + parse_rtattr_nested(sc_attr, MACSEC_RXSC_ATTR_MAX + 1, c); + print_rx_sc(" ", + rta_getattr_u64(sc_attr[MACSEC_RXSC_ATTR_SCI]), + rta_getattr_u32(sc_attr[MACSEC_RXSC_ATTR_ACTIVE]), + sc_attr[MACSEC_RXSC_ATTR_STATS], + sc_attr[MACSEC_RXSC_ATTR_SA_LIST]); + } + + return 0; +} + +static int do_dump(int ifindex) +{ + MACSEC_GENL_REQ(req, MACSEC_BUFLEN, MACSEC_CMD_GET_TXSC, + NLM_F_REQUEST | NLM_F_DUMP); + + memset(&filter, 0, sizeof(filter)); + filter.ifindex = ifindex; + + req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq; + if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0) { + perror("Failed to send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&genl_rth, process, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + return 0; +} + +static int do_show(int argc, char **argv) +{ + int ifindex; + + if (argc == 0) + return do_dump(0); + + ifindex = ll_name_to_index(*argv); + if (ifindex == 0) { + fprintf(stderr, "Device \"%s\" does not exist.\n", *argv); + return -1; + } + + argc--, argv++; + if (argc == 0) + return do_dump(ifindex); + + ipmacsec_usage(); + return -1; +} + +int do_ipmacsec(int argc, char **argv) +{ + if (argc < 1) + ipmacsec_usage(); + + if (matches(*argv, "help") == 0) + ipmacsec_usage(); + + if (genl_init_handle(&genl_rth, MACSEC_GENL_NAME, &genl_family)) + exit(1); + + if (matches(*argv, "show") == 0) + return do_show(argc-1, argv+1); + + if (matches(*argv, "add") == 0) + return do_modify(CMD_ADD, argc-1, argv+1); + if (matches(*argv, "set") == 0) + return do_modify(CMD_UPD, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return do_modify(CMD_DEL, argc-1, argv+1); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip macsec help\".\n", + *argv); + exit(-1); +} + +/* device creation */ +static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_MACSEC_SCI]) { + fprintf(f, "sci %016llx ", + ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI]))); + } + + print_flag(f, tb, "protect", IFLA_MACSEC_PROTECT); + + if (tb[IFLA_MACSEC_CIPHER_SUITE]) { + __u64 csid = rta_getattr_u64(tb[IFLA_MACSEC_CIPHER_SUITE]); + + fprintf(f, "cipher %s ", cs_id_to_name(csid)); + } + + if (tb[IFLA_MACSEC_ICV_LEN]) { + fprintf(f, "icvlen %hhu ", + rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN])); + } + + if (tb[IFLA_MACSEC_ENCODING_SA]) { + fprintf(f, "encodingsa %hhu ", + rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA])); + } + + if (tb[IFLA_MACSEC_VALIDATION]) { + __u8 val = rta_getattr_u8(tb[IFLA_MACSEC_VALIDATION]); + + fprintf(f, "validate %s ", VALIDATE_STR[val]); + } + + print_flag(f, tb, "encrypt", IFLA_MACSEC_ENCRYPT); + print_flag(f, tb, "send_sci", IFLA_MACSEC_INC_SCI); + print_flag(f, tb, "end_station", IFLA_MACSEC_ES); + print_flag(f, tb, "scb", IFLA_MACSEC_SCB); + + print_flag(f, tb, "replay", IFLA_MACSEC_REPLAY_PROTECT); + if (tb[IFLA_MACSEC_WINDOW]) { + fprintf(f, "window %d ", + rta_getattr_u32(tb[IFLA_MACSEC_WINDOW])); + } +} + +static bool check_txsc_flags(bool es, bool scb, bool sci) +{ + if (sci && (es || scb)) + return false; + if (es && scb) + return false; + return true; +} + +static void usage(FILE *f) +{ + fprintf(f, + "Usage: ... macsec [ [ address ] port { 1..2^16-1 } | sci ]\n" + " [ cipher { default | gcm-aes-128 } ]\n" + " [ icvlen { 8..16 } ]\n" + " [ encrypt { on | off } ]\n" + " [ send_sci { on | off } ]\n" + " [ end_station { on | off } ]\n" + " [ scb { on | off } ]\n" + " [ protect { on | off } ]\n" + " [ replay { on | off} window { 0..2^32-1 } ]\n" + " [ validate { strict | check | disabled } ]\n" + " [ encodingsa { 0..3 } ]\n" + ); +} + +static int macsec_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *hdr) +{ + int ret; + __u8 encoding_sa = 0xff; + __u32 window = -1; + struct cipher_args cipher = {0}; + enum macsec_validation_type validate; + bool es = false, scb = false, send_sci = false; + int replay_protect = -1; + struct sci sci = { 0 }; + + ret = get_sci_portaddr(&sci, &argc, &argv, true, true); + if (ret < 0) { + fprintf(stderr, "expected sci\n"); + return -1; + } + + if (ret > 0) { + if (sci.sci) + addattr_l(hdr, MACSEC_BUFLEN, IFLA_MACSEC_SCI, + &sci.sci, sizeof(sci.sci)); + else + addattr_l(hdr, MACSEC_BUFLEN, IFLA_MACSEC_PORT, + &sci.port, sizeof(sci.port)); + } + + while (argc > 0) { + if (strcmp(*argv, "cipher") == 0) { + NEXT_ARG(); + if (cipher.id) + duparg("cipher", *argv); + if (strcmp(*argv, "default") == 0 || + strcmp(*argv, "gcm-aes-128") == 0 || + strcmp(*argv, "GCM-AES-128") == 0) + cipher.id = MACSEC_DEFAULT_CIPHER_ID; + else + invarg("expected: default or gcm-aes-128", + *argv); + } else if (strcmp(*argv, "icvlen") == 0) { + NEXT_ARG(); + if (cipher.icv_len) + duparg("icvlen", *argv); + get_icvlen(&cipher.icv_len, *argv); + } else if (strcmp(*argv, "encrypt") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("encrypt", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + addattr8(hdr, MACSEC_BUFLEN, IFLA_MACSEC_ENCRYPT, i); + } else if (strcmp(*argv, "send_sci") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("send_sci", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + send_sci = i; + addattr8(hdr, MACSEC_BUFLEN, + IFLA_MACSEC_INC_SCI, send_sci); + } else if (strcmp(*argv, "end_station") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("end_station", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + es = i; + addattr8(hdr, MACSEC_BUFLEN, IFLA_MACSEC_ES, es); + } else if (strcmp(*argv, "scb") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("scb", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + scb = i; + addattr8(hdr, MACSEC_BUFLEN, IFLA_MACSEC_SCB, scb); + } else if (strcmp(*argv, "protect") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("protect", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + addattr8(hdr, MACSEC_BUFLEN, IFLA_MACSEC_PROTECT, i); + } else if (strcmp(*argv, "replay") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("replay", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + replay_protect = !!i; + } else if (strcmp(*argv, "window") == 0) { + NEXT_ARG(); + ret = get_u32(&window, *argv, 0); + if (ret) + invarg("expected replay window size", *argv); + } else if (strcmp(*argv, "validate") == 0) { + NEXT_ARG(); + ret = one_of("validate", *argv, + VALIDATE_STR, ARRAY_SIZE(VALIDATE_STR), + (int *)&validate); + if (ret != 0) + return ret; + addattr8(hdr, MACSEC_BUFLEN, + IFLA_MACSEC_VALIDATION, validate); + } else if (strcmp(*argv, "encodingsa") == 0) { + if (encoding_sa != 0xff) + duparg2("encodingsa", "encodingsa"); + NEXT_ARG(); + ret = get_an(&encoding_sa, *argv); + if (ret) + invarg("expected an { 0..3 }", *argv); + } else { + fprintf(stderr, "macsec: unknown command \"%s\"?\n", + *argv); + usage(stderr); + return -1; + } + + argv++; argc--; + } + + if (!check_txsc_flags(es, scb, send_sci)) { + fprintf(stderr, "invalid combination of send_sci/end_station/scb\n"); + return -1; + } + + if (window != -1 && replay_protect == -1) { + fprintf(stderr, + "replay window set, but replay protection not enabled. did you mean 'replay on window %u'?\n", + window); + return -1; + } else if (window == -1 && replay_protect == 1) { + fprintf(stderr, + "replay protection enabled, but no window set. did you mean 'replay on window VALUE'?\n"); + return -1; + } + + if (cipher.id) + addattr_l(hdr, MACSEC_BUFLEN, IFLA_MACSEC_CIPHER_SUITE, + &cipher.id, sizeof(cipher.id)); + if (cipher.icv_len) + addattr_l(hdr, MACSEC_BUFLEN, IFLA_MACSEC_ICV_LEN, + &cipher.icv_len, sizeof(cipher.icv_len)); + + if (replay_protect != -1) { + addattr32(hdr, MACSEC_BUFLEN, IFLA_MACSEC_WINDOW, window); + addattr8(hdr, MACSEC_BUFLEN, IFLA_MACSEC_REPLAY_PROTECT, + replay_protect); + } + + if (encoding_sa != 0xff) { + addattr_l(hdr, MACSEC_BUFLEN, IFLA_MACSEC_ENCODING_SA, + &encoding_sa, sizeof(encoding_sa)); + } + + return 0; +} + +static void macsec_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + usage(f); +} + +struct link_util macsec_link_util = { + .id = "macsec", + .maxattr = IFLA_MACSEC_MAX, + .parse_opt = macsec_parse_opt, + .print_help = macsec_print_help, + .print_opt = macsec_print_opt, +}; diff -Nru iproute2-4.3.0/ip/ipmaddr.c iproute2-4.9.0/ip/ipmaddr.c --- iproute2-4.3.0/ip/ipmaddr.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipmaddr.c 2016-12-12 23:07:42.000000000 +0000 @@ -50,6 +50,7 @@ while (*str && (len < 2 * size)) { int tmp; + if (str[1] == 0) return -1; if (sscanf(str, "%02x", &tmp) != 1) @@ -61,8 +62,7 @@ return len; } -struct ma_info -{ +struct ma_info { struct ma_info *next; int index; int users; @@ -93,19 +93,16 @@ while (fgets(buf, sizeof(buf), fp)) { char hexa[256]; - struct ma_info m; + struct ma_info m = { .addr.family = AF_PACKET }; int len; int st; - memset(&m, 0, sizeof(m)); sscanf(buf, "%d%s%d%d%s", &m.index, m.name, &m.users, &st, hexa); if (filter.dev && strcmp(filter.dev, m.name)) continue; - m.addr.family = AF_PACKET; - - len = parse_hex(hexa, (unsigned char*)&m.addr.data, sizeof (m.addr.data)); + len = parse_hex(hexa, (unsigned char *)&m.addr.data, sizeof(m.addr.data)); if (len >= 0) { struct ma_info *ma = malloc(sizeof(m)); @@ -122,22 +119,21 @@ static void read_igmp(struct ma_info **result_p) { - struct ma_info m; + struct ma_info m = { + .addr.family = AF_INET, + .addr.bitlen = 32, + .addr.bytelen = 4, + }; char buf[256]; FILE *fp = fopen("/proc/net/igmp", "r"); if (!fp) return; - memset(&m, 0, sizeof(m)); if (!fgets(buf, sizeof(buf), fp)) { fclose(fp); return; } - m.addr.family = AF_INET; - m.addr.bitlen = 32; - m.addr.bytelen = 4; - while (fgets(buf, sizeof(buf), fp)) { struct ma_info *ma; @@ -149,7 +145,7 @@ if (filter.dev && strcmp(filter.dev, m.name)) continue; - sscanf(buf, "%08x%d", (__u32*)&m.addr.data, &m.users); + sscanf(buf, "%08x%d", (__u32 *)&m.addr.data, &m.users); ma = malloc(sizeof(m)); memcpy(ma, &m, sizeof(m)); @@ -169,18 +165,15 @@ while (fgets(buf, sizeof(buf), fp)) { char hexa[256]; - struct ma_info m; + struct ma_info m = { .addr.family = AF_INET6 }; int len; - memset(&m, 0, sizeof(m)); sscanf(buf, "%d%s%s%d", &m.index, m.name, hexa, &m.users); if (filter.dev && strcmp(filter.dev, m.name)) continue; - m.addr.family = AF_INET6; - - len = parse_hex(hexa, (unsigned char*)&m.addr.data, sizeof (m.addr.data)); + len = parse_hex(hexa, (unsigned char *)&m.addr.data, sizeof(m.addr.data)); if (len >= 0) { struct ma_info *ma = malloc(sizeof(m)); @@ -200,12 +193,11 @@ if (list->addr.family == AF_PACKET) { SPRINT_BUF(b1); - fprintf(fp, "link %s", ll_addr_n2a((unsigned char*)list->addr.data, + fprintf(fp, "link %s", ll_addr_n2a((unsigned char *)list->addr.data, list->addr.bytelen, 0, b1, sizeof(b1))); } else { - char abuf[256]; - switch(list->addr.family) { + switch (list->addr.family) { case AF_INET: fprintf(fp, "inet "); break; @@ -218,9 +210,7 @@ } fprintf(fp, "%s", format_host(list->addr.family, - -1, - list->addr.data, - abuf, sizeof(abuf))); + -1, list->addr.data)); } if (list->users != 1) fprintf(fp, " users %d", list->users); @@ -256,8 +246,7 @@ if (1) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); - } - else if (matches(*argv, "help") == 0) + } else if (matches(*argv, "help") == 0) usage(); if (filter.dev) duparg2("dev", *argv); @@ -278,11 +267,9 @@ static int multiaddr_modify(int cmd, int argc, char **argv) { - struct ifreq ifr; + struct ifreq ifr = {}; int fd; - memset(&ifr, 0, sizeof(ifr)); - if (cmd == RTM_NEWADDR) cmd = SIOCADDMULTI; else @@ -320,7 +307,7 @@ perror("Cannot create socket"); exit(1); } - if (ioctl(fd, cmd, (char*)&ifr) != 0) { + if (ioctl(fd, cmd, (char *)&ifr) != 0) { perror("ioctl"); exit(1); } diff -Nru iproute2-4.3.0/ip/ipmonitor.c iproute2-4.9.0/ip/ipmonitor.c --- iproute2-4.3.0/ip/ipmonitor.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipmonitor.c 2016-12-12 23:07:42.000000000 +0000 @@ -30,8 +30,7 @@ static void usage(void) { - fprintf(stderr, "Usage: ip monitor [ all | LISTofOBJECTS ] [ FILE ] " - "[ label ] [all-nsid] [dev DEVICE]\n"); + fprintf(stderr, "Usage: ip monitor [ all | LISTofOBJECTS ] [ FILE ] [ label ] [all-nsid] [dev DEVICE]\n"); fprintf(stderr, "LISTofOBJECTS := link | address | route | mroute | prefix |\n"); fprintf(stderr, " neigh | netconf | rule | nsid\n"); fprintf(stderr, "FILE := file FILENAME\n"); @@ -58,7 +57,7 @@ struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; if (n->nlmsg_type == RTM_NEWROUTE || n->nlmsg_type == RTM_DELROUTE) { struct rtmsg *r = NLMSG_DATA(n); @@ -139,8 +138,8 @@ } if (n->nlmsg_type != NLMSG_ERROR && n->nlmsg_type != NLMSG_NOOP && n->nlmsg_type != NLMSG_DONE) { - fprintf(fp, "Unknown message: type=0x%08x(%d) flags=0x%08x(%d)" - "len=0x%08x(%d)\n", n->nlmsg_type, n->nlmsg_type, + fprintf(fp, "Unknown message: type=0x%08x(%d) flags=0x%08x(%d)len=0x%08x(%d)\n", + n->nlmsg_type, n->nlmsg_type, n->nlmsg_flags, n->nlmsg_flags, n->nlmsg_len, n->nlmsg_len); } @@ -150,17 +149,17 @@ int do_ipmonitor(int argc, char **argv) { char *file = NULL; - unsigned groups = 0; - int llink=0; - int laddr=0; - int lroute=0; - int lmroute=0; - int lprefix=0; - int lneigh=0; - int lnetconf=0; - int lrule=0; - int lnsid=0; - int ifindex=0; + unsigned int groups = 0; + int llink = 0; + int laddr = 0; + int lroute = 0; + int lmroute = 0; + int lprefix = 0; + int lneigh = 0; + int lnetconf = 0; + int lrule = 0; + int lnsid = 0; + int ifindex = 0; groups |= nl_mgrp(RTNLGRP_LINK); groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR); @@ -186,22 +185,20 @@ file = *argv; } else if (matches(*argv, "label") == 0) { prefix_banner = 1; - } else if (matches(*argv, "all-nsid") == 0) { - listen_all_nsid = 1; } else if (matches(*argv, "link") == 0) { - llink=1; + llink = 1; groups = 0; } else if (matches(*argv, "address") == 0) { - laddr=1; + laddr = 1; groups = 0; } else if (matches(*argv, "route") == 0) { - lroute=1; + lroute = 1; groups = 0; } else if (matches(*argv, "mroute") == 0) { - lmroute=1; + lmroute = 1; groups = 0; } else if (matches(*argv, "prefix") == 0) { - lprefix=1; + lprefix = 1; groups = 0; } else if (matches(*argv, "neigh") == 0) { lneigh = 1; @@ -216,7 +213,9 @@ lnsid = 1; groups = 0; } else if (strcmp(*argv, "all") == 0) { - prefix_banner=1; + prefix_banner = 1; + } else if (matches(*argv, "all-nsid") == 0) { + listen_all_nsid = 1; } else if (matches(*argv, "help") == 0) { usage(); } else if (strcmp(*argv, "dev") == 0) { @@ -284,12 +283,16 @@ } if (file) { FILE *fp; + int err; + fp = fopen(file, "r"); if (fp == NULL) { perror("Cannot fopen"); exit(-1); } - return rtnl_from_file(fp, accept_msg, stdout); + err = rtnl_from_file(fp, accept_msg, stdout); + fclose(fp); + return err; } if (rtnl_open(&rth, groups) < 0) @@ -298,6 +301,7 @@ exit(1); ll_init_map(&rth); + netns_nsid_socket_init(); netns_map_init(); if (rtnl_listen(&rth, accept_msg, stdout) < 0) diff -Nru iproute2-4.3.0/ip/ipmroute.c iproute2-4.9.0/ip/ipmroute.c --- iproute2-4.3.0/ip/ipmroute.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipmroute.c 2016-12-12 23:07:42.000000000 +0000 @@ -44,8 +44,7 @@ exit(-1); } -struct rtfilter -{ +struct rtfilter { int tb; int af; int iif; @@ -55,12 +54,12 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[RTA_MAX+1]; - char abuf[256]; + struct rtattr *tb[RTA_MAX+1]; char obuf[256]; + SPRINT_BUF(b1); __u32 table; int iif = 0; @@ -90,7 +89,7 @@ return 0; if (tb[RTA_IIF]) - iif = *(int*)RTA_DATA(tb[RTA_IIF]); + iif = *(int *)RTA_DATA(tb[RTA_IIF]); if (filter.iif && filter.iif != iif) return 0; @@ -98,44 +97,34 @@ return 0; if (tb[RTA_DST] && filter.mdst.bitlen > 0) { - inet_prefix dst; + inet_prefix dst = { .family = r->rtm_family }; - memset(&dst, 0, sizeof(dst)); - dst.family = r->rtm_family; memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), RTA_PAYLOAD(tb[RTA_DST])); if (inet_addr_match(&dst, &filter.mdst, filter.mdst.bitlen)) return 0; } if (tb[RTA_SRC] && filter.msrc.bitlen > 0) { - inet_prefix src; + inet_prefix src = { .family = r->rtm_family }; - memset(&src, 0, sizeof(src)); - src.family = r->rtm_family; memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), RTA_PAYLOAD(tb[RTA_SRC])); if (inet_addr_match(&src, &filter.msrc, filter.msrc.bitlen)) return 0; } - family = r->rtm_family == RTNL_FAMILY_IPMR ? AF_INET : AF_INET6; + family = get_real_family(r->rtm_type, r->rtm_family); if (n->nlmsg_type == RTM_DELROUTE) fprintf(fp, "Deleted "); if (tb[RTA_SRC]) len = snprintf(obuf, sizeof(obuf), - "(%s, ", rt_addr_n2a(family, - RTA_PAYLOAD(tb[RTA_SRC]), - RTA_DATA(tb[RTA_SRC]), - abuf, sizeof(abuf))); + "(%s, ", rt_addr_n2a_rta(family, tb[RTA_SRC])); else len = sprintf(obuf, "(unknown, "); if (tb[RTA_DST]) snprintf(obuf + len, sizeof(obuf) - len, - "%s)", rt_addr_n2a(family, - RTA_PAYLOAD(tb[RTA_DST]), - RTA_DATA(tb[RTA_DST]), - abuf, sizeof(abuf))); + "%s)", rt_addr_n2a_rta(family, tb[RTA_DST])); else snprintf(obuf + len, sizeof(obuf) - len, "unknown) "); @@ -180,6 +169,13 @@ fprintf(fp, ", %"PRIu64" arrived on wrong iif.", (uint64_t)mfcs->mfcs_wrong_if); } + if (show_stats && tb[RTA_EXPIRES]) { + struct timeval tv; + + __jiffies_to_tv(&tv, rta_getattr_u64(tb[RTA_EXPIRES])); + fprintf(fp, ", Age %4i.%.2i", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } fprintf(fp, "\n"); fflush(fp); return 0; @@ -212,6 +208,7 @@ while (argc > 0) { if (matches(*argv, "table") == 0) { __u32 tid; + NEXT_ARG(); if (rtnl_rttable_a2n(&tid, *argv)) { if (strcmp(*argv, "all") == 0) { diff -Nru iproute2-4.3.0/ip/ipneigh.c iproute2-4.9.0/ip/ipneigh.c --- iproute2-4.3.0/ip/ipneigh.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipneigh.c 2016-12-12 23:07:42.000000000 +0000 @@ -31,7 +31,7 @@ static struct { int family; - int index; + int index; int state; int unused_only; inet_prefix pfx; @@ -39,20 +39,23 @@ char *flushb; int flushp; int flushe; + int master; } filter; static void usage(void) __attribute__((noreturn)); static void usage(void) { - fprintf(stderr, "Usage: ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ]\n" - " [ nud { permanent | noarp | stale | reachable } ]\n" - " | proxy ADDR } [ dev DEV ]\n"); - fprintf(stderr, " ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"); + fprintf(stderr, "Usage: ip neigh { add | del | change | replace }\n" + " { ADDR [ lladdr LLADDR ] [ nud STATE ] | proxy ADDR } [ dev DEV ]\n"); + fprintf(stderr, " ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"); + fprintf(stderr, " [ vrf NAME ]\n\n"); + fprintf(stderr, "STATE := { permanent | noarp | stale | reachable | none |\n" + " incomplete | delay | probe | failed }\n"); exit(-1); } -static int nud_state_a2n(unsigned *state, const char *arg) +static int nud_state_a2n(unsigned int *state, const char *arg) { if (matches(arg, "permanent") == 0) *state = NUD_PERMANENT; @@ -75,7 +78,7 @@ else { if (get_unsigned(state, arg, 0)) return -1; - if (*state>=0x100 || (*state&((*state)-1))) + if (*state >= 0x100 || (*state&((*state)-1))) return -1; } return 0; @@ -97,22 +100,21 @@ struct { struct nlmsghdr n; struct ndmsg ndm; - char buf[256]; - } req; - char *d = NULL; + char buf[256]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .ndm.ndm_family = preferred_family, + .ndm.ndm_state = NUD_PERMANENT, + }; + char *dev = NULL; int dst_ok = 0; + int dev_ok = 0; int lladdr_ok = 0; - char * lla = NULL; + char *lla = NULL; inet_prefix dst; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.ndm.ndm_family = preferred_family; - req.ndm.ndm_state = NUD_PERMANENT; - while (argc > 0) { if (matches(*argv, "lladdr") == 0) { NEXT_ARG(); @@ -121,7 +123,8 @@ lla = *argv; lladdr_ok = 1; } else if (strcmp(*argv, "nud") == 0) { - unsigned state; + unsigned int state; + NEXT_ARG(); if (nud_state_a2n(&state, *argv)) invarg("nud state is bad", *argv); @@ -134,10 +137,12 @@ duparg("address", *argv); get_addr(&dst, *argv, preferred_family); dst_ok = 1; + dev_ok = 1; req.ndm.ndm_flags |= NTF_PROXY; } else if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); - d = *argv; + dev = *argv; + dev_ok = 1; } else { if (strcmp(*argv, "to") == 0) { NEXT_ARG(); @@ -152,7 +157,7 @@ } argc--; argv++; } - if (d == NULL || !dst_ok || dst.family == AF_UNSPEC) { + if (!dev_ok || !dst_ok || dst.family == AF_UNSPEC) { fprintf(stderr, "Device and destination are required arguments.\n"); exit(-1); } @@ -174,8 +179,8 @@ ll_init_map(&rth); - if ((req.ndm.ndm_ifindex = ll_name_to_index(d)) == 0) { - fprintf(stderr, "Cannot find device \"%s\"\n", d); + if (dev && (req.ndm.ndm_ifindex = ll_name_to_index(dev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", dev); return -1; } @@ -188,11 +193,11 @@ int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct ndmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[NDA_MAX+1]; - char abuf[256]; + struct rtattr *tb[NDA_MAX+1]; + static int logit = 1; if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH && n->nlmsg_type != RTM_GETNEIGH) { @@ -217,16 +222,23 @@ if (!(filter.state&r->ndm_state) && !(r->ndm_flags & NTF_PROXY) && (r->ndm_state || !(filter.state&0x100)) && - (r->ndm_family != AF_DECnet)) + (r->ndm_family != AF_DECnet)) return 0; + if (filter.master && !(n->nlmsg_flags & NLM_F_DUMP_FILTERED)) { + if (logit) { + logit = 0; + fprintf(fp, + "\nWARNING: Kernel does not support filtering by master device\n\n"); + } + } + parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (tb[NDA_DST]) { if (filter.pfx.family) { - inet_prefix dst; - memset(&dst, 0, sizeof(dst)); - dst.family = r->ndm_family; + inet_prefix dst = { .family = r->ndm_family }; + memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST])); if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) return 0; @@ -234,22 +246,24 @@ } if (filter.unused_only && tb[NDA_CACHEINFO]) { struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); + if (ci->ndm_refcnt) return 0; } if (filter.flushb) { struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { if (flush_update()) return -1; } - fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp)); memcpy(fn, n, n->nlmsg_len); fn->nlmsg_type = RTM_DELNEIGH; fn->nlmsg_flags = NLM_F_REQUEST; fn->nlmsg_seq = ++rth.seq; - filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb; filter.flushed++; if (show_stats < 2) return 0; @@ -261,10 +275,7 @@ fprintf(fp, "miss "); if (tb[NDA_DST]) { fprintf(fp, "%s ", - format_host(r->ndm_family, - RTA_PAYLOAD(tb[NDA_DST]), - RTA_DATA(tb[NDA_DST]), - abuf, sizeof(abuf))); + format_host_rta(r->ndm_family, tb[NDA_DST])); } if (!filter.index && r->ndm_ifindex) fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex)); @@ -293,11 +304,13 @@ if (tb[NDA_PROBES] && show_stats) { __u32 p = rta_getattr_u32(tb[NDA_PROBES]); + fprintf(fp, " probes %u", p); } if (r->ndm_state) { int nud = r->ndm_state; + fprintf(fp, " "); #define PRINT_FLAG(f) if (nud & NUD_##f) { \ @@ -327,9 +340,16 @@ static int do_show_or_flush(int argc, char **argv, int flush) { + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req = { + .n.nlmsg_type = RTM_GETNEIGH, + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), + }; char *filter_dev = NULL; int state_given = 0; - struct ndmsg ndm = { 0 }; ipneigh_reset_filter(0); @@ -351,10 +371,31 @@ if (filter_dev) duparg("dev", *argv); filter_dev = *argv; + } else if (strcmp(*argv, "master") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Device does not exist\n", *argv); + addattr32(&req.n, sizeof(req), NDA_MASTER, ifindex); + filter.master = ifindex; + } else if (strcmp(*argv, "vrf") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Not a valid VRF name\n", *argv); + if (!name_is_vrf(*argv)) + invarg("Not a valid VRF name\n", *argv); + addattr32(&req.n, sizeof(req), NDA_MASTER, ifindex); + filter.master = ifindex; } else if (strcmp(*argv, "unused") == 0) { filter.unused_only = 1; } else if (strcmp(*argv, "nud") == 0) { - unsigned state; + unsigned int state; + NEXT_ARG(); if (!state_given) { state_given = 1; @@ -371,7 +412,7 @@ state = 0x100; filter.state |= state; } else if (strcmp(*argv, "proxy") == 0) - ndm.ndm_flags = NTF_PROXY; + req.ndm.ndm_flags = NTF_PROXY; else { if (strcmp(*argv, "to") == 0) { NEXT_ARG(); @@ -392,8 +433,11 @@ fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev); return -1; } + addattr32(&req.n, sizeof(req), NDA_IFINDEX, filter.index); } + req.ndm.ndm_family = filter.family; + if (flush) { int round = 0; char flushb[4096-512]; @@ -404,7 +448,7 @@ filter.state &= ~NUD_FAILED; while (round < MAX_ROUNDS) { - if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) { + if (rtnl_dump_request_n(&rth, &req.n) < 0) { perror("Cannot send dump request"); exit(1); } @@ -418,7 +462,7 @@ if (round == 0) printf("Nothing to flush.\n"); else - printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":""); } fflush(stdout); return 0; @@ -436,9 +480,7 @@ return 1; } - ndm.ndm_family = filter.family; - - if (rtnl_dump_request(&rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg)) < 0) { + if (rtnl_dump_request_n(&rth, &req.n) < 0) { perror("Cannot send dump request"); exit(1); } diff -Nru iproute2-4.3.0/ip/ipnetconf.c iproute2-4.9.0/ip/ipnetconf.c --- iproute2-4.3.0/ip/ipnetconf.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipnetconf.c 2016-12-12 23:07:42.000000000 +0000 @@ -24,8 +24,7 @@ #include "utils.h" #include "ip_common.h" -static struct -{ +static struct { int family; int ifindex; } filter; @@ -38,12 +37,21 @@ exit(-1); } -#define NETCONF_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct netconfmsg)))) +static void print_onoff(FILE *f, const char *flag, __u32 val) +{ + fprintf(f, "%s %s ", flag, val ? "on" : "off"); +} + +static struct rtattr *netconf_rta(struct netconfmsg *ncm) +{ + return (struct rtattr *)((char *)ncm + + NLMSG_ALIGN(sizeof(struct netconfmsg))); +} int print_netconf(const struct sockaddr_nl *who, struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct netconfmsg *ncm = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[NETCONFA_MAX+1]; @@ -65,7 +73,7 @@ if (filter.family && filter.family != ncm->ncm_family) return 0; - parse_rtattr(tb, NETCONFA_MAX, NETCONF_RTA(ncm), + parse_rtattr(tb, NETCONFA_MAX, netconf_rta(ncm), NLMSG_PAYLOAD(n, sizeof(*ncm))); switch (ncm->ncm_family) { @@ -81,7 +89,7 @@ } if (tb[NETCONFA_IFINDEX]) { - int *ifindex = (int *)RTA_DATA(tb[NETCONFA_IFINDEX]); + int *ifindex = (int *)rta_getattr_str(tb[NETCONFA_IFINDEX]); switch (*ifindex) { case NETCONFA_IFINDEX_ALL: @@ -97,10 +105,10 @@ } if (tb[NETCONFA_FORWARDING]) - fprintf(fp, "forwarding %s ", - *(int *)RTA_DATA(tb[NETCONFA_FORWARDING])?"on":"off"); + print_onoff(fp, "forwarding", + rta_getattr_u32(tb[NETCONFA_FORWARDING])); if (tb[NETCONFA_RP_FILTER]) { - int rp_filter = *(int *)RTA_DATA(tb[NETCONFA_RP_FILTER]); + __u32 rp_filter = rta_getattr_u32(tb[NETCONFA_RP_FILTER]); if (rp_filter == 0) fprintf(fp, "rp_filter off "); @@ -112,12 +120,16 @@ fprintf(fp, "rp_filter unknown mode "); } if (tb[NETCONFA_MC_FORWARDING]) - fprintf(fp, "mc_forwarding %d ", - *(int *)RTA_DATA(tb[NETCONFA_MC_FORWARDING])); + print_onoff(fp, "mc_forwarding", + rta_getattr_u32(tb[NETCONFA_MC_FORWARDING])); if (tb[NETCONFA_PROXY_NEIGH]) - fprintf(fp, "proxy_neigh %s ", - *(int *)RTA_DATA(tb[NETCONFA_PROXY_NEIGH])?"on":"off"); + print_onoff(fp, "proxy_neigh", + rta_getattr_u32(tb[NETCONFA_PROXY_NEIGH])); + + if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) + print_onoff(fp, "ignore_routes_with_linkdown", + rta_getattr_u32(tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN])); fprintf(fp, "\n"); fflush(fp); @@ -142,7 +154,11 @@ struct nlmsghdr n; struct netconfmsg ncm; char buf[1024]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .n.nlmsg_type = RTM_GETNETCONF, + }; ipnetconf_reset_filter(0); filter.family = preferred_family; @@ -164,10 +180,6 @@ ll_init_map(&rth); if (filter.ifindex) { - memset(&req, 0, sizeof(req)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; - req.n.nlmsg_type = RTM_GETNETCONF; req.ncm.ncm_family = filter.family; if (filter.ifindex) addattr_l(&req.n, sizeof(req), NETCONFA_IFINDEX, diff -Nru iproute2-4.3.0/ip/ipnetns.c iproute2-4.9.0/ip/ipnetns.c --- iproute2-4.3.0/ip/ipnetns.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipnetns.c 2016-12-12 23:07:42.000000000 +0000 @@ -18,7 +18,7 @@ #include #include "utils.h" -#include "hlist.h" +#include "list.h" #include "ip_common.h" #include "namespace.h" @@ -61,20 +61,19 @@ struct nlmsghdr n; struct rtgenmsg g; char buf[1024]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNSID, + .g.rtgen_family = AF_UNSPEC, + }; int fd; if (have_rtnl_getnsid < 0) { - memset(&req, 0, sizeof(req)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETNSID; - req.g.rtgen_family = AF_UNSPEC; - fd = open("/proc/self/ns/net", O_RDONLY); if (fd < 0) { - perror("open(\"/proc/self/ns/net\")"); - exit(1); + have_rtnl_getnsid = 0; + return 0; } addattr32(&req.n, 1024, NETNSA_FD, fd); @@ -96,17 +95,16 @@ struct nlmsghdr n; struct rtgenmsg g; char buf[1024]; - } req, answer; + } answer, req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNSID, + .g.rtgen_family = AF_UNSPEC, + }; struct rtattr *tb[NETNSA_MAX + 1]; struct rtgenmsg *rthdr; int len, fd; - memset(&req, 0, sizeof(req)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETNSID; - req.g.rtgen_family = AF_UNSPEC; - fd = netns_get_fd(name); if (fd < 0) return fd; @@ -172,7 +170,7 @@ if (netns_map_get_by_nsid(nsid) != NULL) return -EEXIST; - c = malloc(sizeof(*c) + strlen(name)); + c = malloc(sizeof(*c) + strlen(name) + 1); if (c == NULL) { perror("malloc"); return -ENOMEM; @@ -196,6 +194,18 @@ free(c); } +void netns_nsid_socket_init(void) +{ + if (rtnsh.fd > -1 || !ipnetns_have_nsid()) + return; + + if (rtnl_open(&rtnsh, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + exit(1); + } + +} + void netns_map_init(void) { static int initialized; @@ -206,11 +216,6 @@ if (initialized || !ipnetns_have_nsid()) return; - if (rtnl_open(&rtnsh, 0) < 0) { - fprintf(stderr, "Cannot open rtnetlink\n"); - exit(1); - } - dir = opendir(NETNS_RUN_DIR); if (!dir) return; @@ -389,6 +394,7 @@ static int on_netns_exec(char *nsname, void *arg) { char **argv = arg; + cmd_exec(argv[1], argv + 1, true); return 0; } @@ -426,6 +432,7 @@ static int is_pid(const char *str) { int ch; + for (; (ch = *str); str++) { if (!isdigit(ch)) return 0; @@ -470,9 +477,10 @@ strerror(errno)); return -1; } - while((entry = readdir(dir))) { + while ((entry = readdir(dir))) { char pid_net_path[PATH_MAX]; struct stat st; + if (!is_pid(entry->d_name)) continue; snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%s/ns/net", @@ -535,7 +543,7 @@ return -1; } - while((entry = readdir(dir))) { + while ((entry = readdir(dir))) { char name_path[PATH_MAX]; struct stat st; @@ -682,15 +690,14 @@ struct nlmsghdr n; struct rtgenmsg g; char buf[1024]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_NEWNSID, + .g.rtgen_family = AF_UNSPEC, + }; int fd, err = 0; - memset(&req, 0, sizeof(req)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_NEWNSID; - req.g.rtgen_family = AF_UNSPEC; - fd = netns_get_fd(name); if (fd < 0) return fd; @@ -737,6 +744,7 @@ char buf[4096]; struct inotify_event *event; int fd; + fd = inotify_init(); if (fd < 0) { fprintf(stderr, "inotify_init failed: %s\n", @@ -752,8 +760,9 @@ strerror(errno)); return -1; } - for(;;) { + for (;;) { ssize_t len = read(fd, buf, sizeof(buf)); + if (len < 0) { fprintf(stderr, "read failed: %s\n", strerror(errno)); @@ -773,17 +782,23 @@ int do_netns(int argc, char **argv) { - netns_map_init(); + netns_nsid_socket_init(); - if (argc < 1) + if (argc < 1) { + netns_map_init(); return netns_list(0, NULL); + } if ((matches(*argv, "list") == 0) || (matches(*argv, "show") == 0) || - (matches(*argv, "lst") == 0)) + (matches(*argv, "lst") == 0)) { + netns_map_init(); return netns_list(argc-1, argv+1); + } - if ((matches(*argv, "list-id") == 0)) + if ((matches(*argv, "list-id") == 0)) { + netns_map_init(); return netns_list_id(argc-1, argv+1); + } if (matches(*argv, "help") == 0) return usage(); diff -Nru iproute2-4.3.0/ip/ipntable.c iproute2-4.9.0/ip/ipntable.c --- iproute2-4.3.0/ip/ipntable.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipntable.c 2016-12-12 23:07:42.000000000 +0000 @@ -35,7 +35,7 @@ static struct { int family; - int index; + int index; #define NONE_DEV (-1) char name[1024]; } filter; @@ -52,7 +52,7 @@ "PARMS := [ base_reachable MSEC ] [ retrans MSEC ] [ gc_stale MSEC ]\n" " [ delay_probe MSEC ] [ queue LEN ]\n" - " [ app_probs VAL ] [ ucast_probes VAL ] [ mcast_probes VAL ]\n" + " [ app_probes VAL ] [ ucast_probes VAL ] [ mcast_probes VAL ]\n" " [ anycast_delay MSEC ] [ proxy_delay MSEC ] [ proxy_queue LEN ]\n" " [ locktime MSEC ]\n" ); @@ -65,27 +65,20 @@ struct { struct nlmsghdr n; struct ndtmsg ndtm; - char buf[1024]; - } req; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .ndtm.ndtm_family = preferred_family, + }; char *namep = NULL; char *threshsp = NULL; char *gc_intp = NULL; - char parms_buf[1024]; + char parms_buf[1024] = {}; struct rtattr *parms_rta = (struct rtattr *)parms_buf; int parms_change = 0; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - - req.ndtm.ndtm_family = preferred_family; - req.ndtm.ndtm_pad1 = 0; - req.ndtm.ndtm_pad2 = 0; - - memset(&parms_buf, 0, sizeof(parms_buf)); - parms_rta->rta_type = NDTA_PARMS; parms_rta->rta_len = RTA_LENGTH(0); @@ -322,15 +315,13 @@ static const char *ntable_strtime_delta(__u32 msec) { static char str[32]; - struct timeval now; + struct timeval now = {}; time_t t; struct tm *tp; if (msec == 0) goto error; - memset(&now, 0, sizeof(now)); - if (gettimeofday(&now, NULL) < 0) { perror("gettimeofday"); goto error; @@ -349,9 +340,9 @@ return str; } -int print_ntable(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +static int print_ntable(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct ndtmsg *ndtm = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[NDTA_MAX+1]; @@ -407,6 +398,7 @@ if (tb[NDTA_NAME]) { const char *name = rta_getattr_str(tb[NDTA_NAME]); + fprintf(fp, "%s ", name); } @@ -419,18 +411,22 @@ if (tb[NDTA_THRESH1]) { __u32 thresh1 = rta_getattr_u32(tb[NDTA_THRESH1]); + fprintf(fp, "thresh1 %u ", thresh1); } if (tb[NDTA_THRESH2]) { __u32 thresh2 = rta_getattr_u32(tb[NDTA_THRESH2]); + fprintf(fp, "thresh2 %u ", thresh2); } if (tb[NDTA_THRESH3]) { __u32 thresh3 = rta_getattr_u32(tb[NDTA_THRESH3]); + fprintf(fp, "thresh3 %u ", thresh3); } if (tb[NDTA_GC_INTERVAL]) { unsigned long long gc_int = rta_getattr_u64(tb[NDTA_GC_INTERVAL]); + fprintf(fp, "gc_int %llu ", gc_int); } @@ -480,18 +476,22 @@ if (tpb[NDTPA_REFCNT]) { __u32 refcnt = rta_getattr_u32(tpb[NDTPA_REFCNT]); + fprintf(fp, "refcnt %u ", refcnt); } if (tpb[NDTPA_REACHABLE_TIME]) { unsigned long long reachable = rta_getattr_u64(tpb[NDTPA_REACHABLE_TIME]); + fprintf(fp, "reachable %llu ", reachable); } if (tpb[NDTPA_BASE_REACHABLE_TIME]) { unsigned long long breachable = rta_getattr_u64(tpb[NDTPA_BASE_REACHABLE_TIME]); + fprintf(fp, "base_reachable %llu ", breachable); } if (tpb[NDTPA_RETRANS_TIME]) { unsigned long long retrans = rta_getattr_u64(tpb[NDTPA_RETRANS_TIME]); + fprintf(fp, "retrans %llu ", retrans); } @@ -501,14 +501,17 @@ if (tpb[NDTPA_GC_STALETIME]) { unsigned long long gc_stale = rta_getattr_u64(tpb[NDTPA_GC_STALETIME]); + fprintf(fp, "gc_stale %llu ", gc_stale); } if (tpb[NDTPA_DELAY_PROBE_TIME]) { unsigned long long delay_probe = rta_getattr_u64(tpb[NDTPA_DELAY_PROBE_TIME]); + fprintf(fp, "delay_probe %llu ", delay_probe); } if (tpb[NDTPA_QUEUE_LEN]) { __u32 queue = rta_getattr_u32(tpb[NDTPA_QUEUE_LEN]); + fprintf(fp, "queue %u ", queue); } @@ -518,14 +521,17 @@ if (tpb[NDTPA_APP_PROBES]) { __u32 aprobe = rta_getattr_u32(tpb[NDTPA_APP_PROBES]); + fprintf(fp, "app_probes %u ", aprobe); } if (tpb[NDTPA_UCAST_PROBES]) { __u32 uprobe = rta_getattr_u32(tpb[NDTPA_UCAST_PROBES]); + fprintf(fp, "ucast_probes %u ", uprobe); } if (tpb[NDTPA_MCAST_PROBES]) { __u32 mprobe = rta_getattr_u32(tpb[NDTPA_MCAST_PROBES]); + fprintf(fp, "mcast_probes %u ", mprobe); } @@ -535,18 +541,22 @@ if (tpb[NDTPA_ANYCAST_DELAY]) { unsigned long long anycast_delay = rta_getattr_u64(tpb[NDTPA_ANYCAST_DELAY]); + fprintf(fp, "anycast_delay %llu ", anycast_delay); } if (tpb[NDTPA_PROXY_DELAY]) { unsigned long long proxy_delay = rta_getattr_u64(tpb[NDTPA_PROXY_DELAY]); + fprintf(fp, "proxy_delay %llu ", proxy_delay); } if (tpb[NDTPA_PROXY_QLEN]) { __u32 pqueue = rta_getattr_u32(tpb[NDTPA_PROXY_QLEN]); + fprintf(fp, "proxy_queue %u ", pqueue); } if (tpb[NDTPA_LOCKTIME]) { unsigned long long locktime = rta_getattr_u64(tpb[NDTPA_LOCKTIME]); + fprintf(fp, "locktime %llu ", locktime); } @@ -601,7 +611,7 @@ return 0; } -void ipntable_reset_filter(void) +static void ipntable_reset_filter(void) { memset(&filter, 0, sizeof(filter)); } diff -Nru iproute2-4.3.0/ip/ipprefix.c iproute2-4.9.0/ip/ipprefix.c --- iproute2-4.3.0/ip/ipprefix.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipprefix.c 2016-12-12 23:07:42.000000000 +0000 @@ -37,10 +37,10 @@ int print_prefix(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct prefixmsg *prefix = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[RTA_MAX+1]; + struct rtattr *tb[RTA_MAX+1]; int family = preferred_family; if (n->nlmsg_type != RTM_NEWPREFIX) { @@ -71,22 +71,11 @@ parse_rtattr(tb, RTA_MAX, RTM_RTA(prefix), len); - fprintf(fp, "prefix "); - if (tb[PREFIX_ADDRESS]) { - struct in6_addr *pfx; - char abuf[256]; - - pfx = (struct in6_addr *)RTA_DATA(tb[PREFIX_ADDRESS]); - - memset(abuf, '\0', sizeof(abuf)); - fprintf(fp, "%s", rt_addr_n2a(family, - RTA_PAYLOAD(tb[PREFIX_ADDRESS]), - pfx, - abuf, sizeof(abuf))); + fprintf(fp, "prefix %s/%u", + rt_addr_n2a_rta(family, tb[PREFIX_ADDRESS]), + prefix->prefix_len); } - fprintf(fp, "/%u ", prefix->prefix_len); - fprintf(fp, "dev %s ", ll_index_to_name(prefix->prefix_ifindex)); if (prefix->prefix_flags & IF_PREFIX_ONLINK) @@ -96,6 +85,7 @@ if (tb[PREFIX_CACHEINFO]) { struct prefix_cacheinfo *pc; + pc = (struct prefix_cacheinfo *)RTA_DATA(tb[PREFIX_CACHEINFO]); fprintf(fp, "valid %u ", pc->valid_time); diff -Nru iproute2-4.3.0/ip/iproute.c iproute2-4.9.0/ip/iproute.c --- iproute2-4.3.0/ip/iproute.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iproute.c 2016-12-12 23:07:42.000000000 +0000 @@ -29,6 +29,7 @@ #include "rt_names.h" #include "utils.h" #include "ip_common.h" +#include "iproute_lwtunnel.h" #ifndef RTAX_RTTVAR #define RTAX_RTTVAR RTAX_HOPS @@ -47,7 +48,7 @@ [RTAX_SSTHRESH] = "ssthresh", [RTAX_CWND] = "cwnd", [RTAX_ADVMSS] = "advmss", - [RTAX_REORDERING]="reordering", + [RTAX_REORDERING] = "reordering", [RTAX_HOPLIMIT] = "hoplimit", [RTAX_INITCWND] = "initcwnd", [RTAX_FEATURES] = "features", @@ -66,27 +67,28 @@ fprintf(stderr, " ip route showdump\n"); fprintf(stderr, " ip route get ADDRESS [ from ADDRESS iif STRING ]\n"); fprintf(stderr, " [ oif STRING ] [ tos TOS ]\n"); - fprintf(stderr, " [ mark NUMBER ]\n"); + fprintf(stderr, " [ mark NUMBER ] [ vrf NAME ]\n"); fprintf(stderr, " ip route { add | del | change | append | replace } ROUTE\n"); fprintf(stderr, "SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n"); - fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ vrf NAME ] [ proto RTPROTO ]\n"); fprintf(stderr, " [ type TYPE ] [ scope SCOPE ]\n"); fprintf(stderr, "ROUTE := NODE_SPEC [ INFO_SPEC ]\n"); fprintf(stderr, "NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n"); fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); fprintf(stderr, " [ scope SCOPE ] [ metric METRIC ]\n"); fprintf(stderr, "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n"); - fprintf(stderr, "NH := [ via [ FAMILY ] ADDRESS ] [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"); + fprintf(stderr, "NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ]\n"); + fprintf(stderr, " [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"); fprintf(stderr, "FAMILY := [ inet | inet6 | ipx | dnet | mpls | bridge | link ]\n"); fprintf(stderr, "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ] [ as [ to ] ADDRESS ]\n"); fprintf(stderr, " [ rtt TIME ] [ rttvar TIME ] [ reordering NUMBER ]\n"); - fprintf(stderr, " [ window NUMBER] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n"); + fprintf(stderr, " [ window NUMBER ] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n"); fprintf(stderr, " [ ssthresh NUMBER ] [ realms REALM ] [ src ADDRESS ]\n"); fprintf(stderr, " [ rto_min TIME ] [ hoplimit NUMBER ] [ initrwnd NUMBER ]\n"); fprintf(stderr, " [ features FEATURES ] [ quickack BOOL ] [ congctl NAME ]\n"); - fprintf(stderr, " [ pref PREF ]\n"); - fprintf(stderr, "TYPE := [ unicast | local | broadcast | multicast | throw |\n"); - fprintf(stderr, " unreachable | prohibit | blackhole | nat ]\n"); + fprintf(stderr, " [ pref PREF ] [ expires TIME ]\n"); + fprintf(stderr, "TYPE := { unicast | local | broadcast | multicast | throw |\n"); + fprintf(stderr, " unreachable | prohibit | blackhole | nat }\n"); fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n"); fprintf(stderr, "SCOPE := [ host | link | global | NUMBER ]\n"); fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n"); @@ -95,6 +97,8 @@ fprintf(stderr, "TIME := NUMBER[s|ms]\n"); fprintf(stderr, "BOOL := [1|0]\n"); fprintf(stderr, "FEATURES := ecn\n"); + fprintf(stderr, "ENCAPTYPE := [ mpls | ip | ip6 ]\n"); + fprintf(stderr, "ENCAPHDR := [ MPLSLABEL ]\n"); exit(-1); } @@ -109,7 +113,7 @@ int flushe; int protocol, protocolmask; int scope, scopemask; - int type, typemask; + __u64 typemask; int tos, tosmask; int iif, iifmask; int oif, oifmask; @@ -136,10 +140,10 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) { struct rtmsg *r = NLMSG_DATA(n); - inet_prefix dst; - inet_prefix src; - inet_prefix via; - inet_prefix prefsrc; + inet_prefix dst = { .family = r->rtm_family }; + inet_prefix src = { .family = r->rtm_family }; + inet_prefix via = { .family = r->rtm_family }; + inet_prefix prefsrc = { .family = r->rtm_family }; __u32 table; static int ip6_multiple_tables; @@ -174,7 +178,8 @@ return 0; if ((filter.scope^r->rtm_scope)&filter.scopemask) return 0; - if ((filter.type^r->rtm_type)&filter.typemask) + + if (filter.typemask && !(filter.typemask & (1 << r->rtm_type))) return 0; if ((filter.tos^r->rtm_tos)&filter.tosmask) return 0; @@ -194,8 +199,10 @@ return 0; if (filter.rvia.family) { int family = r->rtm_family; + if (tb[RTA_VIA]) { struct rtvia *via = RTA_DATA(tb[RTA_VIA]); + family = via->rtvia_family; } if (family != filter.rvia.family) @@ -204,31 +211,24 @@ if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family) return 0; - memset(&dst, 0, sizeof(dst)); - dst.family = r->rtm_family; if (tb[RTA_DST]) memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8); if (filter.rsrc.family || filter.msrc.family) { - memset(&src, 0, sizeof(src)); - src.family = r->rtm_family; if (tb[RTA_SRC]) memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8); } - if (filter.rvia.bitlen>0) { - memset(&via, 0, sizeof(via)); - via.family = r->rtm_family; + if (filter.rvia.bitlen > 0) { if (tb[RTA_GATEWAY]) memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len/8); if (tb[RTA_VIA]) { size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2; struct rtvia *rtvia = RTA_DATA(tb[RTA_VIA]); + via.family = rtvia->rtvia_family; memcpy(&via.data, rtvia->rtvia_addr, len); } } - if (filter.rprefsrc.bitlen>0) { - memset(&prefsrc, 0, sizeof(prefsrc)); - prefsrc.family = r->rtm_family; + if (filter.rprefsrc.bitlen > 0) { if (tb[RTA_PREFSRC]) memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len/8); } @@ -251,6 +251,7 @@ return 0; if (filter.realmmask) { __u32 realms = 0; + if (tb[RTA_FLOW]) realms = rta_getattr_u32(tb[RTA_FLOW]); if ((realms^filter.realm)&filter.realmmask) @@ -258,20 +259,23 @@ } if (filter.iifmask) { int iif = 0; + if (tb[RTA_IIF]) - iif = *(int*)RTA_DATA(tb[RTA_IIF]); + iif = *(int *)RTA_DATA(tb[RTA_IIF]); if ((iif^filter.iif)&filter.iifmask) return 0; } if (filter.oifmask) { int oif = 0; + if (tb[RTA_OIF]) - oif = *(int*)RTA_DATA(tb[RTA_OIF]); + oif = *(int *)RTA_DATA(tb[RTA_OIF]); if ((oif^filter.oif)&filter.oifmask) return 0; } if (filter.markmask) { int mark = 0; + if (tb[RTA_MARK]) mark = *(int *)RTA_DATA(tb[RTA_MARK]); if ((mark ^ filter.mark) & filter.markmask) @@ -282,7 +286,7 @@ r->rtm_dst_len == 0 && r->rtm_type == RTN_UNREACHABLE && tb[RTA_PRIORITY] && - *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) + *(int *)RTA_DATA(tb[RTA_PRIORITY]) == -1) return 0; return 1; @@ -303,20 +307,20 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[RTA_MAX+1]; - char abuf[256]; - int host_len; + struct rtattr *tb[RTA_MAX+1]; + int host_len, family; __u32 table; + SPRINT_BUF(b1); static int hz; if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { fprintf(stderr, "Not a route: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); - return 0; + return -1; } if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) return 0; @@ -336,16 +340,17 @@ if (filter.flushb) { struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { if (flush_update()) return -1; } - fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp)); memcpy(fn, n, n->nlmsg_len); fn->nlmsg_type = RTM_DELROUTE; fn->nlmsg_flags = NLM_F_REQUEST; fn->nlmsg_seq = ++rth.seq; - filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb; filter.flushed++; if (show_stats < 2) return 0; @@ -353,23 +358,19 @@ if (n->nlmsg_type == RTM_DELROUTE) fprintf(fp, "Deleted "); - if ((r->rtm_type != RTN_UNICAST || show_details > 0) && !filter.type) + if ((r->rtm_type != RTN_UNICAST || show_details > 0) && + (!filter.typemask || (filter.typemask & (1 << r->rtm_type)))) fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); if (tb[RTA_DST]) { + family = get_real_family(r->rtm_type, r->rtm_family); if (r->rtm_dst_len != host_len) { - fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[RTA_DST]), - RTA_DATA(tb[RTA_DST]), - abuf, sizeof(abuf)), - r->rtm_dst_len - ); + fprintf(fp, "%s/%u ", + rt_addr_n2a_rta(family, tb[RTA_DST]), + r->rtm_dst_len); } else { - fprintf(fp, "%s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_DST]), - RTA_DATA(tb[RTA_DST]), - abuf, sizeof(abuf)) - ); + fprintf(fp, "%s ", + format_host_rta(family, tb[RTA_DST])); } } else if (r->rtm_dst_len) { fprintf(fp, "0/%d ", r->rtm_dst_len); @@ -377,30 +378,26 @@ fprintf(fp, "default "); } if (tb[RTA_SRC]) { + family = get_real_family(r->rtm_type, r->rtm_family); if (r->rtm_src_len != host_len) { - fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[RTA_SRC]), - RTA_DATA(tb[RTA_SRC]), - abuf, sizeof(abuf)), - r->rtm_src_len - ); + fprintf(fp, "from %s/%u ", + rt_addr_n2a_rta(family, tb[RTA_SRC]), + r->rtm_src_len); } else { - fprintf(fp, "from %s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_SRC]), - RTA_DATA(tb[RTA_SRC]), - abuf, sizeof(abuf)) - ); + fprintf(fp, "from %s ", + format_host_rta(family, tb[RTA_SRC])); } } else if (r->rtm_src_len) { fprintf(fp, "from 0/%u ", r->rtm_src_len); } if (tb[RTA_NEWDST]) { - fprintf(fp, "as to %s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_NEWDST]), - RTA_DATA(tb[RTA_NEWDST]), - abuf, sizeof(abuf)) - ); + fprintf(fp, "as to %s ", + format_host_rta(r->rtm_family, tb[RTA_NEWDST])); } + + if (tb[RTA_ENCAP]) + lwt_print_encap(fp, tb[RTA_ENCAP_TYPE], tb[RTA_ENCAP]); + if (r->rtm_tos && filter.tosmask != -1) { SPRINT_BUF(b1); fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); @@ -408,42 +405,36 @@ if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) { fprintf(fp, "via %s ", - format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_GATEWAY]), - RTA_DATA(tb[RTA_GATEWAY]), - abuf, sizeof(abuf))); + format_host_rta(r->rtm_family, tb[RTA_GATEWAY])); } if (tb[RTA_VIA]) { size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2; struct rtvia *via = RTA_DATA(tb[RTA_VIA]); + fprintf(fp, "via %s %s ", family_name(via->rtvia_family), - format_host(via->rtvia_family, len, via->rtvia_addr, - abuf, sizeof(abuf))); + format_host(via->rtvia_family, len, via->rtvia_addr)); } if (tb[RTA_OIF] && filter.oifmask != -1) - fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); + fprintf(fp, "dev %s ", ll_index_to_name(*(int *)RTA_DATA(tb[RTA_OIF]))); + if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb) + fprintf(fp, "table %s ", rtnl_rttable_n2a(table, b1, sizeof(b1))); if (!(r->rtm_flags&RTM_F_CLONED)) { - if ((table != RT_TABLE_MAIN || show_details > 0) && !filter.tb) - fprintf(fp, " table %s ", rtnl_rttable_n2a(table, b1, sizeof(b1))); if ((r->rtm_protocol != RTPROT_BOOT || show_details > 0) && filter.protocolmask != -1) - fprintf(fp, " proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1))); + fprintf(fp, "proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1))); if ((r->rtm_scope != RT_SCOPE_UNIVERSE || show_details > 0) && filter.scopemask != -1) - fprintf(fp, " scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1))); + fprintf(fp, "scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1))); } if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { /* Do not use format_host(). It is our local addr and symbolic name will not be useful. */ - fprintf(fp, " src %s ", - rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[RTA_PREFSRC]), - RTA_DATA(tb[RTA_PREFSRC]), - abuf, sizeof(abuf))); + fprintf(fp, "src %s ", + rt_addr_n2a_rta(r->rtm_family, tb[RTA_PREFSRC])); } if (tb[RTA_PRIORITY]) - fprintf(fp, " metric %u ", rta_getattr_u32(tb[RTA_PRIORITY])); + fprintf(fp, "metric %u ", rta_getattr_u32(tb[RTA_PRIORITY])); if (r->rtm_flags & RTNH_F_DEAD) fprintf(fp, "dead "); if (r->rtm_flags & RTNH_F_ONLINK) @@ -457,18 +448,20 @@ if (r->rtm_flags & RTNH_F_LINKDOWN) fprintf(fp, "linkdown "); if (tb[RTA_MARK]) { - unsigned int mark = *(unsigned int*)RTA_DATA(tb[RTA_MARK]); + unsigned int mark = *(unsigned int *)RTA_DATA(tb[RTA_MARK]); + if (mark) { if (mark >= 16) - fprintf(fp, " mark 0x%x", mark); + fprintf(fp, "mark 0x%x ", mark); else - fprintf(fp, " mark %u", mark); + fprintf(fp, "mark %u ", mark); } } if (tb[RTA_FLOW] && filter.realmmask != ~0U) { __u32 to = rta_getattr_u32(tb[RTA_FLOW]); __u32 from = to>>16; + to &= 0xFFFF; fprintf(fp, "realm%s ", from ? "s" : ""); if (from) { @@ -484,7 +477,7 @@ fprintf(fp, "%s cache ", _SL_); -#define PRTFL(fl,flname) if (flags&RTCF_##fl) { \ +#define PRTFL(fl, flname) if (flags&RTCF_##fl) { \ flags &= ~RTCF_##fl; \ fprintf(fp, "%s" flname "%s", first ? "<" : "", flags ? "," : "> "); \ first = 0; } @@ -507,6 +500,7 @@ fprintf(fp, "%s%x> ", first ? "<" : "", flags); if (tb[RTA_CACHEINFO]) { struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]); + if (!hz) hz = get_user_hz(); if (ci->rta_expires != 0) @@ -529,6 +523,7 @@ } } else if (r->rtm_family == AF_INET6) { struct rta_cacheinfo *ci = NULL; + if (tb[RTA_CACHEINFO]) ci = RTA_DATA(tb[RTA_CACHEINFO]); if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) { @@ -555,38 +550,38 @@ } if (tb[RTA_METRICS]) { int i; - unsigned mxlock = 0; + unsigned int mxlock = 0; struct rtattr *mxrta[RTAX_MAX+1]; parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]), RTA_PAYLOAD(tb[RTA_METRICS])); if (mxrta[RTAX_LOCK]) - mxlock = *(unsigned*)RTA_DATA(mxrta[RTAX_LOCK]); + mxlock = *(unsigned *)RTA_DATA(mxrta[RTAX_LOCK]); - for (i=2; i<= RTAX_MAX; i++) { - __u32 val; + for (i = 2; i <= RTAX_MAX; i++) { + __u32 val = 0U; if (mxrta[i] == NULL) continue; - if (i < sizeof(mx_names)/sizeof(char*) && mx_names[i]) + if (i != RTAX_CC_ALGO) + val = rta_getattr_u32(mxrta[i]); + + if (i == RTAX_HOPLIMIT && (int)val == -1) + continue; + + if (i < sizeof(mx_names)/sizeof(char *) && mx_names[i]) fprintf(fp, " %s", mx_names[i]); else fprintf(fp, " metric %d", i); if (mxlock & (1<rtnh_len > sizeof(*nh)) { parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh)); + + if (tb[RTA_ENCAP]) + lwt_print_encap(fp, + tb[RTA_ENCAP_TYPE], + tb[RTA_ENCAP]); + if (tb[RTA_NEWDST]) { + fprintf(fp, " as to %s ", + format_host_rta(r->rtm_family, + tb[RTA_NEWDST])); + } if (tb[RTA_GATEWAY]) { fprintf(fp, " via %s ", - format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_GATEWAY]), - RTA_DATA(tb[RTA_GATEWAY]), - abuf, sizeof(abuf))); + format_host_rta(r->rtm_family, + tb[RTA_GATEWAY])); } if (tb[RTA_VIA]) { size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2; struct rtvia *via = RTA_DATA(tb[RTA_VIA]); + fprintf(fp, "via %s %s ", family_name(via->rtvia_family), - format_host(via->rtvia_family, len, via->rtvia_addr, - abuf, sizeof(abuf))); + format_host(via->rtvia_family, len, via->rtvia_addr)); } if (tb[RTA_FLOW]) { __u32 to = rta_getattr_u32(tb[RTA_FLOW]); __u32 from = to>>16; + to &= 0xFFFF; fprintf(fp, " realm%s ", from ? "s" : ""); if (from) { @@ -667,7 +671,9 @@ fprintf(fp, "(ttl>%d)", nh->rtnh_hops); } else { fprintf(fp, " dev %s", ll_index_to_name(nh->rtnh_ifindex)); - fprintf(fp, " weight %d", nh->rtnh_hops+1); + if (r->rtm_family != AF_MPLS) + fprintf(fp, " weight %d", + nh->rtnh_hops+1); } if (nh->rtnh_flags & RTNH_F_DEAD) fprintf(fp, " dead"); @@ -683,6 +689,7 @@ } if (tb[RTA_PREF]) { unsigned int pref = rta_getattr_u8(tb[RTA_PREF]); + fprintf(fp, " pref "); switch (pref) { @@ -704,9 +711,8 @@ return 0; } - -static int parse_one_nh(struct rtmsg *r, struct rtattr *rta, - struct rtnexthop *rtnh, +static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r, + struct rtattr *rta, struct rtnexthop *rtnh, int *argcp, char ***argvp) { int argc = *argcp; @@ -716,6 +722,7 @@ if (strcmp(*argv, "via") == 0) { inet_prefix addr; int family; + NEXT_ARG(); family = read_family(*argv); if (family == AF_UNSPEC) @@ -730,7 +737,7 @@ rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen; } else { rta_addattr_l(rta, 4096, RTA_VIA, &addr.family, addr.bytelen+2); - rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen+2; + rtnh->rtnh_len += RTA_SPACE(addr.bytelen+2); } } else if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); @@ -739,7 +746,8 @@ exit(1); } } else if (strcmp(*argv, "weight") == 0) { - unsigned w; + unsigned int w; + NEXT_ARG(); if (get_unsigned(&w, *argv, 0) || w == 0 || w > 256) invarg("\"weight\" is invalid\n", *argv); @@ -748,11 +756,27 @@ rtnh->rtnh_flags |= RTNH_F_ONLINK; } else if (matches(*argv, "realms") == 0) { __u32 realm; + NEXT_ARG(); if (get_rt_realms_or_raw(&realm, *argv)) invarg("\"realm\" value is invalid\n", *argv); rta_addattr32(rta, 4096, RTA_FLOW, realm); rtnh->rtnh_len += sizeof(struct rtattr) + 4; + } else if (strcmp(*argv, "encap") == 0) { + int len = rta->rta_len; + + lwt_parse_encap(rta, 4096, &argc, &argv); + rtnh->rtnh_len += rta->rta_len - len; + } else if (strcmp(*argv, "as") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (strcmp(*argv, "to") == 0) + NEXT_ARG(); + get_addr(&addr, *argv, r->rtm_family); + rta_addattr_l(rta, 4096, RTA_NEWDST, &addr.data, + addr.bytelen); + rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen; } else break; } @@ -765,7 +789,7 @@ int argc, char **argv) { char buf[1024]; - struct rtattr *rta = (void*)buf; + struct rtattr *rta = (void *)buf; struct rtnexthop *rtnh; rta->rta_type = RTA_MULTIPATH; @@ -784,7 +808,7 @@ memset(rtnh, 0, sizeof(*rtnh)); rtnh->rtnh_len = sizeof(*rtnh); rta->rta_len += rtnh->rtnh_len; - parse_one_nh(r, rta, rtnh, &argc, &argv); + parse_one_nh(n, r, rta, rtnh, &argc, &argv); rtnh = RTNH_NEXT(rtnh); } @@ -793,16 +817,23 @@ return 0; } -static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) +static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) { struct { struct nlmsghdr n; struct rtmsg r; - char buf[1024]; - } req; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .r.rtm_family = preferred_family, + .r.rtm_table = RT_TABLE_MAIN, + .r.rtm_scope = RT_SCOPE_NOWHERE, + }; char mxbuf[256]; - struct rtattr * mxrta = (void*)mxbuf; - unsigned mxlock = 0; + struct rtattr *mxrta = (void *)mxbuf; + unsigned int mxlock = 0; char *d = NULL; int gw_ok = 0; int dst_ok = 0; @@ -812,15 +843,6 @@ int raw = 0; int type_ok = 0; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.r.rtm_family = preferred_family; - req.r.rtm_table = RT_TABLE_MAIN; - req.r.rtm_scope = RT_SCOPE_NOWHERE; - if (cmd != RTM_DELROUTE) { req.r.rtm_protocol = RTPROT_BOOT; req.r.rtm_scope = RT_SCOPE_UNIVERSE; @@ -833,6 +855,7 @@ while (argc > 0) { if (strcmp(*argv, "src") == 0) { inet_prefix addr; + NEXT_ARG(); get_addr(&addr, *argv, req.r.rtm_family); if (req.r.rtm_family == AF_UNSPEC) @@ -840,6 +863,7 @@ addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen); } else if (strcmp(*argv, "as") == 0) { inet_prefix addr; + NEXT_ARG(); if (strcmp(*argv, "to") == 0) { NEXT_ARG(); @@ -851,6 +875,7 @@ } else if (strcmp(*argv, "via") == 0) { inet_prefix addr; int family; + gw_ok = 1; NEXT_ARG(); family = read_family(*argv); @@ -862,11 +887,14 @@ if (req.r.rtm_family == AF_UNSPEC) req.r.rtm_family = addr.family; if (addr.family == req.r.rtm_family) - addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen); + addattr_l(&req.n, sizeof(req), RTA_GATEWAY, + &addr.data, addr.bytelen); else - addattr_l(&req.n, sizeof(req), RTA_VIA, &addr.family, addr.bytelen+2); + addattr_l(&req.n, sizeof(req), RTA_VIA, + &addr.family, addr.bytelen+2); } else if (strcmp(*argv, "from") == 0) { inet_prefix addr; + NEXT_ARG(); get_prefix(&addr, *argv, req.r.rtm_family); if (req.r.rtm_family == AF_UNSPEC) @@ -877,27 +905,38 @@ } else if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { __u32 tos; + NEXT_ARG(); if (rtnl_dsfield_a2n(&tos, *argv)) invarg("\"tos\" value is invalid\n", *argv); req.r.rtm_tos = tos; + } else if (strcmp(*argv, "expires") == 0) { + __u32 expires; + + NEXT_ARG(); + if (get_u32(&expires, *argv, 0)) + invarg("\"expires\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_EXPIRES, expires); } else if (matches(*argv, "metric") == 0 || matches(*argv, "priority") == 0 || strcmp(*argv, "preference") == 0) { __u32 metric; + NEXT_ARG(); if (get_u32(&metric, *argv, 0)) invarg("\"metric\" value is invalid\n", *argv); addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric); } else if (strcmp(*argv, "scope") == 0) { __u32 scope = 0; + NEXT_ARG(); if (rtnl_rtscope_a2n(&scope, *argv)) invarg("invalid \"scope\" value\n", *argv); req.r.rtm_scope = scope; scope_ok = 1; } else if (strcmp(*argv, "mtu") == 0) { - unsigned mtu; + unsigned int mtu; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1< 255) invarg("\"hoplimit\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit); } else if (strcmp(*argv, "advmss") == 0) { - unsigned mss; + unsigned int mss; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<rta_type = RTA_ENCAP; + rta->rta_len = RTA_LENGTH(0); + + lwt_parse_encap(rta, sizeof(buf), &argc, &argv); + + if (rta->rta_len > RTA_LENGTH(0)) + addraw_l(&req.n, 1024, RTA_DATA(rta), RTA_PAYLOAD(rta)); } else { int type; inet_prefix dst; @@ -1189,22 +1269,17 @@ struct { struct nlmsghdr nlh; struct rtmsg rtm; - } req; - struct sockaddr_nl nladdr; + } req = { + .nlh.nlmsg_len = sizeof(req), + .nlh.nlmsg_type = RTM_GETROUTE, + .nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .rtm.rtm_family = family, + .rtm.rtm_flags = RTM_F_CLONED, + }; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; - memset(&nladdr, 0, sizeof(nladdr)); - memset(&req, 0, sizeof(req)); - nladdr.nl_family = AF_NETLINK; - - req.nlh.nlmsg_len = sizeof(req); - req.nlh.nlmsg_type = RTM_GETROUTE; - req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST; - req.nlh.nlmsg_pid = 0; - req.nlh.nlmsg_seq = rth->dump = ++rth->seq; - req.rtm.rtm_family = family; - req.rtm.rtm_flags |= RTM_F_CLONED; - - return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); + return sendto(rth->fd, (void *)&req, sizeof(req), 0, (struct sockaddr *)&nladdr, sizeof(nladdr)); } static int iproute_flush_cache(void) @@ -1212,19 +1287,19 @@ #define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" int len; - int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY); + int flush_fd = open(ROUTE_FLUSH_PATH, O_WRONLY); char *buffer = "-1"; if (flush_fd < 0) { - fprintf (stderr, "Cannot open \"%s\": %s\n", + fprintf(stderr, "Cannot open \"%s\": %s\n", ROUTE_FLUSH_PATH, strerror(errno)); return -1; } - len = strlen (buffer); + len = strlen(buffer); - if ((write (flush_fd, (void *)buffer, len)) < len) { - fprintf (stderr, "Cannot flush routing cache\n"); + if ((write(flush_fd, (void *)buffer, len)) < len) { + fprintf(stderr, "Cannot flush routing cache\n"); close(flush_fd); return -1; } @@ -1304,6 +1379,7 @@ while (argc > 0) { if (matches(*argv, "table") == 0) { __u32 tid; + NEXT_ARG(); if (rtnl_rttable_a2n(&tid, *argv)) { if (strcmp(*argv, "all") == 0) { @@ -1317,12 +1393,22 @@ } } else filter.tb = tid; + } else if (matches(*argv, "vrf") == 0) { + __u32 tid; + + NEXT_ARG(); + tid = ipvrf_get_table(*argv); + if (tid == 0) + invarg("Invalid VRF\n", *argv); + filter.tb = tid; + filter.typemask = ~(1 << RTN_LOCAL | 1<1?"s":""); + printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":""); } fflush(stdout); return 0; @@ -1537,41 +1627,34 @@ struct { struct nlmsghdr n; struct rtmsg r; - char buf[1024]; - } req; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETROUTE, + .r.rtm_family = preferred_family, + }; char *idev = NULL; char *odev = NULL; int connected = 0; int from_ok = 0; unsigned int mark = 0; - memset(&req, 0, sizeof(req)); - iproute_reset_filter(0); filter.cloned = 2; - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETROUTE; - req.r.rtm_family = preferred_family; - req.r.rtm_table = 0; - req.r.rtm_protocol = 0; - req.r.rtm_scope = 0; - req.r.rtm_type = 0; - req.r.rtm_src_len = 0; - req.r.rtm_dst_len = 0; - req.r.rtm_tos = 0; - while (argc > 0) { if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { __u32 tos; + NEXT_ARG(); if (rtnl_dsfield_a2n(&tos, *argv)) invarg("TOS value is invalid\n", *argv); req.r.rtm_tos = tos; } else if (matches(*argv, "from") == 0) { inet_prefix addr; + NEXT_ARG(); if (matches(*argv, "help") == 0) usage(); @@ -1596,8 +1679,14 @@ req.r.rtm_flags |= RTM_F_NOTIFY; } else if (matches(*argv, "connected") == 0) { connected = 1; + } else if (matches(*argv, "vrf") == 0) { + NEXT_ARG(); + if (!name_is_vrf(*argv)) + invarg("Invalid VRF\n", *argv); + odev = *argv; } else { inet_prefix addr; + if (strcmp(*argv, "to") == 0) { NEXT_ARG(); } @@ -1642,17 +1731,19 @@ if (req.r.rtm_family == AF_UNSPEC) req.r.rtm_family = AF_INET; + req.r.rtm_flags |= RTM_F_LOOKUP_TABLE; + if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) return -2; if (connected && !from_ok) { struct rtmsg *r = NLMSG_DATA(&req.n); int len = req.n.nlmsg_len; - struct rtattr * tb[RTA_MAX+1]; + struct rtattr *tb[RTA_MAX+1]; - if (print_route(NULL, &req.n, (void*)stdout) < 0) { + if (print_route(NULL, &req.n, (void *)stdout) < 0) { fprintf(stderr, "An error :-)\n"); - exit(1); + return -1; } if (req.n.nlmsg_type != RTM_NEWROUTE) { @@ -1689,7 +1780,7 @@ return -2; } - if (print_route(NULL, &req.n, (void*)stdout) < 0) { + if (print_route(NULL, &req.n, (void *)stdout) < 0) { fprintf(stderr, "An error :-)\n"); return -1; } @@ -1697,12 +1788,42 @@ return 0; } +static int rtattr_cmp(const struct rtattr *rta1, const struct rtattr *rta2) +{ + if (!rta1 || !rta2 || rta1->rta_len != rta2->rta_len) + return 1; + + return memcmp(RTA_DATA(rta1), RTA_DATA(rta2), RTA_PAYLOAD(rta1)); +} + static int restore_handler(const struct sockaddr_nl *nl, struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) { - int ret; + struct rtmsg *r = NLMSG_DATA(n); + struct rtattr *tb[RTA_MAX+1]; + int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); + int ret, prio = *(int *)arg; + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + /* Restore routes in correct order: + * 0. ones for local addresses, + * 1. ones for local networks, + * 2. others (remote networks/hosts). + */ + if (!prio && !tb[RTA_GATEWAY] && (!tb[RTA_PREFSRC] || + !rtattr_cmp(tb[RTA_PREFSRC], tb[RTA_DST]))) + goto restore; + else if (prio == 1 && !tb[RTA_GATEWAY] && tb[RTA_PREFSRC] && + rtattr_cmp(tb[RTA_PREFSRC], tb[RTA_DST])) + goto restore; + else if (prio == 2 && tb[RTA_GATEWAY]) + goto restore; + + return 0; + +restore: n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; ll_init_map(&rth); @@ -1735,10 +1856,31 @@ static int iproute_restore(void) { + int pos, prio; + if (route_dump_check_magic()) exit(-1); - exit(rtnl_from_file(stdin, &restore_handler, NULL)); + pos = ftell(stdin); + if (pos == -1) { + perror("Failed to restore: ftell"); + exit(-1); + } + + for (prio = 0; prio < 3; prio++) { + int err; + + err = rtnl_from_file(stdin, &restore_handler, &prio); + if (err) + exit(err); + + if (fseek(stdin, pos, SEEK_SET) == -1) { + perror("Failed to restore: fseek"); + exit(-1); + } + } + + exit(0); } static int show_handler(const struct sockaddr_nl *nl, diff -Nru iproute2-4.3.0/ip/iproute_lwtunnel.c iproute2-4.9.0/ip/iproute_lwtunnel.c --- iproute2-4.3.0/ip/iproute_lwtunnel.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/ip/iproute_lwtunnel.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,412 @@ +/* + * iproute_lwtunnel.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Roopa Prabhu, + * Thomas Graf + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "iproute_lwtunnel.h" + +static int read_encap_type(const char *name) +{ + if (strcmp(name, "mpls") == 0) + return LWTUNNEL_ENCAP_MPLS; + else if (strcmp(name, "ip") == 0) + return LWTUNNEL_ENCAP_IP; + else if (strcmp(name, "ip6") == 0) + return LWTUNNEL_ENCAP_IP6; + else if (strcmp(name, "ila") == 0) + return LWTUNNEL_ENCAP_ILA; + else + return LWTUNNEL_ENCAP_NONE; +} + +static const char *format_encap_type(int type) +{ + switch (type) { + case LWTUNNEL_ENCAP_MPLS: + return "mpls"; + case LWTUNNEL_ENCAP_IP: + return "ip"; + case LWTUNNEL_ENCAP_IP6: + return "ip6"; + case LWTUNNEL_ENCAP_ILA: + return "ila"; + default: + return "unknown"; + } +} + +static void print_encap_mpls(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[MPLS_IPTUNNEL_MAX+1]; + + parse_rtattr_nested(tb, MPLS_IPTUNNEL_MAX, encap); + + if (tb[MPLS_IPTUNNEL_DST]) + fprintf(fp, " %s ", + format_host_rta(AF_MPLS, tb[MPLS_IPTUNNEL_DST])); +} + +static void print_encap_ip(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[LWTUNNEL_IP_MAX+1]; + + parse_rtattr_nested(tb, LWTUNNEL_IP_MAX, encap); + + if (tb[LWTUNNEL_IP_ID]) + fprintf(fp, "id %llu ", ntohll(rta_getattr_u64(tb[LWTUNNEL_IP_ID]))); + + if (tb[LWTUNNEL_IP_SRC]) + fprintf(fp, "src %s ", + rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_SRC])); + + if (tb[LWTUNNEL_IP_DST]) + fprintf(fp, "dst %s ", + rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_DST])); + + if (tb[LWTUNNEL_IP_TTL]) + fprintf(fp, "ttl %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TTL])); + + if (tb[LWTUNNEL_IP_TOS]) + fprintf(fp, "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS])); +} + +static char *ila_csum_mode2name(__u8 csum_mode) +{ + switch (csum_mode) { + case ILA_CSUM_ADJUST_TRANSPORT: + return "adj-transport"; + case ILA_CSUM_NEUTRAL_MAP: + return "neutral-map"; + case ILA_CSUM_NO_ACTION: + return "no-action"; + default: + return "unknown"; + } +} + +static __u8 ila_csum_name2mode(char *name) +{ + if (strcmp(name, "adj-transport") == 0) + return ILA_CSUM_ADJUST_TRANSPORT; + else if (strcmp(name, "neutral-map") == 0) + return ILA_CSUM_NEUTRAL_MAP; + else if (strcmp(name, "no-action") == 0) + return ILA_CSUM_NO_ACTION; + else + return -1; +} + +static void print_encap_ila(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[ILA_ATTR_MAX+1]; + + parse_rtattr_nested(tb, ILA_ATTR_MAX, encap); + + if (tb[ILA_ATTR_LOCATOR]) { + char abuf[ADDR64_BUF_SIZE]; + + addr64_n2a(*(__u64 *)RTA_DATA(tb[ILA_ATTR_LOCATOR]), + abuf, sizeof(abuf)); + fprintf(fp, " %s ", abuf); + } + + if (tb[ILA_ATTR_CSUM_MODE]) + fprintf(fp, " csum-mode %s ", + ila_csum_mode2name(rta_getattr_u8(tb[ILA_ATTR_CSUM_MODE]))); +} + +static void print_encap_ip6(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[LWTUNNEL_IP6_MAX+1]; + + parse_rtattr_nested(tb, LWTUNNEL_IP6_MAX, encap); + + if (tb[LWTUNNEL_IP6_ID]) + fprintf(fp, "id %llu ", ntohll(rta_getattr_u64(tb[LWTUNNEL_IP6_ID]))); + + if (tb[LWTUNNEL_IP6_SRC]) + fprintf(fp, "src %s ", + rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_SRC])); + + if (tb[LWTUNNEL_IP6_DST]) + fprintf(fp, "dst %s ", + rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_DST])); + + if (tb[LWTUNNEL_IP6_HOPLIMIT]) + fprintf(fp, "hoplimit %d ", rta_getattr_u8(tb[LWTUNNEL_IP6_HOPLIMIT])); + + if (tb[LWTUNNEL_IP6_TC]) + fprintf(fp, "tc %d ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC])); +} + +void lwt_print_encap(FILE *fp, struct rtattr *encap_type, + struct rtattr *encap) +{ + int et; + + if (!encap_type) + return; + + et = rta_getattr_u16(encap_type); + + fprintf(fp, " encap %s ", format_encap_type(et)); + + switch (et) { + case LWTUNNEL_ENCAP_MPLS: + print_encap_mpls(fp, encap); + break; + case LWTUNNEL_ENCAP_IP: + print_encap_ip(fp, encap); + break; + case LWTUNNEL_ENCAP_ILA: + print_encap_ila(fp, encap); + break; + case LWTUNNEL_ENCAP_IP6: + print_encap_ip6(fp, encap); + break; + } +} + +static int parse_encap_mpls(struct rtattr *rta, size_t len, int *argcp, char ***argvp) +{ + inet_prefix addr; + int argc = *argcp; + char **argv = *argvp; + + if (get_addr(&addr, *argv, AF_MPLS)) { + fprintf(stderr, "Error: an inet address is expected rather than \"%s\".\n", *argv); + exit(1); + } + + rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, &addr.data, + addr.bytelen); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int parse_encap_ip(struct rtattr *rta, size_t len, int *argcp, char ***argvp) +{ + int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0; + char **argv = *argvp; + int argc = *argcp; + + while (argc > 0) { + if (strcmp(*argv, "id") == 0) { + __u64 id; + + NEXT_ARG(); + if (id_ok++) + duparg2("id", *argv); + if (get_be64(&id, *argv, 0)) + invarg("\"id\" value is invalid\n", *argv); + rta_addattr64(rta, len, LWTUNNEL_IP_ID, id); + } else if (strcmp(*argv, "dst") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (dst_ok++) + duparg2("dst", *argv); + get_addr(&addr, *argv, AF_INET); + rta_addattr_l(rta, len, LWTUNNEL_IP_DST, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "tos") == 0) { + __u32 tos; + + NEXT_ARG(); + if (tos_ok++) + duparg2("tos", *argv); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("\"tos\" value is invalid\n", *argv); + rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos); + } else if (strcmp(*argv, "ttl") == 0) { + __u8 ttl; + + NEXT_ARG(); + if (ttl_ok++) + duparg2("ttl", *argv); + if (get_u8(&ttl, *argv, 0)) + invarg("\"ttl\" value is invalid\n", *argv); + rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl); + } else { + break; + } + argc--; argv++; + } + + /* argv is currently the first unparsed argument, + * but the lwt_parse_encap() caller will move to the next, + * so step back */ + *argcp = argc + 1; + *argvp = argv - 1; + + return 0; +} + +static int parse_encap_ila(struct rtattr *rta, size_t len, + int *argcp, char ***argvp) +{ + __u64 locator; + int argc = *argcp; + char **argv = *argvp; + + if (get_addr64(&locator, *argv) < 0) { + fprintf(stderr, "Bad locator: %s\n", *argv); + exit(1); + } + + argc--; argv++; + + rta_addattr64(rta, 1024, ILA_ATTR_LOCATOR, locator); + + while (argc > 0) { + if (strcmp(*argv, "csum-mode") == 0) { + __u8 csum_mode; + + NEXT_ARG(); + + csum_mode = ila_csum_name2mode(*argv); + if (csum_mode < 0) + invarg("\"csum-mode\" value is invalid\n", *argv); + + rta_addattr8(rta, 1024, ILA_ATTR_CSUM_MODE, csum_mode); + + argc--; argv++; + } else { + break; + } + } + + /* argv is currently the first unparsed argument, + * but the lwt_parse_encap() caller will move to the next, + * so step back + */ + *argcp = argc + 1; + *argvp = argv - 1; + + return 0; +} + +static int parse_encap_ip6(struct rtattr *rta, size_t len, int *argcp, char ***argvp) +{ + int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0; + char **argv = *argvp; + int argc = *argcp; + + while (argc > 0) { + if (strcmp(*argv, "id") == 0) { + __u64 id; + + NEXT_ARG(); + if (id_ok++) + duparg2("id", *argv); + if (get_be64(&id, *argv, 0)) + invarg("\"id\" value is invalid\n", *argv); + rta_addattr64(rta, len, LWTUNNEL_IP6_ID, id); + } else if (strcmp(*argv, "dst") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (dst_ok++) + duparg2("dst", *argv); + get_addr(&addr, *argv, AF_INET6); + rta_addattr_l(rta, len, LWTUNNEL_IP6_DST, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "tc") == 0) { + __u32 tc; + + NEXT_ARG(); + if (tos_ok++) + duparg2("tc", *argv); + if (rtnl_dsfield_a2n(&tc, *argv)) + invarg("\"tc\" value is invalid\n", *argv); + rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc); + } else if (strcmp(*argv, "hoplimit") == 0) { + __u8 hoplimit; + + NEXT_ARG(); + if (ttl_ok++) + duparg2("hoplimit", *argv); + if (get_u8(&hoplimit, *argv, 0)) + invarg("\"hoplimit\" value is invalid\n", *argv); + rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, hoplimit); + } else { + break; + } + argc--; argv++; + } + + /* argv is currently the first unparsed argument, + * but the lwt_parse_encap() caller will move to the next, + * so step back */ + *argcp = argc + 1; + *argvp = argv - 1; + + return 0; +} + +int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp) +{ + struct rtattr *nest; + int argc = *argcp; + char **argv = *argvp; + __u16 type; + + NEXT_ARG(); + type = read_encap_type(*argv); + if (!type) + invarg("\"encap type\" value is invalid\n", *argv); + + NEXT_ARG(); + if (argc <= 1) { + fprintf(stderr, "Error: unexpected end of line after \"encap\"\n"); + exit(-1); + } + + nest = rta_nest(rta, 1024, RTA_ENCAP); + switch (type) { + case LWTUNNEL_ENCAP_MPLS: + parse_encap_mpls(rta, len, &argc, &argv); + break; + case LWTUNNEL_ENCAP_IP: + parse_encap_ip(rta, len, &argc, &argv); + break; + case LWTUNNEL_ENCAP_ILA: + parse_encap_ila(rta, len, &argc, &argv); + break; + case LWTUNNEL_ENCAP_IP6: + parse_encap_ip6(rta, len, &argc, &argv); + break; + default: + fprintf(stderr, "Error: unsupported encap type\n"); + break; + } + rta_nest_end(rta, nest); + + rta_addattr16(rta, 1024, RTA_ENCAP_TYPE, type); + + *argcp = argc; + *argvp = argv; + + return 0; +} diff -Nru iproute2-4.3.0/ip/iproute_lwtunnel.h iproute2-4.9.0/ip/iproute_lwtunnel.h --- iproute2-4.3.0/ip/iproute_lwtunnel.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/ip/iproute_lwtunnel.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,8 @@ +#ifndef __LWTUNNEL_H__ +#define __LETUNNEL_H__ 1 + +int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp); +void lwt_print_encap(FILE *fp, struct rtattr *encap_type, + struct rtattr *encap); + +#endif diff -Nru iproute2-4.3.0/ip/iprule.c iproute2-4.9.0/ip/iprule.c --- iproute2-4.3.0/ip/iprule.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iprule.c 2016-12-12 23:07:42.000000000 +0000 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -27,35 +28,145 @@ #include "utils.h" #include "ip_common.h" +enum list_action { + IPRULE_LIST, + IPRULE_FLUSH, + IPRULE_SAVE, +}; + extern struct rtnl_handle rth; static void usage(void) __attribute__((noreturn)); static void usage(void) { - fprintf(stderr, "Usage: ip rule [ list | add | del | flush | save ] SELECTOR ACTION\n"); - fprintf(stderr, " ip rule restore\n"); - fprintf(stderr, "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n"); - fprintf(stderr, " [ iif STRING ] [ oif STRING ] [ pref NUMBER ]\n"); - fprintf(stderr, "ACTION := [ table TABLE_ID ]\n"); - fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n"); - fprintf(stderr, " [ goto NUMBER ]\n"); - fprintf(stderr, " SUPPRESSOR\n"); - fprintf(stderr, "SUPPRESSOR := [ suppress_prefixlength NUMBER ]\n"); - fprintf(stderr, " [ suppress_ifgroup DEVGROUP ]\n"); - fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n"); + fprintf(stderr, + "Usage: ip rule { add | del } SELECTOR ACTION\n" + " ip rule { flush | save | restore }\n" + " ip rule [ list [ SELECTOR ]]\n" + "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n" + " [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ l3mdev ]\n" + "ACTION := [ table TABLE_ID ]\n" + " [ nat ADDRESS ]\n" + " [ realms [SRCREALM/]DSTREALM ]\n" + " [ goto NUMBER ]\n" + " SUPPRESSOR\n" + "SUPPRESSOR := [ suppress_prefixlength NUMBER ]\n" + " [ suppress_ifgroup DEVGROUP ]\n" + "TABLE_ID := [ local | main | default | NUMBER ]\n"); exit(-1); } +static struct +{ + int not; + int l3mdev; + int iifmask, oifmask; + unsigned int tb; + unsigned int tos, tosmask; + unsigned int pref, prefmask; + unsigned int fwmark, fwmask; + char iif[IFNAMSIZ]; + char oif[IFNAMSIZ]; + inet_prefix src; + inet_prefix dst; +} filter; + +static bool filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) +{ + struct rtmsg *r = NLMSG_DATA(n); + inet_prefix src = { .family = r->rtm_family }; + inet_prefix dst = { .family = r->rtm_family }; + __u32 table; + + if (preferred_family != AF_UNSPEC && r->rtm_family != preferred_family) + return false; + + if (filter.prefmask && + filter.pref ^ (tb[FRA_PRIORITY] ? rta_getattr_u32(tb[FRA_PRIORITY]) : 0)) + return false; + if (filter.not && !(r->rtm_flags & FIB_RULE_INVERT)) + return false; + + if (filter.src.family) { + if (tb[FRA_SRC]) { + memcpy(&src.data, RTA_DATA(tb[FRA_SRC]), + (r->rtm_src_len + 7) / 8); + } + if (filter.src.family != r->rtm_family || + filter.src.bitlen > r->rtm_src_len || + inet_addr_match(&src, &filter.src, filter.src.bitlen)) + return false; + } + + if (filter.dst.family) { + if (tb[FRA_DST]) { + memcpy(&dst.data, RTA_DATA(tb[FRA_DST]), + (r->rtm_dst_len + 7) / 8); + } + if (filter.dst.family != r->rtm_family || + filter.dst.bitlen > r->rtm_dst_len || + inet_addr_match(&dst, &filter.dst, filter.dst.bitlen)) + return false; + } + + if (filter.tosmask && filter.tos ^ r->rtm_tos) + return false; + + if (filter.fwmark) { + __u32 mark = 0; + + if (tb[FRA_FWMARK]) + mark = rta_getattr_u32(tb[FRA_FWMARK]); + if (filter.fwmark ^ mark) + return false; + } + if (filter.fwmask) { + __u32 mask = 0; + + if (tb[FRA_FWMASK]) + mask = rta_getattr_u32(tb[FRA_FWMASK]); + if (filter.fwmask ^ mask) + return false; + } + + if (filter.iifmask) { + if (tb[FRA_IFNAME]) { + if (strcmp(filter.iif, rta_getattr_str(tb[FRA_IFNAME])) != 0) + return false; + } else { + return false; + } + } + + if (filter.oifmask) { + if (tb[FRA_OIFNAME]) { + if (strcmp(filter.oif, rta_getattr_str(tb[FRA_OIFNAME])) != 0) + return false; + } else { + return false; + } + } + + if (filter.l3mdev && !(tb[FRA_L3MDEV] && rta_getattr_u8(tb[FRA_L3MDEV]))) + return false; + + table = rtm_get_table(r, tb); + if (filter.tb > 0 && filter.tb ^ table) + return false; + + return true; +} + int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; int host_len = -1; __u32 table; - struct rtattr * tb[FRA_MAX+1]; - char abuf[256]; + struct rtattr *tb[FRA_MAX+1]; + SPRINT_BUF(b1); if (n->nlmsg_type != RTM_NEWRULE && n->nlmsg_type != RTM_DELRULE) @@ -69,11 +180,15 @@ host_len = af_bit_len(r->rtm_family); + if (!filter_nlmsg(n, tb, host_len)) + return 0; + if (n->nlmsg_type == RTM_DELRULE) fprintf(fp, "Deleted "); if (tb[FRA_PRIORITY]) - fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[FRA_PRIORITY])); + fprintf(fp, "%u:\t", + rta_getattr_u32(tb[FRA_PRIORITY])); else fprintf(fp, "0:\t"); @@ -82,18 +197,12 @@ if (tb[FRA_SRC]) { if (r->rtm_src_len != host_len) { - fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[FRA_SRC]), - RTA_DATA(tb[FRA_SRC]), - abuf, sizeof(abuf)), - r->rtm_src_len - ); + fprintf(fp, "from %s/%u ", + rt_addr_n2a_rta(r->rtm_family, tb[FRA_SRC]), + r->rtm_src_len); } else { - fprintf(fp, "from %s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[FRA_SRC]), - RTA_DATA(tb[FRA_SRC]), - abuf, sizeof(abuf)) - ); + fprintf(fp, "from %s ", + format_host_rta(r->rtm_family, tb[FRA_SRC])); } } else if (r->rtm_src_len) { fprintf(fp, "from 0/%d ", r->rtm_src_len); @@ -103,17 +212,12 @@ if (tb[FRA_DST]) { if (r->rtm_dst_len != host_len) { - fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[FRA_DST]), - RTA_DATA(tb[FRA_DST]), - abuf, sizeof(abuf)), - r->rtm_dst_len - ); + fprintf(fp, "to %s/%u ", + rt_addr_n2a_rta(r->rtm_family, tb[FRA_DST]), + r->rtm_dst_len); } else { - fprintf(fp, "to %s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[FRA_DST]), - RTA_DATA(tb[FRA_DST]), - abuf, sizeof(abuf))); + fprintf(fp, "to %s ", + format_host_rta(r->rtm_family, tb[FRA_DST])); } } else if (r->rtm_dst_len) { fprintf(fp, "to 0/%d ", r->rtm_dst_len); @@ -121,7 +225,8 @@ if (r->rtm_tos) { SPRINT_BUF(b1); - fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + fprintf(fp, "tos %s ", + rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); } if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) { @@ -149,21 +254,29 @@ fprintf(fp, "[detached] "); } + if (tb[FRA_L3MDEV]) { + if (rta_getattr_u8(tb[FRA_L3MDEV])) + fprintf(fp, "lookup [l3mdev-table] "); + } + table = rtm_get_table(r, tb); if (table) { - fprintf(fp, "lookup %s ", rtnl_rttable_n2a(table, b1, sizeof(b1))); + fprintf(fp, "lookup %s ", + rtnl_rttable_n2a(table, b1, sizeof(b1))); if (tb[FRA_SUPPRESS_PREFIXLEN]) { int pl = rta_getattr_u32(tb[FRA_SUPPRESS_PREFIXLEN]); - if (pl != -1) { + + if (pl != -1) fprintf(fp, "suppress_prefixlength %d ", pl); - } } if (tb[FRA_SUPPRESS_IFGROUP]) { int group = rta_getattr_u32(tb[FRA_SUPPRESS_IFGROUP]); + if (group != -1) { SPRINT_BUF(b1); - fprintf(fp, "suppress_ifgroup %s ", rtnl_group_n2a(group, b1, sizeof(b1))); + fprintf(fp, "suppress_ifgroup %s ", + rtnl_group_n2a(group, b1, sizeof(b1))); } } } @@ -171,6 +284,7 @@ if (tb[FRA_FLOW]) { __u32 to = rta_getattr_u32(tb[FRA_FLOW]); __u32 from = to>>16; + to &= 0xFFFF; if (from) { fprintf(fp, "realms %s/", @@ -183,10 +297,8 @@ if (r->rtm_type == RTN_NAT) { if (tb[RTA_GATEWAY]) { fprintf(fp, "map-to %s ", - format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_GATEWAY]), - RTA_DATA(tb[RTA_GATEWAY]), - abuf, sizeof(abuf))); + format_host_rta(r->rtm_family, + tb[RTA_GATEWAY])); } else fprintf(fp, "masquerade"); } else if (r->rtm_type == FR_ACT_GOTO) { @@ -200,7 +312,9 @@ } else if (r->rtm_type == FR_ACT_NOP) fprintf(fp, "nop"); else if (r->rtm_type != RTN_UNICAST) - fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + fprintf(fp, "%s", + rtnl_rtntype_n2a(r->rtm_type, + b1, sizeof(b1))); fprintf(fp, "\n"); fflush(fp); @@ -227,7 +341,8 @@ return 0; } -static int save_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +static int save_rule(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) { int ret; @@ -240,24 +355,134 @@ return ret == n->nlmsg_len ? 0 : ret; } -static int iprule_list_or_save(int argc, char **argv, int save) +static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + struct rtnl_handle rth2; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[FRA_MAX+1]; + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) + return -1; + + parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len); + + if (tb[FRA_PRIORITY]) { + n->nlmsg_type = RTM_DELRULE; + n->nlmsg_flags = NLM_F_REQUEST; + + if (rtnl_open(&rth2, 0) < 0) + return -1; + + if (rtnl_talk(&rth2, n, NULL, 0) < 0) + return -2; + + rtnl_close(&rth2); + } + + return 0; +} + +static int iprule_list_flush_or_save(int argc, char **argv, int action) { - rtnl_filter_t filter = print_rule; + rtnl_filter_t filter_fn; int af = preferred_family; if (af == AF_UNSPEC) af = AF_INET; - if (argc > 0) { + if (action != IPRULE_LIST && argc > 0) { fprintf(stderr, "\"ip rule %s\" does not take any arguments.\n", - save ? "save" : "show"); + action == IPRULE_SAVE ? "save" : "flush"); return -1; } - if (save) { + switch (action) { + case IPRULE_SAVE: if (save_rule_prep()) return -1; - filter = save_rule; + filter_fn = save_rule; + break; + case IPRULE_FLUSH: + filter_fn = flush_rule; + break; + default: + filter_fn = print_rule; + } + + memset(&filter, 0, sizeof(filter)); + + while (argc > 0) { + if (matches(*argv, "preference") == 0 || + matches(*argv, "order") == 0 || + matches(*argv, "priority") == 0) { + __u32 pref; + + NEXT_ARG(); + if (get_u32(&pref, *argv, 0)) + invarg("preference value is invalid\n", *argv); + filter.pref = pref; + filter.prefmask = 1; + } else if (strcmp(*argv, "not") == 0) { + filter.not = 1; + } else if (strcmp(*argv, "tos") == 0) { + __u32 tos; + + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + filter.tos = tos; + filter.tosmask = 1; + } else if (strcmp(*argv, "fwmark") == 0) { + char *slash; + __u32 fwmark, fwmask; + + NEXT_ARG(); + slash = strchr(*argv, '/'); + if (slash != NULL) + *slash = '\0'; + if (get_u32(&fwmark, *argv, 0)) + invarg("fwmark value is invalid\n", *argv); + filter.fwmark = fwmark; + if (slash) { + if (get_u32(&fwmask, slash+1, 0)) + invarg("fwmask value is invalid\n", + slash+1); + filter.fwmask = fwmask; + } + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + strncpy(filter.iif, *argv, IFNAMSIZ); + filter.iifmask = 1; + } else if (strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + strncpy(filter.oif, *argv, IFNAMSIZ); + filter.oifmask = 1; + } else if (strcmp(*argv, "l3mdev") == 0) { + filter.l3mdev = 1; + } else if (matches(*argv, "lookup") == 0 || + matches(*argv, "table") == 0) { + __u32 tid; + + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("table id value is invalid\n", *argv); + filter.tb = tid; + } else if (matches(*argv, "from") == 0 || + matches(*argv, "src") == 0) { + NEXT_ARG(); + get_prefix(&filter.src, *argv, af); + } else { + if (matches(*argv, "dst") == 0 || + matches(*argv, "to") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.dst, *argv, af); + } + argc--; argv++; } if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { @@ -265,7 +490,7 @@ return 1; } - if (rtnl_dump_filter(&rth, filter, stdout) < 0) { + if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return 1; } @@ -285,7 +510,8 @@ ret = fread(&magic, sizeof(magic), 1, stdin); if (magic != rule_dump_magic) { - fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", ret, magic); + fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", + ret, magic); return -1; } @@ -320,49 +546,57 @@ static int iprule_modify(int cmd, int argc, char **argv) { + int l3mdev_rule = 0; int table_ok = 0; + __u32 tid = 0; struct { struct nlmsghdr n; struct rtmsg r; - char buf[1024]; - } req; - - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_type = cmd; - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.r.rtm_family = preferred_family; - req.r.rtm_protocol = RTPROT_BOOT; - req.r.rtm_scope = RT_SCOPE_UNIVERSE; - req.r.rtm_table = 0; - req.r.rtm_type = RTN_UNSPEC; - req.r.rtm_flags = 0; + char buf[1024]; + } req = { + .n.nlmsg_type = cmd, + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .r.rtm_family = preferred_family, + .r.rtm_protocol = RTPROT_BOOT, + .r.rtm_scope = RT_SCOPE_UNIVERSE, + .r.rtm_type = RTN_UNSPEC, + }; if (cmd == RTM_NEWRULE) { req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; req.r.rtm_type = RTN_UNICAST; } + if (cmd == RTM_DELRULE && argc == 0) { + fprintf(stderr, "\"ip rule del\" requires arguments.\n"); + return -1; + } + while (argc > 0) { if (strcmp(*argv, "not") == 0) { req.r.rtm_flags |= FIB_RULE_INVERT; } else if (strcmp(*argv, "from") == 0) { inet_prefix dst; + NEXT_ARG(); get_prefix(&dst, *argv, req.r.rtm_family); req.r.rtm_src_len = dst.bitlen; - addattr_l(&req.n, sizeof(req), FRA_SRC, &dst.data, dst.bytelen); + addattr_l(&req.n, sizeof(req), FRA_SRC, + &dst.data, dst.bytelen); } else if (strcmp(*argv, "to") == 0) { inet_prefix dst; + NEXT_ARG(); get_prefix(&dst, *argv, req.r.rtm_family); req.r.rtm_dst_len = dst.bitlen; - addattr_l(&req.n, sizeof(req), FRA_DST, &dst.data, dst.bytelen); + addattr_l(&req.n, sizeof(req), FRA_DST, + &dst.data, dst.bytelen); } else if (matches(*argv, "preference") == 0 || matches(*argv, "order") == 0 || matches(*argv, "priority") == 0) { __u32 pref; + NEXT_ARG(); if (get_u32(&pref, *argv, 0)) invarg("preference value is invalid\n", *argv); @@ -370,6 +604,7 @@ } else if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { __u32 tos; + NEXT_ARG(); if (rtnl_dsfield_a2n(&tos, *argv)) invarg("TOS value is invalid\n", *argv); @@ -377,26 +612,31 @@ } else if (strcmp(*argv, "fwmark") == 0) { char *slash; __u32 fwmark, fwmask; + NEXT_ARG(); - if ((slash = strchr(*argv, '/')) != NULL) + + slash = strchr(*argv, '/'); + if (slash != NULL) *slash = '\0'; if (get_u32(&fwmark, *argv, 0)) invarg("fwmark value is invalid\n", *argv); addattr32(&req.n, sizeof(req), FRA_FWMARK, fwmark); if (slash) { if (get_u32(&fwmask, slash+1, 0)) - invarg("fwmask value is invalid\n", slash+1); - addattr32(&req.n, sizeof(req), FRA_FWMASK, fwmask); + invarg("fwmask value is invalid\n", + slash+1); + addattr32(&req.n, sizeof(req), + FRA_FWMASK, fwmask); } } else if (matches(*argv, "realms") == 0) { __u32 realm; + NEXT_ARG(); if (get_rt_realms_or_raw(&realm, *argv)) invarg("invalid realms\n", *argv); addattr32(&req.n, sizeof(req), FRA_FLOW, realm); } else if (matches(*argv, "table") == 0 || strcmp(*argv, "lookup") == 0) { - __u32 tid; NEXT_ARG(); if (rtnl_rttable_a2n(&tid, *argv)) invarg("invalid table ID\n", *argv); @@ -410,45 +650,60 @@ } else if (matches(*argv, "suppress_prefixlength") == 0 || strcmp(*argv, "sup_pl") == 0) { int pl; + NEXT_ARG(); if (get_s32(&pl, *argv, 0) || pl < 0) - invarg("suppress_prefixlength value is invalid\n", *argv); - addattr32(&req.n, sizeof(req), FRA_SUPPRESS_PREFIXLEN, pl); + invarg("suppress_prefixlength value is invalid\n", + *argv); + addattr32(&req.n, sizeof(req), + FRA_SUPPRESS_PREFIXLEN, pl); } else if (matches(*argv, "suppress_ifgroup") == 0 || strcmp(*argv, "sup_group") == 0) { NEXT_ARG(); int group; + if (rtnl_group_a2n(&group, *argv)) - invarg("Invalid \"suppress_ifgroup\" value\n", *argv); - addattr32(&req.n, sizeof(req), FRA_SUPPRESS_IFGROUP, group); + invarg("Invalid \"suppress_ifgroup\" value\n", + *argv); + addattr32(&req.n, sizeof(req), + FRA_SUPPRESS_IFGROUP, group); } else if (strcmp(*argv, "dev") == 0 || strcmp(*argv, "iif") == 0) { NEXT_ARG(); - addattr_l(&req.n, sizeof(req), FRA_IFNAME, *argv, strlen(*argv)+1); + addattr_l(&req.n, sizeof(req), FRA_IFNAME, + *argv, strlen(*argv)+1); } else if (strcmp(*argv, "oif") == 0) { NEXT_ARG(); - addattr_l(&req.n, sizeof(req), FRA_OIFNAME, *argv, strlen(*argv)+1); + addattr_l(&req.n, sizeof(req), FRA_OIFNAME, + *argv, strlen(*argv)+1); + } else if (strcmp(*argv, "l3mdev") == 0) { + addattr8(&req.n, sizeof(req), FRA_L3MDEV, 1); + table_ok = 1; + l3mdev_rule = 1; } else if (strcmp(*argv, "nat") == 0 || matches(*argv, "map-to") == 0) { NEXT_ARG(); fprintf(stderr, "Warning: route NAT is deprecated\n"); - addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv)); + addattr32(&req.n, sizeof(req), RTA_GATEWAY, + get_addr32(*argv)); req.r.rtm_type = RTN_NAT; } else { int type; - if (strcmp(*argv, "type") == 0) { + if (strcmp(*argv, "type") == 0) NEXT_ARG(); - } + if (matches(*argv, "help") == 0) usage(); else if (matches(*argv, "goto") == 0) { __u32 target; + type = FR_ACT_GOTO; NEXT_ARG(); if (get_u32(&target, *argv, 0)) invarg("invalid target\n", *argv); - addattr32(&req.n, sizeof(req), FRA_GOTO, target); + addattr32(&req.n, sizeof(req), + FRA_GOTO, target); } else if (matches(*argv, "nop") == 0) type = FR_ACT_NOP; else if (rtnl_rtntype_a2n(&type, *argv)) @@ -460,6 +715,12 @@ argv++; } + if (l3mdev_rule && tid != 0) { + fprintf(stderr, + "table can not be specified for l3mdev rules\n"); + return -EINVAL; + } + if (req.r.rtm_family == AF_UNSPEC) req.r.rtm_family = AF_INET; @@ -472,71 +733,16 @@ return 0; } - -static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) -{ - struct rtnl_handle rth2; - struct rtmsg *r = NLMSG_DATA(n); - int len = n->nlmsg_len; - struct rtattr * tb[FRA_MAX+1]; - - len -= NLMSG_LENGTH(sizeof(*r)); - if (len < 0) - return -1; - - parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len); - - if (tb[FRA_PRIORITY]) { - n->nlmsg_type = RTM_DELRULE; - n->nlmsg_flags = NLM_F_REQUEST; - - if (rtnl_open(&rth2, 0) < 0) - return -1; - - if (rtnl_talk(&rth2, n, NULL, 0) < 0) - return -2; - - rtnl_close(&rth2); - } - - return 0; -} - -static int iprule_flush(int argc, char **argv) -{ - int af = preferred_family; - - if (af == AF_UNSPEC) - af = AF_INET; - - if (argc > 0) { - fprintf(stderr, "\"ip rule flush\" does not allow arguments\n"); - return -1; - } - - if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { - perror("Cannot send dump request"); - return 1; - } - - if (rtnl_dump_filter(&rth, flush_rule, NULL) < 0) { - fprintf(stderr, "Flush terminated\n"); - return 1; - } - - return 0; -} - int do_iprule(int argc, char **argv) { if (argc < 1) { - return iprule_list_or_save(0, NULL, 0); + return iprule_list_flush_or_save(0, NULL, IPRULE_LIST); } else if (matches(argv[0], "list") == 0 || matches(argv[0], "lst") == 0 || matches(argv[0], "show") == 0) { - return iprule_list_or_save(argc-1, argv+1, 0); + return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_LIST); } else if (matches(argv[0], "save") == 0) { - return iprule_list_or_save(argc-1, argv+1, 1); + return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_SAVE); } else if (matches(argv[0], "restore") == 0) { return iprule_restore(); } else if (matches(argv[0], "add") == 0) { @@ -544,11 +750,12 @@ } else if (matches(argv[0], "delete") == 0) { return iprule_modify(RTM_DELRULE, argc-1, argv+1); } else if (matches(argv[0], "flush") == 0) { - return iprule_flush(argc-1, argv+1); + return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_FLUSH); } else if (matches(argv[0], "help") == 0) usage(); - fprintf(stderr, "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv); + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv); exit(-1); } @@ -566,7 +773,8 @@ case RTNL_FAMILY_IP6MR: break; default: - fprintf(stderr, "Multicast rules are only supported for IPv4/IPv6, was: %i\n", + fprintf(stderr, + "Multicast rules are only supported for IPv4/IPv6, was: %i\n", preferred_family); exit(-1); } diff -Nru iproute2-4.3.0/ip/iptoken.c iproute2-4.9.0/ip/iptoken.c --- iproute2-4.3.0/ip/iptoken.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iptoken.c 2016-12-12 23:07:42.000000000 +0000 @@ -38,7 +38,7 @@ static void usage(void) { - fprintf(stderr, "Usage: ip token [ list | set | get ] [ TOKEN ] [ dev DEV ]\n"); + fprintf(stderr, "Usage: ip token [ list | set | del | get ] [ TOKEN ] [ dev DEV ]\n"); exit(-1); } @@ -51,7 +51,6 @@ int len = n->nlmsg_len; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *ltb[IFLA_INET6_MAX + 1]; - char abuf[256]; if (n->nlmsg_type != RTM_NEWLINK) return -1; @@ -79,13 +78,9 @@ return -1; } - fprintf(fp, "token %s ", - format_host(ifi->ifi_family, - RTA_PAYLOAD(ltb[IFLA_INET6_TOKEN]), - RTA_DATA(ltb[IFLA_INET6_TOKEN]), - abuf, sizeof(abuf))); - fprintf(fp, "dev %s ", ll_index_to_name(ifi->ifi_index)); - fprintf(fp, "\n"); + fprintf(fp, "token %s dev %s\n", + format_host_rta(ifi->ifi_family, ltb[IFLA_INET6_TOKEN]), + ll_index_to_name(ifi->ifi_index)); fflush(fp); return 0; @@ -94,14 +89,7 @@ static int iptoken_list(int argc, char **argv) { int af = AF_INET6; - struct rtnl_dump_args da; - const struct rtnl_dump_filter_arg a[2] = { - { .filter = print_token, .arg1 = &da, }, - { .filter = NULL, .arg1 = NULL, }, - }; - - memset(&da, 0, sizeof(da)); - da.fp = stdout; + struct rtnl_dump_args da = { .fp = stdout }; while (argc > 0) { if (strcmp(*argv, "dev") == 0) { @@ -118,7 +106,7 @@ return -1; } - if (rtnl_dump_filter_l(&rth, a) < 0) { + if (rtnl_dump_filter(&rth, print_token, &da) < 0) { fprintf(stderr, "Dump terminated\n"); return -1; } @@ -126,24 +114,21 @@ return 0; } -static int iptoken_set(int argc, char **argv) +static int iptoken_set(int argc, char **argv, bool delete) { struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[512]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_SETLINK, + .ifi.ifi_family = AF_INET6, + }; struct rtattr *afs, *afs6; - bool have_token = false, have_dev = false; - inet_prefix addr; - - memset(&addr, 0, sizeof(addr)); - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_SETLINK; - req.ifi.ifi_family = AF_INET6; + bool have_token = delete, have_dev = false; + inet_prefix addr = { .bytelen = 16, }; while (argc > 0) { if (strcmp(*argv, "dev") == 0) { @@ -158,13 +143,7 @@ if (matches(*argv, "help") == 0) usage(); if (!have_token) { - afs = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC); - afs6 = addattr_nest(&req.n, sizeof(req), AF_INET6); get_prefix(&addr, *argv, req.ifi.ifi_family); - addattr_l(&req.n, sizeof(req), IFLA_INET6_TOKEN, - &addr.data, addr.bytelen); - addattr_nest_end(&req.n, afs6); - addattr_nest_end(&req.n, afs); have_token = true; } } @@ -172,16 +151,21 @@ } if (!have_token) { - fprintf(stderr, "Not enough information: token " - "is required.\n"); + fprintf(stderr, "Not enough information: token is required.\n"); return -1; } if (!have_dev) { - fprintf(stderr, "Not enough information: \"dev\" " - "argument is required.\n"); + fprintf(stderr, "Not enough information: \"dev\" argument is required.\n"); return -1; } + afs = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC); + afs6 = addattr_nest(&req.n, sizeof(req), AF_INET6); + addattr_l(&req.n, sizeof(req), IFLA_INET6_TOKEN, + &addr.data, addr.bytelen); + addattr_nest_end(&req.n, afs6); + addattr_nest_end(&req.n, afs); + if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) return -2; @@ -200,7 +184,9 @@ return iptoken_list(argc - 1, argv + 1); } else if (matches(argv[0], "set") == 0 || matches(argv[0], "add") == 0) { - return iptoken_set(argc - 1, argv + 1); + return iptoken_set(argc - 1, argv + 1, false); + } else if (matches(argv[0], "delete") == 0) { + return iptoken_set(argc - 1, argv + 1, true); } else if (matches(argv[0], "get") == 0) { return iptoken_list(argc - 1, argv + 1); } else if (matches(argv[0], "help") == 0) diff -Nru iproute2-4.3.0/ip/iptunnel.c iproute2-4.9.0/ip/iptunnel.c --- iproute2-4.3.0/ip/iptunnel.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iptunnel.c 2016-12-12 23:07:42.000000000 +0000 @@ -47,15 +47,23 @@ exit(-1); } +static void set_tunnel_proto(struct ip_tunnel_parm *p, int proto) +{ + if (p->iph.protocol && p->iph.protocol != proto) { + fprintf(stderr, + "You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + p->iph.protocol = proto; +} + static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) { int count = 0; - char medium[IFNAMSIZ]; + char medium[IFNAMSIZ] = {}; int isatap = 0; memset(p, 0, sizeof(*p)); - memset(&medium, 0, sizeof(medium)); - p->iph.version = 4; p->iph.ihl = 5; #ifndef IP_DF @@ -68,83 +76,37 @@ NEXT_ARG(); if (strcmp(*argv, "ipip") == 0 || strcmp(*argv, "ip/ip") == 0) { - if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { - fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); - exit(-1); - } - p->iph.protocol = IPPROTO_IPIP; + set_tunnel_proto(p, IPPROTO_IPIP); } else if (strcmp(*argv, "gre") == 0 || strcmp(*argv, "gre/ip") == 0) { - if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { - fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); - exit(-1); - } - p->iph.protocol = IPPROTO_GRE; + set_tunnel_proto(p, IPPROTO_GRE); } else if (strcmp(*argv, "sit") == 0 || strcmp(*argv, "ipv6/ip") == 0) { - if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { - fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); - exit(-1); - } - p->iph.protocol = IPPROTO_IPV6; + set_tunnel_proto(p, IPPROTO_IPV6); } else if (strcmp(*argv, "isatap") == 0) { - if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { - fprintf(stderr, "You managed to ask for more than one tunnel mode.\n"); - exit(-1); - } - p->iph.protocol = IPPROTO_IPV6; + set_tunnel_proto(p, IPPROTO_IPV6); isatap++; } else if (strcmp(*argv, "vti") == 0) { - if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { - fprintf(stderr, "You managed to ask for more than one tunnel mode.\n"); - exit(-1); - } - p->iph.protocol = IPPROTO_IPIP; + set_tunnel_proto(p, IPPROTO_IPIP); p->i_flags |= VTI_ISVTI; } else { - fprintf(stderr,"Unknown tunnel mode \"%s\"\n", *argv); + fprintf(stderr, + "Unknown tunnel mode \"%s\"\n", *argv); exit(-1); } } else if (strcmp(*argv, "key") == 0) { - unsigned uval; NEXT_ARG(); p->i_flags |= GRE_KEY; p->o_flags |= GRE_KEY; - if (strchr(*argv, '.')) - p->i_key = p->o_key = get_addr32(*argv); - else { - if (get_unsigned(&uval, *argv, 0)<0) { - fprintf(stderr, "invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv); - exit(-1); - } - p->i_key = p->o_key = htonl(uval); - } + p->i_key = p->o_key = tnl_parse_key("key", *argv); } else if (strcmp(*argv, "ikey") == 0) { - unsigned uval; NEXT_ARG(); p->i_flags |= GRE_KEY; - if (strchr(*argv, '.')) - p->i_key = get_addr32(*argv); - else { - if (get_unsigned(&uval, *argv, 0)<0) { - fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv); - exit(-1); - } - p->i_key = htonl(uval); - } + p->i_key = tnl_parse_key("ikey", *argv); } else if (strcmp(*argv, "okey") == 0) { - unsigned uval; NEXT_ARG(); p->o_flags |= GRE_KEY; - if (strchr(*argv, '.')) - p->o_key = get_addr32(*argv); - else { - if (get_unsigned(&uval, *argv, 0)<0) { - fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv); - exit(-1); - } - p->o_key = htonl(uval); - } + p->o_key = tnl_parse_key("okey", *argv); } else if (strcmp(*argv, "seq") == 0) { p->i_flags |= GRE_SEQ; p->o_flags |= GRE_SEQ; @@ -177,16 +139,16 @@ p->iph.saddr = htonl(INADDR_ANY); } else if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); - strncpy(medium, *argv, IFNAMSIZ-1); + strncpy(medium, *argv, IFNAMSIZ - 1); } else if (strcmp(*argv, "ttl") == 0 || - strcmp(*argv, "hoplimit") == 0) { - unsigned uval; + strcmp(*argv, "hoplimit") == 0 || + strcmp(*argv, "hlim") == 0) { + __u8 uval; + NEXT_ARG(); if (strcmp(*argv, "inherit") != 0) { - if (get_unsigned(&uval, *argv, 0)) + if (get_u8(&uval, *argv, 0)) invarg("invalid TTL\n", *argv); - if (uval > 255) - invarg("TTL must be <=255\n", *argv); p->iph.ttl = uval; } } else if (strcmp(*argv, "tos") == 0 || @@ -194,6 +156,7 @@ matches(*argv, "dsfield") == 0) { char *dsfield; __u32 uval; + NEXT_ARG(); dsfield = *argv; strsep(&dsfield, "/"); @@ -208,16 +171,17 @@ p->iph.tos |= uval; } } else { - if (strcmp(*argv, "name") == 0) { + if (strcmp(*argv, "name") == 0) NEXT_ARG(); - } else if (matches(*argv, "help") == 0) + else if (matches(*argv, "help") == 0) usage(); + if (p->name[0]) duparg2("name", *argv); - strncpy(p->name, *argv, IFNAMSIZ); + strncpy(p->name, *argv, IFNAMSIZ - 1); if (cmd == SIOCCHGTUNNEL && count == 0) { - struct ip_tunnel_parm old_p; - memset(&old_p, 0, sizeof(old_p)); + struct ip_tunnel_parm old_p = {}; + if (tnl_get_ioctl(*argv, &old_p)) return -1; *p = old_p; @@ -253,10 +217,9 @@ } if (medium[0]) { - p->link = if_nametoindex(medium); + p->link = ll_name_to_index(medium); if (p->link == 0) { - fprintf(stderr, "Cannot find device \"%s\"\n", - medium); + fprintf(stderr, "Cannot find device \"%s\"\n", medium); return -1; } } @@ -279,10 +242,26 @@ return 0; } +static const char *tnl_defname(const struct ip_tunnel_parm *p) +{ + switch (p->iph.protocol) { + case IPPROTO_IPIP: + if (p->i_flags & VTI_ISVTI) + return "ip_vti0"; + else + return "tunl0"; + case IPPROTO_GRE: + return "gre0"; + case IPPROTO_IPV6: + return "sit0"; + } + return NULL; +} static int do_add(int cmd, int argc, char **argv) { struct ip_tunnel_parm p; + const char *basedev; if (parse_args(argc, argv, cmd, &p) < 0) return -1; @@ -292,21 +271,14 @@ return -1; } - switch (p.iph.protocol) { - case IPPROTO_IPIP: - if (p.i_flags & VTI_ISVTI) - return tnl_add_ioctl(cmd, "ip_vti0", p.name, &p); - else - return tnl_add_ioctl(cmd, "tunl0", p.name, &p); - case IPPROTO_GRE: - return tnl_add_ioctl(cmd, "gre0", p.name, &p); - case IPPROTO_IPV6: - return tnl_add_ioctl(cmd, "sit0", p.name, &p); - default: - fprintf(stderr, "cannot determine tunnel mode (ipip, gre, vti or sit)\n"); + basedev = tnl_defname(&p); + if (!basedev) { + fprintf(stderr, + "cannot determine tunnel mode (ipip, gre, vti or sit)\n"); return -1; } - return -1; + + return tnl_add_ioctl(cmd, basedev, p.name, &p); } static int do_del(int argc, char **argv) @@ -316,123 +288,102 @@ if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) return -1; - switch (p.iph.protocol) { - case IPPROTO_IPIP: - if (p.i_flags & VTI_ISVTI) - return tnl_del_ioctl("ip_vti0", p.name, &p); - else - return tnl_del_ioctl("tunl0", p.name, &p); - case IPPROTO_GRE: - return tnl_del_ioctl("gre0", p.name, &p); - case IPPROTO_IPV6: - return tnl_del_ioctl("sit0", p.name, &p); - default: - return tnl_del_ioctl(p.name, p.name, &p); - } - return -1; + return tnl_del_ioctl(tnl_defname(&p) ? : p.name, p.name, &p); } static void print_tunnel(struct ip_tunnel_parm *p) { - struct ip_tunnel_6rd ip6rd; + struct ip_tunnel_6rd ip6rd = {}; char s1[1024]; char s2[1024]; - memset(&ip6rd, 0, sizeof(ip6rd)); - /* Do not use format_host() for local addr, * symbolic name will not be useful. */ - printf("%s: %s/ip remote %s local %s ", + printf("%s: %s/ip remote %s local %s", p->name, tnl_strproto(p->iph.protocol), - p->iph.daddr ? format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)) : "any", - p->iph.saddr ? rt_addr_n2a(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)) : "any"); + p->iph.daddr ? format_host_r(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)) : "any", + p->iph.saddr ? rt_addr_n2a_r(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)) : "any"); if (p->iph.protocol == IPPROTO_IPV6 && (p->i_flags & SIT_ISATAP)) { - struct ip_tunnel_prl prl[16]; + struct ip_tunnel_prl prl[16] = {}; int i; - memset(prl, 0, sizeof(prl)); prl[0].datalen = sizeof(prl) - sizeof(prl[0]); prl[0].addr = htonl(INADDR_ANY); if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl)) - for (i = 1; i < sizeof(prl) / sizeof(prl[0]); i++) - { - if (prl[i].addr != htonl(INADDR_ANY)) { - printf(" %s %s ", - (prl[i].flags & PRL_DEFAULT) ? "pdr" : "pr", - format_host(AF_INET, 4, &prl[i].addr, s1, sizeof(s1))); + for (i = 1; i < ARRAY_SIZE(prl); i++) { + if (prl[i].addr != htonl(INADDR_ANY)) { + printf(" %s %s ", + (prl[i].flags & PRL_DEFAULT) ? "pdr" : "pr", + format_host(AF_INET, 4, &prl[i].addr)); + } } - } } if (p->link) { const char *n = ll_index_to_name(p->link); + if (n) - printf(" dev %s ", n); + printf(" dev %s", n); } if (p->iph.ttl) - printf(" ttl %d ", p->iph.ttl); + printf(" ttl %d", p->iph.ttl); else - printf(" ttl inherit "); + printf(" ttl inherit"); if (p->iph.tos) { SPRINT_BUF(b1); printf(" tos"); - if (p->iph.tos&1) + if (p->iph.tos & 1) printf(" inherit"); - if (p->iph.tos&~1) - printf("%c%s ", p->iph.tos&1 ? '/' : ' ', - rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1))); + if (p->iph.tos & ~1) + printf("%c%s ", p->iph.tos & 1 ? '/' : ' ', + rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1))); } - if (!(p->iph.frag_off&htons(IP_DF))) + if (!(p->iph.frag_off & htons(IP_DF))) printf(" nopmtudisc"); if (p->iph.protocol == IPPROTO_IPV6 && !tnl_ioctl_get_6rd(p->name, &ip6rd) && ip6rd.prefixlen) { - printf(" 6rd-prefix %s/%u ", + printf(" 6rd-prefix %s/%u", inet_ntop(AF_INET6, &ip6rd.prefix, s1, sizeof(s1)), ip6rd.prefixlen); if (ip6rd.relay_prefix) { - printf("6rd-relay_prefix %s/%u ", - format_host(AF_INET, 4, &ip6rd.relay_prefix, s1, sizeof(s1)), + printf(" 6rd-relay_prefix %s/%u", + format_host(AF_INET, 4, &ip6rd.relay_prefix), ip6rd.relay_prefixlen); } } - if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key) + if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key) printf(" key %u", ntohl(p->i_key)); - else if ((p->i_flags|p->o_flags)&GRE_KEY) { - if (p->i_flags&GRE_KEY) - printf(" ikey %u ", ntohl(p->i_key)); - if (p->o_flags&GRE_KEY) - printf(" okey %u ", ntohl(p->o_key)); + else if ((p->i_flags | p->o_flags) & GRE_KEY) { + if (p->i_flags & GRE_KEY) + printf(" ikey %u", ntohl(p->i_key)); + if (p->o_flags & GRE_KEY) + printf(" okey %u", ntohl(p->o_key)); } - if (p->i_flags&GRE_SEQ) + if (p->i_flags & GRE_SEQ) printf("%s Drop packets out of sequence.", _SL_); - if (p->i_flags&GRE_CSUM) + if (p->i_flags & GRE_CSUM) printf("%s Checksum in received packet is required.", _SL_); - if (p->o_flags&GRE_SEQ) + if (p->o_flags & GRE_SEQ) printf("%s Sequence packets on output.", _SL_); - if (p->o_flags&GRE_CSUM) + if (p->o_flags & GRE_CSUM) printf("%s Checksum output packets.", _SL_); } static int do_tunnels_list(struct ip_tunnel_parm *p) { - char name[IFNAMSIZ]; - unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, - rx_fifo, rx_frame, - tx_bytes, tx_packets, tx_errs, tx_drops, - tx_fifo, tx_colls, tx_carrier, rx_multi; - struct ip_tunnel_parm p1; - char buf[512]; + int err = -1; FILE *fp = fopen("/proc/net/dev", "r"); + if (fp == NULL) { perror("fopen"); return -1; @@ -442,26 +393,22 @@ if (!fgets(buf, sizeof(buf), fp) || !fgets(buf, sizeof(buf), fp)) { fprintf(stderr, "/proc/net/dev read error\n"); - fclose(fp); - return -1; + goto end; } while (fgets(buf, sizeof(buf), fp) != NULL) { + char name[IFNAMSIZ]; int index, type; + struct ip_tunnel_parm p1 = {}; char *ptr; + buf[sizeof(buf) - 1] = 0; - if ((ptr = strchr(buf, ':')) == NULL || + ptr = strchr(buf, ':'); + if (ptr == NULL || (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { fprintf(stderr, "Wrong format for /proc/net/dev. Giving up.\n"); - fclose(fp); - return -1; + goto end; } - if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld", - &rx_bytes, &rx_packets, &rx_errs, &rx_drops, - &rx_fifo, &rx_frame, &rx_multi, - &tx_bytes, &tx_packets, &tx_errs, &tx_drops, - &tx_fifo, &tx_colls, &tx_carrier) != 14) - continue; if (p->name[0] && strcmp(p->name, name)) continue; index = ll_name_to_index(name); @@ -474,7 +421,6 @@ } if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) continue; - memset(&p1, 0, sizeof(p1)); if (tnl_get_ioctl(name, &p1)) continue; if ((p->link && p1.link != p->link) || @@ -484,48 +430,30 @@ (p->i_key && p1.i_key != p->i_key)) continue; print_tunnel(&p1); - if (show_stats) { - printf("%s", _SL_); - printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_); - printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s", - rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_); - printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_); - printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld", - tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops); - } + if (show_stats) + tnl_print_stats(ptr); printf("\n"); } + err = 0; + end: fclose(fp); - return 0; + return err; } static int do_show(int argc, char **argv) { - int err; struct ip_tunnel_parm p; + const char *basedev; ll_init_map(&rth); if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) return -1; - switch (p.iph.protocol) { - case IPPROTO_IPIP: - if (p.i_flags & VTI_ISVTI) - err = tnl_get_ioctl(p.name[0] ? p.name : "ip_vti0", &p); - else - err = tnl_get_ioctl(p.name[0] ? p.name : "tunl0", &p); - break; - case IPPROTO_GRE: - err = tnl_get_ioctl(p.name[0] ? p.name : "gre0", &p); - break; - case IPPROTO_IPV6: - err = tnl_get_ioctl(p.name[0] ? p.name : "sit0", &p); - break; - default: - do_tunnels_list(&p); - return 0; - } - if (err) + basedev = tnl_defname(&p); + if (!basedev) + return do_tunnels_list(&p); + + if (tnl_get_ioctl(p.name[0] ? p.name : basedev, &p)) return -1; print_tunnel(&p); @@ -535,14 +463,11 @@ static int do_prl(int argc, char **argv) { - struct ip_tunnel_prl p; + struct ip_tunnel_prl p = {}; int count = 0; int devname = 0; int cmd = 0; - char medium[IFNAMSIZ]; - - memset(&p, 0, sizeof(p)); - memset(&medium, 0, sizeof(medium)); + char medium[IFNAMSIZ] = {}; while (argc > 0) { if (strcmp(*argv, "prl-default") == 0) { @@ -566,11 +491,13 @@ strncpy(medium, *argv, IFNAMSIZ-1); devname++; } else { - fprintf(stderr,"Invalid PRL parameter \"%s\"\n", *argv); + fprintf(stderr, + "Invalid PRL parameter \"%s\"\n", *argv); exit(-1); } if (count > 1) { - fprintf(stderr,"One PRL entry at a time\n"); + fprintf(stderr, + "One PRL entry at a time\n"); exit(-1); } argc--; argv++; @@ -585,15 +512,12 @@ static int do_6rd(int argc, char **argv) { - struct ip_tunnel_6rd ip6rd; + struct ip_tunnel_6rd ip6rd = {}; int devname = 0; int cmd = 0; - char medium[IFNAMSIZ]; + char medium[IFNAMSIZ] = {}; inet_prefix prefix; - memset(&ip6rd, 0, sizeof(ip6rd)); - memset(&medium, 0, sizeof(medium)); - while (argc > 0) { if (strcmp(*argv, "6rd-prefix") == 0) { NEXT_ARG(); @@ -616,7 +540,8 @@ strncpy(medium, *argv, IFNAMSIZ-1); devname++; } else { - fprintf(stderr,"Invalid 6RD parameter \"%s\"\n", *argv); + fprintf(stderr, + "Invalid 6RD parameter \"%s\"\n", *argv); exit(-1); } argc--; argv++; @@ -629,8 +554,35 @@ return tnl_6rd_ioctl(cmd, medium, &ip6rd); } +static int tunnel_mode_is_ipv6(char *tunnel_mode) +{ + static const char * const ipv6_modes[] = { + "ipv6/ipv6", "ip6ip6", + "vti6", + "ip/ipv6", "ipv4/ipv6", "ipip6", "ip4ip6", + "ip6gre", "gre/ipv6", + "any/ipv6", "any" + }; + int i; + + for (i = 0; i < ARRAY_SIZE(ipv6_modes); i++) { + if (strcmp(ipv6_modes[i], tunnel_mode) == 0) + return 1; + } + return 0; +} + int do_iptunnel(int argc, char **argv) { + int i; + + for (i = 0; i < argc - 1; i++) { + if (strcmp(argv[i], "mode") == 0) { + if (tunnel_mode_is_ipv6(argv[i + 1])) + preferred_family = AF_INET6; + break; + } + } switch (preferred_family) { case AF_UNSPEC: preferred_family = AF_INET; @@ -651,19 +603,19 @@ if (argc > 0) { if (matches(*argv, "add") == 0) - return do_add(SIOCADDTUNNEL, argc-1, argv+1); + return do_add(SIOCADDTUNNEL, argc - 1, argv + 1); if (matches(*argv, "change") == 0) - return do_add(SIOCCHGTUNNEL, argc-1, argv+1); + return do_add(SIOCCHGTUNNEL, argc - 1, argv + 1); if (matches(*argv, "delete") == 0) - return do_del(argc-1, argv+1); + return do_del(argc - 1, argv + 1); if (matches(*argv, "show") == 0 || matches(*argv, "lst") == 0 || matches(*argv, "list") == 0) - return do_show(argc-1, argv+1); + return do_show(argc - 1, argv + 1); if (matches(*argv, "prl") == 0) - return do_prl(argc-1, argv+1); + return do_prl(argc - 1, argv + 1); if (matches(*argv, "6rd") == 0) - return do_6rd(argc-1, argv+1); + return do_6rd(argc - 1, argv + 1); if (matches(*argv, "help") == 0) usage(); } else diff -Nru iproute2-4.3.0/ip/iptuntap.c iproute2-4.9.0/ip/iptuntap.c --- iproute2-4.3.0/ip/iptuntap.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/iptuntap.c 2016-12-12 23:07:42.000000000 +0000 @@ -25,6 +25,7 @@ #include #include #include +#include #include "rt_names.h" #include "utils.h" @@ -36,9 +37,9 @@ static void usage(void) { - fprintf(stderr, "Usage: ip tuntap { add | del | show | list | lst | help } [ dev PHYS_DEV ] \n"); + fprintf(stderr, "Usage: ip tuntap { add | del | show | list | lst | help } [ dev PHYS_DEV ]\n"); fprintf(stderr, " [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n"); - fprintf(stderr, " [ one_queue ] [ pi ] [ vnet_hdr ] [ multi_queue ]\n"); + fprintf(stderr, " [ one_queue ] [ pi ] [ vnet_hdr ] [ multi_queue ] [ name NAME ]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where: USER := { STRING | NUMBER }\n"); fprintf(stderr, " GROUP := { STRING | NUMBER }\n"); @@ -104,7 +105,8 @@ return ret; } -static int parse_args(int argc, char **argv, struct ifreq *ifr, uid_t *uid, gid_t *gid) +static int parse_args(int argc, char **argv, + struct ifreq *ifr, uid_t *uid, gid_t *gid) { int count = 0; @@ -117,18 +119,18 @@ NEXT_ARG(); if (matches(*argv, "tun") == 0) { if (ifr->ifr_flags & IFF_TAP) { - fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + fprintf(stderr, "You managed to ask for more than one tunnel mode.\n"); exit(-1); } ifr->ifr_flags |= IFF_TUN; } else if (matches(*argv, "tap") == 0) { if (ifr->ifr_flags & IFF_TUN) { - fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + fprintf(stderr, "You managed to ask for more than one tunnel mode.\n"); exit(-1); } ifr->ifr_flags |= IFF_TAP; } else { - fprintf(stderr,"Unknown tunnel mode \"%s\"\n", *argv); + fprintf(stderr, "Unknown tunnel mode \"%s\"\n", *argv); exit(-1); } } else if (uid && matches(*argv, "user") == 0) { @@ -140,6 +142,7 @@ *uid = user; else { struct passwd *pw = getpwnam(*argv); + if (!pw) { fprintf(stderr, "invalid user \"%s\"\n", *argv); exit(-1); @@ -156,6 +159,7 @@ *gid = group; else { struct group *gr = getgrnam(*argv); + if (!gr) { fprintf(stderr, "invalid group \"%s\"\n", *argv); exit(-1); @@ -271,6 +275,110 @@ printf(" UNKNOWN_FLAGS:%lx", flags); } +static char *pid_name(pid_t pid) +{ + char *comm; + FILE *f; + int err; + + err = asprintf(&comm, "/proc/%d/comm", pid); + if (err < 0) + return NULL; + + f = fopen(comm, "r"); + free(comm); + if (!f) { + perror("fopen"); + return NULL; + } + + if (fscanf(f, "%ms\n", &comm) != 1) { + perror("fscanf"); + comm = NULL; + } + + + if (fclose(f)) + perror("fclose"); + + return comm; +} + +static void show_processes(const char *name) +{ + glob_t globbuf = { }; + char **fd_path; + int err; + + err = glob("/proc/[0-9]*/fd/[0-9]*", GLOB_NOSORT, + NULL, &globbuf); + if (err) + return; + + fd_path = globbuf.gl_pathv; + while (*fd_path) { + const char *dev_net_tun = "/dev/net/tun"; + const size_t linkbuf_len = strlen(dev_net_tun) + 2; + char linkbuf[linkbuf_len], *fdinfo; + int pid, fd; + FILE *f; + + if (sscanf(*fd_path, "/proc/%d/fd/%d", &pid, &fd) != 2) + goto next; + + if (pid == getpid()) + goto next; + + err = readlink(*fd_path, linkbuf, linkbuf_len - 1); + if (err < 0) { + perror("readlink"); + goto next; + } + linkbuf[err] = '\0'; + if (strcmp(dev_net_tun, linkbuf)) + goto next; + + if (asprintf(&fdinfo, "/proc/%d/fdinfo/%d", pid, fd) < 0) + goto next; + + f = fopen(fdinfo, "r"); + free(fdinfo); + if (!f) { + perror("fopen"); + goto next; + } + + while (!feof(f)) { + char *key = NULL, *value = NULL; + + err = fscanf(f, "%m[^:]: %ms\n", &key, &value); + if (err == EOF) { + if (ferror(f)) + perror("fscanf"); + break; + } else if (err == 2 && + !strcmp("iff", key) && + !strcmp(name, value)) { + char *pname = pid_name(pid); + + printf(" %s(%d)", pname ? : "", pid); + free(pname); + } + + free(key); + free(value); + } + if (fclose(f)) + perror("fclose"); + +next: + ++fd_path; + } + + globfree(&globbuf); +} + + static int do_show(int argc, char **argv) { DIR *dir; @@ -300,6 +408,11 @@ if (group != -1) printf(" group %ld", group); printf("\n"); + if (show_details) { + printf("\tAttached to processes:"); + show_processes(d->d_name); + printf("\n"); + } } closedir(dir); return 0; @@ -313,9 +426,9 @@ if (matches(*argv, "delete") == 0) return do_del(argc-1, argv+1); if (matches(*argv, "show") == 0 || - matches(*argv, "lst") == 0 || - matches(*argv, "list") == 0) - return do_show(argc-1, argv+1); + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc-1, argv+1); if (matches(*argv, "help") == 0) usage(); } else diff -Nru iproute2-4.3.0/ip/ipxfrm.c iproute2-4.9.0/ip/ipxfrm.c --- iproute2-4.3.0/ip/ipxfrm.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/ipxfrm.c 2016-12-12 23:07:42.000000000 +0000 @@ -50,7 +50,7 @@ strncat(buf, str, len); \ buf[sizeof(buf) - 1] = '\0'; \ } \ - } while(0); + } while (0); struct xfrm_filter filter; @@ -111,7 +111,7 @@ int t_type; }; -static const struct typeent xfrmproto_types[]= { +static const struct typeent xfrmproto_types[] = { { "esp", IPPROTO_ESP }, { "ah", IPPROTO_AH }, { "comp", IPPROTO_COMP }, { "route2", IPPROTO_ROUTING }, { "hao", IPPROTO_DSTOPTS }, { "ipsec-any", IPSEC_PROTO_ANY }, @@ -124,6 +124,7 @@ for (i = 0; ; i++) { const struct typeent *t = &xfrmproto_types[i]; + if (!t->t_name || t->t_type == -1) break; @@ -141,6 +142,7 @@ for (i = 0; ; i++) { const struct typeent *t = &xfrmproto_types[i]; + if (!t->t_name || t->t_type == -1) break; @@ -152,7 +154,7 @@ return str; } -static const struct typeent algo_types[]= { +static const struct typeent algo_types[] = { { "enc", XFRMA_ALG_CRYPT }, { "auth", XFRMA_ALG_AUTH }, { "comp", XFRMA_ALG_COMP }, { "aead", XFRMA_ALG_AEAD }, { "auth-trunc", XFRMA_ALG_AUTH_TRUNC }, @@ -165,6 +167,7 @@ for (i = 0; ; i++) { const struct typeent *t = &algo_types[i]; + if (!t->t_name || t->t_type == -1) break; @@ -182,6 +185,7 @@ for (i = 0; ; i++) { const struct typeent *t = &algo_types[i]; + if (!t->t_name || t->t_type == -1) break; @@ -281,17 +285,11 @@ __u8 mode, __u32 reqid, __u16 family, int force_spi, FILE *fp, const char *prefix, const char *title) { - char abuf[256]; - if (title) fputs(title, fp); - memset(abuf, '\0', sizeof(abuf)); - fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr), - saddr, abuf, sizeof(abuf))); - memset(abuf, '\0', sizeof(abuf)); - fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr), - &id->daddr, abuf, sizeof(abuf))); + fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr), saddr)); + fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr), &id->daddr)); fprintf(fp, "%s", _SL_); if (prefix) @@ -302,6 +300,7 @@ if (show_stats > 0 || force_spi || id->spi) { __u32 spi = ntohl(id->spi); + fprintf(fp, "spi 0x%08x", spi); if (show_stats > 0) fprintf(fp, "(%u)", spi); @@ -340,6 +339,7 @@ static const char *strxf_limit(__u64 limit) { static char str[32]; + if (limit == XFRM_INF) strcpy(str, "(INF)"); else @@ -389,7 +389,7 @@ if (cfg) { if (prefix) fputs(prefix, fp); - fprintf(fp, "lifetime config:%s",_SL_); + fprintf(fp, "lifetime config:%s", _SL_); if (prefix) fputs(prefix, fp); @@ -441,7 +441,6 @@ void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, FILE *fp, const char *prefix) { - char abuf[256]; __u16 f; f = sel->family; @@ -453,16 +452,12 @@ if (prefix) fputs(prefix, fp); - memset(abuf, '\0', sizeof(abuf)); fprintf(fp, "src %s/%u ", - rt_addr_n2a(f, sizeof(sel->saddr), &sel->saddr, - abuf, sizeof(abuf)), + rt_addr_n2a(f, sizeof(sel->saddr), &sel->saddr), sel->prefixlen_s); - memset(abuf, '\0', sizeof(abuf)); fprintf(fp, "dst %s/%u ", - rt_addr_n2a(f, sizeof(sel->daddr), &sel->daddr, - abuf, sizeof(abuf)), + rt_addr_n2a(f, sizeof(sel->daddr), &sel->daddr), sel->prefixlen_d); if (sel->proto) @@ -538,7 +533,7 @@ if (keylen > 0) { fprintf(fp, "0x"); - for (i = 0; i < keylen; i ++) + for (i = 0; i < keylen; i++) fprintf(fp, "%.2x", (unsigned char)algo->alg_key[i]); if (show_stats > 0) @@ -692,43 +687,48 @@ if (tb[XFRMA_MARK]) { struct rtattr *rta = tb[XFRMA_MARK]; struct xfrm_mark *m = (struct xfrm_mark *) RTA_DATA(rta); + fprintf(fp, "\tmark %#x/%#x", m->v, m->m); fprintf(fp, "%s", _SL_); } if (tb[XFRMA_ALG_AUTH] && !tb[XFRMA_ALG_AUTH_TRUNC]) { struct rtattr *rta = tb[XFRMA_ALG_AUTH]; + xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta), XFRMA_ALG_AUTH, RTA_PAYLOAD(rta), fp, prefix); } if (tb[XFRMA_ALG_AUTH_TRUNC]) { struct rtattr *rta = tb[XFRMA_ALG_AUTH_TRUNC]; + xfrm_auth_trunc_print((struct xfrm_algo_auth *) RTA_DATA(rta), RTA_PAYLOAD(rta), fp, prefix); } if (tb[XFRMA_ALG_AEAD]) { struct rtattr *rta = tb[XFRMA_ALG_AEAD]; + xfrm_aead_print((struct xfrm_algo_aead *)RTA_DATA(rta), RTA_PAYLOAD(rta), fp, prefix); } if (tb[XFRMA_ALG_CRYPT]) { struct rtattr *rta = tb[XFRMA_ALG_CRYPT]; + xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta), XFRMA_ALG_CRYPT, RTA_PAYLOAD(rta), fp, prefix); } if (tb[XFRMA_ALG_COMP]) { struct rtattr *rta = tb[XFRMA_ALG_COMP]; + xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta), XFRMA_ALG_COMP, RTA_PAYLOAD(rta), fp, prefix); } if (tb[XFRMA_ENCAP]) { struct xfrm_encap_tmpl *e; - char abuf[256]; if (prefix) fputs(prefix, fp); @@ -756,21 +756,19 @@ fprintf(fp, "sport %u ", ntohs(e->encap_sport)); fprintf(fp, "dport %u ", ntohs(e->encap_dport)); - memset(abuf, '\0', sizeof(abuf)); fprintf(fp, "addr %s", - rt_addr_n2a(family, sizeof(e->encap_oa), &e->encap_oa, - abuf, sizeof(abuf))); + rt_addr_n2a(family, sizeof(e->encap_oa), &e->encap_oa)); fprintf(fp, "%s", _SL_); } if (tb[XFRMA_TMPL]) { struct rtattr *rta = tb[XFRMA_TMPL]; + xfrm_tmpl_print((struct xfrm_user_tmpl *) RTA_DATA(rta), RTA_PAYLOAD(rta), fp, prefix); } if (tb[XFRMA_COADDR]) { - char abuf[256]; xfrm_address_t *coa; if (prefix) @@ -785,10 +783,8 @@ return; } - memset(abuf, '\0', sizeof(abuf)); fprintf(fp, "%s", - rt_addr_n2a(family, sizeof(*coa), coa, - abuf, sizeof(abuf))); + rt_addr_n2a(family, sizeof(*coa), coa)); fprintf(fp, "%s", _SL_); } @@ -871,9 +867,7 @@ static int xfrm_selector_iszero(struct xfrm_selector *s) { - struct xfrm_selector s0; - - memset(&s0, 0, sizeof(s0)); + struct xfrm_selector s0 = {}; return (memcmp(&s0, s, sizeof(s0)) == 0); } @@ -882,11 +876,9 @@ struct rtattr *tb[], FILE *fp, const char *prefix, const char *title) { - char buf[STRBUF_SIZE]; + char buf[STRBUF_SIZE] = {}; int force_spi = xfrm_xfrmproto_is_ipsec(xsinfo->id.proto); - memset(buf, '\0', sizeof(buf)); - xfrm_id_info_print(&xsinfo->saddr, &xsinfo->id, xsinfo->mode, xsinfo->reqid, xsinfo->family, force_spi, fp, prefix, title); @@ -963,9 +955,7 @@ struct rtattr *tb[], FILE *fp, const char *prefix, const char *title) { - char buf[STRBUF_SIZE]; - - memset(buf, '\0', sizeof(buf)); + char buf[STRBUF_SIZE] = {}; xfrm_selector_print(&xpinfo->sel, preferred_family, fp, title); @@ -1066,11 +1056,8 @@ { int argc = *argcp; char **argv = *argvp; - inet_prefix dst; - inet_prefix src; - - memset(&dst, 0, sizeof(dst)); - memset(&src, 0, sizeof(src)); + inet_prefix dst = {}; + inet_prefix src = {}; while (1) { if (strcmp(*argv, "src") == 0) { @@ -1113,15 +1100,10 @@ filter.id_proto_mask = XFRM_FILTER_MASK_FULL; } else if (strcmp(*argv, "spi") == 0) { - __u32 spi; - NEXT_ARG(); - if (get_u32(&spi, *argv, 0)) + if (get_be32(&id->spi, *argv, 0)) invarg("SPI value is invalid", *argv); - spi = htonl(spi); - id->spi = spi; - filter.id_spi_mask = XFRM_FILTER_MASK_FULL; } else { @@ -1140,11 +1122,11 @@ if (id->spi && id->proto) { if (xfrm_xfrmproto_is_ro(id->proto)) { fprintf(stderr, "\"spi\" is invalid with XFRM-PROTO value \"%s\"\n", - strxf_xfrmproto(id->proto)); + strxf_xfrmproto(id->proto)); exit(1); } else if (id->proto == IPPROTO_COMP && ntohl(id->spi) >= 0x10000) { fprintf(stderr, "SPI value is too large with XFRM-PROTO value \"%s\"\n", - strxf_xfrmproto(id->proto)); + strxf_xfrmproto(id->proto)); exit(1); } } @@ -1238,6 +1220,7 @@ upspec = 0; else { struct protoent *pp; + pp = getprotobyname(*argv); if (pp) upspec = pp->p_proto; @@ -1255,9 +1238,8 @@ NEXT_ARG(); - if (get_u16(&sel->sport, *argv, 0)) + if (get_be16(&sel->sport, *argv, 0)) invarg("value after \"sport\" is invalid", *argv); - sel->sport = htons(sel->sport); if (sel->sport) sel->sport_mask = ~((__u16)0); @@ -1268,9 +1250,8 @@ NEXT_ARG(); - if (get_u16(&sel->dport, *argv, 0)) + if (get_be16(&sel->dport, *argv, 0)) invarg("value after \"dport\" is invalid", *argv); - sel->dport = htons(sel->dport); if (sel->dport) sel->dport_mask = ~((__u16)0); @@ -1304,7 +1285,7 @@ filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; } else if (strcmp(*argv, "key") == 0) { - unsigned uval; + unsigned int uval; grekey = *argv; @@ -1313,7 +1294,7 @@ if (strchr(*argv, '.')) uval = htonl(get_addr32(*argv)); else { - if (get_unsigned(&uval, *argv, 0)<0) { + if (get_unsigned(&uval, *argv, 0) < 0) { fprintf(stderr, "value after \"key\" is invalid\n"); exit(-1); } @@ -1381,13 +1362,10 @@ { int argc = *argcp; char **argv = *argvp; - inet_prefix dst; - inet_prefix src; + inet_prefix dst = {}; + inet_prefix src = {}; char *upspecp = NULL; - memset(&dst, 0, sizeof(dst)); - memset(&src, 0, sizeof(src)); - while (1) { if (strcmp(*argv, "src") == 0) { NEXT_ARG(); diff -Nru iproute2-4.3.0/ip/link_gre6.c iproute2-4.9.0/ip/link_gre6.c --- iproute2-4.3.0/ip/link_gre6.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/link_gre6.c 2016-12-12 23:07:42.000000000 +0000 @@ -38,6 +38,9 @@ fprintf(f, " [ hoplimit TTL ] [ encaplimit ELIM ]\n"); fprintf(f, " [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n"); fprintf(f, " [ dscp inherit ] [ dev PHYS_DEV ]\n"); + fprintf(f, " [ noencap ] [ encap { fou | gue | none } ]\n"); + fprintf(f, " [ encap-sport PORT ] [ encap-dport PORT ]\n"); + fprintf(f, " [ [no]encap-csum ] [ [no]encap-csum6 ] [ [no]encap-remcsum ]\n"); fprintf(f, "\n"); fprintf(f, "Where: NAME := STRING\n"); fprintf(f, " ADDR := IPV6_ADDRESS\n"); @@ -60,37 +63,39 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { + struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); struct { struct nlmsghdr n; struct ifinfomsg i; char buf[1024]; - } req; - struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *linkinfo[IFLA_INFO_MAX+1]; struct rtattr *greinfo[IFLA_GRE_MAX + 1]; __u16 iflags = 0; __u16 oflags = 0; - unsigned ikey = 0; - unsigned okey = 0; + unsigned int ikey = 0; + unsigned int okey = 0; struct in6_addr raddr = IN6ADDR_ANY_INIT; struct in6_addr laddr = IN6ADDR_ANY_INIT; - unsigned link = 0; - unsigned flowinfo = 0; - unsigned flags = 0; + unsigned int link = 0; + unsigned int flowinfo = 0; + unsigned int flags = 0; __u8 hop_limit = DEFAULT_TNL_HOP_LIMIT; __u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; + __u16 encaptype = 0; + __u16 encapflags = TUNNEL_ENCAP_FLAG_CSUM6; + __u16 encapsport = 0; + __u16 encapdport = 0; int len; if (!(n->nlmsg_flags & NLM_F_CREATE)) { - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETLINK; - req.i.ifi_family = preferred_family; - req.i.ifi_index = ifi->ifi_index; - if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) { get_failed: fprintf(stderr, @@ -148,11 +153,23 @@ if (greinfo[IFLA_GRE_FLAGS]) flags = rta_getattr_u32(greinfo[IFLA_GRE_FLAGS]); + + if (greinfo[IFLA_GRE_ENCAP_TYPE]) + encaptype = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_TYPE]); + + if (greinfo[IFLA_GRE_ENCAP_FLAGS]) + encapflags = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_FLAGS]); + + if (greinfo[IFLA_GRE_ENCAP_SPORT]) + encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]); + + if (greinfo[IFLA_GRE_ENCAP_DPORT]) + encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]); } while (argc > 0) { if (!matches(*argv, "key")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); iflags |= GRE_KEY; @@ -170,14 +187,14 @@ ikey = okey = uval; } else if (!matches(*argv, "ikey")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); iflags |= GRE_KEY; if (strchr(*argv, '.')) uval = get_addr32(*argv); else { - if (get_unsigned(&uval, *argv, 0)<0) { + if (get_unsigned(&uval, *argv, 0) < 0) { fprintf(stderr, "invalid value of \"ikey\"\n"); exit(-1); } @@ -185,14 +202,14 @@ } ikey = uval; } else if (!matches(*argv, "okey")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); oflags |= GRE_KEY; if (strchr(*argv, '.')) uval = get_addr32(*argv); else { - if (get_unsigned(&uval, *argv, 0)<0) { + if (get_unsigned(&uval, *argv, 0) < 0) { fprintf(stderr, "invalid value of \"okey\"\n"); exit(-1); } @@ -215,6 +232,7 @@ oflags |= GRE_CSUM; } else if (!matches(*argv, "remote")) { inet_prefix addr; + NEXT_ARG(); get_prefix(&addr, *argv, preferred_family); if (addr.family == AF_UNSPEC) @@ -222,6 +240,7 @@ memcpy(&raddr, &addr.data, sizeof(raddr)); } else if (!matches(*argv, "local")) { inet_prefix addr; + NEXT_ARG(); get_prefix(&addr, *argv, preferred_family); if (addr.family == AF_UNSPEC) @@ -238,6 +257,7 @@ } else if (!matches(*argv, "ttl") || !matches(*argv, "hoplimit")) { __u8 uval; + NEXT_ARG(); if (get_u8(&uval, *argv, 0)) invarg("invalid TTL", *argv); @@ -246,6 +266,7 @@ !matches(*argv, "tclass") || !matches(*argv, "dsfield")) { __u8 uval; + NEXT_ARG(); if (strcmp(*argv, "inherit") == 0) flags |= IP6_TNL_F_USE_ORIG_TCLASS; @@ -258,6 +279,7 @@ } else if (strcmp(*argv, "flowlabel") == 0 || strcmp(*argv, "fl") == 0) { __u32 uval; + NEXT_ARG(); if (strcmp(*argv, "inherit") == 0) flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL; @@ -274,6 +296,40 @@ if (strcmp(*argv, "inherit") != 0) invarg("not inherit", *argv); flags |= IP6_TNL_F_RCV_DSCP_COPY; + } else if (strcmp(*argv, "noencap") == 0) { + encaptype = TUNNEL_ENCAP_NONE; + } else if (strcmp(*argv, "encap") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "fou") == 0) + encaptype = TUNNEL_ENCAP_FOU; + else if (strcmp(*argv, "gue") == 0) + encaptype = TUNNEL_ENCAP_GUE; + else if (strcmp(*argv, "none") == 0) + encaptype = TUNNEL_ENCAP_NONE; + else + invarg("Invalid encap type.", *argv); + } else if (strcmp(*argv, "encap-sport") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "auto") == 0) + encapsport = 0; + else if (get_u16(&encapsport, *argv, 0)) + invarg("Invalid source port.", *argv); + } else if (strcmp(*argv, "encap-dport") == 0) { + NEXT_ARG(); + if (get_u16(&encapdport, *argv, 0)) + invarg("Invalid destination port.", *argv); + } else if (strcmp(*argv, "encap-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "noencap-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "encap-udp6-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "noencap-udp6-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "encap-remcsum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "noencap-remcsum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM; } else usage(); argc--; argv++; @@ -292,19 +348,23 @@ addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4); addattr_l(n, 1024, IFLA_GRE_FLAGS, &flowinfo, 4); + addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype); + addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags); + addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport)); + addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport)); + return 0; } static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { - char s1[1024]; char s2[64]; const char *local = "any"; const char *remote = "any"; - unsigned iflags = 0; - unsigned oflags = 0; - unsigned flags = 0; - unsigned flowinfo = 0; + unsigned int iflags = 0; + unsigned int oflags = 0; + unsigned int flags = 0; + unsigned int flowinfo = 0; struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT; if (!tb) @@ -318,26 +378,28 @@ if (tb[IFLA_GRE_REMOTE]) { struct in6_addr addr; + memcpy(&addr, RTA_DATA(tb[IFLA_GRE_REMOTE]), sizeof(addr)); if (memcmp(&addr, &in6_addr_any, sizeof(addr))) - remote = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1)); + remote = format_host(AF_INET6, sizeof(addr), &addr); } fprintf(f, "remote %s ", remote); if (tb[IFLA_GRE_LOCAL]) { struct in6_addr addr; + memcpy(&addr, RTA_DATA(tb[IFLA_GRE_LOCAL]), sizeof(addr)); if (memcmp(&addr, &in6_addr_any, sizeof(addr))) - local = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1)); + local = format_host(AF_INET6, sizeof(addr), &addr); } fprintf(f, "local %s ", local); if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) { - unsigned link = rta_getattr_u32(tb[IFLA_GRE_LINK]); + unsigned int link = rta_getattr_u32(tb[IFLA_GRE_LINK]); const char *n = if_indextoname(link, s2); if (n) @@ -389,6 +451,49 @@ fputs("icsum ", f); if (oflags & GRE_CSUM) fputs("ocsum ", f); + + if (tb[IFLA_GRE_ENCAP_TYPE] && + rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) { + __u16 type = rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]); + __u16 flags = rta_getattr_u16(tb[IFLA_GRE_ENCAP_FLAGS]); + __u16 sport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_SPORT]); + __u16 dport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_DPORT]); + + fputs("encap ", f); + switch (type) { + case TUNNEL_ENCAP_FOU: + fputs("fou ", f); + break; + case TUNNEL_ENCAP_GUE: + fputs("gue ", f); + break; + default: + fputs("unknown ", f); + break; + } + + if (sport == 0) + fputs("encap-sport auto ", f); + else + fprintf(f, "encap-sport %u", ntohs(sport)); + + fprintf(f, "encap-dport %u ", ntohs(dport)); + + if (flags & TUNNEL_ENCAP_FLAG_CSUM) + fputs("encap-csum ", f); + else + fputs("noencap-csum ", f); + + if (flags & TUNNEL_ENCAP_FLAG_CSUM6) + fputs("encap-csum6 ", f); + else + fputs("noencap-csum6 ", f); + + if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) + fputs("encap-remcsum ", f); + else + fputs("noencap-remcsum ", f); + } } static void gre_print_help(struct link_util *lu, int argc, char **argv, diff -Nru iproute2-4.3.0/ip/link_gre.c iproute2-4.9.0/ip/link_gre.c --- iproute2-4.3.0/ip/link_gre.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/link_gre.c 2016-12-12 23:07:42.000000000 +0000 @@ -50,22 +50,28 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { + struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); struct { struct nlmsghdr n; struct ifinfomsg i; char buf[16384]; - } req; - struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *linkinfo[IFLA_INFO_MAX+1]; struct rtattr *greinfo[IFLA_GRE_MAX + 1]; __u16 iflags = 0; __u16 oflags = 0; - unsigned ikey = 0; - unsigned okey = 0; - unsigned saddr = 0; - unsigned daddr = 0; - unsigned link = 0; + unsigned int ikey = 0; + unsigned int okey = 0; + unsigned int saddr = 0; + unsigned int daddr = 0; + unsigned int link = 0; __u8 pmtudisc = 1; __u8 ttl = 0; __u8 tos = 0; @@ -74,16 +80,9 @@ __u16 encapflags = 0; __u16 encapsport = 0; __u16 encapdport = 0; + __u8 metadata = 0; if (!(n->nlmsg_flags & NLM_F_CREATE)) { - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETLINK; - req.i.ifi_family = preferred_family; - req.i.ifi_index = ifi->ifi_index; - if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) { get_failed: fprintf(stderr, @@ -148,11 +147,14 @@ encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]); if (greinfo[IFLA_GRE_ENCAP_DPORT]) encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]); + + if (greinfo[IFLA_GRE_COLLECT_METADATA]) + metadata = 1; } while (argc > 0) { if (!matches(*argv, "key")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); iflags |= GRE_KEY; @@ -170,14 +172,14 @@ ikey = okey = uval; } else if (!matches(*argv, "ikey")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); iflags |= GRE_KEY; if (strchr(*argv, '.')) uval = get_addr32(*argv); else { - if (get_unsigned(&uval, *argv, 0)<0) { + if (get_unsigned(&uval, *argv, 0) < 0) { fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv); exit(-1); } @@ -185,14 +187,14 @@ } ikey = uval; } else if (!matches(*argv, "okey")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); oflags |= GRE_KEY; if (strchr(*argv, '.')) uval = get_addr32(*argv); else { - if (get_unsigned(&uval, *argv, 0)<0) { + if (get_unsigned(&uval, *argv, 0) < 0) { fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv); exit(-1); } @@ -235,7 +237,7 @@ } } else if (!matches(*argv, "ttl") || !matches(*argv, "hoplimit")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); if (strcmp(*argv, "inherit") != 0) { @@ -291,6 +293,8 @@ encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM; } else if (strcmp(*argv, "noencap-remcsum") == 0) { encapflags |= ~TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "external") == 0) { + metadata = 1; } else usage(); argc--; argv++; @@ -309,17 +313,21 @@ return -1; } - addattr32(n, 1024, IFLA_GRE_IKEY, ikey); - addattr32(n, 1024, IFLA_GRE_OKEY, okey); - addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2); - addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2); - addattr_l(n, 1024, IFLA_GRE_LOCAL, &saddr, 4); - addattr_l(n, 1024, IFLA_GRE_REMOTE, &daddr, 4); - addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1); - if (link) - addattr32(n, 1024, IFLA_GRE_LINK, link); - addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1); - addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1); + if (!metadata) { + addattr32(n, 1024, IFLA_GRE_IKEY, ikey); + addattr32(n, 1024, IFLA_GRE_OKEY, okey); + addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2); + addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2); + addattr_l(n, 1024, IFLA_GRE_LOCAL, &saddr, 4); + addattr_l(n, 1024, IFLA_GRE_REMOTE, &daddr, 4); + addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1); + if (link) + addattr32(n, 1024, IFLA_GRE_LINK, link); + addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1); + addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1); + } else { + addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0); + } addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype); addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags); @@ -329,38 +337,34 @@ return 0; } -static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +static void gre_print_direct_opt(FILE *f, struct rtattr *tb[]) { - char s1[1024]; char s2[64]; const char *local = "any"; const char *remote = "any"; - unsigned iflags = 0; - unsigned oflags = 0; - - if (!tb) - return; + unsigned int iflags = 0; + unsigned int oflags = 0; if (tb[IFLA_GRE_REMOTE]) { - unsigned addr = rta_getattr_u32(tb[IFLA_GRE_REMOTE]); + unsigned int addr = rta_getattr_u32(tb[IFLA_GRE_REMOTE]); if (addr) - remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1)); + remote = format_host(AF_INET, 4, &addr); } fprintf(f, "remote %s ", remote); if (tb[IFLA_GRE_LOCAL]) { - unsigned addr = rta_getattr_u32(tb[IFLA_GRE_LOCAL]); + unsigned int addr = rta_getattr_u32(tb[IFLA_GRE_LOCAL]); if (addr) - local = format_host(AF_INET, 4, &addr, s1, sizeof(s1)); + local = format_host(AF_INET, 4, &addr); } fprintf(f, "local %s ", local); if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) { - unsigned link = rta_getattr_u32(tb[IFLA_GRE_LINK]); + unsigned int link = rta_getattr_u32(tb[IFLA_GRE_LINK]); const char *n = if_indextoname(link, s2); if (n) @@ -412,9 +416,20 @@ fputs("icsum ", f); if (oflags & GRE_CSUM) fputs("ocsum ", f); +} + +static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + if (!tb) + return; + + if (!tb[IFLA_GRE_COLLECT_METADATA]) + gre_print_direct_opt(f, tb); + else + fputs("external ", f); if (tb[IFLA_GRE_ENCAP_TYPE] && - *(__u16 *)RTA_DATA(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) { + rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) { __u16 type = rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]); __u16 flags = rta_getattr_u16(tb[IFLA_GRE_ENCAP_FLAGS]); __u16 sport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_SPORT]); diff -Nru iproute2-4.3.0/ip/link_ip6tnl.c iproute2-4.9.0/ip/link_ip6tnl.c --- iproute2-4.3.0/ip/link_ip6tnl.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/link_ip6tnl.c 2016-12-12 23:07:42.000000000 +0000 @@ -35,8 +35,12 @@ fprintf(f, " [ mode { ip6ip6 | ipip6 | any } ]\n"); fprintf(f, " type ip6tnl [ remote ADDR ] [ local ADDR ]\n"); fprintf(f, " [ dev PHYS_DEV ] [ encaplimit ELIM ]\n"); - fprintf(f ," [ hoplimit HLIM ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n"); + fprintf(f, " [ hoplimit HLIM ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n"); fprintf(f, " [ dscp inherit ] [ fwmark inherit ]\n"); + fprintf(f, " [ noencap ] [ encap { fou | gue | none } ]\n"); + fprintf(f, " [ encap-sport PORT ] [ encap-dport PORT ]\n"); + fprintf(f, " [ [no]encap-csum ] [ [no]encap-csum6 ] [ [no]encap-remcsum ]\n"); + fprintf(f, " [ external ]\n"); fprintf(f, "\n"); fprintf(f, "Where: NAME := STRING\n"); fprintf(f, " ADDR := IPV6_ADDRESS\n"); @@ -58,37 +62,37 @@ static int ip6tunnel_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { + struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); struct { struct nlmsghdr n; struct ifinfomsg i; char buf[2048]; - } req; - struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *linkinfo[IFLA_INFO_MAX+1]; struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1]; int len; - struct in6_addr laddr; - struct in6_addr raddr; + struct in6_addr laddr = {}; + struct in6_addr raddr = {}; __u8 hop_limit = DEFAULT_TNL_HOP_LIMIT; __u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; __u32 flowinfo = 0; __u32 flags = 0; __u32 link = 0; __u8 proto = 0; - - memset(&laddr, 0, sizeof(laddr)); - memset(&raddr, 0, sizeof(raddr)); + __u16 encaptype = 0; + __u16 encapflags = TUNNEL_ENCAP_FLAG_CSUM6; + __u16 encapsport = 0; + __u16 encapdport = 0; + __u8 metadata = 0; if (!(n->nlmsg_flags & NLM_F_CREATE)) { - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETLINK; - req.i.ifi_family = preferred_family; - req.i.ifi_index = ifi->ifi_index; - if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) { get_failed: fprintf(stderr, @@ -139,6 +143,8 @@ if (iptuninfo[IFLA_IPTUN_PROTO]) proto = rta_getattr_u8(iptuninfo[IFLA_IPTUN_PROTO]); + if (iptuninfo[IFLA_IPTUN_COLLECT_METADATA]) + metadata = 1; } while (argc > 0) { @@ -159,6 +165,7 @@ invarg("Cannot guess tunnel mode.", *argv); } else if (strcmp(*argv, "remote") == 0) { inet_prefix addr; + NEXT_ARG(); get_prefix(&addr, *argv, preferred_family); if (addr.family == AF_UNSPEC) @@ -166,6 +173,7 @@ memcpy(&raddr, addr.data, addr.bytelen); } else if (strcmp(*argv, "local") == 0) { inet_prefix addr; + NEXT_ARG(); get_prefix(&addr, *argv, preferred_family); if (addr.family == AF_UNSPEC) @@ -180,16 +188,18 @@ strcmp(*argv, "ttl") == 0 || strcmp(*argv, "hlim") == 0) { __u8 uval; + NEXT_ARG(); if (get_u8(&uval, *argv, 0)) invarg("invalid HLIM", *argv); hop_limit = uval; - } else if (matches(*argv, "encaplimit") == 0) { + } else if (strcmp(*argv, "encaplimit") == 0) { NEXT_ARG(); if (strcmp(*argv, "none") == 0) { flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; } else { __u8 uval; + if (get_u8(&uval, *argv, 0) < -1) invarg("invalid ELIM", *argv); encap_limit = uval; @@ -200,6 +210,7 @@ strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { __u8 uval; + NEXT_ARG(); flowinfo &= ~IP6_FLOWINFO_TCLASS; if (strcmp(*argv, "inherit") == 0) @@ -213,6 +224,7 @@ } else if (strcmp(*argv, "flowlabel") == 0 || strcmp(*argv, "fl") == 0) { __u32 uval; + NEXT_ARG(); flowinfo &= ~IP6_FLOWINFO_FLOWLABEL; if (strcmp(*argv, "inherit") == 0) @@ -235,12 +247,52 @@ if (strcmp(*argv, "inherit") != 0) invarg("not inherit", *argv); flags |= IP6_TNL_F_USE_ORIG_FWMARK; + } else if (strcmp(*argv, "noencap") == 0) { + encaptype = TUNNEL_ENCAP_NONE; + } else if (strcmp(*argv, "encap") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "fou") == 0) + encaptype = TUNNEL_ENCAP_FOU; + else if (strcmp(*argv, "gue") == 0) + encaptype = TUNNEL_ENCAP_GUE; + else if (strcmp(*argv, "none") == 0) + encaptype = TUNNEL_ENCAP_NONE; + else + invarg("Invalid encap type.", *argv); + } else if (strcmp(*argv, "encap-sport") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "auto") == 0) + encapsport = 0; + else if (get_u16(&encapsport, *argv, 0)) + invarg("Invalid source port.", *argv); + } else if (strcmp(*argv, "encap-dport") == 0) { + NEXT_ARG(); + if (get_u16(&encapdport, *argv, 0)) + invarg("Invalid destination port.", *argv); + } else if (strcmp(*argv, "encap-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "noencap-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "encap-udp6-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "noencap-udp6-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "encap-remcsum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "noencap-remcsum") == 0) { + encapflags |= ~TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "external") == 0) { + metadata = 1; } else usage(); argc--, argv++; } addattr8(n, 1024, IFLA_IPTUN_PROTO, proto); + if (metadata) { + addattr_l(n, 1024, IFLA_IPTUN_COLLECT_METADATA, NULL, 0); + return 0; + } addattr_l(n, 1024, IFLA_IPTUN_LOCAL, &laddr, sizeof(laddr)); addattr_l(n, 1024, IFLA_IPTUN_REMOTE, &raddr, sizeof(raddr)); addattr8(n, 1024, IFLA_IPTUN_TTL, hop_limit); @@ -249,12 +301,16 @@ addattr32(n, 1024, IFLA_IPTUN_FLAGS, flags); addattr32(n, 1024, IFLA_IPTUN_LINK, link); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_TYPE, encaptype); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_FLAGS, encapflags); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_SPORT, htons(encapsport)); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_DPORT, htons(encapdport)); + return 0; } static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { - char s1[256]; char s2[64]; int flags = 0; __u32 flowinfo = 0; @@ -284,22 +340,16 @@ if (tb[IFLA_IPTUN_REMOTE]) { fprintf(f, "remote %s ", - rt_addr_n2a(AF_INET6, - RTA_PAYLOAD(tb[IFLA_IPTUN_REMOTE]), - RTA_DATA(tb[IFLA_IPTUN_REMOTE]), - s1, sizeof(s1))); + rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_REMOTE])); } if (tb[IFLA_IPTUN_LOCAL]) { fprintf(f, "local %s ", - rt_addr_n2a(AF_INET6, - RTA_PAYLOAD(tb[IFLA_IPTUN_LOCAL]), - RTA_DATA(tb[IFLA_IPTUN_LOCAL]), - s1, sizeof(s1))); + rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_LOCAL])); } if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) { - unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); + unsigned int link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); const char *n = if_indextoname(link, s2); if (n) @@ -340,6 +390,50 @@ if (flags & IP6_TNL_F_USE_ORIG_FWMARK) fprintf(f, "fwmark inherit "); + + if (tb[IFLA_IPTUN_ENCAP_TYPE] && + rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]) != + TUNNEL_ENCAP_NONE) { + __u16 type = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]); + __u16 flags = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_FLAGS]); + __u16 sport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_SPORT]); + __u16 dport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_DPORT]); + + fputs("encap ", f); + switch (type) { + case TUNNEL_ENCAP_FOU: + fputs("fou ", f); + break; + case TUNNEL_ENCAP_GUE: + fputs("gue ", f); + break; + default: + fputs("unknown ", f); + break; + } + + if (sport == 0) + fputs("encap-sport auto ", f); + else + fprintf(f, "encap-sport %u", ntohs(sport)); + + fprintf(f, "encap-dport %u ", ntohs(dport)); + + if (flags & TUNNEL_ENCAP_FLAG_CSUM) + fputs("encap-csum ", f); + else + fputs("noencap-csum ", f); + + if (flags & TUNNEL_ENCAP_FLAG_CSUM6) + fputs("encap-csum6 ", f); + else + fputs("noencap-csum6 ", f); + + if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) + fputs("encap-remcsum ", f); + else + fputs("noencap-remcsum ", f); + } } static void ip6tunnel_print_help(struct link_util *lu, int argc, char **argv, diff -Nru iproute2-4.3.0/ip/link_iptnl.c iproute2-4.9.0/ip/link_iptnl.c --- iproute2-4.3.0/ip/link_iptnl.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/link_iptnl.c 2016-12-12 23:07:42.000000000 +0000 @@ -36,6 +36,7 @@ fprintf(f, " [ mode { ip6ip | ipip | any } ]\n"); fprintf(f, " [ isatap ]\n"); } + fprintf(f, " [ external ]\n"); fprintf(f, "\n"); fprintf(f, "Where: NAME := STRING\n"); fprintf(f, " ADDR := { IP_ADDRESS | any }\n"); @@ -53,12 +54,18 @@ static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { + struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); struct { struct nlmsghdr n; struct ifinfomsg i; char buf[2048]; - } req; - struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *linkinfo[IFLA_INFO_MAX+1]; struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1]; @@ -71,7 +78,7 @@ __u8 pmtudisc = 1; __u16 iflags = 0; __u8 proto = 0; - struct in6_addr ip6rdprefix; + struct in6_addr ip6rdprefix = {}; __u16 ip6rdprefixlen = 0; __u32 ip6rdrelayprefix = 0; __u16 ip6rdrelayprefixlen = 0; @@ -79,18 +86,9 @@ __u16 encapflags = 0; __u16 encapsport = 0; __u16 encapdport = 0; - - memset(&ip6rdprefix, 0, sizeof(ip6rdprefix)); + __u8 metadata = 0; if (!(n->nlmsg_flags & NLM_F_CREATE)) { - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETLINK; - req.i.ifi_family = preferred_family; - req.i.ifi_index = ifi->ifi_index; - if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) { get_failed: fprintf(stderr, @@ -165,6 +163,8 @@ if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]) ip6rdrelayprefixlen = rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]); + if (iptuninfo[IFLA_IPTUN_COLLECT_METADATA]) + metadata = 1; } while (argc > 0) { @@ -197,6 +197,7 @@ strcmp(*argv, "tclass") == 0 || matches(*argv, "dsfield") == 0) { __u32 uval; + NEXT_ARG(); if (strcmp(*argv, "inherit") != 0) { if (rtnl_dsfield_a2n(&uval, *argv)) @@ -260,8 +261,11 @@ encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM; } else if (strcmp(*argv, "noencap-remcsum") == 0) { encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "external") == 0) { + metadata = 1; } else if (strcmp(*argv, "6rd-prefix") == 0) { inet_prefix prefix; + NEXT_ARG(); if (get_prefix(&prefix, *argv, AF_INET6)) invarg("invalid 6rd_prefix\n", *argv); @@ -269,6 +273,7 @@ ip6rdprefixlen = prefix.bitlen; } else if (strcmp(*argv, "6rd-relay_prefix") == 0) { inet_prefix prefix; + NEXT_ARG(); if (get_prefix(&prefix, *argv, AF_INET)) invarg("invalid 6rd-relay_prefix\n", *argv); @@ -276,6 +281,7 @@ ip6rdrelayprefixlen = prefix.bitlen; } else if (strcmp(*argv, "6rd-reset") == 0) { inet_prefix prefix; + get_prefix(&prefix, "2002::", AF_INET6); memcpy(&ip6rdprefix, prefix.data, 16); ip6rdprefixlen = 16; @@ -291,6 +297,11 @@ exit(-1); } + if (metadata) { + addattr_l(n, 1024, IFLA_IPTUN_COLLECT_METADATA, NULL, 0); + return 0; + } + addattr32(n, 1024, IFLA_IPTUN_LINK, link); addattr32(n, 1024, IFLA_IPTUN_LOCAL, laddr); addattr32(n, 1024, IFLA_IPTUN_REMOTE, raddr); @@ -332,25 +343,25 @@ return; if (tb[IFLA_IPTUN_REMOTE]) { - unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_REMOTE]); + unsigned int addr = rta_getattr_u32(tb[IFLA_IPTUN_REMOTE]); if (addr) - remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1)); + remote = format_host(AF_INET, 4, &addr); } fprintf(f, "remote %s ", remote); if (tb[IFLA_IPTUN_LOCAL]) { - unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]); + unsigned int addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]); if (addr) - local = format_host(AF_INET, 4, &addr, s1, sizeof(s1)); + local = format_host(AF_INET, 4, &addr); } fprintf(f, "local %s ", local); if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) { - unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); + unsigned int link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); const char *n = if_indextoname(link, s2); if (n) @@ -400,8 +411,7 @@ prefixlen); if (relayprefix) { printf("6rd-relay_prefix %s/%u ", - format_host(AF_INET, 4, &relayprefix, s1, - sizeof(s1)), + format_host(AF_INET, 4, &relayprefix), relayprefixlen); } } diff -Nru iproute2-4.3.0/ip/link_veth.c iproute2-4.9.0/ip/link_veth.c --- iproute2-4.3.0/ip/link_veth.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/link_veth.c 2016-12-12 23:07:42.000000000 +0000 @@ -37,7 +37,7 @@ char *type = NULL; int index = 0; int err, len; - struct rtattr * data; + struct rtattr *data; int group; struct ifinfomsg *ifm, *peer_ifm; unsigned int ifi_flags, ifi_change; diff -Nru iproute2-4.3.0/ip/link_vti6.c iproute2-4.9.0/ip/link_vti6.c --- iproute2-4.3.0/ip/link_vti6.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/link_vti6.c 2016-12-12 23:07:42.000000000 +0000 @@ -42,31 +42,29 @@ static int vti6_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { + struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); struct { struct nlmsghdr n; struct ifinfomsg i; char buf[1024]; - } req; - struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *linkinfo[IFLA_INFO_MAX+1]; struct rtattr *vtiinfo[IFLA_VTI_MAX + 1]; struct in6_addr saddr; struct in6_addr daddr; - unsigned ikey = 0; - unsigned okey = 0; - unsigned link = 0; + unsigned int ikey = 0; + unsigned int okey = 0; + unsigned int link = 0; int len; if (!(n->nlmsg_flags & NLM_F_CREATE)) { - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETLINK; - req.i.ifi_family = preferred_family; - req.i.ifi_index = ifi->ifi_index; - if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) { get_failed: fprintf(stderr, @@ -110,7 +108,7 @@ while (argc > 0) { if (!matches(*argv, "key")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); if (strchr(*argv, '.')) @@ -126,7 +124,7 @@ ikey = okey = uval; } else if (!matches(*argv, "ikey")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); if (strchr(*argv, '.')) @@ -140,7 +138,7 @@ } ikey = uval; } else if (!matches(*argv, "okey")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); if (strchr(*argv, '.')) @@ -160,6 +158,7 @@ exit(-1); } else { inet_prefix addr; + get_prefix(&addr, *argv, AF_INET6); memcpy(&daddr, addr.data, addr.bytelen); } @@ -170,6 +169,7 @@ exit(-1); } else { inet_prefix addr; + get_prefix(&addr, *argv, AF_INET6); memcpy(&saddr, addr.data, addr.bytelen); } @@ -195,7 +195,6 @@ static void vti6_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { - char s1[1024]; char s2[64]; const char *local = "any"; const char *remote = "any"; @@ -208,7 +207,7 @@ if (tb[IFLA_VTI_REMOTE]) { memcpy(&daddr, RTA_DATA(tb[IFLA_VTI_REMOTE]), sizeof(daddr)); - remote = format_host(AF_INET6, 16, &daddr, s1, sizeof(s1)); + remote = format_host(AF_INET6, 16, &daddr); } fprintf(f, "remote %s ", remote); @@ -216,13 +215,13 @@ if (tb[IFLA_VTI_LOCAL]) { memcpy(&saddr, RTA_DATA(tb[IFLA_VTI_LOCAL]), sizeof(saddr)); - local = format_host(AF_INET6, 16, &saddr, s1, sizeof(s1)); + local = format_host(AF_INET6, 16, &saddr); } fprintf(f, "local %s ", local); if (tb[IFLA_VTI_LINK] && *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK])) { - unsigned link = *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK]); + unsigned int link = *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK]); const char *n = if_indextoname(link, s2); if (n) diff -Nru iproute2-4.3.0/ip/link_vti.c iproute2-4.9.0/ip/link_vti.c --- iproute2-4.3.0/ip/link_vti.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/link_vti.c 2016-12-12 23:07:42.000000000 +0000 @@ -46,31 +46,29 @@ static int vti_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { + struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); struct { struct nlmsghdr n; struct ifinfomsg i; char buf[1024]; - } req; - struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *linkinfo[IFLA_INFO_MAX+1]; struct rtattr *vtiinfo[IFLA_VTI_MAX + 1]; - unsigned ikey = 0; - unsigned okey = 0; - unsigned saddr = 0; - unsigned daddr = 0; - unsigned link = 0; + unsigned int ikey = 0; + unsigned int okey = 0; + unsigned int saddr = 0; + unsigned int daddr = 0; + unsigned int link = 0; int len; if (!(n->nlmsg_flags & NLM_F_CREATE)) { - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETLINK; - req.i.ifi_family = preferred_family; - req.i.ifi_index = ifi->ifi_index; - if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) { get_failed: fprintf(stderr, @@ -114,7 +112,7 @@ while (argc > 0) { if (!matches(*argv, "key")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); if (strchr(*argv, '.')) @@ -130,7 +128,7 @@ ikey = okey = uval; } else if (!matches(*argv, "ikey")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); if (strchr(*argv, '.')) @@ -144,7 +142,7 @@ } ikey = uval; } else if (!matches(*argv, "okey")) { - unsigned uval; + unsigned int uval; NEXT_ARG(); if (strchr(*argv, '.')) @@ -198,7 +196,6 @@ static void vti_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { - char s1[1024]; char s2[64]; const char *local = "any"; const char *remote = "any"; @@ -207,25 +204,25 @@ return; if (tb[IFLA_VTI_REMOTE]) { - unsigned addr = *(__u32 *)RTA_DATA(tb[IFLA_VTI_REMOTE]); + unsigned int addr = *(__u32 *)RTA_DATA(tb[IFLA_VTI_REMOTE]); if (addr) - remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1)); + remote = format_host(AF_INET, 4, &addr); } fprintf(f, "remote %s ", remote); if (tb[IFLA_VTI_LOCAL]) { - unsigned addr = *(__u32 *)RTA_DATA(tb[IFLA_VTI_LOCAL]); + unsigned int addr = *(__u32 *)RTA_DATA(tb[IFLA_VTI_LOCAL]); if (addr) - local = format_host(AF_INET, 4, &addr, s1, sizeof(s1)); + local = format_host(AF_INET, 4, &addr); } fprintf(f, "local %s ", local); if (tb[IFLA_VTI_LINK] && *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK])) { - unsigned link = *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK]); + unsigned int link = *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK]); const char *n = if_indextoname(link, s2); if (n) diff -Nru iproute2-4.3.0/ip/Makefile iproute2-4.9.0/ip/Makefile --- iproute2-4.3.0/ip/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -7,7 +7,7 @@ iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \ link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \ iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \ - iplink_geneve.o iplink_vrf.o + iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o RTMONOBJ=rtmon.o @@ -24,8 +24,10 @@ all: $(TARGETS) $(SCRIPTS) ip: $(IPOBJ) $(LIBNETLINK) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ rtmon: $(RTMONOBJ) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ install: all install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR) diff -Nru iproute2-4.3.0/ip/rtmon.c iproute2-4.9.0/ip/rtmon.c --- iproute2-4.3.0/ip/rtmon.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/rtmon.c 2016-12-12 23:07:42.000000000 +0000 @@ -25,13 +25,13 @@ #include "utils.h" #include "libnetlink.h" -int resolve_hosts = 0; +int resolve_hosts; static int init_phase = 1; static void write_stamp(FILE *fp) { char buf[128]; - struct nlmsghdr *n1 = (void*)buf; + struct nlmsghdr *n1 = (void *)buf; struct timeval tv; n1->nlmsg_type = NLMSG_TSTAMP; @@ -40,18 +40,19 @@ n1->nlmsg_pid = 0; n1->nlmsg_len = NLMSG_LENGTH(4*2); gettimeofday(&tv, NULL); - ((__u32*)NLMSG_DATA(n1))[0] = tv.tv_sec; - ((__u32*)NLMSG_DATA(n1))[1] = tv.tv_usec; - fwrite((void*)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp); + ((__u32 *)NLMSG_DATA(n1))[0] = tv.tv_sec; + ((__u32 *)NLMSG_DATA(n1))[1] = tv.tv_usec; + fwrite((void *)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp); } static int dump_msg(const struct sockaddr_nl *who, struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; + if (!init_phase) write_stamp(fp); - fwrite((void*)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp); + fwrite((void *)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp); fflush(fp); return 0; } @@ -75,7 +76,7 @@ FILE *fp; struct rtnl_handle rth; int family = AF_UNSPEC; - unsigned groups = ~0U; + unsigned int groups = ~0U; int llink = 0; int laddr = 0; int lroute = 0; @@ -115,13 +116,13 @@ usage(); file = argv[1]; } else if (matches(argv[1], "link") == 0) { - llink=1; + llink = 1; groups = 0; } else if (matches(argv[1], "address") == 0) { - laddr=1; + laddr = 1; groups = 0; } else if (matches(argv[1], "route") == 0) { - lroute=1; + lroute = 1; groups = 0; } else if (strcmp(argv[1], "all") == 0) { groups = ~0U; @@ -176,7 +177,7 @@ init_phase = 0; - if (rtnl_listen(&rth, dump_msg, (void*)fp) < 0) + if (rtnl_listen(&rth, dump_msg, (void *)fp) < 0) exit(2); exit(0); diff -Nru iproute2-4.3.0/ip/tcp_metrics.c iproute2-4.9.0/ip/tcp_metrics.c --- iproute2-4.3.0/ip/tcp_metrics.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/tcp_metrics.c 2016-12-12 23:07:42.000000000 +0000 @@ -95,7 +95,6 @@ struct genlmsghdr *ghdr; struct rtattr *attrs[TCP_METRICS_ATTR_MAX + 1], *a; int len = n->nlmsg_len; - char abuf[256]; inet_prefix daddr, saddr; int family, i, atype, stype, dlen = 0, slen = 0; @@ -113,47 +112,44 @@ parse_rtattr(attrs, TCP_METRICS_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len); - a = attrs[TCP_METRICS_ATTR_ADDR_IPV4]; - if (a) { + if (attrs[TCP_METRICS_ATTR_ADDR_IPV4]) { if (f.daddr.family && f.daddr.family != AF_INET) return 0; + a = attrs[TCP_METRICS_ATTR_ADDR_IPV4]; memcpy(&daddr.data, RTA_DATA(a), 4); daddr.bytelen = 4; family = AF_INET; atype = TCP_METRICS_ATTR_ADDR_IPV4; dlen = RTA_PAYLOAD(a); - } else { - a = attrs[TCP_METRICS_ATTR_ADDR_IPV6]; - if (a) { - if (f.daddr.family && f.daddr.family != AF_INET6) - return 0; - memcpy(&daddr.data, RTA_DATA(a), 16); - daddr.bytelen = 16; - family = AF_INET6; - atype = TCP_METRICS_ATTR_ADDR_IPV6; - dlen = RTA_PAYLOAD(a); - } else + } else if (attrs[TCP_METRICS_ATTR_ADDR_IPV6]) { + if (f.daddr.family && f.daddr.family != AF_INET6) return 0; + a = attrs[TCP_METRICS_ATTR_ADDR_IPV6]; + memcpy(&daddr.data, RTA_DATA(a), 16); + daddr.bytelen = 16; + family = AF_INET6; + atype = TCP_METRICS_ATTR_ADDR_IPV6; + dlen = RTA_PAYLOAD(a); + } else { + return 0; } - a = attrs[TCP_METRICS_ATTR_SADDR_IPV4]; - if (a) { + if (attrs[TCP_METRICS_ATTR_SADDR_IPV4]) { if (f.saddr.family && f.saddr.family != AF_INET) return 0; + a = attrs[TCP_METRICS_ATTR_SADDR_IPV4]; memcpy(&saddr.data, RTA_DATA(a), 4); saddr.bytelen = 4; stype = TCP_METRICS_ATTR_SADDR_IPV4; slen = RTA_PAYLOAD(a); - } else { + } else if (attrs[TCP_METRICS_ATTR_SADDR_IPV6]) { + if (f.saddr.family && f.saddr.family != AF_INET6) + return 0; a = attrs[TCP_METRICS_ATTR_SADDR_IPV6]; - if (a) { - if (f.saddr.family && f.saddr.family != AF_INET6) - return 0; - memcpy(&saddr.data, RTA_DATA(a), 16); - saddr.bytelen = 16; - stype = TCP_METRICS_ATTR_SADDR_IPV6; - slen = RTA_PAYLOAD(a); - } + memcpy(&saddr.data, RTA_DATA(a), 16); + saddr.bytelen = 16; + stype = TCP_METRICS_ATTR_SADDR_IPV6; + slen = RTA_PAYLOAD(a); } if (f.daddr.family && f.daddr.bitlen >= 0 && @@ -168,6 +164,7 @@ if (f.flushb) { struct nlmsghdr *fn; + TCPM_REQUEST(req2, 128, TCP_METRICS_CMD_DEL, NLM_F_REQUEST); addattr_l(&req2.n, sizeof(req2), atype, &daddr.data, @@ -193,7 +190,7 @@ fprintf(fp, "Deleted "); fprintf(fp, "%s", - format_host(family, dlen, &daddr.data, abuf, sizeof(abuf))); + format_host(family, dlen, &daddr.data)); a = attrs[TCP_METRICS_ATTR_AGE]; if (a) { @@ -297,8 +294,7 @@ if (slen) { fprintf(fp, " source %s", - format_host(family, slen, &saddr.data, abuf, - sizeof(abuf))); + format_host(family, slen, &saddr.data)); } fprintf(fp, "\n"); @@ -333,6 +329,7 @@ if (strcmp(*argv, "src") == 0 || strcmp(*argv, "source") == 0) { char *who = *argv; + NEXT_ARG(); if (matches(*argv, "help") == 0) usage(); @@ -354,6 +351,7 @@ } } else { char *who = "address"; + if (strcmp(*argv, "addr") == 0 || strcmp(*argv, "address") == 0) { who = *argv; @@ -400,17 +398,9 @@ ack = 0; } - if (genl_family < 0) { - if (rtnl_open_byproto(&grth, 0, NETLINK_GENERIC) < 0) { - fprintf(stderr, "Cannot open generic netlink socket\n"); - exit(1); - } - genl_family = genl_resolve_family(&grth, - TCP_METRICS_GENL_NAME); - if (genl_family < 0) - exit(1); - req.n.nlmsg_type = genl_family; - } + if (genl_init_handle(&grth, TCP_METRICS_GENL_NAME, &genl_family)) + exit(1); + req.n.nlmsg_type = genl_family; if (!(cmd & CMD_FLUSH) && (atype >= 0 || (cmd & CMD_DEL))) { if (ack) @@ -504,7 +494,7 @@ if (matches(argv[0], "help") == 0) usage(); - fprintf(stderr, "Command \"%s\" is unknown, " - "try \"ip tcp_metrics help\".\n", *argv); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip tcp_metrics help\".\n", + *argv); exit(-1); } diff -Nru iproute2-4.3.0/ip/tunnel.c iproute2-4.9.0/ip/tunnel.c --- iproute2-4.3.0/ip/tunnel.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/tunnel.c 2016-12-12 23:07:42.000000000 +0000 @@ -72,7 +72,7 @@ int err; strncpy(ifr.ifr_name, basedev, IFNAMSIZ); - ifr.ifr_ifru.ifru_data = (void*)p; + ifr.ifr_ifru.ifru_data = (void *)p; fd = socket(preferred_family, SOCK_DGRAM, 0); if (fd < 0) { @@ -180,3 +180,46 @@ { return tnl_gen_ioctl(SIOCGET6RD, name, p, EINVAL); } + +__be32 tnl_parse_key(const char *name, const char *key) +{ + unsigned int uval; + + if (strchr(key, '.')) + return get_addr32(key); + + if (get_unsigned(&uval, key, 0) < 0) { + fprintf(stderr, "invalid value for \"%s\": \"%s\";", name, key); + fprintf(stderr, " it should be an unsigned integer\n"); + exit(-1); + } + return htonl(uval); +} + +/* tnl_print_stats - print tunnel statistics + * + * @buf - tunnel interface's line in /proc/net/dev, + * starting past the interface name and following colon + */ +void tnl_print_stats(const char *buf) +{ + unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, + rx_fifo, rx_frame, + tx_bytes, tx_packets, tx_errs, tx_drops, + tx_fifo, tx_colls, tx_carrier, rx_multi; + + if (sscanf(buf, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu", + &rx_bytes, &rx_packets, &rx_errs, &rx_drops, + &rx_fifo, &rx_frame, &rx_multi, + &tx_bytes, &tx_packets, &tx_errs, &tx_drops, + &tx_fifo, &tx_colls, &tx_carrier) != 14) + return; + + printf("%s", _SL_); + printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_); + printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s", + rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_); + printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_); + printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld", + tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops); +} diff -Nru iproute2-4.3.0/ip/tunnel.h iproute2-4.9.0/ip/tunnel.h --- iproute2-4.3.0/ip/tunnel.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/tunnel.h 2016-12-12 23:07:42.000000000 +0000 @@ -31,5 +31,7 @@ int tnl_prl_ioctl(int cmd, const char *name, void *p); int tnl_6rd_ioctl(int cmd, const char *name, void *p); int tnl_ioctl_get_6rd(const char *name, void *p); +__be32 tnl_parse_key(const char *name, const char *key); +void tnl_print_stats(const char *buf); #endif diff -Nru iproute2-4.3.0/ip/xfrm_monitor.c iproute2-4.9.0/ip/xfrm_monitor.c --- iproute2-4.3.0/ip/xfrm_monitor.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/xfrm_monitor.c 2016-12-12 23:07:42.000000000 +0000 @@ -46,10 +46,10 @@ static int xfrm_acquire_print(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct xfrm_user_acquire *xacq = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[XFRMA_MAX+1]; + struct rtattr *tb[XFRMA_MAX+1]; __u16 family; len -= NLMSG_LENGTH(sizeof(*xacq)); @@ -71,6 +71,7 @@ fprintf(fp, "proto %s ", strxf_xfrmproto(xacq->id.proto)); if (show_stats > 0 || xacq->id.spi) { __u32 spi = ntohl(xacq->id.spi); + fprintf(fp, "spi 0x%08x", spi); if (show_stats > 0) fprintf(fp, "(%u)", spi); @@ -107,7 +108,7 @@ static int xfrm_state_flush_print(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct xfrm_usersa_flush *xsf = NLMSG_DATA(n); int len = n->nlmsg_len; const char *str; @@ -137,8 +138,8 @@ static int xfrm_policy_flush_print(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - struct rtattr * tb[XFRMA_MAX+1]; - FILE *fp = (FILE*)arg; + struct rtattr *tb[XFRMA_MAX+1]; + FILE *fp = (FILE *)arg; int len = n->nlmsg_len; len -= NLMSG_SPACE(0); @@ -175,10 +176,10 @@ static int xfrm_report_print(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct xfrm_user_report *xrep = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[XFRMA_MAX+1]; + struct rtattr *tb[XFRMA_MAX+1]; __u16 family; len -= NLMSG_LENGTH(sizeof(*xrep)); @@ -210,7 +211,8 @@ static void xfrm_ae_flags_print(__u32 flags, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; + fprintf(fp, " (0x%x) ", flags); if (!flags) return; @@ -225,12 +227,8 @@ static void xfrm_usersa_print(const struct xfrm_usersa_id *sa_id, __u32 reqid, FILE *fp) { - char buf[256]; - - buf[0] = 0; fprintf(fp, "dst %s ", - rt_addr_n2a(sa_id->family, sizeof(sa_id->daddr), &sa_id->daddr, - buf, sizeof(buf))); + rt_addr_n2a(sa_id->family, sizeof(sa_id->daddr), &sa_id->daddr)); fprintf(fp, " reqid 0x%x", reqid); @@ -241,17 +239,14 @@ static int xfrm_ae_print(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct xfrm_aevent_id *id = NLMSG_DATA(n); - char abuf[256]; fprintf(fp, "Async event "); xfrm_ae_flags_print(id->flags, arg); - fprintf(fp,"\n\t"); - memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "\n\t"); fprintf(fp, "src %s ", rt_addr_n2a(id->sa_id.family, - sizeof(id->saddr), &id->saddr, - abuf, sizeof(abuf))); + sizeof(id->saddr), &id->saddr)); xfrm_usersa_print(&id->sa_id, id->reqid, fp); @@ -263,16 +258,13 @@ static void xfrm_print_addr(FILE *fp, int family, xfrm_address_t *a) { - char buf[256]; - - buf[0] = 0; - fprintf(fp, "%s", rt_addr_n2a(family, sizeof(*a), a, buf, sizeof(buf))); + fprintf(fp, "%s", rt_addr_n2a(family, sizeof(*a), a)); } static int xfrm_mapping_print(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct xfrm_user_mapping *map = NLMSG_DATA(n); fprintf(fp, "Mapping change "); @@ -293,7 +285,7 @@ struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; if (timestamp) print_timestamp(fp); @@ -353,13 +345,13 @@ int do_xfrm_monitor(int argc, char **argv) { char *file = NULL; - unsigned groups = ~((unsigned)0); /* XXX */ - int lacquire=0; - int lexpire=0; - int laevent=0; - int lpolicy=0; - int lsa=0; - int lreport=0; + unsigned int groups = ~((unsigned)0); /* XXX */ + int lacquire = 0; + int lexpire = 0; + int laevent = 0; + int lpolicy = 0; + int lsa = 0; + int lreport = 0; rtnl_close(&rth); @@ -370,22 +362,22 @@ } else if (matches(*argv, "all-nsid") == 0) { listen_all_nsid = 1; } else if (matches(*argv, "acquire") == 0) { - lacquire=1; + lacquire = 1; groups = 0; } else if (matches(*argv, "expire") == 0) { - lexpire=1; + lexpire = 1; groups = 0; } else if (matches(*argv, "SA") == 0) { - lsa=1; + lsa = 1; groups = 0; } else if (matches(*argv, "aevent") == 0) { - laevent=1; + laevent = 1; groups = 0; } else if (matches(*argv, "policy") == 0) { - lpolicy=1; + lpolicy = 1; groups = 0; } else if (matches(*argv, "report") == 0) { - lreport=1; + lreport = 1; groups = 0; } else if (matches(*argv, "help") == 0) { usage(); @@ -411,12 +403,16 @@ if (file) { FILE *fp; + int err; + fp = fopen(file, "r"); if (fp == NULL) { perror("Cannot fopen"); exit(-1); } - return rtnl_from_file(fp, xfrm_accept_msg, (void*)stdout); + err = rtnl_from_file(fp, xfrm_accept_msg, stdout); + fclose(fp); + return err; } if (rtnl_open_byproto(&rth, groups, NETLINK_XFRM) < 0) @@ -424,7 +420,7 @@ if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0) exit(1); - if (rtnl_listen(&rth, xfrm_accept_msg, (void*)stdout) < 0) + if (rtnl_listen(&rth, xfrm_accept_msg, (void *)stdout) < 0) exit(2); return 0; diff -Nru iproute2-4.3.0/ip/xfrm_policy.c iproute2-4.9.0/ip/xfrm_policy.c --- iproute2-4.3.0/ip/xfrm_policy.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/xfrm_policy.c 2016-12-12 23:07:42.000000000 +0000 @@ -33,7 +33,7 @@ #include "xfrm.h" #include "ip_common.h" -//#define NLMSG_DELETEALL_BUF_SIZE (4096-512) +/* #define NLMSG_DELETEALL_BUF_SIZE (4096-512) */ #define NLMSG_DELETEALL_BUF_SIZE 8192 /* @@ -241,41 +241,35 @@ return 0; } -static int xfrm_policy_modify(int cmd, unsigned flags, int argc, char **argv) +static int xfrm_policy_modify(int cmd, unsigned int flags, int argc, char **argv) { struct rtnl_handle rth; struct { struct nlmsghdr n; struct xfrm_userpolicy_info xpinfo; char buf[RTA_BUF_SIZE]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpinfo)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .xpinfo.sel.family = preferred_family, + .xpinfo.lft.soft_byte_limit = XFRM_INF, + .xpinfo.lft.hard_byte_limit = XFRM_INF, + .xpinfo.lft.soft_packet_limit = XFRM_INF, + .xpinfo.lft.hard_packet_limit = XFRM_INF, + }; char *dirp = NULL; char *selp = NULL; char *ptypep = NULL; char *sctxp = NULL; - struct xfrm_userpolicy_type upt; - char tmpls_buf[XFRM_TMPLS_BUF_SIZE]; + struct xfrm_userpolicy_type upt = {}; + char tmpls_buf[XFRM_TMPLS_BUF_SIZE] = {}; int tmpls_len = 0; struct xfrm_mark mark = {0, 0}; struct { struct xfrm_user_sec_ctx sctx; char str[CTX_BUF_SIZE]; - } ctx; - - memset(&req, 0, sizeof(req)); - memset(&upt, 0, sizeof(upt)); - memset(&tmpls_buf, 0, sizeof(tmpls_buf)); - memset(&ctx, 0, sizeof(ctx)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpinfo)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.xpinfo.sel.family = preferred_family; - - req.xpinfo.lft.soft_byte_limit = XFRM_INF; - req.xpinfo.lft.hard_byte_limit = XFRM_INF; - req.xpinfo.lft.soft_packet_limit = XFRM_INF; - req.xpinfo.lft.hard_packet_limit = XFRM_INF; + } ctx = {}; while (argc > 0) { if (strcmp(*argv, "dir") == 0) { @@ -376,7 +370,7 @@ int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, (void *)&mark, sizeof(mark)); if (r < 0) { - fprintf(stderr, "%s: XFRMA_MARK failed\n",__func__); + fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__); exit(1); } } @@ -459,13 +453,13 @@ int xfrm_policy_print(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - struct rtattr * tb[XFRMA_MAX+1]; - struct rtattr * rta; + struct rtattr *tb[XFRMA_MAX+1]; + struct rtattr *rta; struct xfrm_userpolicy_info *xpinfo = NULL; struct xfrm_user_polexpire *xpexp = NULL; struct xfrm_userpolicy_id *xpid = NULL; __u8 ptype = XFRM_POLICY_TYPE_MAIN; - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; int len = n->nlmsg_len; if (n->nlmsg_type != XFRM_MSG_NEWPOLICY && @@ -526,7 +520,7 @@ fprintf(fp, "Expired "); if (n->nlmsg_type == XFRM_MSG_DELPOLICY) { - //xfrm_policy_id_print(); + /* xfrm_policy_id_print(); */ if (!tb[XFRMA_POLICY]) { fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: no XFRMA_POLICY\n"); return -1; @@ -561,27 +555,23 @@ struct nlmsghdr n; struct xfrm_userpolicy_id xpid; char buf[RTA_BUF_SIZE]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = delete ? XFRM_MSG_DELPOLICY + : XFRM_MSG_GETPOLICY, + }; char *dirp = NULL; char *selp = NULL; char *indexp = NULL; char *ptypep = NULL; char *sctxp = NULL; - struct xfrm_userpolicy_type upt; + struct xfrm_userpolicy_type upt = {}; struct xfrm_mark mark = {0, 0}; struct { struct xfrm_user_sec_ctx sctx; char str[CTX_BUF_SIZE]; - } ctx; - - - memset(&req, 0, sizeof(req)); - memset(&upt, 0, sizeof(upt)); - memset(&ctx, 0, sizeof(ctx)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = delete ? XFRM_MSG_DELPOLICY : XFRM_MSG_GETPOLICY; + } ctx = {}; while (argc > 0) { if (strcmp(*argv, "dir") == 0) { @@ -659,7 +649,7 @@ int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, (void *)&mark, sizeof(mark)); if (r < 0) { - fprintf(stderr, "%s: XFRMA_MARK failed\n",__func__); + fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__); exit(1); } } @@ -684,14 +674,12 @@ static int xfrm_policy_get(int argc, char **argv) { - char buf[NLMSG_BUF_SIZE]; + char buf[NLMSG_BUF_SIZE] = {}; struct nlmsghdr *n = (struct nlmsghdr *)buf; - memset(buf, 0, sizeof(buf)); - xfrm_policy_get_or_delete(argc, argv, 0, n, sizeof(buf)); - if (xfrm_policy_print(NULL, n, (void*)stdout) < 0) { + if (xfrm_policy_print(NULL, n, (void *)stdout) < 0) { fprintf(stderr, "An error :-)\n"); exit(1); } @@ -761,7 +749,7 @@ xpid->index = xpinfo->index; xb->offset += new_n->nlmsg_len; - xb->nlmsg_count ++; + xb->nlmsg_count++; return 0; } @@ -915,12 +903,12 @@ exit(0); } -static int print_spdinfo( struct nlmsghdr *n, void *arg) +static int print_spdinfo(struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; __u32 *f = NLMSG_DATA(n); - struct rtattr * tb[XFRMA_SPD_MAX+1]; - struct rtattr * rta; + struct rtattr *tb[XFRMA_SPD_MAX+1]; + struct rtattr *rta; int len = n->nlmsg_len; @@ -933,7 +921,7 @@ rta = XFRMSAPD_RTA(f); parse_rtattr(tb, XFRMA_SPD_MAX, rta, len); - fprintf(fp,"\t SPD"); + fprintf(fp, "\t SPD"); if (tb[XFRMA_SPD_INFO]) { struct xfrmu_spdinfo *si; @@ -942,16 +930,16 @@ return -1; } si = RTA_DATA(tb[XFRMA_SPD_INFO]); - fprintf(fp," IN %d", si->incnt); - fprintf(fp," OUT %d", si->outcnt); - fprintf(fp," FWD %d", si->fwdcnt); + fprintf(fp, " IN %d", si->incnt); + fprintf(fp, " OUT %d", si->outcnt); + fprintf(fp, " FWD %d", si->fwdcnt); if (show_stats) { - fprintf(fp," (Sock:"); - fprintf(fp," IN %d", si->inscnt); - fprintf(fp," OUT %d", si->outscnt); - fprintf(fp," FWD %d", si->fwdscnt); - fprintf(fp,")"); + fprintf(fp, " (Sock:"); + fprintf(fp, " IN %d", si->inscnt); + fprintf(fp, " OUT %d", si->outscnt); + fprintf(fp, " FWD %d", si->fwdscnt); + fprintf(fp, ")"); } fprintf(fp, "%s", _SL_); @@ -965,34 +953,36 @@ return -1; } sh = RTA_DATA(tb[XFRMA_SPD_HINFO]); - fprintf(fp,"\t SPD buckets:"); - fprintf(fp," count %d", sh->spdhcnt); - fprintf(fp," Max %d", sh->spdhmcnt); + fprintf(fp, "\t SPD buckets:"); + fprintf(fp, " count %d", sh->spdhcnt); + fprintf(fp, " Max %d", sh->spdhmcnt); fprintf(fp, "%s", _SL_); } if (tb[XFRMA_SPD_IPV4_HTHRESH]) { struct xfrmu_spdhthresh *th; + if (RTA_PAYLOAD(tb[XFRMA_SPD_IPV4_HTHRESH]) < sizeof(*th)) { fprintf(stderr, "SPDinfo: Wrong len %d\n", len); return -1; } th = RTA_DATA(tb[XFRMA_SPD_IPV4_HTHRESH]); - fprintf(fp,"\t SPD IPv4 thresholds:"); - fprintf(fp," local %d", th->lbits); - fprintf(fp," remote %d", th->rbits); + fprintf(fp, "\t SPD IPv4 thresholds:"); + fprintf(fp, " local %d", th->lbits); + fprintf(fp, " remote %d", th->rbits); fprintf(fp, "%s", _SL_); } if (tb[XFRMA_SPD_IPV6_HTHRESH]) { struct xfrmu_spdhthresh *th; + if (RTA_PAYLOAD(tb[XFRMA_SPD_IPV6_HTHRESH]) < sizeof(*th)) { fprintf(stderr, "SPDinfo: Wrong len %d\n", len); return -1; } th = RTA_DATA(tb[XFRMA_SPD_IPV6_HTHRESH]); - fprintf(fp,"\t SPD IPv6 thresholds:"); - fprintf(fp," local %d", th->lbits); - fprintf(fp," remote %d", th->rbits); + fprintf(fp, "\t SPD IPv6 thresholds:"); + fprintf(fp, " local %d", th->lbits); + fprintf(fp, " remote %d", th->rbits); fprintf(fp, "%s", _SL_); } } @@ -1000,7 +990,7 @@ if (oneline) fprintf(fp, "\n"); - return 0; + return 0; } static int xfrm_spd_setinfo(int argc, char **argv) @@ -1010,18 +1000,16 @@ struct nlmsghdr n; __u32 flags; char buf[RTA_BUF_SIZE]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_NEWSPDINFO, + .flags = 0XFFFFFFFF, + }; char *thr4 = NULL; char *thr6 = NULL; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = XFRM_MSG_NEWSPDINFO; - req.flags = 0XFFFFFFFF; - while (argc > 0) { if (strcmp(*argv, "hthresh4") == 0) { struct xfrmu_spdhthresh thr; @@ -1078,14 +1066,12 @@ struct nlmsghdr n; __u32 flags; char ans[128]; - } req; - - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = XFRM_MSG_GETSPDINFO; - req.flags = 0XFFFFFFFF; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_GETSPDINFO, + .flags = 0XFFFFFFFF, + }; if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) exit(1); @@ -1093,7 +1079,7 @@ if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) exit(2); - print_spdinfo(&req.n, (void*)stdout); + print_spdinfo(&req.n, (void *)stdout); rtnl_close(&rth); @@ -1106,16 +1092,13 @@ struct { struct nlmsghdr n; char buf[RTA_BUF_SIZE]; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(0), /* nlmsg data is nothing */ + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_FLUSHPOLICY, + }; char *ptypep = NULL; - struct xfrm_userpolicy_type upt; - - memset(&req, 0, sizeof(req)); - memset(&upt, 0, sizeof(upt)); - - req.n.nlmsg_len = NLMSG_LENGTH(0); /* nlmsg data is nothing */ - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = XFRM_MSG_FLUSHPOLICY; + struct xfrm_userpolicy_type upt = {}; while (argc > 0) { if (strcmp(*argv, "ptype") == 0) { diff -Nru iproute2-4.3.0/ip/xfrm_state.c iproute2-4.9.0/ip/xfrm_state.c --- iproute2-4.3.0/ip/xfrm_state.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/ip/xfrm_state.c 2016-12-12 23:07:42.000000000 +0000 @@ -32,7 +32,7 @@ #include "xfrm.h" #include "ip_common.h" -//#define NLMSG_DELETEALL_BUF_SIZE (4096-512) +/* #define NLMSG_DELETEALL_BUF_SIZE (4096-512) */ #define NLMSG_DELETEALL_BUF_SIZE 8192 /* @@ -107,7 +107,7 @@ fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] limit LIMIT\n"); fprintf(stderr, "LIMIT := { time-soft | time-hard | time-use-soft | time-use-hard } SECONDS |\n"); fprintf(stderr, " { byte-soft | byte-hard } SIZE | { packet-soft | packet-hard } COUNT\n"); - fprintf(stderr, "ENCAP := { espinudp | espinudp-nonike } SPORT DPORT OADDR\n"); + fprintf(stderr, "ENCAP := { espinudp | espinudp-nonike } SPORT DPORT OADDR\n"); exit(-1); } @@ -142,7 +142,7 @@ if (len > max) invarg("ALGO-KEYMAT value makes buffer overflow\n", key); - for (i = - (plen % 2), j = 0; j < len; i += 2, j++) { + for (i = -(plen % 2), j = 0; j < len; i += 2, j++) { char vbuf[3]; __u8 val; @@ -175,11 +175,9 @@ int argc = *argcp; char **argv = *argvp; - if (get_u32(seq, *argv, 0)) + if (get_be32(seq, *argv, 0)) invarg("SEQ value is invalid", *argv); - *seq = htonl(*seq); - *argcp = argc; *argvp = argv; @@ -266,16 +264,25 @@ return 0; } -static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv) +static int xfrm_state_modify(int cmd, unsigned int flags, int argc, char **argv) { struct rtnl_handle rth; struct { struct nlmsghdr n; struct xfrm_usersa_info xsinfo; - char buf[RTA_BUF_SIZE]; - } req; - struct xfrm_replay_state replay; - struct xfrm_replay_state_esn replay_esn; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .xsinfo.family = preferred_family, + .xsinfo.lft.soft_byte_limit = XFRM_INF, + .xsinfo.lft.hard_byte_limit = XFRM_INF, + .xsinfo.lft.soft_packet_limit = XFRM_INF, + .xsinfo.lft.hard_packet_limit = XFRM_INF, + }; + struct xfrm_replay_state replay = {}; + struct xfrm_replay_state_esn replay_esn = {}; __u32 replay_window = 0; __u32 seq = 0, oseq = 0, seq_hi = 0, oseq_hi = 0; char *idp = NULL; @@ -290,22 +297,7 @@ struct { struct xfrm_user_sec_ctx sctx; char str[CTX_BUF_SIZE]; - } ctx; - - memset(&req, 0, sizeof(req)); - memset(&replay, 0, sizeof(replay)); - memset(&replay_esn, 0, sizeof(replay_esn)); - memset(&ctx, 0, sizeof(ctx)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.xsinfo.family = preferred_family; - - req.xsinfo.lft.soft_byte_limit = XFRM_INF; - req.xsinfo.lft.hard_byte_limit = XFRM_INF; - req.xsinfo.lft.soft_packet_limit = XFRM_INF; - req.xsinfo.lft.hard_packet_limit = XFRM_INF; + } ctx = {}; while (argc > 0) { if (strcmp(*argv, "mode") == 0) { @@ -356,16 +348,14 @@ } else if (strcmp(*argv, "encap") == 0) { struct xfrm_encap_tmpl encap; inet_prefix oa; - NEXT_ARG(); + NEXT_ARG(); xfrm_encap_type_parse(&encap.encap_type, &argc, &argv); NEXT_ARG(); - if (get_u16(&encap.encap_sport, *argv, 0)) + if (get_be16(&encap.encap_sport, *argv, 0)) invarg("SPORT value after \"encap\" is invalid", *argv); - encap.encap_sport = htons(encap.encap_sport); NEXT_ARG(); - if (get_u16(&encap.encap_dport, *argv, 0)) + if (get_be16(&encap.encap_dport, *argv, 0)) invarg("DPORT value after \"encap\" is invalid", *argv); - encap.encap_dport = htons(encap.encap_dport); NEXT_ARG(); get_addr(&oa, *argv, AF_UNSPEC); memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa)); @@ -373,7 +363,7 @@ (void *)&encap, sizeof(encap)); } else if (strcmp(*argv, "coa") == 0) { inet_prefix coa; - xfrm_address_t xcoa; + xfrm_address_t xcoa = {}; if (coap) duparg("coa", *argv); @@ -387,7 +377,6 @@ if (coa.bytelen > sizeof(xcoa)) invarg("value after \"coa\" is too large", *argv); - memset(&xcoa, 0, sizeof(xcoa)); memcpy(&xcoa, &coa.data, coa.bytelen); addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR, @@ -408,6 +397,7 @@ } else { /* try to assume ALGO */ int type = xfrm_algotype_getbyname(*argv); + switch (type) { case XFRMA_ALG_AEAD: case XFRMA_ALG_CRYPT: @@ -701,31 +691,26 @@ struct { struct nlmsghdr n; struct xfrm_userspi_info xspi; - char buf[RTA_BUF_SIZE]; - } req; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_ALLOCSPI, + .xspi.info.family = preferred_family, +#if 0 + .xspi.lft.soft_byte_limit = XFRM_INF, + .xspi.lft.hard_byte_limit = XFRM_INF, + .xspi.lft.soft_packet_limit = XFRM_INF, + .xspi.lft.hard_packet_limit = XFRM_INF, +#endif + }; char *idp = NULL; char *minp = NULL; char *maxp = NULL; struct xfrm_mark mark = {0, 0}; - char res_buf[NLMSG_BUF_SIZE]; + char res_buf[NLMSG_BUF_SIZE] = {}; struct nlmsghdr *res_n = (struct nlmsghdr *)res_buf; - memset(res_buf, 0, sizeof(res_buf)); - - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = XFRM_MSG_ALLOCSPI; - req.xspi.info.family = preferred_family; - -#if 0 - req.xsinfo.lft.soft_byte_limit = XFRM_INF; - req.xsinfo.lft.hard_byte_limit = XFRM_INF; - req.xsinfo.lft.soft_packet_limit = XFRM_INF; - req.xsinfo.lft.hard_packet_limit = XFRM_INF; -#endif - while (argc > 0) { if (strcmp(*argv, "mode") == 0) { NEXT_ARG(); @@ -827,7 +812,7 @@ if (rtnl_talk(&rth, &req.n, res_n, sizeof(res_buf)) < 0) exit(2); - if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) { + if (xfrm_state_print(NULL, res_n, (void *)stdout) < 0) { fprintf(stderr, "An error :-)\n"); exit(1); } @@ -868,9 +853,9 @@ int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; - struct rtattr * tb[XFRMA_MAX+1]; - struct rtattr * rta; + FILE *fp = (FILE *)arg; + struct rtattr *tb[XFRMA_MAX+1]; + struct rtattr *rta; struct xfrm_usersa_info *xsinfo = NULL; struct xfrm_user_expire *xexp = NULL; struct xfrm_usersa_id *xsid = NULL; @@ -924,7 +909,7 @@ parse_rtattr(tb, XFRMA_MAX, rta, len); if (n->nlmsg_type == XFRM_MSG_DELSA) { - //xfrm_policy_id_print(); + /* xfrm_policy_id_print(); */ if (!tb[XFRMA_SA]) { fprintf(stderr, "Buggy XFRM_MSG_DELSA: no XFRMA_SA\n"); @@ -958,19 +943,17 @@ struct { struct nlmsghdr n; struct xfrm_usersa_id xsid; - char buf[RTA_BUF_SIZE]; - } req; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA, + .xsid.family = preferred_family, + }; struct xfrm_id id; char *idp = NULL; struct xfrm_mark mark = {0, 0}; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA; - req.xsid.family = preferred_family; - while (argc > 0) { xfrm_address_t saddr; @@ -1017,15 +1000,13 @@ if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) exit(2); } else { - char buf[NLMSG_BUF_SIZE]; + char buf[NLMSG_BUF_SIZE] = {}; struct nlmsghdr *res_n = (struct nlmsghdr *)buf; - memset(buf, 0, sizeof(buf)); - if (rtnl_talk(&rth, &req.n, res_n, sizeof(req)) < 0) exit(2); - if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) { + if (xfrm_state_print(NULL, res_n, (void *)stdout) < 0) { fprintf(stderr, "An error :-)\n"); exit(1); } @@ -1087,7 +1068,7 @@ sizeof(xsid->daddr)); xb->offset += new_n->nlmsg_len; - xb->nlmsg_count ++; + xb->nlmsg_count++; return 0; } @@ -1097,7 +1078,7 @@ char *idp = NULL; struct rtnl_handle rth; - if(argc > 0) + if (argc > 0) filter.use = 1; filter.xsinfo.family = preferred_family; @@ -1231,7 +1212,7 @@ static int print_sadinfo(struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; __u32 *f = NLMSG_DATA(n); struct rtattr *tb[XFRMA_SAD_MAX+1]; struct rtattr *rta; @@ -1249,11 +1230,11 @@ parse_rtattr(tb, XFRMA_SAD_MAX, rta, len); if (tb[XFRMA_SAD_CNT]) { - fprintf(fp,"\t SAD"); + fprintf(fp, "\t SAD"); cnt = (__u32 *)RTA_DATA(tb[XFRMA_SAD_CNT]); - fprintf(fp," count %d", *cnt); + fprintf(fp, " count %d", *cnt); } else { - fprintf(fp,"BAD SAD info returned\n"); + fprintf(fp, "BAD SAD info returned\n"); return -1; } @@ -1262,20 +1243,20 @@ struct xfrmu_sadhinfo *si; if (RTA_PAYLOAD(tb[XFRMA_SAD_HINFO]) < sizeof(*si)) { - fprintf(fp,"BAD SAD length returned\n"); + fprintf(fp, "BAD SAD length returned\n"); return -1; } si = RTA_DATA(tb[XFRMA_SAD_HINFO]); - fprintf(fp," (buckets "); - fprintf(fp,"count %d", si->sadhcnt); - fprintf(fp," Max %d", si->sadhmcnt); - fprintf(fp,")"); + fprintf(fp, " (buckets "); + fprintf(fp, "count %d", si->sadhcnt); + fprintf(fp, " Max %d", si->sadhmcnt); + fprintf(fp, ")"); } } - fprintf(fp,"\n"); + fprintf(fp, "\n"); - return 0; + return 0; } static int xfrm_sad_getinfo(int argc, char **argv) @@ -1285,13 +1266,12 @@ struct nlmsghdr n; __u32 flags; char ans[64]; - } req; - - memset(&req, 0, sizeof(req)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.flags)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = XFRM_MSG_GETSADINFO; - req.flags = 0XFFFFFFFF; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.flags)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_GETSADINFO, + .flags = 0XFFFFFFFF, + }; if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) exit(1); @@ -1299,7 +1279,7 @@ if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) exit(2); - print_sadinfo(&req.n, (void*)stdout); + print_sadinfo(&req.n, (void *)stdout); rtnl_close(&rth); @@ -1312,16 +1292,13 @@ struct { struct nlmsghdr n; struct xfrm_usersa_flush xsf; - } req; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_FLUSHSA, + }; char *protop = NULL; - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = XFRM_MSG_FLUSHSA; - req.xsf.proto = 0; - while (argc > 0) { if (strcmp(*argv, "proto") == 0) { int ret; diff -Nru iproute2-4.3.0/lib/color.c iproute2-4.9.0/lib/color.c --- iproute2-4.3.0/lib/color.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/color.c 2016-12-12 23:07:42.000000000 +0000 @@ -1,5 +1,8 @@ #include #include +#include +#include +#include #include "color.h" @@ -32,7 +35,8 @@ C_MAGENTA, C_BLUE, C_GREEN, - C_RED + C_RED, + C_CLEAR }; static int color_is_enabled; @@ -62,3 +66,27 @@ va_end(args); return ret; } + +enum color_attr ifa_family_color(__u8 ifa_family) +{ + switch (ifa_family) { + case AF_INET: + return COLOR_INET; + case AF_INET6: + return COLOR_INET6; + default: + return COLOR_CLEAR; + } +} + +enum color_attr oper_state_color(__u8 state) +{ + switch (state) { + case IF_OPER_UP: + return COLOR_OPERSTATE_UP; + case IF_OPER_DOWN: + return COLOR_OPERSTATE_DOWN; + default: + return COLOR_CLEAR; + } +} diff -Nru iproute2-4.3.0/lib/coverity_model.c iproute2-4.9.0/lib/coverity_model.c --- iproute2-4.3.0/lib/coverity_model.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/lib/coverity_model.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Coverity Scan model + * + * This is a modeling file for Coverity Scan. Modeling helps to avoid false + * positives. + * + * - A model file can't import any header files. + * - Therefore only some built-in primitives like int, char and void are + * available but not wchar_t, NULL etc. + * - Modeling doesn't need full structs and typedefs. Rudimentary structs + * and similar types are sufficient. + * - An uninitialized local pointer is not an error. It signifies that the + * variable could be either NULL or have some data. + * + * Coverity Scan doesn't pick up modifications automatically. The model file + * must be uploaded by an admin. + */ diff -Nru iproute2-4.3.0/lib/dnet_ntop.c iproute2-4.9.0/lib/dnet_ntop.c --- iproute2-4.3.0/lib/dnet_ntop.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/dnet_ntop.c 2016-12-12 23:07:42.000000000 +0000 @@ -98,5 +98,3 @@ return NULL; } - - diff -Nru iproute2-4.3.0/lib/inet_proto.c iproute2-4.9.0/lib/inet_proto.c --- iproute2-4.3.0/lib/inet_proto.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/inet_proto.c 2016-12-12 23:07:42.000000000 +0000 @@ -67,5 +67,3 @@ } return -1; } - - diff -Nru iproute2-4.3.0/lib/ipx_ntop.c iproute2-4.9.0/lib/ipx_ntop.c --- iproute2-4.3.0/lib/ipx_ntop.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/ipx_ntop.c 2016-12-12 23:07:42.000000000 +0000 @@ -68,5 +68,3 @@ return NULL; } - - diff -Nru iproute2-4.3.0/lib/ipx_pton.c iproute2-4.9.0/lib/ipx_pton.c --- iproute2-4.3.0/lib/ipx_pton.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/ipx_pton.c 2016-12-12 23:07:42.000000000 +0000 @@ -6,18 +6,6 @@ #include "utils.h" -static u_int32_t hexget(char c) -{ - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - if (c >= '0' && c <= '9') - return c - '0'; - - return 0xf0; -} - static int ipx_getnet(u_int32_t *net, const char *str) { int i; @@ -25,7 +13,7 @@ for(i = 0; *str && (i < 8); i++) { - if ((tmp = hexget(*str)) & 0xf0) { + if ((tmp = get_hex(*str)) == -1) { if (*str == '.') return 0; else @@ -49,11 +37,11 @@ u_int32_t tmp; for(i = 0; i < 6; i++) { - if ((tmp = hexget(*str++)) & 0xf0) + if ((tmp = get_hex(*str++)) == -1) return -1; node[i] = (u_int8_t)tmp; node[i] <<= 4; - if ((tmp = hexget(*str++)) & 0xf0) + if ((tmp = get_hex(*str++)) == -1) return -1; node[i] |= (u_int8_t)tmp; if (*str == ':') diff -Nru iproute2-4.3.0/lib/json_writer.c iproute2-4.9.0/lib/json_writer.c --- iproute2-4.3.0/lib/json_writer.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/json_writer.c 2016-12-12 23:07:42.000000000 +0000 @@ -33,7 +33,7 @@ static void jsonw_indent(json_writer_t *self) { unsigned i; - for (i = 0; i <= self->depth; ++i) + for (i = 0; i < self->depth; ++i) fputs(" ", self->out); } @@ -102,7 +102,6 @@ self->depth = 0; self->pretty = false; self->sep = '\0'; - putc('{', self->out); } return self; } @@ -113,8 +112,7 @@ json_writer_t *self = *self_p; assert(self->depth == 0); - jsonw_eol(self); - fputs("}\n", self->out); + fputs("\n", self->out); fflush(self->out); free(self); *self_p = NULL; @@ -269,6 +267,7 @@ { json_writer_t *wr = jsonw_new(stdout); + jsonw_start_object(wr); jsonw_pretty(wr, true); jsonw_name(wr, "Vyatta"); jsonw_start_object(wr); @@ -305,6 +304,7 @@ jsonw_end_object(wr); + jsonw_end_object(wr); jsonw_destroy(&wr); return 0; } diff -Nru iproute2-4.3.0/lib/libgenl.c iproute2-4.9.0/lib/libgenl.c --- iproute2-4.3.0/lib/libgenl.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/libgenl.c 2016-12-12 23:07:42.000000000 +0000 @@ -61,3 +61,20 @@ return genl_parse_getfamily(&req.n); } +int genl_init_handle(struct rtnl_handle *grth, const char *family, + int *genl_family) +{ + if (*genl_family >= 0) + return 0; + + if (rtnl_open_byproto(grth, 0, NETLINK_GENERIC) < 0) { + fprintf(stderr, "Cannot open generic netlink socket\n"); + return -1; + } + + *genl_family = genl_resolve_family(grth, family); + if (*genl_family < 0) + return -1; + + return 0; +} diff -Nru iproute2-4.3.0/lib/libnetlink.c iproute2-4.9.0/lib/libnetlink.c --- iproute2-4.3.0/lib/libnetlink.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/libnetlink.c 2016-12-12 23:07:42.000000000 +0000 @@ -43,7 +43,7 @@ } } -int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions, int protocol) { socklen_t addr_len; @@ -58,12 +58,14 @@ return -1; } - if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, + &sndbuf, sizeof(sndbuf)) < 0) { perror("SO_SNDBUF"); return -1; } - if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) { + if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, + &rcvbuf, sizeof(rcvbuf)) < 0) { perror("SO_RCVBUF"); return -1; } @@ -72,12 +74,14 @@ rth->local.nl_family = AF_NETLINK; rth->local.nl_groups = subscriptions; - if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { + if (bind(rth->fd, (struct sockaddr *)&rth->local, + sizeof(rth->local)) < 0) { perror("Cannot bind netlink socket"); return -1; } addr_len = sizeof(rth->local); - if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { + if (getsockname(rth->fd, (struct sockaddr *)&rth->local, + &addr_len) < 0) { perror("Cannot getsockname"); return -1; } @@ -86,14 +90,15 @@ return -1; } if (rth->local.nl_family != AF_NETLINK) { - fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); + fprintf(stderr, "Wrong address family %d\n", + rth->local.nl_family); return -1; } rth->seq = time(NULL); return 0; } -int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions) { return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); } @@ -112,21 +117,64 @@ /* attribute has to be NLMSG aligned */ struct rtattr ext_req __attribute__ ((aligned(NLMSG_ALIGNTO))); __u32 ext_filter_mask; + } req = { + .nlh.nlmsg_len = sizeof(req), + .nlh.nlmsg_type = type, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ifm.ifi_family = family, + .ext_req.rta_type = IFLA_EXT_MASK, + .ext_req.rta_len = RTA_LENGTH(sizeof(__u32)), + .ext_filter_mask = filt_mask, + }; + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_wilddump_req_filter_fn(struct rtnl_handle *rth, int family, int type, + req_filter_fn_t filter_fn) +{ + struct { + struct nlmsghdr nlh; + struct ifinfomsg ifm; + char buf[1024]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlh.nlmsg_type = type, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ifm.ifi_family = family, + }; + int err; + + if (!filter_fn) + return -EINVAL; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + + return send(rth->fd, &req, req.nlh.nlmsg_len, 0); +} + +int rtnl_wilddump_stats_req_filter(struct rtnl_handle *rth, int fam, int type, + __u32 filt_mask) +{ + struct { + struct nlmsghdr nlh; + struct if_stats_msg ifsm; } req; memset(&req, 0, sizeof(req)); - req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)); req.nlh.nlmsg_type = type; req.nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_seq = rth->dump = ++rth->seq; - req.ifm.ifi_family = family; + req.ifsm.family = fam; + req.ifsm.filter_mask = filt_mask; - req.ext_req.rta_type = IFLA_EXT_MASK; - req.ext_req.rta_len = RTA_LENGTH(sizeof(__u32)); - req.ext_filter_mask = filt_mask; - - return send(rth->fd, (void*)&req, sizeof(req), 0); + return send(rth->fd, &req, sizeof(req), 0); } int rtnl_send(struct rtnl_handle *rth, const void *buf, int len) @@ -155,10 +203,11 @@ for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status); h = NLMSG_NEXT(h, status)) { if (h->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) fprintf(stderr, "ERROR truncated\n"); - else + else errno = -err->error; return -1; } @@ -169,7 +218,12 @@ int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) { - struct nlmsghdr nlh; + struct nlmsghdr nlh = { + .nlmsg_len = NLMSG_LENGTH(len), + .nlmsg_type = type, + .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlmsg_seq = rth->dump = ++rth->seq, + }; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct iovec iov[2] = { { .iov_base = &nlh, .iov_len = sizeof(nlh) }, @@ -177,16 +231,31 @@ }; struct msghdr msg = { .msg_name = &nladdr, - .msg_namelen = sizeof(nladdr), + .msg_namelen = sizeof(nladdr), .msg_iov = iov, .msg_iovlen = 2, }; - nlh.nlmsg_len = NLMSG_LENGTH(len); - nlh.nlmsg_type = type; - nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; - nlh.nlmsg_pid = 0; - nlh.nlmsg_seq = rth->dump = ++rth->seq; + return sendmsg(rth->fd, &msg, 0); +} + +int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n) +{ + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + struct iovec iov = { + .iov_base = n, + .iov_len = n->nlmsg_len + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + n->nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; + n->nlmsg_pid = 0; + n->nlmsg_seq = rth->dump = ++rth->seq; return sendmsg(rth->fd, &msg, 0); } @@ -202,7 +271,7 @@ .msg_iov = &iov, .msg_iovlen = 1, }; - char buf[16384]; + char buf[32768]; int dump_intr = 0; iov.iov_base = buf; @@ -232,12 +301,15 @@ fwrite(buf, 1, NLMSG_ALIGN(status), rth->dump_fp); for (a = arg; a->filter; a++) { - struct nlmsghdr *h = (struct nlmsghdr*)buf; + struct nlmsghdr *h = (struct nlmsghdr *)buf; + msglen = status; while (NLMSG_OK(h, msglen)) { int err = 0; + h->nlmsg_flags &= ~a->nc_flags; + if (nladdr.nl_pid != 0 || h->nlmsg_pid != rth->local.nl_pid || h->nlmsg_seq != rth->dump) @@ -251,7 +323,8 @@ break; /* process next filter */ } if (h->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { fprintf(stderr, "ERROR truncated\n"); @@ -296,27 +369,27 @@ } } -int rtnl_dump_filter(struct rtnl_handle *rth, +int rtnl_dump_filter_nc(struct rtnl_handle *rth, rtnl_filter_t filter, - void *arg1) + void *arg1, __u16 nc_flags) { const struct rtnl_dump_filter_arg a[2] = { - { .filter = filter, .arg1 = arg1, }, - { .filter = NULL, .arg1 = NULL, }, + { .filter = filter, .arg1 = arg1, .nc_flags = nc_flags, }, + { .filter = NULL, .arg1 = NULL, .nc_flags = 0, }, }; return rtnl_dump_filter_l(rth, a); } int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, - struct nlmsghdr *answer, size_t len) + struct nlmsghdr *answer, size_t maxlen) { int status; - unsigned seq; + unsigned int seq; struct nlmsghdr *h; - struct sockaddr_nl nladdr; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct iovec iov = { - .iov_base = (void*) n, + .iov_base = n, .iov_len = n->nlmsg_len }; struct msghdr msg = { @@ -325,10 +398,7 @@ .msg_iov = &iov, .msg_iovlen = 1, }; - char buf[32768]; - - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; + char buf[32768] = {}; n->nlmsg_seq = seq = ++rtnl->seq; @@ -341,8 +411,6 @@ return -1; } - memset(buf,0,sizeof(buf)); - iov.iov_base = buf; while (1) { iov.iov_len = sizeof(buf); @@ -360,19 +428,23 @@ return -1; } if (msg.msg_namelen != sizeof(nladdr)) { - fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); + fprintf(stderr, + "sender address length == %d\n", + msg.msg_namelen); exit(1); } - for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); - if (l < 0 || len>status) { + if (l < 0 || len > status) { if (msg.msg_flags & MSG_TRUNC) { fprintf(stderr, "Truncated message\n"); return -1; } - fprintf(stderr, "!!!malformed message: len=%d\n", len); + fprintf(stderr, + "!!!malformed message: len=%d\n", + len); exit(1); } @@ -381,37 +453,40 @@ h->nlmsg_seq != seq) { /* Don't forget to skip that message. */ status -= NLMSG_ALIGN(len); - h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); continue; } if (h->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + if (l < sizeof(struct nlmsgerr)) { fprintf(stderr, "ERROR truncated\n"); } else if (!err->error) { if (answer) memcpy(answer, h, - MIN(len, h->nlmsg_len)); + MIN(maxlen, h->nlmsg_len)); return 0; } - fprintf(stderr, "RTNETLINK answers: %s\n", - strerror(-err->error)); + if (rtnl->proto != NETLINK_SOCK_DIAG) + fprintf(stderr, + "RTNETLINK answers: %s\n", + strerror(-err->error)); errno = -err->error; return -1; } if (answer) { memcpy(answer, h, - MIN(len, h->nlmsg_len)); + MIN(maxlen, h->nlmsg_len)); return 0; } fprintf(stderr, "Unexpected reply!!!\n"); status -= NLMSG_ALIGN(len); - h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { @@ -445,7 +520,7 @@ { int status; struct nlmsghdr *h; - struct sockaddr_nl nladdr; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct iovec iov; struct msghdr msg = { .msg_name = &nladdr, @@ -461,11 +536,6 @@ msg.msg_controllen = sizeof(cmsgbuf); } - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - nladdr.nl_pid = 0; - nladdr.nl_groups = 0; - iov.iov_base = buf; while (1) { struct rtnl_ctrl_data ctrl; @@ -488,7 +558,9 @@ return -1; } if (msg.msg_namelen != sizeof(nladdr)) { - fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen); + fprintf(stderr, + "Sender address length == %d\n", + msg.msg_namelen); exit(1); } @@ -506,17 +578,19 @@ } } - for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { int err; int len = h->nlmsg_len; int l = len - sizeof(*h); - if (l<0 || len>status) { + if (l < 0 || len > status) { if (msg.msg_flags & MSG_TRUNC) { fprintf(stderr, "Truncated message\n"); return -1; } - fprintf(stderr, "!!!malformed message: len=%d\n", len); + fprintf(stderr, + "!!!malformed message: len=%d\n", + len); exit(1); } @@ -525,7 +599,7 @@ return err; status -= NLMSG_ALIGN(len); - h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { fprintf(stderr, "Message truncated\n"); @@ -542,14 +616,9 @@ void *jarg) { int status; - struct sockaddr_nl nladdr; - char buf[16384]; - struct nlmsghdr *h = (void*)buf; - - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - nladdr.nl_pid = 0; - nladdr.nl_groups = 0; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + char buf[16384]; + struct nlmsghdr *h = (struct nlmsghdr *)buf; while (1) { int err, len; @@ -569,7 +638,7 @@ len = h->nlmsg_len; l = len - sizeof(*h); - if (l<0 || len>sizeof(buf)) { + if (l < 0 || len > sizeof(buf)) { fprintf(stderr, "!!!malformed message: len=%d @%lu\n", len, ftell(rtnl)); return -1; @@ -629,7 +698,9 @@ struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { - fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); + fprintf(stderr, + "addattr_l ERROR: message exceeded bound of %d\n", + maxlen); return -1; } rta = NLMSG_TAIL(n); @@ -643,7 +714,9 @@ int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len) { if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) { - fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen); + fprintf(stderr, + "addraw_l ERROR: message exceeded bound of %d\n", + maxlen); return -1; } @@ -692,10 +765,12 @@ struct rtattr *subrta; if (RTA_ALIGN(rta->rta_len) + len > maxlen) { - fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen); + fprintf(stderr, + "rta_addattr32: Error! max allowed bound %d exceeded\n", + maxlen); return -1; } - subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len)); subrta->rta_type = type; subrta->rta_len = len; memcpy(RTA_DATA(subrta), &data, 4); @@ -710,10 +785,12 @@ int len = RTA_LENGTH(alen); if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) { - fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen); + fprintf(stderr, + "rta_addattr_l: Error! max allowed bound %d exceeded\n", + maxlen); return -1; } - subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len)); subrta->rta_type = type; subrta->rta_len = len; memcpy(RTA_DATA(subrta), data, alen); @@ -767,14 +844,16 @@ type = rta->rta_type & ~flags; if ((type <= max) && (!tb[type])) tb[type] = rta; - rta = RTA_NEXT(rta,len); + rta = RTA_NEXT(rta, len); } if (len) - fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", + len, rta->rta_len); return 0; } -int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len) +int parse_rtattr_byindex(struct rtattr *tb[], int max, + struct rtattr *rta, int len) { int i = 0; @@ -782,10 +861,11 @@ while (RTA_OK(rta, len)) { if (rta->rta_type <= max && i < max) tb[i++] = rta; - rta = RTA_NEXT(rta,len); + rta = RTA_NEXT(rta, len); } if (len) - fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", + len, rta->rta_len); return i; } @@ -796,13 +876,16 @@ return rta; rta = RTA_NEXT(rta, len); } + if (len) - fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", + len, rta->rta_len); return NULL; } -int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, - int len) +int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, + struct rtattr *rta, + int len) { if (RTA_PAYLOAD(rta) < len) return -1; diff -Nru iproute2-4.3.0/lib/ll_addr.c iproute2-4.9.0/lib/ll_addr.c --- iproute2-4.3.0/lib/ll_addr.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/ll_addr.c 2016-12-12 23:07:42.000000000 +0000 @@ -41,18 +41,9 @@ if (alen == 16 && type == ARPHRD_TUNNEL6) { return inet_ntop(AF_INET6, addr, buf, blen); } - l = 0; - for (i=0; inlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) return 0; - if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi))) + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi))) return -1; im = ll_get_by_index(ifi->ifi_index); @@ -103,7 +103,6 @@ return 0; } - memset(tb, 0, sizeof(tb)); parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); ifname = rta_getattr_str(tb[IFLA_IFNAME]); if (ifname == NULL) diff -Nru iproute2-4.3.0/lib/ll_proto.c iproute2-4.9.0/lib/ll_proto.c --- iproute2-4.3.0/lib/ll_proto.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/ll_proto.c 2016-12-12 23:07:42.000000000 +0000 @@ -111,8 +111,7 @@ return 0; } } - if (get_u16(id, buf, 0)) + if (get_be16(id, buf, 0)) return -1; - *id = htons(*id); return 0; } diff -Nru iproute2-4.3.0/lib/ll_types.c iproute2-4.9.0/lib/ll_types.c --- iproute2-4.3.0/lib/ll_types.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/ll_types.c 2016-12-12 23:07:42.000000000 +0000 @@ -100,11 +100,13 @@ __PF(IEEE80211_PRISM,ieee802.11/prism) __PF(IEEE80211_RADIOTAP,ieee802.11/radiotap) __PF(IEEE802154, ieee802.15.4) +__PF(IEEE802154_MONITOR, ieee802.15.4/monitor) __PF(PHONET, phonet) __PF(PHONET_PIPE, phonet_pipe) __PF(CAIF, caif) __PF(IP6GRE, gre6) __PF(NETLINK, netlink) +__PF(6LOWPAN, 6lowpan) __PF(NONE, none) __PF(VOID,void) diff -Nru iproute2-4.3.0/lib/Makefile iproute2-4.9.0/lib/Makefile --- iproute2-4.3.0/lib/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -15,10 +15,10 @@ all: libnetlink.a libutil.a libnetlink.a: $(NLOBJ) - $(AR) rcs $@ $(NLOBJ) + $(QUIET_AR)$(AR) rcs $@ $^ libutil.a: $(UTILOBJ) $(ADDLIB) - $(AR) rcs $@ $(UTILOBJ) $(ADDLIB) + $(QUIET_AR)$(AR) rcs $@ $^ install: diff -Nru iproute2-4.3.0/lib/names.c iproute2-4.9.0/lib/names.c --- iproute2-4.3.0/lib/names.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/names.c 2016-12-12 23:07:42.000000000 +0000 @@ -54,15 +54,12 @@ { struct db_names *db; - db = malloc(sizeof(*db)); + db = calloc(1, sizeof(*db)); if (!db) return NULL; - memset(db, 0, sizeof(*db)); - db->size = MAX_ENTRIES; - db->hash = malloc(sizeof(struct db_entry *) * db->size); - memset(db->hash, 0, sizeof(struct db_entry *) * db->size); + db->hash = calloc(db->size, sizeof(struct db_entry *)); return db; } diff -Nru iproute2-4.3.0/lib/rt_names.c iproute2-4.9.0/lib/rt_names.c --- iproute2-4.3.0/lib/rt_names.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/rt_names.c 2016-12-12 23:07:42.000000000 +0000 @@ -17,21 +17,20 @@ #include #include #include +#include +#include #include #include #include "rt_names.h" - -#ifndef CONFDIR -#define CONFDIR "/etc/iproute2" -#endif +#include "utils.h" #define NAME_MAX_LEN 512 struct rtnl_hash_entry { - struct rtnl_hash_entry *next; - const char * name; + struct rtnl_hash_entry *next; + const char *name; unsigned int id; }; @@ -81,7 +80,7 @@ return; } - if (id<0) + if (id < 0) continue; entry = malloc(sizeof(*entry)); @@ -111,7 +110,7 @@ fclose(fp); return; } - if (id<0 || id>size) + if (id < 0 || id > size) continue; tab[id] = strdup(namebuf); @@ -119,23 +118,23 @@ fclose(fp); } -static char * rtnl_rtprot_tab[256] = { - [RTPROT_UNSPEC] = "none", - [RTPROT_REDIRECT] ="redirect", - [RTPROT_KERNEL] = "kernel", - [RTPROT_BOOT] = "boot", - [RTPROT_STATIC] = "static", - - [RTPROT_GATED] = "gated", - [RTPROT_RA] = "ra", - [RTPROT_MRT] = "mrt", - [RTPROT_ZEBRA] ="zebra", - [RTPROT_BIRD] = "bird", - [RTPROT_BABEL] = "babel", +static char *rtnl_rtprot_tab[256] = { + [RTPROT_UNSPEC] = "none", + [RTPROT_REDIRECT] = "redirect", + [RTPROT_KERNEL] = "kernel", + [RTPROT_BOOT] = "boot", + [RTPROT_STATIC] = "static", + + [RTPROT_GATED] = "gated", + [RTPROT_RA] = "ra", + [RTPROT_MRT] = "mrt", + [RTPROT_ZEBRA] = "zebra", + [RTPROT_BIRD] = "bird", + [RTPROT_BABEL] = "babel", [RTPROT_DNROUTED] = "dnrouted", - [RTPROT_XORP] = "xorp", - [RTPROT_NTK] = "ntk", - [RTPROT_DHCP] = "dhcp", + [RTPROT_XORP] = "xorp", + [RTPROT_NTK] = "ntk", + [RTPROT_DHCP] = "dhcp", }; @@ -148,9 +147,9 @@ rtnl_rtprot_tab, 256); } -const char * rtnl_rtprot_n2a(int id, char *buf, int len) +const char *rtnl_rtprot_n2a(int id, char *buf, int len) { - if (id<0 || id>=256) { + if (id < 0 || id >= 256) { snprintf(buf, len, "%u", id); return buf; } @@ -166,7 +165,7 @@ int rtnl_rtprot_a2n(__u32 *id, const char *arg) { - static char *cache = NULL; + static char *cache; static unsigned long res; char *end; int i; @@ -179,7 +178,7 @@ if (!rtnl_rtprot_init) rtnl_rtprot_initialize(); - for (i=0; i<256; i++) { + for (i = 0; i < 256; i++) { if (rtnl_rtprot_tab[i] && strcmp(rtnl_rtprot_tab[i], arg) == 0) { cache = rtnl_rtprot_tab[i]; @@ -196,8 +195,13 @@ return 0; } -static char * rtnl_rtscope_tab[256] = { - "global", + +static char *rtnl_rtscope_tab[256] = { + [RT_SCOPE_UNIVERSE] = "global", + [RT_SCOPE_NOWHERE] = "nowhere", + [RT_SCOPE_HOST] = "host", + [RT_SCOPE_LINK] = "link", + [RT_SCOPE_SITE] = "site", }; static int rtnl_rtscope_init; @@ -205,33 +209,32 @@ static void rtnl_rtscope_initialize(void) { rtnl_rtscope_init = 1; - rtnl_rtscope_tab[RT_SCOPE_NOWHERE] = "nowhere"; - rtnl_rtscope_tab[RT_SCOPE_HOST] = "host"; - rtnl_rtscope_tab[RT_SCOPE_LINK] = "link"; - rtnl_rtscope_tab[RT_SCOPE_SITE] = "site"; rtnl_tab_initialize(CONFDIR "/rt_scopes", rtnl_rtscope_tab, 256); } const char *rtnl_rtscope_n2a(int id, char *buf, int len) { - if (id<0 || id>=256) { + if (id < 0 || id >= 256) { snprintf(buf, len, "%d", id); return buf; } + if (!rtnl_rtscope_tab[id]) { if (!rtnl_rtscope_init) rtnl_rtscope_initialize(); } + if (rtnl_rtscope_tab[id]) return rtnl_rtscope_tab[id]; + snprintf(buf, len, "%d", id); return buf; } int rtnl_rtscope_a2n(__u32 *id, const char *arg) { - static const char *cache = NULL; + static const char *cache; static unsigned long res; char *end; int i; @@ -244,7 +247,7 @@ if (!rtnl_rtscope_init) rtnl_rtscope_initialize(); - for (i=0; i<256; i++) { + for (i = 0; i < 256; i++) { if (rtnl_rtscope_tab[i] && strcmp(rtnl_rtscope_tab[i], arg) == 0) { cache = rtnl_rtscope_tab[i]; @@ -262,7 +265,7 @@ } -static char * rtnl_rtrealm_tab[256] = { +static char *rtnl_rtrealm_tab[256] = { "unknown", }; @@ -277,7 +280,7 @@ const char *rtnl_rtrealm_n2a(int id, char *buf, int len) { - if (id<0 || id>=256) { + if (id < 0 || id >= 256) { snprintf(buf, len, "%d", id); return buf; } @@ -294,7 +297,7 @@ int rtnl_rtrealm_a2n(__u32 *id, const char *arg) { - static char *cache = NULL; + static char *cache; static unsigned long res; char *end; int i; @@ -307,7 +310,7 @@ if (!rtnl_rtrealm_init) rtnl_rtrealm_initialize(); - for (i=0; i<256; i++) { + for (i = 0; i < 256; i++) { if (rtnl_rtrealm_tab[i] && strcmp(rtnl_rtrealm_tab[i], arg) == 0) { cache = rtnl_rtrealm_tab[i]; @@ -329,7 +332,7 @@ static struct rtnl_hash_entry main_table_entry = { .name = "main" }; static struct rtnl_hash_entry local_table_entry = { .name = "local" }; -static struct rtnl_hash_entry * rtnl_rttable_hash[256] = { +static struct rtnl_hash_entry *rtnl_rttable_hash[256] = { [RT_TABLE_DEFAULT] = &dflt_table_entry, [RT_TABLE_MAIN] = &main_table_entry, [RT_TABLE_LOCAL] = &local_table_entry, @@ -339,6 +342,8 @@ static void rtnl_rttable_initialize(void) { + struct dirent *de; + DIR *d; int i; rtnl_rttable_init = 1; @@ -348,9 +353,33 @@ } rtnl_hash_initialize(CONFDIR "/rt_tables", rtnl_rttable_hash, 256); + + d = opendir(CONFDIR "/rt_tables.d"); + if (!d) + return; + + while ((de = readdir(d)) != NULL) { + char path[PATH_MAX]; + size_t len; + + if (*de->d_name == '.') + continue; + + /* only consider filenames ending in '.conf' */ + len = strlen(de->d_name); + if (len <= 5) + continue; + if (strcmp(de->d_name + len - 5, ".conf")) + continue; + + snprintf(path, sizeof(path), + CONFDIR "/rt_tables.d/%s", de->d_name); + rtnl_hash_initialize(path, rtnl_rttable_hash, 256); + } + closedir(d); } -const char * rtnl_rttable_n2a(__u32 id, char *buf, int len) +const char *rtnl_rttable_n2a(__u32 id, char *buf, int len) { struct rtnl_hash_entry *entry; @@ -371,7 +400,7 @@ int rtnl_rttable_a2n(__u32 *id, const char *arg) { - static const char *cache = NULL; + static const char *cache; static unsigned long res; struct rtnl_hash_entry *entry; char *end; @@ -385,7 +414,7 @@ if (!rtnl_rttable_init) rtnl_rttable_initialize(); - for (i=0; i<256; i++) { + for (i = 0; i < 256; i++) { entry = rtnl_rttable_hash[i]; while (entry && strcmp(entry->name, arg)) entry = entry->next; @@ -405,7 +434,7 @@ } -static char * rtnl_rtdsfield_tab[256] = { +static char *rtnl_rtdsfield_tab[256] = { "0", }; @@ -420,7 +449,7 @@ const char *rtnl_dsfield_n2a(int id, char *buf, int len) { - if (id<0 || id>=256) { + if (id < 0 || id >= 256) { snprintf(buf, len, "%d", id); return buf; } @@ -437,7 +466,7 @@ int rtnl_dsfield_a2n(__u32 *id, const char *arg) { - static char *cache = NULL; + static char *cache; static unsigned long res; char *end; int i; @@ -450,7 +479,7 @@ if (!rtnl_rtdsfield_init) rtnl_rtdsfield_initialize(); - for (i=0; i<256; i++) { + for (i = 0; i < 256; i++) { if (rtnl_rtdsfield_tab[i] && strcmp(rtnl_rtdsfield_tab[i], arg) == 0) { cache = rtnl_rtdsfield_tab[i]; @@ -468,9 +497,11 @@ } -static struct rtnl_hash_entry dflt_group_entry = { .id = 0, .name = "default" }; +static struct rtnl_hash_entry dflt_group_entry = { + .id = 0, .name = "default" +}; -static struct rtnl_hash_entry * rtnl_group_hash[256] = { +static struct rtnl_hash_entry *rtnl_group_hash[256] = { [0] = &dflt_group_entry, }; @@ -485,7 +516,7 @@ int rtnl_group_a2n(int *id, const char *arg) { - static const char *cache = NULL; + static const char *cache; static unsigned long res; struct rtnl_hash_entry *entry; char *end; @@ -499,7 +530,7 @@ if (!rtnl_group_init) rtnl_group_initialize(); - for (i=0; i<256; i++) { + for (i = 0; i < 256; i++) { entry = rtnl_group_hash[i]; while (entry && strcmp(entry->name, arg)) entry = entry->next; @@ -526,10 +557,13 @@ if (!rtnl_group_init) rtnl_group_initialize(); - for (i=0; i<256; i++) { + for (i = 0; i < 256; i++) { entry = rtnl_group_hash[i]; - if (entry && entry->id == id) { - return entry->name; + + while (entry) { + if (entry->id == id) + return entry->name; + entry = entry->next; } } @@ -589,7 +623,7 @@ int nl_proto_a2n(__u32 *id, const char *arg) { - static char *cache = NULL; + static char *cache; static unsigned long res; char *end; int i; diff -Nru iproute2-4.3.0/lib/utils.c iproute2-4.9.0/lib/utils.c --- iproute2-4.3.0/lib/utils.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/lib/utils.c 2016-12-12 23:07:42.000000000 +0000 @@ -37,6 +37,18 @@ int timestamp_short = 0; +int get_hex(char c) +{ + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= '0' && c <= '9') + return c - '0'; + + return -1; +} + int get_integer(int *val, const char *arg, int base) { long res; @@ -95,7 +107,7 @@ /* try coverting dotted quad to CIDR */ if (!get_addr_1(&addr, arg, AF_INET) && addr.family == AF_INET) { int b = mask2bits(addr.data[0]); - + if (b >= 0) { *val = b; return 0; @@ -191,7 +203,7 @@ *val = t; if (*val < t) *val += 1; - + return 0; } @@ -353,6 +365,39 @@ return 0; } +int get_be64(__be64 *val, const char *arg, int base) +{ + __u64 v; + int ret = get_u64(&v, arg, base); + + if (!ret) + *val = htonll(v); + + return ret; +} + +int get_be32(__be32 *val, const char *arg, int base) +{ + __u32 v; + int ret = get_u32(&v, arg, base); + + if (!ret) + *val = htonl(v); + + return ret; +} + +int get_be16(__be16 *val, const char *arg, int base) +{ + __u16 v; + int ret = get_u16(&v, arg, base); + + if (!ret) + *val = htons(v); + + return ret; +} + /* This uses a non-standard parsing (ie not inet_aton, or inet_pton) * because of legacy choice to parse 10.8 as 10.8.0.0 not 10.0.0.8 */ @@ -363,7 +408,7 @@ for (i = 0; i < 4; i++) { unsigned long n; char *endp; - + n = strtoul(cp, &endp, 0); if (n > 255) return -1; /* bogus network value */ @@ -384,6 +429,41 @@ return 1; } +int get_addr64(__u64 *ap, const char *cp) +{ + int i; + + union { + __u16 v16[4]; + __u64 v64; + } val; + + for (i = 0; i < 4; i++) { + unsigned long n; + char *endp; + + n = strtoul(cp, &endp, 16); + if (n > 0xffff) + return -1; /* bogus network value */ + + if (endp == cp) /* no digits */ + return -1; + + val.v16[i] = htons(n); + + if (*endp == '\0') + break; + + if (i == 3 || *endp != ':') + return -1; /* extra characters */ + cp = endp + 1; + } + + *ap = val.v64; + + return 1; +} + int get_addr_1(inet_prefix *addr, const char *name, int family) { memset(addr, 0, sizeof(*addr)); @@ -532,7 +612,7 @@ { if (get_addr_1(dst, arg, family)) { fprintf(stderr, "Error: %s address is expected rather than \"%s\".\n", - family_name(family) ,arg); + family_name(dst->family) ,arg); exit(1); } return 0; @@ -544,9 +624,10 @@ fprintf(stderr, "Error: \"%s\" may be inet prefix, but it is not allowed in this context.\n", arg); exit(1); } + if (get_prefix_1(dst, arg, family)) { fprintf(stderr, "Error: %s prefix is expected rather than \"%s\".\n", - family_name(family) ,arg); + family_name(dst->family) ,arg); exit(1); } return 0; @@ -667,7 +748,7 @@ return sysconf(_SC_CLK_TCK); } -const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen) +const char *rt_addr_n2a_r(int af, int len, const void *addr, char *buf, int buflen) { switch (af) { case AF_INET: @@ -690,6 +771,13 @@ } } +const char *rt_addr_n2a(int af, int len, const void *addr) +{ + static char buf[256]; + + return rt_addr_n2a_r(af, len, addr, buf, 256); +} + int read_family(const char *name) { int family = AF_UNSPEC; @@ -783,7 +871,7 @@ } #endif -const char *format_host(int af, int len, const void *addr, +const char *format_host_r(int af, int len, const void *addr, char *buf, int buflen) { #ifdef RESOLVE_HOSTNAMES @@ -797,7 +885,14 @@ return n; } #endif - return rt_addr_n2a(af, len, addr, buf, buflen); + return rt_addr_n2a_r(af, len, addr, buf, buflen); +} + +const char *format_host(int af, int len, const void *addr) +{ + static char buf[256]; + + return format_host_r(af, len, addr, buf, 256); } @@ -816,9 +911,9 @@ return buf; } -__u8* hexstring_a2n(const char *str, __u8 *buf, int blen) +__u8 *hexstring_a2n(const char *str, __u8 *buf, int blen, unsigned int *len) { - int cnt = 0; + unsigned int cnt = 0; char *endptr; if (strlen(str) % 2) @@ -829,15 +924,44 @@ strncpy(tmpstr, str, 2); tmpstr[2] = '\0'; + errno = 0; tmp = strtoul(tmpstr, &endptr, 16); if (errno != 0 || tmp > 0xFF || *endptr != '\0') return NULL; buf[cnt++] = tmp; str += 2; } + + if (len) + *len = cnt; + return buf; } +int addr64_n2a(__u64 addr, char *buff, size_t len) +{ + __u16 *words = (__u16 *)&addr; + __u16 v; + int i, ret; + size_t written = 0; + char *sep = ":"; + + for (i = 0; i < 4; i++) { + v = ntohs(words[i]); + + if (i == 3) + sep = ""; + + ret = snprintf(&buff[written], len - written, "%x%s", v, sep); + if (ret < 0) + return ret; + + written += ret; + } + + return written; +} + int print_timestamp(FILE *fp) { struct timeval tv; @@ -997,3 +1121,47 @@ sprintf(buf, "%d", val); return buf; } + +int get_guid(__u64 *guid, const char *arg) +{ + unsigned long int tmp; + char *endptr; + int i; + +#define GUID_STR_LEN 23 + /* Verify strict format: format string must be + * xx:xx:xx:xx:xx:xx:xx:xx where xx can be an arbitrary + * hex digit + */ + + if (strlen(arg) != GUID_STR_LEN) + return -1; + + /* make sure columns are in place */ + for (i = 0; i < 7; i++) + if (arg[2 + i * 3] != ':') + return -1; + + *guid = 0; + for (i = 0; i < 8; i++) { + tmp = strtoul(arg + i * 3, &endptr, 16); + if (endptr != arg + i * 3 + 2) + return -1; + + if (tmp > 255) + return -1; + + *guid |= tmp << (56 - 8 * i); + } + + return 0; +} + +/* This is a necessary workaround for multicast route dumps */ +int get_real_family(int rtm_type, int rtm_family) +{ + if (rtm_type != RTN_MULTICAST) + return rtm_family; + + return rtm_family == RTNL_FAMILY_IPMR ? AF_INET : AF_INET6; +} diff -Nru iproute2-4.3.0/Makefile iproute2-4.9.0/Makefile --- iproute2-4.3.0/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -1,3 +1,7 @@ +ifndef VERBOSE +MAKEFLAGS += --no-print-directory +endif + PREFIX?=/usr LIBDIR?=$(PREFIX)/lib SBINDIR?=/sbin @@ -7,6 +11,7 @@ MANDIR?=$(DATADIR)/man ARPDDIR?=/var/lib/arpd KERNEL_INCLUDE?=/usr/include +BASH_COMPDIR?=$(DATADIR)/bash-completion/completions # Path to db_185.h include DBM_INCLUDE:=$(DESTDIR)/usr/include @@ -29,8 +34,8 @@ #options for mpls ADDLIB+=mpls_ntop.o mpls_pton.o -CC = gcc -HOSTCC = gcc +CC := gcc +HOSTCC ?= $(CC) DEFINES += -D_GNU_SOURCE # Turn on transparent support for LFS DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE @@ -41,7 +46,7 @@ CFLAGS := $(WFLAGS) $(CCOPTS) -I../include $(DEFINES) $(CFLAGS) YACCFLAGS = -d -t -v -SUBDIRS=lib ip tc bridge misc netem genl tipc man +SUBDIRS=lib ip tc bridge misc netem genl tipc devlink man LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a LDLIBS += $(LIBNETLINK) @@ -49,7 +54,7 @@ all: Config @set -e; \ for i in $(SUBDIRS); \ - do $(MAKE) $(MFLAGS) -C $$i; done + do echo; echo $$i; $(MAKE) $(MFLAGS) -C $$i; done Config: sh configure $(KERNEL_INCLUDE) @@ -66,6 +71,8 @@ $(DESTDIR)$(DOCDIR)/examples/diffserv @for i in $(SUBDIRS) doc; do $(MAKE) -C $$i install; done install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(BASH_COMPDIR) + install -m 0644 bash-completion/tc $(DESTDIR)$(BASH_COMPDIR) snapshot: echo "static const char SNAPSHOT[] = \""`date +%y%m%d`"\";" \ diff -Nru iproute2-4.3.0/man/man7/tc-hfsc.7 iproute2-4.9.0/man/man7/tc-hfsc.7 --- iproute2-4.3.0/man/man7/tc-hfsc.7 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man7/tc-hfsc.7 2016-12-12 23:07:42.000000000 +0000 @@ -555,8 +555,8 @@ . \fBtc\fR(8), \fBtc\-hfsc\fR(8), \fBtc\-stab\fR(8) -Please direct bugreports and patches to: +Please direct bugreports and patches to: . .SH "AUTHOR" . -Manpage created by Michal Soltys (sol...@ziu.info) +Manpage created by Michal Soltys (soltys@ziu.info) diff -Nru iproute2-4.3.0/man/man8/bridge.8 iproute2-4.9.0/man/man8/bridge.8 --- iproute2-4.3.0/man/man8/bridge.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/bridge.8 2016-12-12 23:07:42.000000000 +0000 @@ -20,19 +20,20 @@ .IR OPTIONS " := { " \fB\-V\fR[\fIersion\fR] | \fB\-s\fR[\fItatistics\fR] | -\fB\-n\fR[\fIetns\fR] name } -\fB\-b\fR[\fIatch\fR] filename } +\fB\-n\fR[\fIetns\fR] name | +\fB\-b\fR[\fIatch\fR] filename | +\fB\-j\fR[\fIson\fR] } .ti -8 .BR "bridge link set" -.B dev +.B dev .IR DEV .IR " [ " -.B cost +.B cost .IR COST " ] [ " -.B priority -.IR PRIO " ] [ " -.B state +.B priority +.IR PRIO " ] [ " +.B state .IR STATE "] [" .BR guard " { " on " | " off " } ] [ " .BR hairpin " { " on " | " off " } ] [ " @@ -42,21 +43,22 @@ .BR learning_sync " { " on " | " off " } ] [ " .BR flood " { " on " | " off " } ] [ " .BR hwmode " { " vepa " | " veb " } ] [ " -.BR self " ] [ " master " ] " +.BR mcast_flood " { " on " | " off " } ] [ " +.BR self " ] [ " master " ]" .ti -8 .BR "bridge link" " [ " show " ] [ " -.B dev +.B dev .IR DEV " ]" .ti -8 -.BR "bridge fdb" " { " add " | " append " | " del " } " +.BR "bridge fdb" " { " add " | " append " | " del " | " replace " } " .I LLADDR -.B dev +.B dev .IR DEV " { " -.BR local " | " temp " } [ " +.BR local " | " static " | " dynamic " } [ " .BR self " ] [ " master " ] [ " router " ] [ " use " ] [ " -.B dst +.B dst .IR IPADDR " ] [ " .B vni .IR VNI " ] [" @@ -67,12 +69,12 @@ .ti -8 .BR "bridge fdb" " [ " show " ] [ " -.B dev +.B dev .IR DEV " ]" .ti -8 .BR "bridge mdb" " { " add " | " del " } " -.B dev +.B dev .IR DEV .B port .IR PORT @@ -84,21 +86,21 @@ .ti -8 .BR "bridge mdb show " [ " -.B dev +.B dev .IR DEV " ]" .ti -8 .BR "bridge vlan" " { " add " | " del " } " -.B dev +.B dev .IR DEV -.B vid +.B vid .IR VID " [ " -.BR pvid " ] [ " untagged " ] [ " -.BR self " ] [ " master " ] " +.BR pvid " ] [ " untagged " ] [ " +.BR self " ] [ " master " ] " .ti -8 .BR "bridge vlan" " [ " show " ] [ " -.B dev +.B dev .IR DEV " ]" .ti -8 @@ -119,6 +121,10 @@ As a rule, the information is statistics or some time values. .TP +.BR "\-d" , " \-details" +print detailed information about MDB router ports. + +.TP .BR "\-n" , " \-net" , " \-netns " switches .B bridge @@ -149,6 +155,10 @@ If there were any errors during execution of the commands, the application return code will be non zero. +.TP +.BR "\-json" +Display results in JSON format. Currently available for vlan and fdb. + .SH BRIDGE - COMMAND SYNTAX .SS @@ -159,7 +169,7 @@ - Bridge port. .TP -.B fdb +.B fdb - Forwarding Database entry. .TP @@ -230,14 +240,14 @@ .sp .B 1 -- STP LISTENING state. Only valid if STP is enabled on the brige. In this -state the port for list for STP BPDUs and drop all other traffic. +- STP LISTENING state. Only valid if STP is enabled on the bridge. In this +state the port listens for STP BPDUs and drops all other traffic frames. .sp .B 2 - STP LEARNING state. Only valid if STP is enabled on the bridge. In this state the port will accept traffic only for the purpose of updating MAC -adress tables. +address tables. .sp .B 3 @@ -252,7 +262,7 @@ .TP .BR "guard on " or " guard off " -Controls whether STP BPUDs will be processed by the bridge port. By default, +Controls whether STP BPDUs will be processed by the bridge port. By default, the flag is turned off allowed BPDU processing. Turning this flag on will cause the port to stop processing STP BPDUs. @@ -301,6 +311,10 @@ - bridging happens in hardware. .TP +.BR "mcast_flood on " or " mcast_flood off " +Controls whether a given port will be flooded with multicast traffic for which there is no MDB entry. By default this flag is on. + +.TP .BI self link setting is configured on specified physical device @@ -319,7 +333,7 @@ .SH bridge fdb - forwarding database management .B fdb -objects contain known Ethernet addresses on a link. +objects contain known Ethernet addresses on a link. .P The corresponding commands display fdb entries, add new entries, @@ -338,6 +352,18 @@ .BI dev " DEV" the interface to which this address is associated. +.B local +- is a local permanent fdb entry +.sp + +.B static +- is a static (no arp) fdb entry +.sp + +.B dynamic +- is a dynamic reachable age-able fdb entry +.sp + .B self - the address is associated with the port drivers fdb. Usually hardware. .sp @@ -384,7 +410,7 @@ .BI via " DEVICE" device name of the outgoing interface for the VXLAN device driver to reach the -remote VXLAN tunnel endpoint. +remote VXLAN tunnel endpoint. .SS bridge fdb append - append a forwarding database entry This command adds a new fdb entry with an already known @@ -398,14 +424,21 @@ .PP The arguments are the same as with -.BR "bridge fdb add" , +.BR "bridge fdb add" . .SS bridge fdb delete - delete a forwarding database entry This command removes an existing fdb entry. .PP The arguments are the same as with -.BR "bridge fdb add" , +.BR "bridge fdb add" . + +.SS bridge fdb replace - replace a forwarding database entry +If no matching entry is found, a new one will be created instead. + +.PP +The arguments are the same as with +.BR "bridge fdb add" . .SS bridge fdb show - list forwarding entries. @@ -484,6 +517,11 @@ option, the command becomes verbose. It prints out the ports known to have a connected router. +.PP +With the +.B -statistics +option, the command displays timer values for mdb and router port entries. + .SH bridge vlan - VLAN filter list .B vlan @@ -523,8 +561,8 @@ .BI master the vlan is configured on the software bridge (default). -.SS bridge vlan delete - delete a forwarding database entry -This command removes an existing fdb entry. +.SS bridge vlan delete - delete a vlan filter entry +This command removes an existing vlan filter entry. .PP The arguments are the same as with @@ -537,11 +575,16 @@ This command displays the current VLAN filter table. +.PP +With the +.B -statistics +option, the command displays per-vlan traffic statistics. + .SH bridge monitor - state monitoring The .B bridge -utility can monitor the state of devices and addresses +utility can monitor the state of devices and addresses continuously. This option has a slightly different format. Namely, the .B monitor @@ -553,7 +596,7 @@ .I OBJECT-LIST is the list of object types that we want to monitor. It may contain -.BR link ", " fdb ", and " mdb "." +.BR link ", " fdb ", and " mdb "." If no .B file argument is given, diff -Nru iproute2-4.3.0/man/man8/devlink.8 iproute2-4.9.0/man/man8/devlink.8 --- iproute2-4.3.0/man/man8/devlink.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/devlink.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,89 @@ +.TH DEVLINK 8 "14 Mar 2016" "iproute2" "Linux" +.SH NAME +devlink \- Devlink tool +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B devlink +.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OBJECT " := { " +.BR dev " | " port " | " monitor " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-n\fR[\fIno-nice-names\fR] } + +.SH OPTIONS + +.TP +.BR "\-V" , " -Version" +Print the version of the +.B devlink +utility and exit. + +.TP +.BR "\-n" , " -no-nice-names" +Turn off printing out nice names, for example netdevice ifnames instead of devlink port identification. + +.SS +.I OBJECT + +.TP +.B dev +- devlink device. + +.TP +.B port +- devlink port. + +.TP +.B monitor +- watch for netlink messages. + +.SS +.I COMMAND + +Specifies the action to perform on the object. +The set of possible actions depends on the object type. +As a rule, it is possible to +.B show +(or +.B list +) objects, but some objects do not allow all of these operations +or have some additional commands. The +.B help +command is available for all objects. It prints +out a list of available commands and argument syntax conventions. +.sp +If no command is given, some default command is assumed. +Usually it is +.B list +or, if the objects of this class cannot be listed, +.BR "help" . + +.SH EXIT STATUS +Exit status is 0 if command was successful or a positive integer upon failure. + +.SH SEE ALSO +.BR devlink-dev (8), +.BR devlink-port (8), +.BR devlink-monitor (8), +.BR devlink-sb (8), +.br + +.SH REPORTING BUGS +Report any bugs to the Network Developers mailing list +.B +where the development and maintenance is primarily done. +You do not have to be subscribed to the list to send a message there. + +.SH AUTHOR +Jiri Pirko diff -Nru iproute2-4.3.0/man/man8/devlink-dev.8 iproute2-4.9.0/man/man8/devlink-dev.8 --- iproute2-4.3.0/man/man8/devlink-dev.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/devlink-dev.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,94 @@ +.TH DEVLINK\-DEV 8 "14 Mar 2016" "iproute2" "Linux" +.SH NAME +devlink-dev \- devlink device configuration +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B devlink +.RI "[ " OPTIONS " ]" +.B dev +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-n\fR[\fIno-nice-names\fR] } + +.ti -8 +.B devlink dev show +.RI "[ " DEV " ]" + +.ti -8 +.B devlink dev help + +.ti -8 +.BR "devlink dev eswitch set" +.IR DEV +.RI "[ " +.BR mode " { " legacy " | " switchdev " } " +.RI "]" + +.ti -8 +.BR "devlink dev eswitch show" +.IR DEV + +.SH "DESCRIPTION" +.SS devlink dev show - display devlink device attributes + +.PP +.I "DEV" +- specifies the devlink device to show. +If this argument is omitted all devices are listed. + +.in +4 +Format is: +.in +2 +BUS_NAME/BUS_ADDRESS + +.SS devlink dev eswitch show - display devlink device eswitch attributes +.SS devlink dev eswitch set - sets devlink device eswitch attributes + +.TP +.BR mode " { " legacy " | " switchdev " } " +set eswitch mode + +.I legacy +- Legacy SRIOV + +.I switchdev +- SRIOV switchdev offloads + +.SH "EXAMPLES" +.PP +devlink dev show +.RS 4 +Shows the state of all devlink devices on the system. +.RE +.PP +devlink dev show pci/0000:01:00.0 +.RS 4 +Shows the state of specified devlink device. +.RE +.PP +devlink dev eswitch show pci/0000:01:00.0 +.RS 4 +Shows the eswitch mode of specified devlink device. +.RE +.PP +devlink dev eswitch set pci/0000:01:00.0 mode switchdev +.RS 4 +Sets the eswitch mode of specified devlink device to switchdev. + +.SH SEE ALSO +.BR devlink (8), +.BR devlink-port (8), +.BR devlink-sb (8), +.BR devlink-monitor (8), +.br + +.SH AUTHOR +Jiri Pirko diff -Nru iproute2-4.3.0/man/man8/devlink-monitor.8 iproute2-4.9.0/man/man8/devlink-monitor.8 --- iproute2-4.3.0/man/man8/devlink-monitor.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/devlink-monitor.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,37 @@ +.TH DEVLINK\-MONITOR 8 "14 Mar 2016" "iproute2" "Linux" +.SH "NAME" +devlink-monitor \- state monitoring +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.BR "devlink monitor" " [ " all " |" +.IR OBJECT-LIST " ]" +.sp + +.SH DESCRIPTION +The +.B devlink +utility can monitor the state of devlink devices and ports +continuously. This option has a slightly different format. Namely, the +.B monitor +command is the first in the command line and then the object list. + +.I OBJECT-LIST +is the list of object types that we want to monitor. +It may contain +.BR dev ", " port ". + +.B devlink +opens Devlink Netlink socket, listens on it and dumps state changes. + +.SH SEE ALSO +.BR devlink (8), +.BR devlink-dev (8), +.BR devlink-sb (8), +.BR devlink-port (8), +.br + +.SH AUTHOR +Jiri Pirko diff -Nru iproute2-4.3.0/man/man8/devlink-port.8 iproute2-4.9.0/man/man8/devlink-port.8 --- iproute2-4.3.0/man/man8/devlink-port.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/devlink-port.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,128 @@ +.TH DEVLINK\-PORT 8 "14 Mar 2016" "iproute2" "Linux" +.SH NAME +devlink-port \- devlink port configuration +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B devlink +.RI "[ " OPTIONS " ]" +.B port +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-n\fR[\fIno-nice-names\fR] } + +.ti -8 +.BR "devlink port set " +.IR DEV/PORT_INDEX +.RI "[ " +.BR type " { " eth " | " ib " | " auto " }" +.RI "]" + +.ti -8 +.BR "devlink port split " +.IR DEV/PORT_INDEX +.BR count +.IR COUNT + +.ti -8 +.BR "devlink port unsplit " +.IR DEV/PORT_INDEX + +.ti -8 +.B devlink port show +.RI "[ " DEV/PORT_INDEX " ]" + +.ti -8 +.B devlink port help + +.SH "DESCRIPTION" +.SS devlink port set - change devlink port attributes + +.PP +.B "DEV/PORT_INDEX" +- specifies the devlink port to operate on. + +.in +4 +Format is: +.in +2 +BUS_NAME/BUS_ADDRESS/PORT_INDEX + +.TP +.BR type " { " eth " | " ib " | " auto " } " +set port type + +.I eth +- Ethernet + +.I ib +- Infiniband + +.I auto +- autoselect + +.SS devlink port split - split devlink port into more + +.PP +.B "DEV/PORT_INDEX" +- specifies the devlink port to operate on. + +.TP +.BI count " COUNT" +number of ports to split to. + +.SS devlink port unsplit - unsplit previously split devlink port +Could be performed on any split port of the same split group. + +.PP +.B "DEV/PORT_INDEX" +- specifies the devlink port to operate on. + +.SS devlink port show - display devlink port attributes + +.PP +.I "DEV/PORT_INDEX" +- specifies the devlink port to show. +If this argument is omitted all ports are listed. + +.SH "EXAMPLES" +.PP +devlink port show +.RS 4 +Shows the state of all devlink ports on the system. +.RE +.PP +devlink port show pci/0000:01:00.0/1 +.RS 4 +Shows the state of specified devlink port. +.RE +.PP +devlink port set pci/0000:01:00.0/1 type eth +.RS 4 +Set type of specified devlink port to Ethernet. +.RE +.PP +devlink port split pci/0000:01:00.0/1 count 4 +.RS 4 +Split the specified devlink port into four ports. +.RE +.PP +devlink port unsplit pci/0000:01:00.0/1 +.RS 4 +Unplit the specified previously split devlink port. + +.SH SEE ALSO +.BR devlink (8), +.BR devlink-dev (8), +.BR devlink-sb (8), +.BR devlink-monitor (8), +.br + +.SH AUTHOR +Jiri Pirko diff -Nru iproute2-4.3.0/man/man8/devlink-sb.8 iproute2-4.9.0/man/man8/devlink-sb.8 --- iproute2-4.3.0/man/man8/devlink-sb.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/devlink-sb.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,313 @@ +.TH DEVLINK\-SB 8 "14 Apr 2016" "iproute2" "Linux" +.SH NAME +devlink-sb \- devlink shared buffer configuration +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B devlink +.RI "[ " OPTIONS " ]" +.B sb +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-n\fR[\fIno-nice-names\fR] } + +.ti -8 +.BR "devlink sb show " +.RI "[ " DEV " [ " +.B sb +.IR SB_INDEX " ] ]" + +.ti -8 +.BR "devlink sb pool show " +.RI "[ " DEV " [ " +.B sb +.IR SB_INDEX " ] " +.br +.B pool +.IR POOL_INDEX " ]" + +.ti -8 +.BI "devlink sb pool set " DEV " +.RB "[ " sb +.IR SB_INDEX " ] " +.br +.BI pool " POOL_INDEX " +.br +.BI size " POOL_SIZE " +.br +.BR thtype " { " static " | " dynamic " }" + +.ti -8 +.BR "devlink sb port pool show " +.RI "[ " DEV/PORT_INDEX " [ " +.B sb +.IR SB_INDEX " ] " +.br +.B pool +.IR POOL_INDEX " ]" + +.ti -8 +.BI "devlink sb port pool set " DEV/PORT_INDEX " +.RB "[ " sb +.IR SB_INDEX " ] " +.br +.BI pool " POOL_INDEX " +.br +.BI th " THRESHOLD " + +.ti -8 +.BR "devlink sb tc bind show " +.RI "[ " DEV/PORT_INDEX " [ " +.B sb +.IR SB_INDEX " ] " +.br +.BI tc " TC_INDEX " +.br +.B type +.RB "{ " ingress " | " egress " } ]" + +.ti -8 +.BI "devlink sb tc bind set " DEV/PORT_INDEX " +.RB "[ " sb +.IR SB_INDEX " ] " +.br +.BI tc " TC_INDEX " +.br +.BR type " { " ingress " | " egress " }" +.br +.BI pool " POOL_INDEX " +.br +.BI th " THRESHOLD " + +.ti -8 +.BR "devlink sb occupancy show " +.RI "{ " DEV " | " DEV/PORT_INDEX " } [ " +.B sb +.IR SB_INDEX " ] " + +.ti -8 +.BR "devlink sb occupancy snapshot " +.IR DEV " [ " +.B sb +.IR SB_INDEX " ]" + +.ti -8 +.BR "devlink sb occupancy clearmax " +.IR DEV " [ " +.B sb +.IR SB_INDEX " ]" + +.ti -8 +.B devlink sb help + +.SH "DESCRIPTION" +.SS devlink sb show - display available shared buffers and their attributes + +.PP +.I "DEV" +- specifies the devlink device to show shared buffers. +If this argument is omitted all shared buffers of all devices are listed. + +.PP +.I "SB_INDEX" +- specifies the shared buffer. +If this argument is omitted shared buffer with index 0 is selected. +Behaviour of this argument it the same for every command. + +.SS devlink sb pool show - display available pools and their attributes + +.PP +.I "DEV" +- specifies the devlink device to show pools. +If this argument is omitted all pools of all devices are listed. + +.SS devlink sb pool set - set attributes of pool + +.PP +.I "DEV" +- specifies the devlink device to set pool. + +.TP +.BI size " POOL_SIZE" +size of the pool in Bytes. + +.TP +.BR thtype " { " static " | " dynamic " } " +pool threshold type. + +.I static +- Threshold values for the pool will be passed in Bytes. + +.I dynamic +- Threshold values ("to_alpha") for the pool will be used to compute alpha parameter according to formula: +.br +.in +16 +alpha = 2 ^ (to_alpha - 10) +.in -16 + +.in +10 +The range of the passed value is between 0 to 20. The computed alpha is used to determine the maximum usage of the flow: +.in -10 +.br +.in +16 +max_usage = alpha / (1 + alpha) * Free_Buffer +.in -16 + +.SS devlink sb port pool show - display port-pool combinations and threshold for each +.I "DEV/PORT_INDEX" +- specifies the devlink port. + +.TP +.BI pool " POOL_INDEX" +pool index. + +.SS devlink sb port pool set - set port-pool threshold +.I "DEV/PORT_INDEX" +- specifies the devlink port. + +.TP +.BI pool " POOL_INDEX" +pool index. + +.TP +.BI th " THRESHOLD" +threshold value. Type of the value is either Bytes or "to_alpha", depends on +.B thtype +set for the pool. + +.SS devlink sb tc bind show - display port-TC to pool bindings and threshold for each + +.I "DEV/PORT_INDEX" +- specifies the devlink port. + +.TP +.BI tc " TC_INDEX" +index of either ingress or egress TC, usually in range 0 to 8 (depends on device). + +.TP +.BR type " { " ingress " | " egress " } " +TC type. + +.SS devlink sb tc bind set - set port-TC to pool binding with specified threshold + +.I "DEV/PORT_INDEX" +- specifies the devlink port. + +.TP +.BI tc " TC_INDEX" +index of either ingress or egress TC, usually in range 0 to 8 (depends on device). + +.TP +.BR type " { " ingress " | " egress " } " +TC type. + +.TP +.BI pool " POOL_INDEX" +index of pool to bind this to. + +.TP +.BI th " THRESHOLD" +threshold value. Type of the value is either Bytes or "to_alpha", depends on +.B thtype +set for the pool. + +.SS devlink sb occupancy show - display shared buffer occupancy values for device or port + +.PP +This command is used to browse shared buffer occupancy values. Values are showed for every port-pool combination as well as for all port-TC combinations (with pool this port-TC is bound to). Format of value is: +.br +.in +16 +current_value/max_value +.in -16 +Note that before showing values, one has to issue +.b occupancy snapshot +command first. + +.PP +.I "DEV" +- specifies the devlink device to show occupancy values for. + +.I "DEV/PORT_INDEX" +- specifies the devlink port to show occupancy values for. + +.SS devlink sb occupancy snapshot - take occupancy snapshot of shared buffer for device +This command is used to take a snapshot of shared buffer occupancy values. After that, the values can be showed using +.B occupancy show +command. + +.PP +.I "DEV" +- specifies the devlink device to take occupancy snapshot on. + +.SS devlink sb occupancy clearmax - clear occupancy watermarks of shared buffer for device +This command is used to reset maximal occupancy values reached for whole device. Note that before browsing reset values, one has to issue +.B occupancy snapshot +command. + +.PP +.I "DEV" +- specifies the devlink device to clear occupancy watermarks on. + +.SH "EXAMPLES" +.PP +devlink sb show +.RS 4 +List available share buffers. +.RE +.PP +devlink sb pool show +.RS 4 +List available pools and their config. +.RE +.PP +devlink sb port pool show pci/0000:03:00.0/1 pool 0 +.RS 4 +Show port-pool setup for specified port and pool. +.RE +.PP +sudo devlink sb port pool set pci/0000:03:00.0/1 pool 0 th 15 +.RS 4 +Change threshold for port specified port and pool. +.RE +.PP +devlink sb tc bind show pci/0000:03:00.0/1 tc 0 type ingress +.RS 4 +Show pool binding and threshold for specified port and TC. +.RE +.PP +sudo devlink sb tc bind set pci/0000:03:00.0/1 tc 0 type ingress pool 0 th 9 +.RS 4 +Set pool binding and threshold for specified port and TC. +.RE +.PP +sudo devlink sb occupancy snapshot pci/0000:03:00.0 +.RS 4 +Make a snapshot of occupancy of shared buffer for specified devlink device. +.RE +.PP +devlink sb occupancy show pci/0000:03:00.0/1 +.RS 4 +Show occupancy for specified port from the snapshot. +.RE +.PP +sudo devlink sb occupancy clearmax pci/0000:03:00.0 +.RS 4 +Clear watermarks for shared buffer of specified devlink device. + + +.SH SEE ALSO +.BR devlink (8), +.BR devlink-dev (8), +.BR devlink-port (8), +.BR devlink-monitor (8), +.br + +.SH AUTHOR +Jiri Pirko diff -Nru iproute2-4.3.0/man/man8/ifcfg.8 iproute2-4.9.0/man/man8/ifcfg.8 --- iproute2-4.3.0/man/man8/ifcfg.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ifcfg.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,6 +1,6 @@ .TH IFCFG 8 "September 24 2009" "iproute2" "Linux" .SH NAME -ifcfg \- simplistic script which replaces ifconfig IP managment +ifcfg \- simplistic script which replaces ifconfig IP management .SH SYNOPSIS .ad l .in +8 diff -Nru iproute2-4.3.0/man/man8/ip.8 iproute2-4.9.0/man/man8/ip.8 --- iproute2-4.3.0/man/man8/ip.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip.8 2016-12-12 23:07:42.000000000 +0000 @@ -12,7 +12,7 @@ .sp .ti -8 -.B ip +.B ip .RB "[ " -force " ] " .BI "-batch " filename .sp @@ -21,7 +21,8 @@ .IR OBJECT " := { " .BR link " | " address " | " addrlabel " | " route " | " rule " | " neigh " | "\ ntable " | " tunnel " | " tuntap " | " maddress " | " mroute " | " mrule " | "\ - monitor " | " xfrm " | " netns " | " l2tp " | " tcp_metrics " }" + monitor " | " xfrm " | " netns " | " l2tp " | " tcp_metrics " | " token " | "\ + macsec " }" .sp .ti -8 @@ -29,10 +30,22 @@ \fB\-V\fR[\fIersion\fR] | \fB\-h\fR[\fIuman-readable\fR] | \fB\-s\fR[\fItatistics\fR] | +\fB\-d\fR[\fIetails\fR] | \fB\-r\fR[\fIesolve\fR] | +\fB\-iec\fR | \fB\-f\fR[\fIamily\fR] { .BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | " +\fB-4\fR | +\fB-6\fR | +\fB-I\fR | +\fB-D\fR | +\fB-B\fR | +\fB-0\fR | +\fB-l\fR[\fIoops\fR] { \fBmaximum-addr-flush-attempts\fR } | \fB\-o\fR[\fIneline\fR] | +\fB\-rc\fR[\fIvbuf\fR] [\fBsize\fR] | +\fB\-t\fR[\fIimestamp\fR] | +\fB\-ts\fR[\fIhort\fR] | \fB\-n\fR[\fIetns\fR] name | \fB\-a\fR[\fIll\fR] | \fB\-c\fR[\fIolor\fR] } @@ -179,6 +192,20 @@ .BR "\-t" , " \-timestamp" display current time when using monitor option. +.TP +.BR "\-ts" , " \-tshort" +Like +.BR \-timestamp , +but use shorter format. + +.TP +.BR "\-rc" , " \-rcvbuf" +Set the netlink socket receive buffer size, defaults to 1MB. + +.TP +.BR "\-iec" +print human readable rates in IEC units (e.g. 1Ki = 1024). + .SH IP - COMMAND SYNTAX .SS @@ -241,6 +268,10 @@ - manage TCP Metrics .TP +.B token +- manage tokenized interface identifiers. + +.TP .B tunnel - tunnel over IP. @@ -305,6 +336,7 @@ .BR ip-route (8), .BR ip-rule (8), .BR ip-tcp_metrics (8), +.BR ip-token (8), .BR ip-tunnel (8), .BR ip-xfrm (8) .br diff -Nru iproute2-4.3.0/man/man8/ip-address.8.in iproute2-4.9.0/man/man8/ip-address.8.in --- iproute2-4.3.0/man/man8/ip-address.8.in 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-address.8.in 2016-12-12 23:07:42.000000000 +0000 @@ -23,7 +23,7 @@ .IB IFADDR " dev " IFNAME " [ " mngtmpaddr " ]" .ti -8 -.BR "ip address" " { " show " | " save " | " flush " } [ " dev +.BR "ip address" " { " save " | " flush " } [ " dev .IR IFNAME " ] [ " .B scope .IR SCOPE-ID " ] [ " @@ -33,6 +33,23 @@ .IR PATTERN " ] [ " up " ]" .ti -8 +.BR "ip address" " [ " show " [ " dev +.IR IFNAME " ] [ " +.B scope +.IR SCOPE-ID " ] [ " +.B to +.IR PREFIX " ] [ " FLAG-LIST " ] [ " +.B label +.IR PATTERN " ] [ " +.B master +.IR DEVICE " ] [ " +.B type +.IR TYPE " ] [ " +.B vrf +.IR NAME " ] [ " +.BR up " ] ]" + +.ti -8 .BR "ip address" " { " showdump " | " restore " }" .ti -8 @@ -58,21 +75,22 @@ .ti -8 .IR FLAG " := " -.RB "[ " permanent " | " dynamic " | " secondary " | " primary " | \ -[ - ] " tentative " | [ - ] " deprecated " | [ - ] " dadfailed " | "\ -temporary " | " CONFFLAG-LIST " ]" +.RB "[ " permanent " | " dynamic " | " secondary " | " primary " |" +.RB [ - ] tentative " | [" - ] deprecated " | [" - ] dadfailed " |" +.BR temporary " |" +.IR CONFFLAG-LIST " ]" .ti -8 .IR CONFFLAG-LIST " := [ " CONFFLAG-LIST " ] " CONFFLAG .ti -8 .IR CONFFLAG " := " -.RB "[ " home " | " mngtmpaddr " | " nodad " | " noprefixroute " ]" +.RB "[ " home " | " mngtmpaddr " | " nodad " | " noprefixroute " | " autojoin " ]" .ti -8 .IR LIFETIME " := [ " .BI valid_lft " LFT" -.RB "| " preferred_lft +.RB "] [ " preferred_lft .IR LFT " ]" .ti -8 @@ -80,6 +98,38 @@ .BR forever " |" .IR SECONDS " ]" +.ti -8 +.IR TYPE " := [ " +.BR bridge " | " +.BR bridge_slave " |" +.BR bond " | " +.BR bond_slave " |" +.BR can " | " +.BR dummy " | " +.BR hsr " | " +.BR ifb " | " +.BR ipoib " |" +.BR macvlan " | " +.BR macvtap " | " +.BR vcan " | " +.BR veth " | " +.BR vlan " | " +.BR vxlan " |" +.BR ip6tnl " |" +.BR ipip " |" +.BR sit " |" +.BR gre " |" +.BR gretap " |" +.BR ip6gre " |" +.BR ip6gretap " |" +.BR vti " |" +.BR vrf " |" +.BR nlmon " |" +.BR ipvlan " |" +.BR lowpan " |" +.BR geneve " |" +.BR macsec " ]" + .SH "DESCRIPTION" The .B address @@ -201,6 +251,26 @@ an address to add this flag will remove the automatically added prefix route, changing it to remove this flag will create the prefix route automatically. +.TP +.B autojoin +Joining multicast groups on Ethernet level via +.B "ip maddr" +command does not work if connected to an Ethernet switch that does IGMP +snooping since the switch would not replicate multicast packets on ports that +did not have IGMP reports for the multicast addresses. + +Linux VXLAN interfaces created via +.B "ip link add vxlan" +have the +.B group +option that enables them to do the required join. + +Using the +.B autojoin +flag when adding a multicast address enables similar functionality for +Openvswitch VXLAN interfaces as well as other tunneling mechanisms that need to +receive multicast traffic. + .SS ip address delete - delete protocol address .B Arguments: coincide with the arguments of @@ -230,6 +300,24 @@ is a usual shell style pattern. .TP +.BI master " DEVICE" +only list interfaces enslaved to this master device. + +.TP +.BI vrf " NAME " +only list interfaces enslaved to this vrf. + +.TP +.BI type " TYPE" +only list interfaces of the given type. + +Note that the type name is not checked against the list of supported types - +instead it is sent as-is to the kernel. Later it is used to filter the returned +interface list by comparing it with the relevant attribute in case the kernel +didn't filter already. Therefore any string is accepted, but may lead to empty +output. + +.TP .B up only list running interfaces. @@ -280,8 +368,8 @@ .PP This command has the same arguments as -.B show. -The difference is that it does not run when no arguments are given. +.BR show " except that " type " and " master " selectors are not supported." +Another difference is that it does not run when no arguments are given. .PP .B Warning: diff -Nru iproute2-4.3.0/man/man8/ip-addrlabel.8 iproute2-4.9.0/man/man8/ip-addrlabel.8 --- iproute2-4.3.0/man/man8/ip-addrlabel.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-addrlabel.8 2016-12-12 23:07:42.000000000 +0000 @@ -6,21 +6,9 @@ .ad l .in +8 .ti -8 -.B ip -.RI "[ " OPTIONS " ]" -.B addrlabel +.B ip addrlabel .RI " { " COMMAND " | " .BR help " }" -.sp - -.ti -8 -.IR OPTIONS " := { " -\fB\-V\fR[\fIersion\fR] | -\fB\-s\fR[\fItatistics\fR] | -\fB\-r\fR[\fIesolve\fR] | -\fB\-f\fR[\fIamily\fR] { -.BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | " -\fB\-o\fR[\fIneline\fR] } .ti -8 .BR "ip addrlabel" " { " add " | " del " } " prefix @@ -66,4 +54,3 @@ .SH AUTHOR Manpage by Yoshifuji Hideaki / 吉藤英明 - diff -Nru iproute2-4.3.0/man/man8/ip-fou.8 iproute2-4.9.0/man/man8/ip-fou.8 --- iproute2-4.3.0/man/man8/ip-fou.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-fou.8 2016-12-12 23:07:42.000000000 +0000 @@ -56,7 +56,7 @@ .PP .SS Configure a FOU receive port for GRE bound to 7777 .nf -# ip fou add port 8888 ipproto 47 +# ip fou add port 7777 ipproto 47 .PP .SS Configure a FOU receive port for IPIP bound to 8888 .nf diff -Nru iproute2-4.3.0/man/man8/ip-l2tp.8 iproute2-4.9.0/man/man8/ip-l2tp.8 --- iproute2-4.3.0/man/man8/ip-l2tp.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-l2tp.8 2016-12-12 23:07:42.000000000 +0000 @@ -15,10 +15,7 @@ .ti -8 .BR "ip l2tp add tunnel" .br -.B remote -.RI "[ " ADDR " ]" -.B local -.RI "[ " ADDR " ]" +.BI remote " ADDR " local " ADDR " .br .B tunnel_id .IR ID @@ -33,6 +30,12 @@ .IR PORT .RB " ]" .br +.RB "[ " udp_csum " { " on " | " off " } ]" +.br +.RB "[ " udp6_csum_tx " { " on " | " off " } ]" +.br +.RB "[ " udp6_csum_rx " { " on " | " off " } ]" +.br .ti -8 .BR "ip l2tp add session" .RB "[ " name @@ -54,6 +57,8 @@ .br .RB "[ " l2spec_type " { " none " | " default " } ]" .br +.RB "[ " seq " { " none " | " send " | " recv " | " both " } ]" +.br .RB "[ " offset .IR OFFSET .RB " ] [ " peer_offset @@ -73,24 +78,21 @@ .IR ID .br .ti -8 -.BR "ip l2tp show tunnel" -.B "[" tunnel_id -.IR ID -.B "]" +.BR "ip l2tp show tunnel" " [ " tunnel_id +.IR ID " ]" .br .ti -8 -.BR "ip l2tp show session" -.B "[" tunnel_id -.IR ID -.B "] [" session_id -.IR ID -.B "]" +.BR "ip l2tp show session" " [ " tunnel_id +.IR ID .B " ] [" +.B session_id +.IR ID " ]" .br .ti -8 .IR NAME " := " .IR STRING .ti -8 -.IR ADDR " := { " IP_ADDRESS " }" +.IR ADDR " := { " IP_ADDRESS " |" +.BR any " }" .ti -8 .IR PORT " := { " NUMBER " }" .ti -8 @@ -160,9 +162,6 @@ acting upon network failures. .SS ip l2tp add tunnel - add a new tunnel .TP -.BI name " NAME " -sets the session network interface name. Default is l2tpethN. -.TP .BI tunnel_id " ID" set the tunnel id, which is a 32-bit integer value. Uniquely identifies the tunnel. The value used must match the peer_tunnel_id @@ -197,6 +196,33 @@ set the UDP destination port to be used for the tunnel. Must be present when udp encapsulation is selected. Ignored when ip encapsulation is selected. +.TP +.BI udp_csum " STATE" +(IPv4 only) control if IPv4 UDP checksums should be calculated and checked for the +encapsulating UDP packets, when UDP encapsulating is selected. +Default is +.BR off "." +.br +Valid values are: +.BR on ", " off "." +.TP +.BI udp6_csum_tx " STATE" +(IPv6 only) control if IPv6 UDP checksums should be calculated for encapsulating +UDP packets, when UDP encapsulating is selected. +Default is +.BR on "." +.br +Valid values are: +.BR on ", " off "." +.TP +.BI udp6_csum_rx " STATE" +(IPv6 only) control if IPv6 UDP checksums should be checked for the encapsulating +UDP packets, when UDP encapsulating is selected. +Default is +.BR on "." +.br +Valid values are: +.BR on ", " off "." .SS ip l2tp del tunnel - destroy a tunnel .TP .BI tunnel_id " ID" @@ -245,7 +271,20 @@ set the layer2specific header type of the session. .br Valid values are: -.BR none ", " udp "." +.BR none ", " default "." +.TP +.BI seq " SEQ" +controls sequence numbering to prevent or detect out of order packets. +.B send +puts a sequence number in the default layer2specific header of each +outgoing packet. +.B recv +reorder packets if they are received out of order. +Default is +.BR none "." +.br +Valid values are: +.BR none ", " send ", " recv ", " both "." .TP .BI offset " OFFSET" sets the byte offset from the L2TP header where user data starts in diff -Nru iproute2-4.3.0/man/man8/ip-link.8.in iproute2-4.9.0/man/man8/ip-link.8.in --- iproute2-4.3.0/man/man8/ip-link.8.in 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-link.8.in 2016-12-12 23:07:42.000000000 +0000 @@ -6,25 +6,12 @@ .ad l .in +8 .ti -8 -.B ip -.RI "[ " OPTIONS " ]" -.B link +.B ip link .RI " { " COMMAND " | " .BR help " }" .sp .ti -8 -.IR OPTIONS " := { " -\fB\-V\fR[\fIersion\fR] | -\fB\-h\fR[\fIuman-readable\fR] | -\fB\-s\fR[\fItatistics\fR] | -\fB\-r\fR[\fIesolve\fR] | -\fB\-f\fR[\fIamily\fR] { -.BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | " -\fB\-o\fR[\fIneline\fR] | -\fB\-br\fR[\fIief\fR] } - -.ti -8 .BI "ip link add" .RB "[ " link .IR DEVICE " ]" @@ -49,38 +36,10 @@ .RB "[ " numrxqueues .IR QUEUE_COUNT " ]" .br -.BR type " TYPE" +.BI type " TYPE" .RI "[ " ARGS " ]" .ti -8 -.IR TYPE " := [ " -.BR bridge " | " -.BR bond " | " -.BR can " | " -.BR dummy " | " -.BR hsr " | " -.BR ifb " | " -.BR ipoib " |" -.BR macvlan " | " -.BR macvtap " | " -.BR vcan " | " -.BR veth " | " -.BR vlan " | " -.BR vxlan " |" -.BR ip6tnl " |" -.BR ipip " |" -.BR sit " |" -.BR gre " |" -.BR gretap " |" -.BR ip6gre " |" -.BR ip6gretap " |" -.BR vti " |" -.BR nlmon " |" -.BR ipvlan " |" -.BR lowpan " |" -.BR geneve " ]" - -.ti -8 .BR "ip link delete " { .IR DEVICE " | " .BI "group " GROUP @@ -92,85 +51,160 @@ .BR "ip link set " { .IR DEVICE " | " .BI "group " GROUP -.RB "} { " up " | " down " | " arp " { " on " | " off " } |" +} +.br +.RB "[ { " up " | " down " } ]" +.br +.RB "[ " type +.IR "ETYPE TYPE_ARGS" " ]" .br -.BR promisc " { " on " | " off " } |" +.RB "[ " arp " { " on " | " off " } ]" .br -.BR allmulticast " { " on " | " off " } |" +.RB "[ " dynamic " { " on " | " off " } ]" .br -.BR dynamic " { " on " | " off " } |" +.RB "[ " multicast " { " on " | " off " } ]" .br -.BR multicast " { " on " | " off " } |" +.RB "[ " allmulticast " { " on " | " off " } ]" .br -.BR protodown " { " on " | " off " } |" +.RB "[ " promisc " { " on " | " off " } ]" .br -.B txqueuelen -.IR PACKETS " |" +.RB "[ " protodown " { " on " | " off " } ]" .br -.B name -.IR NEWNAME " |" +.RB "[ " trailers " { " on " | " off " } ]" .br -.B address -.IR LLADDR " |" -.B broadcast -.IR LLADDR " |" +.RB "[ " txqueuelen +.IR PACKETS " ]" .br -.B mtu -.IR MTU " |" +.RB "[ " name +.IR NEWNAME " ]" .br -.B netns -.IR PID " |" +.RB "[ " address +.IR LLADDR " ]" .br -.B netns -.IR NETNSNAME " |" +.RB "[ " broadcast +.IR LLADDR " ]" .br -.B alias -.IR NAME " |" +.RB "[ " mtu +.IR MTU " ]" .br -.B vf +.RB "[ " netns " {" +.IR PID " | " NETNSNAME " } ]" +.br +.RB "[ " link-netnsid +.IR ID " ]" +.br +.RB "[ " alias +.IR NAME " ]" +.br +.RB "[ " vf .IR NUM " [" .B mac -.IR LLADDR " ] [" -.B vlan -.IR VLANID " [ " -.B qos -.IR VLAN-QOS " ] ] [" -.B rate -.IR TXRATE " ] [" -.B max_tx_rate -.IR TXRATE " ] [" -.B min_tx_rate -.IR TXRATE " ] [" -.B spoofchk { on | off } ] [ -.B state { auto | enable | disable} -] | +.IR LLADDR " ]" .br -.B master -.IR DEVICE " |" +.in +9 +.RI "[ " VFVLAN-LIST " ]" .br -.B nomaster " |" +.RB "[ " rate +.IR TXRATE " ]" .br -.B addrgenmode { eui64 | none } +.RB "[ " max_tx_rate +.IR TXRATE " ]" +.br +.RB "[ " min_tx_rate +.IR TXRATE " ]" +.br +.RB "[ " spoofchk " { " on " | " off " } ]" +.br +.RB "[ " query_rss " { " on " | " off " } ]" +.br +.RB "[ " state " { " auto " | " enable " | " disable " } ]" +.br +.RB "[ " trust " { " on " | " off " } ]" +.br +.RB "[ " node_guid " eui64 ]" +.br +.RB "[ " port_guid " eui64 ] ]" +.br +.in -9 +.RB "[ " master +.IR DEVICE " ]" +.br +.RB "[ " nomaster " ]" +.br +.RB "[ " vrf +.IR NAME " ]" +.br +.RB "[ " addrgenmode " { " eui64 " | " none " | " stable_secret " | " random " } ]" +.br +.RB "[ " macaddr " { " flush " | { " add " | " del " } " +.IR MACADDR " | set [ " +.IR MACADDR " [ " +.IR MACADDR " [ ... ] ] ] } ]" .br -.B link-netnsid ID -.BR " }" - .ti -8 .B ip link show .RI "[ " DEVICE " | " .B group -.IR GROUP " | " -.BR up " | " +.IR GROUP " ] [" +.BR up " ] [" .B master -.IR DEVICE " | " +.IR DEVICE " ] [" .B type -.IR TYPE " ]" +.IR ETYPE " ]" +.B vrf +.IR NAME " ]" .ti -8 .B ip link help .RI "[ " TYPE " ]" +.ti -8 +.IR TYPE " := [ " +.BR bridge " | " +.BR bond " | " +.BR can " | " +.BR dummy " | " +.BR hsr " | " +.BR ifb " | " +.BR ipoib " |" +.BR macvlan " | " +.BR macvtap " | " +.BR vcan " | " +.BR veth " | " +.BR vlan " | " +.BR vxlan " |" +.BR ip6tnl " |" +.BR ipip " |" +.BR sit " |" +.BR gre " |" +.BR gretap " |" +.BR ip6gre " |" +.BR ip6gretap " |" +.BR vti " |" +.BR nlmon " |" +.BR ipvlan " |" +.BR lowpan " |" +.BR geneve " |" +.BR vrf " |" +.BR macsec " ]" + +.ti -8 +.IR ETYPE " := [ " TYPE " |" +.BR bridge_slave " | " bond_slave " ]" + +.ti -8 +.IR VFVLAN-LIST " := [ " VFVLAN-LIST " ] " VFVLAN + +.ti -8 +.IR VFVLAN " := " +.RB "[ " vlan +.IR VLANID " [ " +.B qos +.IR VLAN-QOS " ] [" +.B proto +.IR VLAN-PROTO " ] ]" + .SH "DESCRIPTION" .SS ip link add - add virtual link @@ -260,6 +294,12 @@ .sp .BR geneve - GEneric NEtwork Virtualization Encapsulation +.sp +.BR macsec +- Interface for IEEE 802.1AE MAC Security (MACsec) +.sp +.BR vrf +- Interface for L3 VRF domains .in -8 .TP @@ -283,29 +323,29 @@ .BI "ip link add .BI link " DEVICE " .BI name " NAME " -.BI type " vlan " -.R " [ " +.B "type vlan" +[ .BI protocol " VLAN_PROTO " -.R " ] " +] .BI id " VLANID " -.R " [ " +[ .BR reorder_hdr " { " on " | " off " } " -.R " ] " -.R " [ " +] +[ .BR gvrp " { " on " | " off " } " -.R " ] " -.R " [ " +] +[ .BR mvrp " { " on " | " off " } " -.R " ] " -.R " [ " +] +[ .BR loose_binding " { " on " | " off " } " -.R " ] " -.R " [ " +] +[ .BI ingress-qos-map " QOS-MAP " -.R " ] " -.R " [ " +] +[ .BI egress-qos-map " QOS-MAP " -.R " ] " +] .in +8 .sp @@ -385,44 +425,51 @@ the following additional arguments are supported: .BI "ip link add " DEVICE -.BI type " vxlan " id " ID" -.R " [ " +.BI type " vxlan " id " VNI" +[ .BI dev " PHYS_DEV " .RB " ] [ { " group " | " remote " } " .I IPADDR -.R " ] [ " -.BI local " IPADDR " -.R " ] [ " +] [ +.B local +.RI "{ "IPADDR " | "any " } " +] [ .BI ttl " TTL " -.R " ] [ " +] [ .BI tos " TOS " -.R " ] [ " +] [ +.BI flowlabel " FLOWLABEL " +] [ .BI dstport " PORT " -.R " ] [ " +] [ .BI srcport " MIN MAX " -.R " ] [ " -.I "[no]learning " -.R " ] [ " -.I "[no]proxy " -.R " ] [ " -.I "[no]rsc " -.R " ] [ " -.I "[no]l2miss " -.R " ] [ " -.I "[no]l3miss " -.R " ] [ " -.I "[no]udpcsum " -.R " ] [ " -.I "[no]udp6zerocsumtx " -.R " ] [ " -.I "[no]udp6zerocsumrx " -.R " ] [ " +] [ +.RB [ no ] learning +] [ +.RB [ no ] proxy +] [ +.RB [ no ] rsc +] [ +.RB [ no ] l2miss +] [ +.RB [ no ] l3miss +] [ +.RB [ no ] udpcsum +] [ +.RB [ no ] udp6zerocsumtx +] [ +.RB [ no ] udp6zerocsumrx +] [ .BI ageing " SECONDS " -.R " ] [ " +] [ .BI maxaddress " NUMBER " -.R " ] [ " +] [ +.RB [ no ] external +] [ .B gbp -.R " ]" +] [ +.B gpe +] .in +8 .sp @@ -461,6 +508,10 @@ - specifies the TOS value to use in outgoing packets. .sp +.BI flowlabel " FLOWLABEL" +- specifies the flow label to use in outgoing packets. + +.sp .BI dstport " PORT" - specifies the UDP destination port to communicate to the remote VXLAN tunnel endpoint. @@ -470,37 +521,37 @@ source ports to communicate to the remote VXLAN tunnel endpoint. .sp -.I [no]learning +.RB [ no ] learning - specifies if unknown source link layer addresses and IP addresses are entered into the VXLAN device forwarding database. .sp -.I [no]rsc +.RB [ no ] rsc - specifies if route short circuit is turned on. .sp -.I [no]proxy +.RB [ no ] proxy - specifies ARP proxy is turned on. .sp -.I [no]l2miss +.RB [ no ] l2miss - specifies if netlink LLADDR miss notifications are generated. .sp -.I [no]l3miss +.RB [ no ] l3miss - specifies if netlink IP ADDR miss notifications are generated. .sp -.I [no]udpcsum -- specifies if UDP checksum is filled in +.RB [ no ] udpcsum +- specifies if UDP checksum is calculated for transmitted packets over IPv4. .sp -.I [no]udp6zerocsumtx -- specifies if UDP checksum is filled in +.RB [ no ] udp6zerocsumtx +- skip UDP checksum calculation for transmitted packets over IPv6. .sp -.I [no]udp6zerocsumrx -- specifies if UDP checksum is received +.RB [ no ] udp6zerocsumrx +- allow incoming UDP packets over IPv6 with zero checksum field. .sp .BI ageing " SECONDS" @@ -511,6 +562,12 @@ - specifies the maximum number of FDB entries. .sp +.RB [ no ] external +- specifies whether an external control plane +.RB "(e.g. " "ip route encap" ) +or the internal FDB should be used. + +.sp .B gbp - enables the Group Policy extension (VXLAN-GBP). @@ -553,6 +610,13 @@ .in -4 +.sp +.B gpe +- enables the Generic Protocol extension (VXLAN-GPE). Currently, this is +only supported together with the +.B external +keyword. + .in -8 .TP @@ -562,19 +626,19 @@ the following additional arguments are supported: .BI "ip link add " DEVICE -.BR type " { gre | ipip | sit } " +.BR type " { " gre " | " ipip " | " sit " }" .BI " remote " ADDR " local " ADDR -.R " [ " -.BR encap " { fou | gue | none } " -.R " ] [ " -.BI "encap-sport { " PORT " | auto } " -.R " ] [ " +[ +.BR encap " { " fou " | " gue " | " none " }" +] [ +.BR encap-sport " { " \fIPORT " | " auto " }" +] [ .BI "encap-dport " PORT -.R " ] [ " -.I " [no]encap-csum " -.R " ] [ " -.I " [no]encap-remcsum " -.R " ]" +] [ +.RB [ no ] encap-csum +] [ +.RB [ no ] encap-remcsum +] .in +8 .sp @@ -587,12 +651,12 @@ It must be an address on another interface on this host. .sp -.BR encap " { fou | gue | none } " +.BR encap " { " fou " | " gue " | " none " }" - specifies type of secondary UDP encapsulation. "fou" indicates Foo-Over-UDP, "gue" indicates Generic UDP Encapsulation. .sp -.BI "encap-sport { " PORT " | auto } " +.BR encap-sport " { " \fIPORT " | " auto " }" - specifies the source port in UDP encapsulation. .IR PORT indicates the port by number, "auto" @@ -601,12 +665,12 @@ encapsulated packet). .sp -.I [no]encap-csum +.RB [ no ] encap-csum - specifies if UDP checksums are enabled in the secondary encapsulation. .sp -.I [no]encap-remcsum +.RB [ no ] encap-remcsum - specifies if Remote Checksum Offload is enabled. This is only applicable for Generic UDP Encapsulation. @@ -619,26 +683,28 @@ the following additional arguments are supported: .BI "ip link add " DEVICE -.BI type " { ip6gre | ip6gretap } " remote " ADDR " local " ADDR -.R " [ " -.I "[i|o]seq]" -.R " ] [ " -.I "[i|o]key" KEY -.R " ] [ " -.I " [i|o]csum " -.R " ] [ " +.BR type " { " ip6gre " | " ip6gretap " }" +.BI remote " ADDR " local " ADDR" +[ +.RB [ i | o ] seq +] [ +.RB [ i | o ] key +.I KEY +] [ +.RB [ i | o ] csum +] [ .BI hoplimit " TTL " -.R " ] [ " +] [ .BI encaplimit " ELIM " -.R " ] [ " +] [ .BI tclass " TCLASS " -.R " ] [ " +] [ .BI flowlabel " FLOWLABEL " -.R " ] [ " +] [ .BI "dscp inherit" -.R " ] [ " +] [ .BI dev " PHYS_DEV " -.R " ]" +] .in +8 .sp @@ -651,7 +717,7 @@ It must be an address on another interface on this host. .sp -.BI [i|o]seq +.RB [ i | o ] seq - serialize packets. The .B oseq @@ -661,7 +727,7 @@ flag requires that all input packets are serialized. .sp -.BI [i|o]key " KEY" +.RB [ i | o ] key " \fIKEY" - use keyed GRE with key .IR KEY ". "KEY is either a number or an IPv4 address-like dotted quad. @@ -673,7 +739,7 @@ parameters specify different keys for input and output. .sp -.BI [i|o]csum +.RB [ i | o ] csum - generate/require checksums for tunneled packets. The .B ocsum @@ -725,7 +791,7 @@ the following additional arguments are supported: .BI "ip link add " DEVICE " name " NAME -.BI type " ipoib [ " pkey " PKEY ] [" mode " MODE " ] +.BR "type ipoib " [ " pkey \fIPKEY" " ] [ " mode " \fIMODE \fR]" .in +8 .sp @@ -742,12 +808,24 @@ the following additional arguments are supported: .BI "ip link add " DEVICE -.BI type " geneve " id " ID " remote " IPADDR" -.R " [ " +.BI type " geneve " id " VNI " remote " IPADDR" +[ .BI ttl " TTL " -.R " ] [ " +] [ .BI tos " TOS " -.R " ]" +] [ +.BI flowlabel " FLOWLABEL " +] [ +.BI dstport " PORT" +] [ +.RB [ no ] external +] [ +.RB [ no ] udpcsum +] [ +.RB [ no ] udp6zerocsumtx +] [ +.RB [ no ] udp6zerocsumrx +] .in +8 .sp @@ -766,6 +844,36 @@ .BI tos " TOS" - specifies the TOS value to use in outgoing packets. +.sp +.BI flowlabel " FLOWLABEL" +- specifies the flow label to use in outgoing packets. + +.sp +.BI dstport " PORT" +- select a destination port other than the default of 6081. + +.sp +.RB [ no ] external +- make this tunnel externally controlled (or not, which is the default). This +flag is mutually exclusive with the +.BR id , +.BR remote , +.BR ttl , +.BR tos " and " flowlabel +options. + +.sp +.RB [ no ] udpcsum +- specifies if UDP checksum is calculated for transmitted packets over IPv4. + +.sp +.RB [ no ] udp6zerocsumtx +- skip UDP checksum calculation for transmitted packets over IPv6. + +.sp +.RB [ no ] udp6zerocsumrx +- allow incoming UDP packets over IPv6 with zero checksum field. + .in -8 .TP @@ -779,7 +887,7 @@ .BI "ip link add link " DEVICE " name " NAME .BR type " { " macvlan " | " macvtap " } " .BR mode " { " private " | " vepa " | " bridge " | " passthru -.BR " [ " nopromisc " ] } " +.RB " [ " nopromisc " ] | " source " } " .in +8 .sp @@ -816,6 +924,158 @@ forces the underlying interface into promiscuous mode. Passing the .BR nopromisc " flag prevents this, so the promisc flag may be controlled " using standard tools. + +.B mode source +- allows one to set a list of allowed mac address, which is used to match +against source mac address from received frames on underlying interface. This +allows creating mac based VLAN associations, instead of standard port or tag +based. The feature is useful to deploy 802.1x mac based behavior, +where drivers of underlying interfaces doesn't allows that. +.in -8 + +.TP +High-availability Seamless Redundancy (HSR) Support +For a link of type +.I HSR +the following additional arguments are supported: + +.BI "ip link add link " DEVICE " name " NAME " type hsr" +.BI slave1 " SLAVE1-IF " slave2 " SLAVE2-IF " +.RB [ " supervision" +.IR ADDR-BYTE " ] [" +.BR version " { " 0 " | " 1 " } ]" + +.in +8 +.sp +.BR type " hsr " +- specifies the link type to use, here HSR. + +.BI slave1 " SLAVE1-IF " +- Specifies the physical device used for the first of the two ring ports. + +.BI slave2 " SLAVE2-IF " +- Specifies the physical device used for the second of the two ring ports. + +.BI supervision " ADDR-BYTE" +- The last byte of the multicast address used for HSR supervision frames. +Default option is "0", possible values 0-255. + +.BR version " { " 0 " | " 1 " }" +- Selects the protocol version of the interface. Default option is "0", which +corresponds to the 2010 version of the HSR standard. Option "1" activates the +2012 version. +.in -8 + +.TP +MACsec Type Support +For a link of type +.I MACsec +the following additional arguments are supported: + +.BI "ip link add link " DEVICE " name " NAME " type macsec" +[ [ +.BI address " " +] +.BI port " PORT" +| +.BI sci " SCI" +] [ +.BI cipher " CIPHER_SUITE" +] [ +.BR icvlen " { " +.IR 8..16 " } ] [" +.BR encrypt " {" +.BR on " | " off " } ] [ " +.BR send_sci " { " on " | " off " } ] [" +.BR end_station " { " on " | " off " } ] [" +.BR scb " { " on " | " off " } ] [" +.BR protect " { " on " | " off " } ] [" +.BR replay " { " on " | " off " }" +.BR window " { " +.IR 0..2^32-1 " } ] [" +.BR validate " { " strict " | " check " | " disabled " } ] [" +.BR encodingsa " { " +.IR 0..3 " } ]" + +.in +8 +.sp +.BI address " " +- sets the system identifier component of secure channel for this MACsec device. + +.sp +.BI port " PORT " +- sets the port number component of secure channel for this MACsec device, in a +range from 1 to 65535 inclusive. Numbers with a leading " 0 " or " 0x " are +interpreted as octal and hexadecimal, respectively. + +.sp +.BI sci " SCI " +- sets the secure channel identifier for this MACsec device. +.I SCI +is a 64bit wide number in hexadecimal format. + +.sp +.BI cipher " CIPHER_SUITE " +- defines the cipher suite to use. + +.sp +.BI icvlen " LENGTH " +- sets the length of the Integrity Check Value (ICV). + +.sp +.BR "encrypt on " or " encrypt off" +- switches between authenticated encryption, or authenticity mode only. + +.sp +.BR "send_sci on " or " send_sci off" +- specifies whether the SCI is included in every packet, or only when it is necessary. + +.sp +.BR "end_station on " or " end_station off" +- sets the End Station bit. + +.sp +.BR "scb on " or " scb off" +- sets the Single Copy Broadcast bit. + +.sp +.BR "protect on " or " protect off" +- enables MACsec protection on the device. + +.sp +.BR "replay on " or " replay off" +- enables replay protection on the device. + +.in +8 + +.sp +.BI window " SIZE " +- sets the size of the replay window. + +.in -8 + +.sp +.BR "validate strict " or " validate check " or " validate disabled" +- sets the validation mode on the device. + +.sp +.BI encodingsa " AN " +- sets the active secure association for transmission. + +.in -8 + +.TP +VRF Type Support +For a link of type +.I VRF +the following additional arguments are supported: + +.BI "ip link add " DEVICE " type vrf table " TABLE + +.in +8 +.sp +.BR table " table id associated with VRF device" + .in -8 .SS ip link delete - delete virtual link @@ -835,6 +1095,18 @@ .SS ip link set - change device attributes +.PP +.B Warning: +If multiple parameter changes are requested, +.B ip +aborts immediately after any of the changes have failed. +This is the only case when +.B ip +can move the system to an unpredictable state. The solution +is to avoid changing several parameters with one +.B ip link set +call. + .TP .BI dev " DEVICE " .I DEVICE @@ -987,6 +1259,19 @@ as 0 disables VLAN tagging and filtering for the VF. .sp +.BI proto " VLAN-PROTO" +- assign VLAN PROTOCOL for the VLAN tag, either 802.1Q or 802.1ad. +Setting to 802.1ad, all traffic sent from the VF will be tagged with VLAN S-Tag. +Incoming traffic will have VLAN S-Tags stripped before being passed to the VF. +Setting to 802.1ad also enables an option to concatenate another VLAN tag, so both +S-TAG and C-TAG will be inserted/stripped for outgoing/incoming traffic, respectively. +If not specified, the value is assumed to be 802.1Q. Both the +.B vf +and +.B vlan +parameters must be specified. + +.sp .BI rate " TXRATE" -- change the allowed transmit bandwidth, in Mbps, for the specified VF. Setting this parameter to 0 disables rate limiting. @@ -1013,11 +1298,24 @@ .BI spoofchk " on|off" - turn packet spoof checking on or off for the specified VF. .sp +.BI query_rss " on|off" +- toggle the ability of querying the RSS configuration of a specific VF. VF RSS information like RSS hash key may be considered sensitive on some devices where this information is shared between VF and PF and thus its querying may be prohibited by default. +.sp .BI state " auto|enable|disable" - set the virtual link state as seen by the specified VF. Setting to auto means a reflection of the PF link state, enable lets the VF to communicate with other VFs on this host even if the PF link state is down, disable causes the HW to drop any packets sent by the VF. +.sp +.BI trust " on|off" +- trust the specified VF user. This enables that VF user can set a specific feature +which may impact security and/or performance. (e.g. VF multicast promiscuous mode) +.sp +.BI node_guid " eui64" +- configure node GUID for the VF. +.sp +.BI port_guid " eui64" +- configure port GUID for the VF. .in -8 .TP @@ -1029,24 +1327,184 @@ unset master device of the device (release device). .TP -.BR "addrgenmode eui64 " or " addrgenmode none" -set IPv6 address generation mode +.BI addrgenmode " eui64|none|stable_secret|random" +set the IPv6 address generation mode + +.I eui64 +- use a Modified EUI-64 format interface identifier + +.I none +- disable automatic address generation + +.I stable_secret +- generate the interface identifier based on a preset /proc/sys/net/ipv6/conf/{default,DEVICE}/stable_secret + +.I random +- like stable_secret, but auto-generate a new random secret if none is set .TP .BR "link-netnsid " set peer netnsid for a cross-netns interface -.PP -.B Warning: -If multiple parameter changes are requested, -.B ip -aborts immediately after any of the changes have failed. -This is the only case when -.B ip -can move the system to an unpredictable state. The solution -is to avoid changing several parameters with one -.B ip link set -call. +.TP +.BI type " ETYPE TYPE_ARGS" +Change type-specific settings. For a list of supported types and arguments refer +to the description of +.B "ip link add" +above. In addition to that, it is possible to manipulate settings to slave +devices: + +.TP +Bridge Slave Support +For a link with master +.B bridge +the following additional arguments are supported: + +.B "ip link set type bridge_slave" +[ +.BI state " STATE" +] [ +.BI priority " PRIO" +] [ +.BI cost " COST" +] [ +.BR guard " { " on " | " off " }" +] [ +.BR hairpin " { " on " | " off " }" +] [ +.BR fastleave " { " on " | " off " }" +] [ +.BR root_block " { " on " | " off " }" +] [ +.BR learning " { " on " | " off " }" +] [ +.BR flood " { " on " | " off " }" +] [ +.BR proxy_arp " { " on " | " off " }" +] [ +.BR proxy_arp_wifi " { " on " | " off " }" +] [ +.BI mcast_router " MULTICAST_ROUTER" +] [ +.BR mcast_fast_leave " { " on " | " off "}" +] [ +.BR mcast_flood " { " on " | " off " } ]" + +.in +8 +.sp +.BI state " STATE" +- Set port state. +.I STATE +is a number representing the following states: +.BR 0 " (disabled)," +.BR 1 " (listening)," +.BR 2 " (learning)," +.BR 3 " (forwarding)," +.BR 4 " (blocking)." + +.BI priority " PRIO" +- set port priority (a 16bit unsigned value). + +.BI cost " COST" +- set port cost (a 32bit unsigned value). + +.BR guard " { " on " | " off " }" +- block incoming BPDU packets on this port. + +.BR hairpin " { " on " | " off " }" +- enable hairpin mode on this port. This will allow incoming packets on this +port to be reflected back. + +.BR fastleave " { " on " | " off " }" +- enable multicast fast leave on this port. + +.BR root_block " { " on " | " off " }" +- block this port from becoming the bridge's root port. + +.BR learning " { " on " | " off " }" +- allow MAC address learning on this port. + +.BR flood " { " on " | " off " }" +- open the flood gates on this port, i.e. forward all unicast frames to this +port also. Requires +.BR proxy_arp " and " proxy_arp_wifi +to be turned off. + +.BR proxy_arp " { " on " | " off " }" +- enable proxy ARP on this port. + +.BR proxy_arp_wifi " { " on " | " off " }" +- enable proxy ARP on this port which meets extended requirements by IEEE +802.11 and Hotspot 2.0 specifications. + +.BI mcast_router " MULTICAST_ROUTER" +- configure this port for having multicast routers attached. A port with a +multicast router will receive all multicast traffic. +.I MULTICAST_ROUTER +may be either +.B 0 +to disable multicast routers on this port, +.B 1 +to let the system detect the presence of of routers (this is the default), +.B 2 +to permanently enable multicast traffic forwarding on this port or +.B 3 +to enable multicast routers temporarily on this port, not depending on incoming +queries. + +.BR mcast_fast_leave " { " on " | " off " }" +- this is a synonym to the +.B fastleave +option above. + +.BR mcast_flood " { " on " | " off " }" +- controls whether a given port will be flooded with multicast traffic for which there is no MDB entry. + +.in -8 + +.TP +Bonding Slave Support +For a link with master +.B bond +the following additional arguments are supported: + +.B "ip link set type bond_slave" +[ +.BI queue_id " ID" +] + +.in +8 +.sp +.BI queue_id " ID" +- set the slave's queue ID (a 16bit unsigned value). + +.in -8 + +.TP +MACVLAN and MACVTAP Support +Modify list of allowed macaddr for link in source mode. + +.B "ip link set type { macvlan | macvap } " +[ +.BI macaddr " " "" COMMAND " " MACADDR " ..." +] + +Commands: +.in +8 +.B add +- add MACADDR to allowed list +.sp +.B set +- replace allowed list +.sp +.B del +- remove MACADDR from allowed list +.sp +.B flush +- flush whole allowed list +.sp +.in -8 + .SS ip link show - display device attributes @@ -1071,30 +1529,20 @@ specifies the master device which enslaves devices to show. .TP +.BI vrf " NAME " +.I NAME +speficies the VRF which enslaves devices to show. + +.TP .BI type " TYPE " .I TYPE specifies the type of devices to show. -.TP -The show command has additional formatting options: - -.RS -.TP -.BR "\-s" , " \-stats", " \-statistics" -output more statistics about packet usage. - -.TP -.BR "\-d", " \-details" -output more detailed information. - -.TP -.BR "\-h", " \-human", " \-human-readable" -output statistics with human readable values number followed by suffix - -.TP -.BR "\-iec" -print human readable rates in IEC units (ie. 1K = 1024). -.RE +Note that the type name is not checked against the list of supported types - +instead it is sent as-is to the kernel. Later it is used to filter the returned +interface list by comparing it with the relevant attribute in case the kernel +didn't filter already. Therefore any string is accepted, but may lead to empty +output. .SS ip link help - display help diff -Nru iproute2-4.3.0/man/man8/ip-macsec.8 iproute2-4.9.0/man/man8/ip-macsec.8 --- iproute2-4.3.0/man/man8/ip-macsec.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-macsec.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,109 @@ +.TH IP\-MACSEC 8 "07 Mar 2016" "iproute" "Linux" +.SH NAME +ip-macsec \- MACsec device configuration +.SH "SYNOPSIS" +.BI "ip link add link " DEVICE " name " NAME " type macsec " +[ [ +.BI address " " +] +.BI port " PORT" +| +.BI sci " " +] [ +.BR cipher " { " default " | " gcm-aes-128 " } ] [" +.BI icvlen " ICVLEN" +] [ +.BR encrypt " { " on " | " off " } ] [" +.BR send_sci " { " on " | " off " } ] [" +.BR end_station " { " on " | " off " } ] [" +.BR scb " { " on " | " off " } ] [" +.BR protect " { " on " | " off " } ] [" +.BR replay " { " on " | " off " } ] [" +.BI window " WINDOW" +] [ +.BR validate " { " strict " | " check " | " disabled " } ] [" +.BI encodingsa " SA" +] + +.BI "ip macsec add " DEV " tx sa" +.RI "{ " 0..3 " } [ " OPTS " ]" +.BI key " ID KEY" +.br +.BI "ip macsec set " DEV " tx sa" +.RI "{ " 0..3 " } [ " OPTS " ]" +.br +.BI "ip macsec del " DEV " tx sa" +.RI "{ " 0..3 " }" + +.BI "ip macsec add " DEV " rx " SCI +.RB [ " on " | " off " ] +.br +.BI "ip macsec set " DEV " rx " SCI +.RB [ " on " | " off " ] +.br +.BI "ip macsec del " DEV " rx " SCI + +.BI "ip macsec add " DEV " rx " SCI " sa" +.RI "{ " 0..3 " } [ " OPTS " ]" +.BI key " ID KEY" +.br +.BI "ip macsec set " DEV " rx " SCI " sa" +.RI "{ " 0..3 " } [ " OPTS " ]" +.br +.BI "ip macsec del " DEV " rx " SCI " sa" +.RI "{ " 0..3 " }" + +.B ip macsec show +.RI [ " DEV " ] + +.IR OPTS " := [ " +.BR pn " { " +.IR 1..2^32-1 " } ] [" +.BR on " | " off " ]" +.br +.IR SCI " := { " +.B sci +.IR " | " +.BI port +.IR PORT +.BI address " " +} +.br +.IR PORT " := { " 1..2^16-1 " } " + + +.SH DESCRIPTION +The +.B ip macsec +commands are used to configure transmit secure associations and receive secure channels and their secure associations on a MACsec device created with the +.B ip link add +command using the +.I macsec +type. + +.SH EXAMPLES +.PP +.SS Create a MACsec device on link eth0 +.nf +# ip link add link eth0 macsec0 type macsec port 11 encrypt on +.PP +.SS Configure a secure association on that device +.nf +# ip macsec add macsec0 tx sa 0 pn 1024 on key 01 81818181818181818181818181818181 +.PP +.SS Configure a receive channel +.nf +# ip macsec add macsec0 rx port 1234 address c6:19:52:8f:e6:a0 +.PP +.SS Configure a receive association +.nf +# ip macsec add macsec0 rx port 1234 address c6:19:52:8f:e6:a0 sa 0 pn 1 on key 00 82828282828282828282828282828282 +.PP +.SS Display MACsec configuration +.nf +# ip macsec show +.SH SEE ALSO +.br +.BR ip-link (8) +.SH AUTHOR +Sabrina Dubroca diff -Nru iproute2-4.3.0/man/man8/ip-monitor.8 iproute2-4.9.0/man/man8/ip-monitor.8 --- iproute2-4.3.0/man/man8/ip-monitor.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-monitor.8 2016-12-12 23:07:42.000000000 +0000 @@ -6,9 +6,7 @@ .ad l .in +8 .ti -8 -.BR "ip " " [ " -.IR ip-OPTIONS " ]" -.BR "monitor" " [ " all " |" +.BR "ip monitor" " [ " all " |" .IR OBJECT-LIST " ] [" .BI file " FILENAME " ] [ diff -Nru iproute2-4.3.0/man/man8/ip-mroute.8 iproute2-4.9.0/man/man8/ip-mroute.8 --- iproute2-4.3.0/man/man8/ip-mroute.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-mroute.8 2016-12-12 23:07:42.000000000 +0000 @@ -6,7 +6,7 @@ .ad l .in +8 .ti -8 -.BR "ip " " [ ip-OPTIONS ] " "mroute show" " [ [ " +.BR "ip mroute show" " [ [ " .BR " to " " ] " .IR PREFIX " ] [ " .B from diff -Nru iproute2-4.3.0/man/man8/ip-neighbour.8 iproute2-4.9.0/man/man8/ip-neighbour.8 --- iproute2-4.3.0/man/man8/ip-neighbour.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-neighbour.8 2016-12-12 23:07:42.000000000 +0000 @@ -18,7 +18,9 @@ .IR ADDR " [ " .B lladdr .IR LLADDR " ] [ " -.BR nud " { " permanent " | " noarp " | " stale " | " reachable " } ] | " proxy +.B nud +.IR STATE " ] |" +.B proxy .IR ADDR " } [ " .B dev .IR DEV " ]" @@ -29,13 +31,19 @@ .B dev .IR DEV " ] [ " .B nud -.IR STATE " ]" +.IR STATE " ] [ " +.B vrf +.IR NAME " ] " +.ti -8 +.IR STATE " := {" +.BR permanent " | " noarp " | " stale " | " reachable " | " none " |" +.BR incomplete " | " delay " | " probe " | " failed " }" .SH DESCRIPTION -The +The .B ip neigh -command manipulates +command manipulates .I neighbour objects that establish bindings between protocol addresses and link layer addresses for hosts sharing the same link. @@ -75,12 +83,13 @@ .BR "null" . .TP -.BI nud " NUD_STATE" +.BI nud " STATE" the state of the neighbour entry. .B nud is an abbreviation for 'Neighbour Unreachability Detection'. The state can take one of the following values: +.RS .TP .B permanent the neighbour entry is valid forever and can be only @@ -100,6 +109,24 @@ .B ip neigh does not change the neighbour state if it was valid and the address is not changed by this command. +.TP +.B none +this is a pseudo state used when initially creating a neighbour entry or after +trying to remove it before it becomes free to do so. +.TP +.B incomplete +the neighbour entry has not (yet) been validated/resolved. +.TP +.B delay +neighbor entry validation is currently delayed. +.TP +.B probe +neighbor is being probed. +.TP +.B failed +max number of probes exceeded without success, neighbor validation has +ultimately failed. +.RE .RE .TP @@ -139,6 +166,10 @@ only list the neighbours attached to this device. .TP +.BI vrf " NAME" +only list the neighbours for given VRF. + +.TP .BI proxy list neighbour proxies. @@ -147,7 +178,7 @@ only list neighbours which are not currently in use. .TP -.BI nud " NUD_STATE" +.BI nud " STATE" only list neighbour entries in this state. .I NUD_STATE takes values listed below or the special value diff -Nru iproute2-4.3.0/man/man8/ip-netconf.8 iproute2-4.9.0/man/man8/ip-netconf.8 --- iproute2-4.3.0/man/man8/ip-netconf.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-netconf.8 2016-12-12 23:07:42.000000000 +0000 @@ -15,7 +15,7 @@ .B ip netconf utility can monitor IPv4 and IPv6 parameters (see .BR "/proc/sys/net/ipv[4|6]/conf/[all|DEV]/" ")" -like forwarding, rp_filter +like forwarding, rp_filter, proxy_neigh, ignore_routes_with_linkdown or mc_forwarding status. If no interface is specified, the entry diff -Nru iproute2-4.3.0/man/man8/ip-netns.8 iproute2-4.9.0/man/man8/ip-netns.8 --- iproute2-4.3.0/man/man8/ip-netns.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-netns.8 2016-12-12 23:07:42.000000000 +0000 @@ -13,7 +13,7 @@ .BR help " }" .sp .ti -8 -.BR "ip netns" " { " list " } " +.BR "ip netns" " [ " list " ]" .ti -8 .B ip netns add @@ -24,7 +24,7 @@ .RI "[ " NETNSNAME " ]" .ti -8 -.BR "ip netns" " { " set " } " +.B ip netns set .I NETNSNAME NETNSID .ti -8 diff -Nru iproute2-4.3.0/man/man8/ip-ntable.8 iproute2-4.9.0/man/man8/ip-ntable.8 --- iproute2-4.3.0/man/man8/ip-ntable.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-ntable.8 2016-12-12 23:07:42.000000000 +0000 @@ -8,7 +8,7 @@ .ti -8 .B ip .RI "[ " OPTIONS " ]" -.B address +.B ntable .RI " { " COMMAND " | " .BR help " }" .sp @@ -17,34 +17,39 @@ .BR "ip ntable change name" .IR NAME " [ " .B dev -.IR DEV " ] " PARMS - -.ti -8 -.IR PARMS " := { " +.IR DEV " ] [" .B thresh1 -.IR VAL " | " +.IR VAL " ] [" .B thresh2 -.IR VAL " | " +.IR VAL " ] [" .B thresh3 -.IR VAL " | " +.IR VAL " ] [" .B gc_int -.IR MSEC " | " +.IR MSEC " ] [" .B base_reachable -.IR MSEC " | " +.IR MSEC " ] [" .B retrans -.IR MSEC " | " "gc_stale MSEC " " | " +.IR MSEC " ] [" +.B gc_stale +.IR MSEC " ] [" .B delay_probe -.IR MSEC " | " "queue LEN " " | " +.IR MSEC " ] [" +.B queue +.IR LEN " ] [" .B app_probs -.IR VAL " | " +.IR VAL " ] [" .B ucast_probes -.IR VAL " | " "mcast_probes VAL " " | " +.IR VAL " ] [" +.B mcast_probes +.IR VAL " ] [" .B anycast_delay -.IR MSEC " | " +.IR MSEC " ] [" .B proxy_delay -.IR MSEC " | " "proxy_queue LEN " " | " +.IR MSEC " ] [" +.B proxy_queue +.IR LEN " ] [" .B locktime -.IR MSEC " }" +.IR MSEC " ]" .ti -8 .BR "ip ntable show" " [ " @@ -55,7 +60,7 @@ .SH DESCRIPTION .I ip ntable -controls the parameters for the neighbour tables. +controls the parameters for the neighbour tables. .SS ip ntable show - list the ip neighbour tables @@ -98,4 +103,4 @@ .BR ip (8) .SH AUTHOR -Manpage by Stephen Hemminger +Manpage by Stephen Hemminger diff -Nru iproute2-4.3.0/man/man8/ip-route.8.in iproute2-4.9.0/man/man8/ip-route.8.in --- iproute2-4.3.0/man/man8/ip-route.8.in 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-route.8.in 2016-12-12 23:07:42.000000000 +0000 @@ -16,7 +16,7 @@ .ti -8 .BR "ip route" " { " -.BR list " | " flush " } " +.BR show " | " flush " } " .I SELECTOR .ti -8 @@ -33,7 +33,9 @@ .RB " ] [ " oif .IR STRING " ] [ " .B tos -.IR TOS " ]" +.IR TOS " ] [ " +.B vrf +.IR NAME " ] " .ti -8 .BR "ip route" " { " add " | " del " | " change " | " append " | "\ @@ -50,6 +52,8 @@ .IR PREFIX " ] [ " .B table .IR TABLE_ID " ] [ " +.B vrf +.IR NAME " ] [ " .B proto .IR RTPROTO " ] [ " .B type @@ -80,6 +84,8 @@ .ti -8 .IR NH " := [ " +.B encap +.IR ENCAP " ] [ " .B via [ .IR FAMILY " ] " ADDRESS " ] [ " @@ -128,9 +134,11 @@ .B quickack .IR BOOL " ] [ " .B congctl -.IR NAME " ]" +.IR NAME " ] [ " .B pref -.IR PREF " ]" +.IR PREF " ] [ " +.B expires +.IR TIME " ]" .ti -8 .IR TYPE " := [ " @@ -164,6 +172,26 @@ .IR PREF " := [ " .BR low " | " medium " | " high " ]" +.ti -8 +.IR ENCAP " := [ " +.IR MPLS " | " IP " ]" + +.ti -8 +.IR ENCAP_MPLS " := " +.BR mpls " [ " +.IR LABEL " ]" + +.ti -8 +.IR ENCAP_IP " := " +.B ip +.B id +.IR TUNNEL_ID +.B dst +.IR REMOTE_IP " [ " +.B tos +.IR TOS " ] [" +.B ttl +.IR TTL " ]" .SH DESCRIPTION .B ip route @@ -345,6 +373,11 @@ table by default. .TP +.BI vrf " NAME" +the vrf name to add this route to. Implicitly means the table +associated with the VRF. + +.TP .BI dev " NAME" the output device name. @@ -589,10 +622,58 @@ - the route has a highest priority .sp +.TP +.BI encap " ENCAPTYPE ENCAPHDR" +attach tunnel encapsulation attributes to this route. +.sp +.I ENCAPTYPE +is a string specifying the supported encapsulation type. Namely: + +.in +8 +.BI mpls +- encapsulation type MPLS +.sp +.BI ip +- IP encapsulation (Geneve, GRE, VXLAN, ...) +.sp + +.in -8 +.I ENCAPHDR +is a set of encapsulation attributes specific to the +.I ENCAPTYPE. + +.in +8 +.B mpls +.in +2 +.I MPLSLABEL +- mpls label stack with labels separated by +.I "/" +.in -2 +.sp + +.B ip +.in +2 +.B id +.I TUNNEL_ID +.B dst +.IR REMOTE_IP " [ " +.B tos +.IR TOS " ] [" +.B ttl +.IR TTL " ]" +.in -2 +.sp + .in -8 .RE .TP +.BI expires " TIME " "(4.4+ only)" +the route will be deleted after the expires time. +.B Only +support IPv6 at present. + +.TP ip route delete delete route .RS @@ -674,6 +755,10 @@ .in -8 .TP +.BI vrf " NAME" +show the routes for the table associated with the vrf name + +.TP .B cloned .TP .B cached @@ -783,6 +868,10 @@ force the output device on which this packet will be routed. .TP +.BI vrf " NAME" +force the vrf device on which this packet will be routed. + +.TP .B connected if no source address .RB "(option " from ")" @@ -835,6 +924,12 @@ already exist in the table will be ignored. .RE +.SH NOTES +Starting with Linux kernel version 3.6, there is no routing cache for IPv4 +anymore. Hence +.B "ip route show cached" +will never print any entries on systems with this or newer kernel versions. + .SH EXAMPLES .PP ip ro @@ -847,7 +942,11 @@ Adds a default route (for all addresses) via the local gateway 192.168.1.1 that can be reached on device eth0. .RE - +.PP +ip route add 10.1.1.0/30 encap mpls 200/300 via 10.1.1.1 dev eth0 +.RS 4 +Adds an ipv4 route with mpls encapsulation attributes attached to it. +.RE .SH SEE ALSO .br .BR ip (8) diff -Nru iproute2-4.3.0/man/man8/ip-rule.8 iproute2-4.9.0/man/man8/ip-rule.8 --- iproute2-4.3.0/man/man8/ip-rule.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-rule.8 2016-12-12 23:07:42.000000000 +0000 @@ -9,20 +9,27 @@ .B ip .RI "[ " OPTIONS " ]" .B rule -.RI " { " COMMAND " | " +.RI "{ " COMMAND " | " .BR help " }" .sp .ti -8 .B ip rule -.RB " [ " list " | " add " | " del " | " flush " | " save " ]" +.RB "[ " list +.RI "[ " SELECTOR " ]]" + +.ti -8 +.B ip rule +.RB "{ " add " | " del " }" .I SELECTOR ACTION .ti -8 -.B ip rule " restore " +.B ip rule +.RB "{ " flush " | " save " | " restore " }" .ti -8 .IR SELECTOR " := [ " +.BR not " ] [" .B from .IR PREFIX " ] [ " .B to @@ -30,13 +37,14 @@ .B tos .IR TOS " ] [ " .B fwmark -.IR FWMARK[/MASK] " ] [ " +.IR FWMARK\fR[\fB/\fIMASK "] ] [ " .B iif .IR STRING " ] [ " .B oif .IR STRING " ] [ " .B pref -.IR NUMBER " ]" +.IR NUMBER " ] [ " +.BR l3mdev " ]" .ti -8 .IR ACTION " := [ " @@ -45,8 +53,9 @@ .B nat .IR ADDRESS " ] [ " .B realms -.RI "[" SRCREALM "/]" DSTREALM " ]" -.I SUPPRESSOR +.RI "[" SRCREALM "\fB/\fR]" DSTREALM " ] [" +.B goto +.IR NUMBER " ] " SUPPRESSOR .ti -8 .IR SUPPRESSOR " := [ " @@ -62,7 +71,7 @@ .SH DESCRIPTION .I ip rule -manipulates rules +manipulates rules in the routing policy database control the route selection algorithm. .P @@ -111,8 +120,6 @@ .B local table is a special routing table containing high priority control routes for local and broadcast addresses. -.sp -Rule 0 is special. It cannot be deleted or overridden. .TP 2. @@ -216,8 +223,10 @@ .TP .BI priority " PREFERENCE" -the priority of this rule. Each rule should have an explicitly -set +the priority of this rule. +.I PREFERENCE +is an unsigned integer value, higher number means lower priority. Each rule +should have an explicitly set .I unique priority value. The options preference and order are synonyms with priority. diff -Nru iproute2-4.3.0/man/man8/ip-token.8 iproute2-4.9.0/man/man8/ip-token.8 --- iproute2-4.3.0/man/man8/ip-token.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-token.8 2016-12-12 23:07:42.000000000 +0000 @@ -7,23 +7,27 @@ .in +8 .ti -8 .B ip token -.RI " { " COMMAND " | " +.RI "{ " COMMAND " | " .BR help " }" .sp .ti -8 -.BR "ip token" " { " set " } " +.B ip token set .IR TOKEN .B dev .IR DEV .ti -8 -.BR "ip token" " { " get " } " -.B dev +.B ip token del dev .IR DEV .ti -8 -.BR "ip token" " { " list " }" +.B ip token get +.RB "[ " dev +.IR DEV " ]" + +.ti -8 +.BR "ip token" " [ " list " ]" .SH "DESCRIPTION" IPv6 tokenized interface identifier support is used for assigning well-known @@ -37,14 +41,19 @@ [1]: . .SS ip token set - set an interface token -set the interface token to the kernel. Once a token is set, it cannot be -removed from the interface, only overwritten. +set the interface token to the kernel. .TP .I TOKEN the interface identifier token address. .TP .BI dev " DEV" the networking interface. + +.SS ip token del - delete an interface token +delete the interface token from the kernel. +.TP +.BI dev " DEV" +the networking interface. .SS ip token get - get the interface token from the kernel show a tokenized interface identifier of a particular networking device. diff -Nru iproute2-4.3.0/man/man8/ip-tunnel.8 iproute2-4.9.0/man/man8/ip-tunnel.8 --- iproute2-4.3.0/man/man8/ip-tunnel.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-tunnel.8 2016-12-12 23:07:42.000000000 +0000 @@ -11,7 +11,7 @@ .ti -8 .BR "ip " .RI "[ " OPTIONS " ]" -.BR "tunnel" " { " add " | " change " | " del " | " show " | " prl " }" +.BR "tunnel" " { " add " | " change " | " del " | " show " | " prl " | " 6rd " }" .RI "[ " NAME " ]" .br .RB "[ " mode @@ -42,6 +42,12 @@ .B prl-delete .IR ADDR " ]" .br +.RB "[ " 6rd-prefix +.IR ADDR " ] [" +.B 6rd-relay_prefix +.IR ADDR " ] [ +.BR 6rd-reset " ]" +.br .RB "[ [" no "]" pmtudisc " ]" .RB "[ " dev .IR PHYS_DEV " ]" @@ -75,9 +81,6 @@ .ti -8 .IR KEY " := { " DOTTED_QUAD " | " NUMBER " }" -.ti -8 -.IR TIME " := " NUMBER "[s|ms]" - .SH DESCRIPTION .B tunnel objects are tunnels, encapsulating packets in IP packets and then diff -Nru iproute2-4.3.0/man/man8/ip-xfrm.8 iproute2-4.9.0/man/man8/ip-xfrm.8 --- iproute2-4.3.0/man/man8/ip-xfrm.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ip-xfrm.8 2016-12-12 23:07:42.000000000 +0000 @@ -57,6 +57,8 @@ .IR ADDR "[/" PLEN "] ]" .RB "[ " ctx .IR CTX " ]" +.RB "[ " extra-flag +.IR EXTRA-FLAG-LIST " ]" .ti -8 .B "ip xfrm state allocspi" @@ -121,7 +123,7 @@ .ti -8 .IR ALGO " :=" -.RB "{ " enc " | " auth " } " +.RB "{ " enc " | " auth " } " .IR ALGO-NAME " " ALGO-KEYMAT " |" .br .B auth-trunc @@ -196,6 +198,13 @@ .IR SPORT " " DPORT " " OADDR .ti -8 +.IR EXTRA-FLAG-LIST " := [ " EXTRA-FLAG-LIST " ] " EXTRA-FLAG + +.ti -8 +.IR EXTRA-FLAG " := " +.B dont-encap-dscp + +.ti -8 .BR "ip xfrm policy" " { " add " | " update " }" .I SELECTOR .B dir @@ -247,6 +256,8 @@ .IR ACTION " ]" .RB "[ " priority .IR PRIORITY " ]" +.RB "[ " flag +.IR FLAG-LIST "]" .ti -8 .B "ip xfrm policy flush" diff -Nru iproute2-4.3.0/man/man8/lnstat.8 iproute2-4.9.0/man/man8/lnstat.8 --- iproute2-4.3.0/man/man8/lnstat.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/lnstat.8 2016-12-12 23:07:42.000000000 +0000 @@ -172,7 +172,7 @@ size was reached. .sp .B icmp_error -Number of packets wich could not be tracked due to error situation. This is a +Number of packets which could not be tracked due to error situation. This is a subset of \fBinvalid\fP. .sp .B expect_new diff -Nru iproute2-4.3.0/man/man8/Makefile iproute2-4.9.0/man/man8/Makefile --- iproute2-4.3.0/man/man8/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -7,14 +7,17 @@ tc-mqprio.8 tc-netem.8 tc-pfifo.8 tc-pfifo_fast.8 tc-prio.8 tc-red.8 \ tc-sfb.8 tc-sfq.8 tc-stab.8 tc-tbf.8 \ bridge.8 rtstat.8 ctstat.8 nstat.8 routef.8 \ - ip-addrlabel.8 ip-fou.8 ip-gue.8 ip-l2tp.8 \ + ip-addrlabel.8 ip-fou.8 ip-gue.8 ip-l2tp.8 ip-macsec.8 \ ip-maddress.8 ip-monitor.8 ip-mroute.8 ip-neighbour.8 \ ip-netns.8 ip-ntable.8 ip-rule.8 ip-tunnel.8 ip-xfrm.8 \ ip-tcp_metrics.8 ip-netconf.8 ip-token.8 \ tipc.8 tipc-bearer.8 tipc-link.8 tipc-media.8 tipc-nametable.8 \ tipc-node.8 tipc-socket.8 \ tc-basic.8 tc-cgroup.8 tc-flow.8 tc-flower.8 tc-fw.8 tc-route.8 \ - tc-tcindex.8 tc-u32.8 + tc-tcindex.8 tc-u32.8 tc-matchall.8 \ + tc-connmark.8 tc-csum.8 tc-mirred.8 tc-nat.8 tc-pedit.8 tc-police.8 \ + tc-simple.8 tc-skbedit.8 tc-vlan.8 tc-xt.8 tc-ife.8 tc-skbmod.8 \ + devlink.8 devlink-dev.8 devlink-monitor.8 devlink-port.8 devlink-sb.8 all: $(TARGETS) diff -Nru iproute2-4.3.0/man/man8/routel.8 iproute2-4.9.0/man/man8/routel.8 --- iproute2-4.3.0/man/man8/routel.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/routel.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,16 +1,16 @@ .TH "ROUTEL" "8" "3 Jan, 2008" "iproute2" "Linux" .SH "NAME" -.LP +.LP routel \- list routes with pretty output format .br routef \- flush routes .SH "SYNTAX" -.LP +.LP routel [\fItablenr\fP [\fIraw ip args...\fP]] -.br +.br routef .SH "DESCRIPTION" -.LP +.LP These programs are a set of helper scripts you can use instead of raw iproute2 commands. .br The routel script will list routes in a format that some might consider easier to interpret then the ip route list equivalent. @@ -18,15 +18,15 @@ The routef script does not take any arguments and will simply flush the routing table down the drain. Beware! This means deleting all routes which will make your network unusable! .SH "FILES" -.LP -\fI/usr/bin/routef\fP -.br -\fI/usr/bin/routel\fP +.LP +\fI/usr/bin/routef\fP +.br +\fI/usr/bin/routel\fP .SH "AUTHORS" -.LP +.LP The routel script was written by Stephen R. van den Berg , 1999/04/18 and donated to the public domain. .br This manual page was written by Andreas Henriksson , for the Debian GNU/Linux system. .SH "SEE ALSO" -.LP +.LP ip(8) diff -Nru iproute2-4.3.0/man/man8/rtacct.8 iproute2-4.9.0/man/man8/rtacct.8 --- iproute2-4.3.0/man/man8/rtacct.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/rtacct.8 2016-12-12 23:07:42.000000000 +0000 @@ -35,6 +35,7 @@ .TP .B \-s, \-\-noupdate Do not update history, so that the next time you will see counters including values accumulated to the moment of this measurement too. +.TP .B \-j, \-\-json Display results in JSON format. .TP @@ -47,4 +48,3 @@ .SH SEE ALSO lnstat(8) - diff -Nru iproute2-4.3.0/man/man8/rtmon.8 iproute2-4.9.0/man/man8/rtmon.8 --- iproute2-4.3.0/man/man8/rtmon.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/rtmon.8 2016-12-12 23:07:42.000000000 +0000 @@ -10,11 +10,11 @@ command. .PP .B rtmon -listens on -.I netlink +listens on +.I netlink socket and monitors routing table changes. -.I rtmon +.I rtmon can be started before the first network configuration command is issued. For example if you insert: @@ -61,7 +61,7 @@ .SH SEE ALSO .BR ip (8) .SH AUTHOR -.B rtmon +.B rtmon was written by Alexey Kuznetsov . .PP This manual page was written by Michael Prokop , diff -Nru iproute2-4.3.0/man/man8/rtpr.8 iproute2-4.9.0/man/man8/rtpr.8 --- iproute2-4.3.0/man/man8/rtpr.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/rtpr.8 2016-12-12 23:07:42.000000000 +0000 @@ -13,7 +13,7 @@ .SH EXAMPLES .TP -ip --onenline address show | rtpr +ip --oneline address show | rtpr Undo oneline converted .B ip-address output. diff -Nru iproute2-4.3.0/man/man8/ss.8 iproute2-4.9.0/man/man8/ss.8 --- iproute2-4.3.0/man/man8/ss.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/ss.8 2016-12-12 23:07:42.000000000 +0000 @@ -12,7 +12,7 @@ It can display more TCP and state informations than other tools. .SH OPTIONS -When no option is used ss displays a list of +When no option is used ss displays a list of open non-listening sockets (e.g. TCP/UNIX/UDP) that have established connection. .TP .B \-h, \-\-help @@ -21,6 +21,9 @@ .B \-V, \-\-version Output version information. .TP +.B \-H, \-\-no-header +Suppress header line. +.TP .B \-n, \-\-numeric Do not try to resolve service names. .TP @@ -48,6 +51,11 @@ .B \-i, \-\-info Show internal TCP information. .TP +.B \-K, \-\-kill +Attempts to forcibly close sockets. This option displays sockets that are +successfully closed and silently skips sockets that the kernel does not support +closing. It supports IPv4 and IPv6 sockets only. +.TP .B \-s, \-\-summary Print summary statistics. This option does not parse socket lists obtaining summary from various sources. It is useful when amount of sockets is so huge @@ -114,6 +122,9 @@ .B \-x, \-\-unix Display Unix domain sockets (alias for -f unix). .TP +.B \-S, \-\-sctp +Display SCTP sockets. +.TP .B \-f FAMILY, \-\-family=FAMILY Display sockets of type FAMILY. Currently the following families are supported: unix, inet, inet6, link, netlink. @@ -189,10 +200,10 @@ .BR /usr/share/doc/iproute-doc/ss.html " (package iproutedoc)", .br .BR RFC " 793 " -- https://tools.ietf.org/rfc/rfc793.txt (TCP states) +- https://tools.ietf.org/rfc/rfc793.txt (TCP states) .SH AUTHOR -.I ss +.I ss was written by Alexey Kuznetsov, . .PP This manual page was written by Michael Prokop diff -Nru iproute2-4.3.0/man/man8/tc.8 iproute2-4.9.0/man/man8/tc.8 --- iproute2-4.3.0/man/man8/tc.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc.8 2016-12-12 23:07:42.000000000 +0000 @@ -28,7 +28,7 @@ .B tc .RI "[ " OPTIONS " ]" -.B filter [ add | change | replace | delete ] dev +.B filter [ add | change | replace | delete | get ] dev DEV .B [ parent qdisc-id @@ -181,12 +181,17 @@ .TP tcindex Filter packets based on traffic control index. See -.BR tc-index (8). +.BR tc-tcindex (8). .TP u32 Generic filtering on arbitrary packet data, assisted by syntax to abstract common operations. See .BR tc-u32 (8) for details. +.TP +matchall +Traffic control filter that matches every packet. See +.BR tc-matchall (8) +for details. .SH CLASSLESS QDISCS The classless qdiscs are: @@ -571,6 +576,14 @@ it is created. .TP +get +Displays a single filter given the interface, parent ID, priority, protocol and handle ID. + +.TP +show +Displays all filters attached to the given interface. A valid parent ID must be passed. + +.TP link Only available for qdiscs and performs a replace where the node must exist already. @@ -609,7 +622,7 @@ .TP .BR "\-cf" , " \-conf " -specifies path to the config file. This option is used in conjuction with other options (e.g. +specifies path to the config file. This option is used in conjunction with other options (e.g. .BR -nm ")." .SH FORMAT @@ -732,4 +745,3 @@ .SH AUTHOR Manpage maintained by bert hubert (ahu@ds9a.nl) - diff -Nru iproute2-4.3.0/man/man8/tc-bfifo.8 iproute2-4.9.0/man/man8/tc-bfifo.8 --- iproute2-4.3.0/man/man8/tc-bfifo.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-bfifo.8 2016-12-12 23:07:42.000000000 +0000 @@ -6,37 +6,37 @@ .SH SYNOPSIS .B tc qdisc ... add pfifo -.B [ limit +.B [ limit packets .B ] .P .B tc qdisc ... add bfifo -.B [ limit +.B [ limit bytes .B ] .SH DESCRIPTION The pfifo and bfifo qdiscs are unadorned First In, First Out queues. They are the -simplest queues possible and therefore have no overhead. +simplest queues possible and therefore have no overhead. .B pfifo -constrains the queue size as measured in packets. +constrains the queue size as measured in packets. .B bfifo does so as measured in bytes. -Like all non-default qdiscs, they maintain statistics. This might be a reason to prefer +Like all non-default qdiscs, they maintain statistics. This might be a reason to prefer pfifo or bfifo over the default. .SH ALGORITHM A list of packets is maintained, when a packet is enqueued it gets inserted at the tail of -a list. When a packet needs to be sent out to the network, it is taken from the head of the list. +a list. When a packet needs to be sent out to the network, it is taken from the head of the list. If the list is too long, no further packets are allowed on. This is called 'tail drop'. .SH PARAMETERS -.TP +.TP limit -Maximum queue size. Specified in bytes for bfifo, in packets for pfifo. For pfifo, defaults -to the interface txqueuelen, as specified with +Maximum queue size. Specified in bytes for bfifo, in packets for pfifo. For pfifo, defaults +to the interface txqueuelen, as specified with .BR ifconfig (8) or .BR ip (8). @@ -48,20 +48,20 @@ Note: The link layer header was considered when counting packets length. .SH OUTPUT -The output of +The output of .B tc -s qdisc ls -contains the limit, either in packets or in bytes, and the number of bytes -and packets actually sent. An unsent and dropped packet only appears between braces +contains the limit, either in packets or in bytes, and the number of bytes +and packets actually sent. An unsent and dropped packet only appears between braces and is not counted as 'Sent'. -In this example, the queue length is 100 packets, 45894 bytes were sent over 681 packets. +In this example, the queue length is 100 packets, 45894 bytes were sent over 681 packets. No packets were dropped, and as the pfifo queue does not slow down packets, there were also no overlimits: .P .nf -# tc -s qdisc ls dev eth0 +# tc -s qdisc ls dev eth0 qdisc pfifo 8001: dev eth0 limit 100p - Sent 45894 bytes 681 pkts (dropped 0, overlimits 0) + Sent 45894 bytes 681 pkts (dropped 0, overlimits 0) .fi If a backlog occurs, this is displayed as well. @@ -72,5 +72,3 @@ Alexey N. Kuznetsov, This manpage maintained by bert hubert - - diff -Nru iproute2-4.3.0/man/man8/tc-bpf.8 iproute2-4.9.0/man/man8/tc-bpf.8 --- iproute2-4.3.0/man/man8/tc-bpf.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-bpf.8 2016-12-12 23:07:42.000000000 +0000 @@ -14,6 +14,10 @@ UDS_FILE ] [ .B verbose ] [ +.B skip_hw +| +.B skip_sw +] [ .B police POLICE_SPEC ] [ .B action @@ -137,6 +141,16 @@ program was successful. By default, only on error, the verifier log is being emitted to the user. +.SS skip_hw | skip_sw +hardware offload control flags. By default TC will try to offload +filters to hardware if possible. +.B skip_hw +explicitly disables the attempt to offload. +.B skip_sw +forces the offload and disables running the eBPF program in the kernel. +If hardware offload is not possible and this flag was set kernel will +report an error and filter will not be installed at all. + .SS police is an optional parameter for an eBPF/cBPF classifier that specifies a police in @@ -394,7 +408,7 @@ \&. This approach's advantage is that tc will place the file descriptors into the environment and thus make them available just like stdin, stdout, stderr file descriptors, meaning, in case user applications run from within -this fd-owner shell, they can terminate and restart without loosing eBPF +this fd-owner shell, they can terminate and restart without losing eBPF maps file descriptors. Example invocation with the previous classifier and action mixture: @@ -844,7 +858,7 @@ Basically, such a minimal generator is equivalent to: .in +4n -.B tcpdump -iem1 -ddd 'tcp[tcpflags] & tcp-syn != 0' | tr '\\n' ',' > /var/bpf/tcp-syn +.B tcpdump -iem1 -ddd 'tcp[tcpflags] & tcp-syn != 0' | tr '\\\\n' ',' > /var/bpf/tcp-syn .in Since diff -Nru iproute2-4.3.0/man/man8/tc-cbq.8 iproute2-4.9.0/man/man8/tc-cbq.8 --- iproute2-4.3.0/man/man8/tc-cbq.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-cbq.8 2016-12-12 23:07:42.000000000 +0000 @@ -5,56 +5,56 @@ .B tc qdisc ... dev dev .B ( parent -classid -.B | root) [ handle -major: -.B ] cbq [ allot +classid +.B | root) [ handle +major: +.B ] cbq [ allot bytes .B ] avpkt bytes .B bandwidth rate -.B [ cell +.B [ cell bytes .B ] [ ewma log .B ] [ mpu bytes -.B ] +.B ] .B tc class ... dev dev -.B parent +.B parent major:[minor] -.B [ classid +.B [ classid major:minor .B ] cbq allot bytes -.B [ bandwidth -rate -.B ] [ rate +.B [ bandwidth +rate +.B ] [ rate rate .B ] prio priority .B [ weight weight -.B ] [ minburst +.B ] [ minburst +packets +.B ] [ maxburst packets -.B ] [ maxburst -packets -.B ] [ ewma +.B ] [ ewma log .B ] [ cell bytes .B ] avpkt bytes .B [ mpu -bytes +bytes .B ] [ bounded isolated ] [ split handle .B & defmap defmap -.B ] [ estimator +.B ] [ estimator interval timeconstant .B ] @@ -62,7 +62,7 @@ Class Based Queueing is a classful qdisc that implements a rich linksharing hierarchy of classes. It contains shaping elements as well as prioritizing capabilities. Shaping is performed using link -idle time calculations based on the timing of dequeue events and +idle time calculations based on the timing of dequeue events and underlying link bandwidth. .SH SHAPING ALGORITHM @@ -85,71 +85,71 @@ Conversely, an idle link might amass a huge avgidle, which would then allow infinite bandwidths after a few hours of silence. To prevent -this, avgidle is capped at +this, avgidle is capped at .B maxidle. If overlimit, in theory, the CBQ could throttle itself for exactly the amount of time that was calculated to pass between packets, and then pass one packet, and throttle again. Due to timer resolution constraints, -this may not be feasible, see the +this may not be feasible, see the .B minburst parameter below. .SH CLASSIFICATION Within the one CBQ instance many classes may exist. Each of these classes -contains another qdisc, by default +contains another qdisc, by default .BR tc-pfifo (8). -When enqueueing a packet, CBQ starts at the root and uses various methods to -determine which class should receive the data. +When enqueueing a packet, CBQ starts at the root and uses various methods to +determine which class should receive the data. -In the absence of uncommon configuration options, the process is rather easy. -At each node we look for an instruction, and then go to the class the -instruction refers us to. If the class found is a barren leaf-node (without -children), we enqueue the packet there. If it is not yet a leaf node, we do -the whole thing over again starting from that node. +In the absence of uncommon configuration options, the process is rather easy. +At each node we look for an instruction, and then go to the class the +instruction refers us to. If the class found is a barren leaf-node (without +children), we enqueue the packet there. If it is not yet a leaf node, we do +the whole thing over again starting from that node. -The following actions are performed, in order at each node we visit, until one +The following actions are performed, in order at each node we visit, until one sends us to another node, or terminates the process. .TP (i) -Consult filters attached to the class. If sent to a leafnode, we are done. +Consult filters attached to the class. If sent to a leafnode, we are done. Otherwise, restart. .TP (ii) -Consult the defmap for the priority assigned to this packet, which depends +Consult the defmap for the priority assigned to this packet, which depends on the TOS bits. Check if the referral is leafless, otherwise restart. .TP (iii) -Ask the defmap for instructions for the 'best effort' priority. Check the +Ask the defmap for instructions for the 'best effort' priority. Check the answer for leafness, otherwise restart. .TP (iv) If none of the above returned with an instruction, enqueue at this node. .P This algorithm makes sure that a packet always ends up somewhere, even while -you are busy building your configuration. +you are busy building your configuration. For more details, see .BR tc-cbq-details(8). .SH LINK SHARING ALGORITHM -When dequeuing for sending to the network device, CBQ decides which of its +When dequeuing for sending to the network device, CBQ decides which of its classes will be allowed to send. It does so with a Weighted Round Robin process in which each class with packets gets a chance to send in turn. The WRR process -starts by asking the highest priority classes (lowest numerically - +starts by asking the highest priority classes (lowest numerically - highest semantically) for packets, and will continue to do so until they -have no more data to offer, in which case the process repeats for lower +have no more data to offer, in which case the process repeats for lower priorities. -Classes by default borrow bandwidth from their siblings. A class can be -prevented from doing so by declaring it 'bounded'. A class can also indicate +Classes by default borrow bandwidth from their siblings. A class can be +prevented from doing so by declaring it 'bounded'. A class can also indicate its unwillingness to lend out bandwidth by being 'isolated'. .SH QDISC The root of a CBQ qdisc class tree has the following parameters: -.TP +.TP parent major:minor | root This mandatory parameter determines the place of the CBQ instance, either at the .B root @@ -159,7 +159,7 @@ Like all other qdiscs, the CBQ can be assigned a handle. Should consist only of a major number, followed by a colon. Optional, but very useful if classes will be generated within this qdisc. -.TP +.TP allot bytes This allotment is the 'chunkiness' of link sharing and is used for determining packet transmission time tables. The qdisc allot differs slightly from the class allot discussed @@ -170,23 +170,23 @@ for making sure 'allot' has a safe value. Mandatory. .TP bandwidth rate -To determine the idle time, CBQ must know the bandwidth of your underlying +To determine the idle time, CBQ must know the bandwidth of your underlying physical interface, or parent qdisc. This is a vital parameter, more about it later. Mandatory. .TP cell The cell size determines he granularity of packet transmission time calculations. Has a sensible default. -.TP +.TP mpu A zero sized packet may still take time to transmit. This value is the lower cap for packet transmission time calculations - packets smaller than this value are still deemed to have this size. Defaults to zero. .TP ewma log -When CBQ needs to measure the average idle time, it does so using an +When CBQ needs to measure the average idle time, it does so using an Exponentially Weighted Moving Average which smooths out measurements into -a moving average. The EWMA LOG determines how much smoothing occurs. Lower -values imply greater sensitivity. Must be between 0 and 31. Defaults +a moving average. The EWMA LOG determines how much smoothing occurs. Lower +values imply greater sensitivity. Must be between 0 and 31. Defaults to 5. .P A CBQ qdisc does not shape out of its own accord. It only needs to know certain @@ -195,40 +195,40 @@ .SH CLASSES Classes have a host of parameters to configure their operation. -.TP +.TP parent major:minor -Place of this class within the hierarchy. If attached directly to a qdisc +Place of this class within the hierarchy. If attached directly to a qdisc and not to another class, minor can be omitted. Mandatory. -.TP +.TP classid major:minor Like qdiscs, classes can be named. The major number must be equal to the -major number of the qdisc to which it belongs. Optional, but needed if this +major number of the qdisc to which it belongs. Optional, but needed if this class is going to have children. -.TP +.TP weight weight -When dequeuing to the interface, classes are tried for traffic in a +When dequeuing to the interface, classes are tried for traffic in a round-robin fashion. Classes with a higher configured qdisc will generally have more traffic to offer during each round, so it makes sense to allow it to dequeue more traffic. All weights under a class are normalized, so -only the ratios matter. Defaults to the configured rate, unless the priority +only the ratios matter. Defaults to the configured rate, unless the priority of this class is maximal, in which case it is set to 1. -.TP +.TP allot bytes Allot specifies how many bytes a qdisc can dequeue -during each round of the process. This parameter is weighted using the +during each round of the process. This parameter is weighted using the renormalized class weight described above. Silently capped at a minimum of 3/2 avpkt. Mandatory. -.TP +.TP prio priority -In the round-robin process, classes with the lowest priority field are tried +In the round-robin process, classes with the lowest priority field are tried for packets first. Mandatory. -.TP +.TP avpkt See the QDISC section. -.TP +.TP rate rate Maximum rate this class and all its children combined can send at. Mandatory. @@ -238,7 +238,7 @@ used to determine maxidle and offtime, which are only calculated when specifying maxburst or minburst. Mandatory if specifying maxburst or minburst. -.TP +.TP maxburst This number of packets is used to calculate maxidle so that when avgidle is at maxidle, this number of average packets can be burst @@ -246,7 +246,7 @@ bursts. You can't set maxidle directly, only via this parameter. .TP -minburst +minburst As mentioned before, CBQ needs to throttle in case of overlimit. The ideal solution is to do so for exactly the calculated idle time, and pass 1 packet. However, Unix kernels generally have a @@ -269,21 +269,21 @@ avgidle is capped at -10us. Optional. .TP -bounded +bounded Signifies that this class will not borrow bandwidth from its siblings. -.TP +.TP isolated Means that this class will not borrow bandwidth to its siblings -.TP +.TP split major:minor & defmap bitmap[/bitmap] -If consulting filters attached to a class did not give a verdict, +If consulting filters attached to a class did not give a verdict, CBQ can also classify based on the packet's priority. There are 16 -priorities available, numbered from 0 to 15. +priorities available, numbered from 0 to 15. -The defmap specifies which priorities this class wants to receive, -specified as a bitmap. The Least Significant Bit corresponds to priority -zero. The +The defmap specifies which priorities this class wants to receive, +specified as a bitmap. The Least Significant Bit corresponds to priority +zero. The .B split parameter tells CBQ at which class the decision must be made, which should be a (grand)parent of the class you are adding. @@ -291,7 +291,7 @@ As an example, 'tc class add ... classid 10:1 cbq .. split 10:0 defmap c0' configures class 10:0 to send packets with priorities 6 and 7 to 10:1. -The complimentary configuration would then +The complimentary configuration would then be: 'tc class add ... classid 10:2 cbq ... split 10:0 defmap 3f' Which would send all packets 0, 1, 2, 3, 4 and 5 to 10:1. .TP @@ -301,22 +301,22 @@ it uses a very simple estimator that measures once every .B interval microseconds how much traffic has passed. This again is a EWMA, for which -the time constant can be specified, also in microseconds. The +the time constant can be specified, also in microseconds. The .B time constant -corresponds to the sluggishness of the measurement or, conversely, to the +corresponds to the sluggishness of the measurement or, conversely, to the sensitivity of the average to short bursts. Higher values mean less -sensitivity. +sensitivity. .SH BUGS -The actual bandwidth of the underlying link may not be known, for example -in the case of PPoE or PPTP connections which in fact may send over a +The actual bandwidth of the underlying link may not be known, for example +in the case of PPoE or PPTP connections which in fact may send over a pipe, instead of over a physical device. CBQ is quite resilient to major errors in the configured bandwidth, probably a the cost of coarser shaping. -Default kernels rely on coarse timing information for making decisions. These +Default kernels rely on coarse timing information for making decisions. These may make shaping precise in the long term, but inaccurate on second long scales. -See +See .BR tc-cbq-details(8) for hints on how to improve this. @@ -327,7 +327,7 @@ Management Models for Packet Networks", IEEE/ACM Transactions on Networking, Vol.3, No.4, 1995 -.TP +.TP o Sally Floyd, "Notes on CBQ and Guaranteed Service", 1995 @@ -336,7 +336,7 @@ Sally Floyd, "Notes on Class-Based Queueing: Setting Parameters", 1996 -.TP +.TP o Sally Floyd and Michael Speer, "Experimental Results for Class-Based Queueing", 1998, not published. @@ -349,5 +349,3 @@ .SH AUTHOR Alexey N. Kuznetsov, . This manpage maintained by bert hubert - - diff -Nru iproute2-4.3.0/man/man8/tc-cbq-details.8 iproute2-4.9.0/man/man8/tc-cbq-details.8 --- iproute2-4.3.0/man/man8/tc-cbq-details.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-cbq-details.8 2016-12-12 23:07:42.000000000 +0000 @@ -5,54 +5,54 @@ .B tc qdisc ... dev dev .B ( parent -classid -.B | root) [ handle -major: +classid +.B | root) [ handle +major: .B ] cbq avpkt bytes .B bandwidth rate -.B [ cell +.B [ cell bytes .B ] [ ewma log .B ] [ mpu bytes -.B ] +.B ] .B tc class ... dev dev -.B parent +.B parent major:[minor] -.B [ classid +.B [ classid major:minor .B ] cbq allot bytes -.B [ bandwidth -rate -.B ] [ rate +.B [ bandwidth +rate +.B ] [ rate rate .B ] prio priority .B [ weight weight -.B ] [ minburst +.B ] [ minburst +packets +.B ] [ maxburst packets -.B ] [ maxburst -packets -.B ] [ ewma +.B ] [ ewma log .B ] [ cell bytes .B ] avpkt bytes .B [ mpu -bytes +bytes .B ] [ bounded isolated ] [ split handle .B & defmap defmap -.B ] [ estimator +.B ] [ estimator interval timeconstant .B ] @@ -60,7 +60,7 @@ Class Based Queueing is a classful qdisc that implements a rich linksharing hierarchy of classes. It contains shaping elements as well as prioritizing capabilities. Shaping is performed using link -idle time calculations based on the timing of dequeue events and +idle time calculations based on the timing of dequeue events and underlying link bandwidth. .SH SHAPING ALGORITHM @@ -71,10 +71,10 @@ be idle 90% of the time. If it isn't, it needs to be throttled so that it IS idle 90% of the time. -From the kernel's perspective, this is hard to measure, so CBQ instead -derives the idle time from the number of microseconds (in fact, jiffies) -that elapse between requests from the device driver for more data. Combined -with the knowledge of packet sizes, this is used to approximate how full or +From the kernel's perspective, this is hard to measure, so CBQ instead +derives the idle time from the number of microseconds (in fact, jiffies) +that elapse between requests from the device driver for more data. Combined +with the knowledge of packet sizes, this is used to approximate how full or empty the link is. This is rather circumspect and doesn't always arrive at proper @@ -84,9 +84,9 @@ will also never achieve 100mbit/s because of the way the bus is designed - again, how do we calculate the idle time? -The physical link bandwidth may be ill defined in case of not-quite-real -network devices like PPP over Ethernet or PPTP over TCP/IP. The effective -bandwidth in that case is probably determined by the efficiency of pipes +The physical link bandwidth may be ill defined in case of not-quite-real +network devices like PPP over Ethernet or PPTP over TCP/IP. The effective +bandwidth in that case is probably determined by the efficiency of pipes to userspace - which not defined. During operations, the effective idletime is measured using an @@ -104,59 +104,59 @@ Conversely, an idle link might amass a huge avgidle, which would then allow infinite bandwidths after a few hours of silence. To prevent -this, avgidle is capped at +this, avgidle is capped at .B maxidle. If overlimit, in theory, the CBQ could throttle itself for exactly the amount of time that was calculated to pass between packets, and then pass one packet, and throttle again. Due to timer resolution constraints, -this may not be feasible, see the +this may not be feasible, see the .B minburst parameter below. .SH CLASSIFICATION Within the one CBQ instance many classes may exist. Each of these classes -contains another qdisc, by default +contains another qdisc, by default .BR tc-pfifo (8). -When enqueueing a packet, CBQ starts at the root and uses various methods to +When enqueueing a packet, CBQ starts at the root and uses various methods to determine which class should receive the data. If a verdict is reached, this process is repeated for the recipient class which might have further means of classifying traffic to its children, if any. -CBQ has the following methods available to classify a packet to any child +CBQ has the following methods available to classify a packet to any child classes. .TP (i) .B skb->priority class encoding. -Can be set from userspace by an application with the +Can be set from userspace by an application with the .B SO_PRIORITY setsockopt. -The +The .B skb->priority class encoding -only applies if the skb->priority holds a major:minor handle of an existing +only applies if the skb->priority holds a major:minor handle of an existing class within this qdisc. .TP (ii) tc filters attached to the class. .TP (iii) -The defmap of a class, as set with the +The defmap of a class, as set with the .B split & defmap parameters. The defmap may contain instructions for each possible Linux packet priority. .P -Each class also has a +Each class also has a .B level. Leaf nodes, attached to the bottom of the class hierarchy, have a level of 0. .SH CLASSIFICATION ALGORITHM -Classification is a loop, which terminates when a leaf class is found. At any +Classification is a loop, which terminates when a leaf class is found. At any point the loop may jump to the fallback algorithm. The loop consists of the following steps: -.TP +.TP (i) If the packet is generated locally and has a valid classid encoded within its .B skb->priority, @@ -169,40 +169,40 @@ If it is a leaf, choose it and terminate. .TP (iii) -If the tc filters did not return a class, but did return a classid, -try to find a class with that id within this qdisc. +If the tc filters did not return a class, but did return a classid, +try to find a class with that id within this qdisc. Check if the found class is of a lower .B level than the current class. If so, and the returned class is not a leaf node, restart the loop at the found class. If it is a leaf node, terminate. -If we found an upward reference to a higher level, enter the fallback +If we found an upward reference to a higher level, enter the fallback algorithm. .TP (iv) If the tc filters did not return a class, nor a valid reference to one, consider the minor number of the reference to be the priority. Retrieve a class from the defmap of this class for the priority. If this did not -contain a class, consult the defmap of this class for the +contain a class, consult the defmap of this class for the +.B BEST_EFFORT +class. If this is an upward reference, or no .B BEST_EFFORT -class. If this is an upward reference, or no -.B BEST_EFFORT class was defined, enter the fallback algorithm. If a valid class was found, and it is not a -leaf node, restart the loop at this class. If it is a leaf, choose it and +leaf node, restart the loop at this class. If it is a leaf, choose it and terminate. If -neither the priority distilled from the classid, nor the -.B BEST_EFFORT +neither the priority distilled from the classid, nor the +.B BEST_EFFORT priority yielded a class, enter the fallback algorithm. .P The fallback algorithm resides outside of the loop and is as follows. .TP (i) -Consult the defmap of the class at which the jump to fallback occured. If -the defmap contains a class for the +Consult the defmap of the class at which the jump to fallback occurred. If +the defmap contains a class for the .B priority -of the class (which is related to the TOS field), choose this class and -terminate. +of the class (which is related to the TOS field), choose this class and +terminate. .TP (ii) Consult the map for a class for the @@ -212,28 +212,28 @@ (iii) Choose the class at which break out to the fallback algorithm occurred. Terminate. .P -The packet is enqueued to the class which was chosen when either algorithm +The packet is enqueued to the class which was chosen when either algorithm terminated. It is therefore possible for a packet to be enqueued *not* at a leaf node, but in the middle of the hierarchy. .SH LINK SHARING ALGORITHM -When dequeuing for sending to the network device, CBQ decides which of its +When dequeuing for sending to the network device, CBQ decides which of its classes will be allowed to send. It does so with a Weighted Round Robin process in which each class with packets gets a chance to send in turn. The WRR process -starts by asking the highest priority classes (lowest numerically - +starts by asking the highest priority classes (lowest numerically - highest semantically) for packets, and will continue to do so until they -have no more data to offer, in which case the process repeats for lower +have no more data to offer, in which case the process repeats for lower priorities. .B CERTAINTY ENDS HERE, ANK PLEASE HELP Each class is not allowed to send at length though - they can only dequeue a -configurable amount of data during each round. +configurable amount of data during each round. If a class is about to go overlimit, and it is not .B bounded it will try to borrow avgidle from siblings that are not -.B isolated. +.B isolated. This process is repeated from the bottom upwards. If a class is unable to borrow enough avgidle to send a packet, it is throttled and not asked for a packet for enough time for the avgidle to increase above zero. @@ -244,7 +244,7 @@ .SH QDISC The root qdisc of a CBQ class tree has the following parameters: -.TP +.TP parent major:minor | root This mandatory parameter determines the place of the CBQ instance, either at the .B root @@ -259,22 +259,22 @@ at a minimum of 2/3 of the interface MTU. Mandatory. .TP bandwidth rate -To determine the idle time, CBQ must know the bandwidth of your underlying +To determine the idle time, CBQ must know the bandwidth of your underlying physical interface, or parent qdisc. This is a vital parameter, more about it later. Mandatory. .TP cell The cell size determines he granularity of packet transmission time calculations. Has a sensible default. -.TP +.TP mpu A zero sized packet may still take time to transmit. This value is the lower cap for packet transmission time calculations - packets smaller than this value are still deemed to have this size. Defaults to zero. .TP ewma log -When CBQ needs to measure the average idle time, it does so using an +When CBQ needs to measure the average idle time, it does so using an Exponentially Weighted Moving Average which smooths out measurements into -a moving average. The EWMA LOG determines how much smoothing occurs. Defaults +a moving average. The EWMA LOG determines how much smoothing occurs. Defaults to 5. Lower values imply greater sensitivity. Must be between 0 and 31. .P A CBQ qdisc does not shape out of its own accord. It only needs to know certain @@ -283,35 +283,35 @@ .SH CLASSES Classes have a host of parameters to configure their operation. -.TP +.TP parent major:minor -Place of this class within the hierarchy. If attached directly to a qdisc +Place of this class within the hierarchy. If attached directly to a qdisc and not to another class, minor can be omitted. Mandatory. -.TP +.TP classid major:minor Like qdiscs, classes can be named. The major number must be equal to the -major number of the qdisc to which it belongs. Optional, but needed if this +major number of the qdisc to which it belongs. Optional, but needed if this class is going to have children. -.TP +.TP weight weight -When dequeuing to the interface, classes are tried for traffic in a +When dequeuing to the interface, classes are tried for traffic in a round-robin fashion. Classes with a higher configured qdisc will generally have more traffic to offer during each round, so it makes sense to allow it to dequeue more traffic. All weights under a class are normalized, so -only the ratios matter. Defaults to the configured rate, unless the priority +only the ratios matter. Defaults to the configured rate, unless the priority of this class is maximal, in which case it is set to 1. -.TP +.TP allot bytes Allot specifies how many bytes a qdisc can dequeue -during each round of the process. This parameter is weighted using the +during each round of the process. This parameter is weighted using the renormalized class weight described above. -.TP +.TP priority priority -In the round-robin process, classes with the lowest priority field are tried +In the round-robin process, classes with the lowest priority field are tried for packets first. Mandatory. -.TP +.TP rate rate Maximum rate this class and all its children combined can send at. Mandatory. @@ -321,7 +321,7 @@ used to determine maxidle and offtime, which are only calculated when specifying maxburst or minburst. Mandatory if specifying maxburst or minburst. -.TP +.TP maxburst This number of packets is used to calculate maxidle so that when avgidle is at maxidle, this number of average packets can be burst @@ -329,7 +329,7 @@ bursts. You can't set maxidle directly, only via this parameter. .TP -minburst +minburst As mentioned before, CBQ needs to throttle in case of overlimit. The ideal solution is to do so for exactly the calculated idle time, and pass 1 packet. However, Unix kernels generally have a @@ -352,21 +352,21 @@ avgidle is capped at -10us. .TP -bounded +bounded Signifies that this class will not borrow bandwidth from its siblings. -.TP +.TP isolated Means that this class will not borrow bandwidth to its siblings -.TP +.TP split major:minor & defmap bitmap[/bitmap] -If consulting filters attached to a class did not give a verdict, +If consulting filters attached to a class did not give a verdict, CBQ can also classify based on the packet's priority. There are 16 -priorities available, numbered from 0 to 15. +priorities available, numbered from 0 to 15. -The defmap specifies which priorities this class wants to receive, -specified as a bitmap. The Least Significant Bit corresponds to priority -zero. The +The defmap specifies which priorities this class wants to receive, +specified as a bitmap. The Least Significant Bit corresponds to priority +zero. The .B split parameter tells CBQ at which class the decision must be made, which should be a (grand)parent of the class you are adding. @@ -374,7 +374,7 @@ As an example, 'tc class add ... classid 10:1 cbq .. split 10:0 defmap c0' configures class 10:0 to send packets with priorities 6 and 7 to 10:1. -The complimentary configuration would then +The complimentary configuration would then be: 'tc class add ... classid 10:2 cbq ... split 10:0 defmap 3f' Which would send all packets 0, 1, 2, 3, 4 and 5 to 10:1. .TP @@ -384,11 +384,11 @@ it uses a very simple estimator that measures once every .B interval microseconds how much traffic has passed. This again is a EWMA, for which -the time constant can be specified, also in microseconds. The +the time constant can be specified, also in microseconds. The .B time constant -corresponds to the sluggishness of the measurement or, conversely, to the +corresponds to the sluggishness of the measurement or, conversely, to the sensitivity of the average to short bursts. Higher values mean less -sensitivity. +sensitivity. @@ -399,7 +399,7 @@ Management Models for Packet Networks", IEEE/ACM Transactions on Networking, Vol.3, No.4, 1995 -.TP +.TP o Sally Floyd, "Notes on CBQ and Guarantee Service", 1995 @@ -408,7 +408,7 @@ Sally Floyd, "Notes on Class-Based Queueing: Setting Parameters", 1996 -.TP +.TP o Sally Floyd and Michael Speer, "Experimental Results for Class-Based Queueing", 1998, not published. @@ -421,5 +421,3 @@ .SH AUTHOR Alexey N. Kuznetsov, . This manpage maintained by bert hubert - - diff -Nru iproute2-4.3.0/man/man8/tc-connmark.8 iproute2-4.9.0/man/man8/tc-connmark.8 --- iproute2-4.3.0/man/man8/tc-connmark.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-connmark.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,55 @@ +.TH "Connmark retriever action in tc" 8 "11 Jan 2016" "iproute2" "Linux" + +.SH NAME +connmark - netfilter connmark retriever action +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action connmark " [ " zone" +.IR u16_zone_index " ] [ " CONTROL " ] [" +.BI index " u32_index " +] + +.ti -8 +.IR CONTROL " := { " reclassify " | " pipe " | " drop " | " continue " | " ok " }" +.SH DESCRIPTION +The connmark action is used to restore the connection's mark value into the +packet's fwmark. +.SH OPTIONS +.TP +.BI zone " u16_zone_index" +Specify the conntrack zone when doing conntrack lookups for packets. +.I u16_zone_index +is a 16bit unsigned decimal value. +.TP +.I CONTROL +How to continue after executing this action. +.RS +.TP +.B reclassify +Restarts classification by jumping back to the first filter attached to this +action's parent. +.TP +.B pipe +Continue with the next action, this is the default. +.TP +.B drop +.TQ +.B shot +Packet will be dropped without running further actions. +.TP +.B continue +Continue classification with next filter in line. +.TP +.B pass +Return to calling qdisc for packet processing. This ends the classification +process. +.RE +.TP +.BI index " u32_index " +Specify an index for this action in order to being able to identify it in later +commands. +.I u32_index +is a 32bit unsigned decimal value. +.SH SEE ALSO +.BR tc (8) diff -Nru iproute2-4.3.0/man/man8/tc-csum.8 iproute2-4.9.0/man/man8/tc-csum.8 --- iproute2-4.3.0/man/man8/tc-csum.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-csum.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,69 @@ +.TH "Checksum action in tc" 8 "11 Jan 2015" "iproute2" "Linux" + +.SH NAME +csum - checksum update action +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action csum" +.I UPDATE + +.ti -8 +.IR UPDATE " := " TARGET " [ " UPDATE " ]" + +.ti -8 +.IR TARGET " := { " +.BR ip4h " |" +.BR icmp " |" +.BR igmp " |" +.BR tcp " |" +.BR udp " |" +.BR udplite " |" +.IR SWEETS " }" + +.ti -8 +.IR SWEETS " := { " +.BR and " | " or " | " + " }" +.SH DESCRIPTION +The +.B csum +action triggers checksum recalculation of specified packet headers. It is +commonly used after packet editing using the +.B pedit +action to fix for then incorrect checksums. +.SH OPTIONS +.TP +.I TARGET +Specify which headers to update: IPv4 header +.RB ( ip4h ), +ICMP header +.RB ( icmp ), +IGMP header +.RB ( igmp ), +TCP header +.RB ( tcp ), +UDP header +.RB ( udp ") or" +UDPLite header +.RB ( udplite ). +.TP +.B SWEETS +These are merely syntactic sugar and ignored internally. +.SH EXAMPLES +The following performs stateless NAT for incoming packets from 192.168.1.100 to +new destination 18.52.86.120 (0x12345678 in hex). Assuming these are UDP +packets, both IP and UDP checksums have to be recalculated: + +.RS +.EX +# tc qdisc add dev eth0 ingress handle ffff: +# tc filter add eth0 prio 1 protocol ip parent ffff: \\ + u32 match ip src 192.168.1.100/32 flowid :1 \\ + action pedit munge ip dst set 0x12345678 pipe \\ + csum ip and udp +.EE +.RE + +.SH SEE ALSO +.BR tc (8), +.BR tc-pedit (8) diff -Nru iproute2-4.3.0/man/man8/tc-drr.8 iproute2-4.9.0/man/man8/tc-drr.8 --- iproute2-4.3.0/man/man8/tc-drr.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-drr.8 2016-12-12 23:07:42.000000000 +0000 @@ -92,4 +92,3 @@ .SH AUTHOR sched_drr was written by Patrick McHardy. - diff -Nru iproute2-4.3.0/man/man8/tc-flow.8 iproute2-4.9.0/man/man8/tc-flow.8 --- iproute2-4.3.0/man/man8/tc-flow.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-flow.8 2016-12-12 23:07:42.000000000 +0000 @@ -73,8 +73,10 @@ .I ID may be .BR root ", " none -or a hexadecimal class ID in the form [\fIX\fB:\fR]\fIY\fR. If \fIX\fR is -omitted, it is assumed to be zero. +or a hexadecimal class ID in the form [\fIX\fB:\fR]\fIY\fR. \fIX\fR must +match qdisc's/class's major handle (if omitted, the correct value is chosen +automatically). If the whole \fBbaseclass\fR is omitted, \fIY\fR defaults +to 1. .TP .BI divisor " NUM" Number of buckets to use for sorting into. Keys are calculated modulo @@ -239,7 +241,7 @@ divisor 1024 .EE .TP -Map destination IPs of 192.168.0.0/24 to classids 1-257: +Map destination IPs of 192.168.0.0/24 to classids 1-256: .EX tc filter add ... flow map \\ diff -Nru iproute2-4.3.0/man/man8/tc-flower.8 iproute2-4.9.0/man/man8/tc-flower.8 --- iproute2-4.3.0/man/man8/tc-flower.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-flower.8 2016-12-12 23:07:42.000000000 +0000 @@ -18,10 +18,16 @@ .ti -8 .IR MATCH " := { " .B indev -.IR ifname " | { " +.IR ifname " | " +.BR skip_sw " | " skip_hw +.R " | { " .BR dst_mac " | " src_mac " } " .IR mac_address " | " -.BR eth_type " { " ipv4 " | " ipv6 " | " +.B vlan_id +.IR VID " | " +.B vlan_prio +.IR PRIORITY " | " +.BR vlan_eth_type " { " ipv4 " | " ipv6 " | " .IR ETH_TYPE " } | " .BR ip_proto " { " tcp " | " udp " | " .IR IP_PROTO " } | { " @@ -55,12 +61,29 @@ .B tc invocation. .TP +.BI skip_sw +Do not process filter by software. If hardware has no offload support for this +filter, or TC offload is not enabled for the interface, operation will fail. +.TP +.BI skip_hw +Do not process filter by hardware. +.TP .BI dst_mac " mac_address" .TQ .BI src_mac " mac_address" Match on source or destination MAC address. .TP -.BI eth_type " ETH_TYPE" +.BI vlan_id " VID" +Match on vlan tag id. +.I VID +is an unsigned 12bit value in decimal format. +.TP +.BI vlan_prio " priority" +Match on vlan tag priority. +.I PRIORITY +is an unsigned 3bit value in decimal format. +.TP +.BI vlan_eth_type " VLAN_ETH_TYPE" Match on layer three protocol. .I ETH_TYPE may be either diff -Nru iproute2-4.3.0/man/man8/tc-fq.8 iproute2-4.9.0/man/man8/tc-fq.8 --- iproute2-4.3.0/man/man8/tc-fq.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-fq.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,6 +1,6 @@ .TH FQ 8 "10 Sept 2015" "iproute2" "Linux" .SH NAME -Fair Queuing (FQ) \- Traffic Pacing +FQ \- Fair Queue traffic policing .SH SYNOPSIS .B tc qdisc ... fq [ diff -Nru iproute2-4.3.0/man/man8/tc-hfsc.8 iproute2-4.9.0/man/man8/tc-hfsc.8 --- iproute2-4.3.0/man/man8/tc-hfsc.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-hfsc.8 2016-12-12 23:07:42.000000000 +0000 @@ -54,8 +54,8 @@ . \fBtc\fR(8), \fBtc\-hfsc\fR(7), \fBtc\-stab\fR(8) -Please direct bugreports and patches to: +Please direct bugreports and patches to: . .SH "AUTHOR" . -Manpage created by Michal Soltys (sol...@ziu.info) +Manpage created by Michal Soltys (soltys@ziu.info) diff -Nru iproute2-4.3.0/man/man8/tc-htb.8 iproute2-4.9.0/man/man8/tc-htb.8 --- iproute2-4.3.0/man/man8/tc-htb.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-htb.8 2016-12-12 23:07:42.000000000 +0000 @@ -5,30 +5,30 @@ .B tc qdisc ... dev dev .B ( parent -classid -.B | root) [ handle -major: -.B ] htb [ default +classid +.B | root) [ handle +major: +.B ] htb [ default minor-id -.B ] +.B ] .B tc class ... dev dev -.B parent +.B parent major:[minor] -.B [ classid +.B [ classid major:minor .B ] htb rate rate .B [ ceil -rate -.B ] burst +rate +.B ] burst bytes .B [ cburst bytes .B ] [ prio priority -.B ] +.B ] .SH DESCRIPTION HTB is meant as a more understandable and intuitive replacement for @@ -37,9 +37,9 @@ physical link to simulate several slower links and to send different kinds of traffic on different simulated links. In both cases, you have to specify how to divide the physical link into simulated links and -how to decide which simulated link to use for a given packet to be sent. +how to decide which simulated link to use for a given packet to be sent. -Unlike CBQ, HTB shapes traffic based on the Token Bucket Filter algorithm +Unlike CBQ, HTB shapes traffic based on the Token Bucket Filter algorithm which does not depend on interface characteristics and so does not need to know the underlying bandwidth of the outgoing interface. @@ -49,30 +49,30 @@ .SH CLASSIFICATION Within the one HTB instance many classes may exist. Each of these classes -contains another qdisc, by default +contains another qdisc, by default .BR tc-pfifo (8). -When enqueueing a packet, HTB starts at the root and uses various methods to -determine which class should receive the data. +When enqueueing a packet, HTB starts at the root and uses various methods to +determine which class should receive the data. -In the absence of uncommon configuration options, the process is rather easy. -At each node we look for an instruction, and then go to the class the -instruction refers us to. If the class found is a barren leaf-node (without -children), we enqueue the packet there. If it is not yet a leaf node, we do -the whole thing over again starting from that node. +In the absence of uncommon configuration options, the process is rather easy. +At each node we look for an instruction, and then go to the class the +instruction refers us to. If the class found is a barren leaf-node (without +children), we enqueue the packet there. If it is not yet a leaf node, we do +the whole thing over again starting from that node. -The following actions are performed, in order at each node we visit, until one +The following actions are performed, in order at each node we visit, until one sends us to another node, or terminates the process. .TP (i) -Consult filters attached to the class. If sent to a leafnode, we are done. +Consult filters attached to the class. If sent to a leafnode, we are done. Otherwise, restart. .TP (ii) If none of the above returned with an instruction, enqueue at this node. .P This algorithm makes sure that a packet always ends up somewhere, even while -you are busy building your configuration. +you are busy building your configuration. .SH LINK SHARING ALGORITHM FIXME @@ -80,7 +80,7 @@ .SH QDISC The root of a HTB qdisc class tree has the following parameters: -.TP +.TP parent major:minor | root This mandatory parameter determines the place of the HTB instance, either at the .B root @@ -90,54 +90,54 @@ Like all other qdiscs, the HTB can be assigned a handle. Should consist only of a major number, followed by a colon. Optional, but very useful if classes will be generated within this qdisc. -.TP +.TP default minor-id Unclassified traffic gets sent to the class with this minor-id. .SH CLASSES Classes have a host of parameters to configure their operation. -.TP +.TP parent major:minor -Place of this class within the hierarchy. If attached directly to a qdisc +Place of this class within the hierarchy. If attached directly to a qdisc and not to another class, minor can be omitted. Mandatory. -.TP +.TP classid major:minor Like qdiscs, classes can be named. The major number must be equal to the -major number of the qdisc to which it belongs. Optional, but needed if this +major number of the qdisc to which it belongs. Optional, but needed if this class is going to have children. -.TP +.TP prio priority -In the round-robin process, classes with the lowest priority field are tried +In the round-robin process, classes with the lowest priority field are tried for packets first. Mandatory. -.TP +.TP rate rate Maximum rate this class and all its children are guaranteed. Mandatory. .TP ceil rate -Maximum rate at which a class can send, if its parent has bandwidth to spare. +Maximum rate at which a class can send, if its parent has bandwidth to spare. Defaults to the configured rate, which implies no borrowing -.TP +.TP burst bytes -Amount of bytes that can be burst at +Amount of bytes that can be burst at .B ceil speed, in excess of the configured -.B rate. +.B rate. Should be at least as high as the highest burst of all children. -.TP +.TP cburst bytes Amount of bytes that can be burst at 'infinite' speed, in other words, as fast as the interface can transmit them. For perfect evening out, should be equal to at most one average packet. Should be at least as high as the highest cburst of all children. .SH NOTES -Due to Unix timing constraints, the maximum ceil rate is not infinite and may in fact be quite low. On Intel, +Due to Unix timing constraints, the maximum ceil rate is not infinite and may in fact be quite low. On Intel, there are 100 timer events per second, the maximum rate is that rate at which 'burst' bytes are sent each timer tick. -From this, the minimum burst size for a specified rate can be calculated. For i386, a 10mbit rate requires a 12 kilobyte +From this, the minimum burst size for a specified rate can be calculated. For i386, a 10mbit rate requires a 12 kilobyte burst as 100*12kb*8 equals 10mbit. .SH SEE ALSO @@ -146,5 +146,3 @@ HTB website: http://luxik.cdi.cz/~devik/qos/htb/ .SH AUTHOR Martin Devera . This manpage maintained by bert hubert - - diff -Nru iproute2-4.3.0/man/man8/tc-ife.8 iproute2-4.9.0/man/man8/tc-ife.8 --- iproute2-4.3.0/man/man8/tc-ife.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-ife.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,140 @@ +.TH "IFE action in tc" 8 "22 Apr 2016" "iproute2" "Linux" + +.SH NAME +IFE - encapsulate/decapsulate metadata +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " " action ife" +.IR DIRECTION " [ " ACTION " ] " +.RB "[ " dst +.IR DMAC " ] " +.RB "[ " src +.IR SMAC " ] " +.RB "[ " type +.IR TYPE " ] " +.R "[ " +.IR CONTROL " ] " +.RB "[ " index +.IR INDEX " ] " + +.ti -8 +.IR DIRECTION " := { " +.BR decode " | " encode " }" + +.ti -8 +.IR ACTION " := { " +.BI allow " ATTR" +.RB "| " use +.IR "ATTR value" " }" + +.ti -8 +.IR ATTR " := { " +.BR mark " | " prio " | " tcindex " }" + +.ti -8 +.IR CONTROL " := { " +.BR reclassify " | " use " | " pipe " | " drop " | " continue " | " ok " }" +.SH DESCRIPTION +The +.B ife +action allows for a sending side to encapsulate arbitrary metadata, which is +then decapsulated by the receiving end. The sender runs in encoding mode and +the receiver in decode mode. Both sender and receiver must specify the same +ethertype. In the future, a registered ethertype may be available as a default. +.SH OPTIONS +.TP +.B decode +For the receiving side; decode the metadata if the packet matches. +.TP +.B encode +For the sending side. Encode the specified metadata if the packet matches. +.TP +.B allow +Encode direction only. Allows encoding specified metadata. +.TP +.B use +Encode direction only. Enforce static encoding of specified metadata. +.TP +.BR mark " [ " +.IR u32_value " ]" +The value to set for the skb mark. The u32 value is required only when +.BR use " is specified." +.TP +.BR prio " [ " +.IR u32_value " ]" +The value to set for priority in the skb structure. The u32 value is required +only when +.BR use " is specified." +.TP +.BR tcindex " [" +.IR u16_value " ]" +Value to set for the traffic control index in the skb structure. The u16 value +is required only when +.BR use " is specified." +.TP +.BI dmac " DMAC" +.TQ +.BI smac " SMAC" +Optional six byte destination or source MAC address to encode. +.TP +.BI type " TYPE" +Optional 16-bit ethertype to encode. +.TP +.BI CONTROL +Action to take following an encode/decode. +.TP +.BI index " INDEX" +Assign a unique ID to this action instead of letting the kernel choose one +automatically. +.I INDEX +is a 32bit unsigned integer greater than zero. +.SH EXAMPLES + +On the receiving side, match packets with ethertype 0xdead and restart +classification so that it will match ICMP on the next rule, at prio 3: +.RS +.EX +# tc qdisc add dev eth0 handle ffff: ingress +# tc filter add dev eth0 parent ffff: prio 2 protocol 0xdead \\ + u32 match u32 0 0 flowid 1:1 \\ + action ife decode reclassify +# tc filter add dev eth0 parent ffff: priod 3 protocol ip \\ + u32 match ip protocol 0xff flowid 1:1 \\ + action continue +.EE +.RE + +Match with skb mark of 17: + +.RS +.EX +# tc filter add dev eth0 parent ffff: prio 4 protocol ip \\ + handle 0x11 fw flowid 1:1 \\ + action ok +.EE +.RE + +Configure the sending side to encode for the filters above. Use a destination +IP address of 192.168.122.237/24, then tag with skb mark of decimal 17. Encode +the packaet with ethertype 0xdead, add skb->mark to whitelist of metadatum to +send, and rewrite the destination MAC address to 02:15:15:15:15:15. + +.RS +.EX +# tc qdisc add dev eth0 root handle 1: prio +# tc filter add dev eth0 parent 1: protocol ip prio 10 u32 \\ + match ip dst 192.168.122.237/24 \\ + match ip protocol 1 0xff \\ + flowid 1:2 \\ + action skbedit mark 17 \\ + action ife encode \\ + type 0xDEAD \\ + allow mark \\ + dst 02:15:15:15:15:15 +.EE +.RE + +.SH SEE ALSO +.BR tc (8), +.BR tc-u32 (8) diff -Nru iproute2-4.3.0/man/man8/tc-matchall.8 iproute2-4.9.0/man/man8/tc-matchall.8 --- iproute2-4.3.0/man/man8/tc-matchall.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-matchall.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,76 @@ +.TH "Match-all classifier in tc" 8 "21 Oct 2015" "iproute2" "Linux" + +.SH NAME +matchall \- traffic control filter that matches every packet +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " " filter " ... " matchall " [ " +.BR skip_sw " | " skip_hw +.R " ] [ " +.B action +.IR ACTION_SPEC " ] [ " +.B classid +.IR CLASSID " ]" +.SH DESCRIPTION +The +.B matchall +filter allows to classify every packet that flows on the port and run a +action on it. +.SH OPTIONS +.TP +.BI action " ACTION_SPEC" +Apply an action from the generic actions framework on matching packets. +.TP +.BI classid " CLASSID" +Push matching packets into the class identified by +.IR CLASSID . +.TP +.BI skip_sw +Do not process filter by software. If hardware has no offload support for this +filter, or TC offload is not enabled for the interface, operation will fail. +.TP +.BI skip_hw +Do not process filter by hardware. +.SH EXAMPLES +To create ingress mirroring from port eth1 to port eth2: +.RS +.EX + +tc qdisc add dev eth1 handle ffff: ingress +tc filter add dev eth1 parent ffff: \\ + matchall skip_sw \\ + action mirred egress mirror \\ + dev eth2 +.EE +.RE + +The first command creats an ingress qdisc with handle +.BR ffff: +on device +.BR eth1 +where the second command attaches a matchall filters on it that mirrors the +packets to device eth2. + +To create egress mirroring from port eth1 to port eth2: +.EX + +tc qdisc add dev eth1 handle 1: root prio +tc filter add dev eth1 parent 1: \\ + matchall skip_sw \\ + action mirred egress mirror \\ + dev eth2 +.EE +.RE + +The first command creats an egress qdisc with handle +.BR 1: +that replaces the root qdisc on device +.BR eth1 +where the second command attaches a matchall filters on it that mirrors the +packets to device eth2. + + +.EE +.SH SEE ALSO +.BR tc (8), diff -Nru iproute2-4.3.0/man/man8/tc-mirred.8 iproute2-4.9.0/man/man8/tc-mirred.8 --- iproute2-4.3.0/man/man8/tc-mirred.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-mirred.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,101 @@ +.TH "Mirror/redirect action in tc" 8 "11 Jan 2015" "iproute2" "Linux" + +.SH NAME +mirred - mirror/redirect action +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action mirred" +.I DIRECTION ACTION +.RB "[ " index +.IR INDEX " ] " +.BI dev " DEVICENAME" + +.ti -8 +.IR DIRECTION " := { " +.BR ingress " | " egress " }" + +.ti -8 +.IR ACTION " := { " +.BR mirror " | " redirect " }" +.SH DESCRIPTION +The +.B mirred +action allows packet mirroring (copying) or redirecting (stealing) the packet it +receives. Mirroring is what is sometimes referred to as Switch Port Analyzer +(SPAN) and is commonly used to analyze and/or debug flows. +.SH OPTIONS +.TP +.B ingress +.TQ +.B egress +Specify the direction in which the packet shall appear on the destination +interface. Currently only +.B egress +is implemented. +.TP +.B mirror +.TQ +.B redirect +Define whether the packet should be copied +.RB ( mirror ) +or moved +.RB ( redirect ) +to the destination interface. +.TP +.BI index " INDEX" +Assign a unique ID to this action instead of letting the kernel choose one +automatically. +.I INDEX +is a 32bit unsigned integer greater than zero. +.TP +.BI dev " DEVICENAME" +Specify the network interface to redirect or mirror to. +.SH EXAMPLES +Limit ingress bandwidth on eth0 to 1mbit/s, redirect exceeding traffic to lo for +debugging purposes: + +.RS +.EX +# tc qdisc add dev eth0 handle ffff: ingress +# tc filter add dev eth0 parent ffff: u32 \\ + match u32 0 0 \\ + action police rate 1mbit burst 100k conform-exceed pipe \\ + action mirred egress redirect dev lo +.EE +.RE + +Mirror all incoming ICMP packets on eth0 to a dummy interface for examination +with e.g. tcpdump: + +.RS +.EX +# ip link add dummy0 type dummy +# ip link set dummy0 up +# tc qdisc add dev eth0 handle ffff: ingress +# tc filter add dev eth0 parent ffff: protocol ip \\ + u32 match ip protocol 1 0xff \\ + action mirred egress mirror dev dummy0 +.EE +.RE + +Using an +.B ifb +interface, it is possible to send ingress traffic through an instance of +.BR sfq : + +.RS +.EX +# modprobe ifb +# ip link set ifb0 up +# tc qdisc add dev ifb0 root sfq +# tc qdisc add dev eth0 handle ffff: ingress +# tc filter add dev eth0 parent ffff: u32 \\ + match u32 0 0 \\ + action mirred egress redirect dev ifb0 +.EE +.RE + +.SH SEE ALSO +.BR tc (8), +.BR tc-u32 (8) diff -Nru iproute2-4.3.0/man/man8/tc-mqprio.8 iproute2-4.9.0/man/man8/tc-mqprio.8 --- iproute2-4.3.0/man/man8/tc-mqprio.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-mqprio.8 2016-12-12 23:07:42.000000000 +0000 @@ -85,7 +85,7 @@ .SH QDISC PARAMETERS .TP num_tc -Number of traffic classes to use upto 16 classes supported. +Number of traffic classes to use. Up to 16 classes supported. .TP map diff -Nru iproute2-4.3.0/man/man8/tc-nat.8 iproute2-4.9.0/man/man8/tc-nat.8 --- iproute2-4.3.0/man/man8/tc-nat.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-nat.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,78 @@ +.TH "NAT action in tc" 8 "12 Jan 2015" "iproute2" "Linux" + +.SH NAME +nat - stateless native address translation action +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action nat" +.I DIRECTION OLD NEW + +.ti -8 +.IR DIRECTION " := { " +.BR ingress " | " egress " }" + +.ti -8 +.IR OLD " := " IPV4_ADDR_SPEC + +.ti -8 +.IR NEW " := " IPV4_ADDR_SPEC + +.ti -8 +.IR IPV4_ADDR_SPEC " := { " +.BR default " | " any " | " all " | " +\fIin_addr\fR[\fB/\fR{\fIprefix\fR|\fInetmask\fR}] +.SH DESCRIPTION +The +.B nat +action allows to perform NAT without the overhead of conntrack, which is +desirable if the number of flows or addresses to perform NAT on is large. This +action is best used in combination with the +.B u32 +filter to allow for efficient lookups of a large number of stateless NAT rules +in constant time. +.SH OPTIONS +.TP +.B ingress +Translate destination addresses, i.e. perform DNAT. +.TP +.B egress +Translate source addresses, i.e. perform SNAT. +.TP +.I OLD +Specifies addresses which should be translated. +.TP +.I NEW +Specifies addresses which +.I OLD +should be translated into. +.SH NOTES +The accepted address format in +.IR OLD " and " NEW +is quite flexible. It may either consist of one of the keywords +.BR default ", " any " or " all , +representing the all-zero IP address or a combination of IP address and netmask +or prefix length separated by a slash +.RB ( / ) +sign. In any case, the mask (or prefix length) value of +.I OLD +is used for +.I NEW +as well so that a one-to-one mapping of addresses is assured. + +Address translation is done using a combination of binary operations. First, the +original (source or destination) address is matched against the value of +.IR OLD . +If the original address fits, the new address is created by taking the leading +bits from +.I NEW +(defined by the netmask of +.IR OLD ) +and taking the remaining bits from the original address. + +There is rudimental support for upper layer protocols, namely TCP, UDP and ICMP. +While for the first two only checksum recalculation is performed, the action +also takes care of embedded IP headers in ICMP packets by translating the +respective address therein, too. +.SH SEE ALSO +.BR tc (8) diff -Nru iproute2-4.3.0/man/man8/tc-netem.8 iproute2-4.9.0/man/man8/tc-netem.8 --- iproute2-4.3.0/man/man8/tc-netem.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-netem.8 2016-12-12 23:07:42.000000000 +0000 @@ -2,9 +2,9 @@ .SH NAME NetEm \- Network Emulator .SH SYNOPSIS -.B "tc qdisc ... dev" +.B "tc qdisc ... dev" .IR DEVICE " ] " -.BR "add netem" +.BR "add netem" .I OPTIONS .IR OPTIONS " := [ " LIMIT " ] [ " DELAY " ] [ " LOSS \ @@ -15,15 +15,15 @@ .I packets .IR DELAY " := " -.BI delay +.BI delay .IR TIME " [ " JITTER " [ " CORRELATION " ]]]" .br - [ + [ .BR distribution " { "uniform " | " normal " | " pareto " | " paretonormal " } ]" .IR LOSS " := " .BR loss " { " -.BI random +.BI random .IR PERCENT " [ " CORRELATION " ] |" .br .RB " " state @@ -44,13 +44,13 @@ .IR REORDERING " := " .B reorder .IR PERCENT " [ " CORRELATION " ] [ " -.B gap +.B gap .IR DISTANCE " ]" .IR RATE " := " .B rate .IR RATE " [ " PACKETOVERHEAD " [ " CELLSIZE " [ " CELLOVERHEAD " ]]]]" - + .SH DESCRIPTION NetEm is an enhancement of the Linux traffic control facilities @@ -139,11 +139,11 @@ 50%) while the others are delayed by 10 ms. .SS rate -delay packets based on packet size and is a replacement for +delay packets based on packet size and is a replacement for .IR TBF . Rate can be -specified in common units (e.g. 100kbit). Optional -.I PACKETOVERHEAD +specified in common units (e.g. 100kbit). Optional +.I PACKETOVERHEAD (in bytes) specify an per packet overhead and can be negative. A positive value can be used to simulate additional link layer headers. A negative value can be used to artificial strip the Ethernet header (e.g. -14) and/or simulate a link layer @@ -152,7 +152,7 @@ example has an payload cellsize of 48 bytes and 5 byte per cell header. If a packet is 50 byte then ATM must use two cells: 2 * 48 bytes payload including 2 * 5 byte header, thus consume 106 byte on the wire. The last optional value -.I CELLOVERHEAD +.I CELLOVERHEAD can be used to specify per cell overhead - for our ATM example 5. .I CELLOVERHEAD can be negative, but use negative values with caution. diff -Nru iproute2-4.3.0/man/man8/tc-pedit.8 iproute2-4.9.0/man/man8/tc-pedit.8 --- iproute2-4.3.0/man/man8/tc-pedit.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-pedit.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,230 @@ +.TH "Generic packet editor action in tc" 8 "12 Jan 2015" "iproute2" "Linux" + +.SH NAME +pedit - generic packet editor action +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action pedit munge " { +.IR RAW_OP " | " LAYERED_OP " } [ " CONTROL " ]" + +.ti -8 +.IR RAW_OP " := " +.BI offset " OFFSET" +.RB "{ " u8 " | " u16 " | " u32 " } [" +.IR AT_SPEC " ] " CMD_SPEC + +.ti -8 +.IR AT_SPEC " := " +.BI at " AT " offmask " MASK " shift " SHIFT" + +.ti -8 +.IR LAYERED_OP " := { " +.BI ip " IPHDR_FIELD" +| +.BI ip6 " IP6HDR_FIELD" +| +.BI udp " UDPHDR_FIELD" +| +.BI tcp " TCPHDR_FIELD" +| +.BI icmp " ICMPHDR_FIELD" +.RI } " CMD_SPEC" + +.ti -8 +.IR IPHDR_FIELD " := { " +.BR src " | " dst " | " tos " | " dsfield " | " ihl " | " protocol " |" +.BR precedence " | " nofrag " | " firstfrag " | " ce " | " df " |" +.BR mf " | " dport " | " sport " | " icmp_type " | " icmp_code " }" + +.ti -8 +.IR CMD_SPEC " := {" +.BR clear " | " invert " | " set +.IR VAL " | " +.BR preserve " } [ " retain +.IR RVAL " ]" + +.ti -8 +.IR CONTROL " := {" +.BR reclassify " | " pipe " | " drop " | " shot " | " continue " | " pass " }" +.SH DESCRIPTION +The +.B pedit +action can be used to change arbitrary packet data. The location of data to +change can either be specified by giving an offset and size as in +.IR RAW_OP , +or for header values by naming the header and field to edit the size is then +chosen automatically based on the header field size. Currently this is supported +only for IPv4 headers. +.SH OPTIONS +.TP +.BI offset " OFFSET " "\fR{ \fBu32 \fR| \fBu16 \fR| \fBu8 \fR}" +Specify the offset at which to change data. +.I OFFSET +is a signed integer, it's base is automatically chosen (e.g. hex if prefixed by +.B 0x +or octal if prefixed by +.BR 0 ). +The second argument specifies the length of data to change, that is four bytes +.RB ( u32 ), +two bytes +.RB ( u16 ) +or a single byte +.RB ( u8 ). +.TP +.BI at " AT " offmask " MASK " shift " SHIFT" +This is an optional part of +.IR RAW_OP +which allows to have a variable +.I OFFSET +depending on packet data at offset +.IR AT , +which is binary ANDed with +.I MASK +and right-shifted by +.I SHIFT +before adding it to +.IR OFFSET . +.TP +.BI ip " IPHDR_FIELD" +Change an IPv4 header field. The supported keywords for +.I IPHDR_FIELD +are: +.RS +.TP +.B src +.TQ +.B dst +Source or destination IP address, a four-byte value. +.TP +.B tos +.TQ +.B dsfield +.TQ +.B precedence +Type Of Service field, an eight-bit value. +.TP +.B ihl +Change the IP Header Length field, a four-bit value. +.TP +.B protocol +Next-layer Protocol field, an eight-bit value. +.TP +.B nofrag +.TQ +.B firstfrag +.TQ +.B ce +.TQ +.B df +.TQ +.B mf +Change IP header flags. Note that the value to pass to the +.B set +command is not just a bit value, but the full byte including the flags field. +Though only the relevant bits of that value are respected, the rest ignored. +.TP +.B dport +.TQ +.B sport +Destination or source port numbers, a 16-bit value. Indeed, IPv4 headers don't +contain this information. Instead, this will set an offset which suits at least +TCP and UDP if the IP header is of minimum size (20 bytes). If not, this will do +unexpected things. +.TP +.B icmp_type +.TQ +.B icmp_code +Again, this allows to change data past the actual IP header itself. It assumes +an ICMP header is present immediately following the (minimal sized) IP header. +If it is not or the latter is bigger than the minimum of 20 bytes, this will do +unexpected things. These fields are eight-bit values. +.RE +.TP +.B clear +Clear the addressed data (i.e., set it to zero). +.TP +.B invert +Swap every bit in the addressed data. +.TP +.BI set " VAL" +Set the addressed data to a specific value. The size of +.I VAL +is defined by either one of the +.BR u32 ", " u16 " or " u8 +keywords in +.IR RAW_OP , +or the size of the addressed header field in +.IR LAYERED_OP . +.TP +.B preserve +Keep the addressed data as is. +.TP +.BI retain " RVAL" +This optional extra part of +.I CMD_SPEC +allows to exclude bits from being changed. +.TP +.I CONTROL +The following keywords allow to control how the tree of qdisc, classes, +filters and actions is further traversed after this action. +.RS +.TP +.B reclassify +Restart with the first filter in the current list. +.TP +.B pipe +Continue with the next action attached to the same filter. +.TP +.B drop +.TQ +.B shot +Drop the packet. +.TP +.B continue +Continue classification with the next filter in line. +.TP +.B pass +Finish classification process and return to calling qdisc for further packet +processing. This is the default. +.RE +.SH EXAMPLES +Being able to edit packet data, one could do all kinds of things, such as e.g. +implementing port redirection. Certainly not the most useful application, but +as an example it should do: + +First, qdiscs need to be set up to attach filters to. For the receive path, a simple +.B ingress +qdisc will do, for transmit path a classful qdisc +.RB ( HTB +in this case) is necessary: + +.RS +.EX +tc qdisc replace dev eth0 root handle 1: htb +tc qdisc add dev eth0 ingress handle ffff: +.EE +.RE + +Finally, a filter with +.B pedit +action can be added for each direction. In this case, +.B u32 +is used matching on the port number to redirect from, while +.B pedit +then does the actual rewriting: + +.RS +.EX +tc filter add dev eth0 parent 1: u32 \\ + match ip dport 23 0xffff \\ + action pedit pedit munge ip dport set 22 +tc filter add dev eth0 parent ffff: u32 \\ + match ip sport 22 0xffff \\ + action pedit pedit munge ip sport set 23 +.EE +.RE +.SH SEE ALSO +.BR tc (8), +.BR tc-htb (8), +.BR tc-u32 (8) diff -Nru iproute2-4.3.0/man/man8/tc-pfifo_fast.8 iproute2-4.9.0/man/man8/tc-pfifo_fast.8 --- iproute2-4.3.0/man/man8/tc-pfifo_fast.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-pfifo_fast.8 2016-12-12 23:07:42.000000000 +0000 @@ -13,14 +13,14 @@ In this sense this qdisc is magic, and unlike other qdiscs. .SH ALGORITHM -The algorithm is very similar to that of the classful +The algorithm is very similar to that of the classful .BR tc-prio (8) -qdisc. +qdisc. .B pfifo_fast is like three .BR tc-pfifo (8) queues side by side, where packets can be enqueued in any of the three bands -based on their Type of Service bits or assigned priority. +based on their Type of Service bits or assigned priority. Not all three bands are dequeued simultaneously - as long as lower bands have traffic, higher bands are never dequeued. This can be used to @@ -28,7 +28,7 @@ Each band can be txqueuelen packets long, as configured with .BR ifconfig (8) -or +or .BR ip (8). Additional packets coming in are not enqueued but are instead dropped. @@ -36,7 +36,7 @@ .BR tc-prio (8) for complete details on how TOS bits are translated into bands. .SH PARAMETERS -.TP +.TP txqueuelen The length of the three bands depends on the interface txqueuelen, as specified with @@ -46,7 +46,7 @@ .SH BUGS Does not maintain statistics and does not show up in tc qdisc ls. This is because -it is the automatic default in the absence of a configured qdisc. +it is the automatic default in the absence of a configured qdisc. .SH SEE ALSO .BR tc (8) @@ -55,5 +55,3 @@ Alexey N. Kuznetsov, This manpage maintained by bert hubert - - diff -Nru iproute2-4.3.0/man/man8/tc-police.8 iproute2-4.9.0/man/man8/tc-police.8 --- iproute2-4.3.0/man/man8/tc-police.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-police.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,146 @@ +.TH "Policing action in tc" 8 "20 Jan 2015" "iproute2" "Linux" + +.SH NAME +police - policing action +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action police" +.BI rate " RATE " burst +.IR BYTES [\fB/ BYTES "] [" +.B mtu +.IR BYTES [\fB/ BYTES "] ] [" +.BI peakrate " RATE" +] [ +.BI overhead " BYTES" +] [ +.BI linklayer " TYPE" +] [ +.IR CONTROL " ]" + +.ti -8 +.BR tc " ... " filter " ... [ " estimator +.IR "SAMPLE AVERAGE " ] +.BR "action police avrate" +.IR RATE " [ " CONTROL " ]" + +.ti -8 +.IR CONTROL " :=" +.BI conform-exceed " EXCEEDACT\fR[\fB/\fINOTEXCEEDACT" + +.ti -8 +.IR EXCEEDACT/NOTEXCEEDACT " := { " +.BR pipe " | " ok " | " reclassify " | " drop " | " continue " }" +.SH DESCRIPTION +The +.B police +action allows to limit bandwidth of traffic matched by the filter it is +attached to. Basically there are two different algorithms available to measure +the packet rate: The first one uses an internal dual token bucket and is +configured using the +.BR rate ", " burst ", " mtu ", " peakrate ", " overhead " and " linklayer +parameters. The second one uses an in-kernel sampling mechanism. It can be +fine-tuned using the +.B estimator +filter parameter. +.SH OPTIONS +.TP +.BI rate " RATE" +The maximum traffic rate of packets passing this action. Those exceeding it will +be treated as defined by the +.B conform-exceed +option. +.TP +.BI burst " BYTES\fR[\fB/\fIBYTES\fR]" +Set the maximum allowed burst in bytes, optionally followed by a slash ('/') +sign and cell size which must be a power of 2. +.TP +.BI mtu " BYTES\fR[\fB/\fIBYTES\fR]" +This is the maximum packet size handled by the policer (larger ones will be +handled like they exceeded the configured rate). Setting this value correctly +will improve the scheduler's precision. +Value formatting is identical to +.B burst +above. Defaults to unlimited. +.TP +.BI peakrate " RATE" +Set the maximum bucket depletion rate, exceeding +.BR rate . +.TP +.BI avrate " RATE" +Make use of an in-kernel bandwidth rate estimator and match the given +.I RATE +against it. +.TP +.BI overhead " BYTES" +Account for protocol overhead of encapsulating output devices when computing +.BR rate " and " peakrate . +.TP +.BI linklayer " TYPE" +Specify the link layer type. +.I TYPE +may be one of +.B ethernet +(the default), +.BR atm " or " adsl +(which are synonyms). It is used to align the precomputed rate tables to ATM +cell sizes, for +.B ethernet +no action is taken. +.TP +.BI estimator " SAMPLE AVERAGE" +Fine-tune the in-kernel packet rate estimator. +.IR SAMPLE " and " AVERAGE +are time values and control the frequency in which samples are taken and over +what timespan an average is built. +.TP +.BI conform-exceed " EXCEEDACT\fR[\fB/\fINOTEXCEEDACT\fR]" +Define how to handle packets which exceed or conform the +configured bandwidth limit. Possible values are: +.RS +.IP continue +Don't do anything, just continue with the next action in line. +.IP drop +Drop the packet immediately. +.IP shot +This is a synonym to +.BR drop . +.IP ok +Accept the packet. This is the default for conforming packets. +.IP pass +This is a synonym to +.BR ok . +.IP reclassify +Treat the packet as non-matching to the filter this action is attached to and +continue with the next filter in line (if any). This is the default for +exceeding packets. +.IP pipe +Pass the packet to the next action in line. +.SH EXAMPLES +A typical application of the police action is to enforce ingress traffic rate +by dropping exceeding packets. Although better done on the sender's side, +especially in scenarios with lack of peer control (e.g. with dial-up providers) +this is often the best one can do in order to keep latencies low under high +load. The following establishes input bandwidth policing to 1mbit/s using the +.B ingress +qdisc and +.B u32 +filter: + +.RS +.EX +# tc qdisc add dev eth0 handle ffff: ingress +# tc filter add dev eth0 parent ffff: u32 \\ + match u32 0 0 \\ + police rate 1mbit burst 100k +.EE +.RE + +As an action can not live on it's own, there always has to be a filter involved as link between qdisc and action. The example above uses +.B u32 +for that, which is configured to effectively match any packet (passing it to the +.B police +action thereby). + +.SH SEE ALSO +.BR tc (8) diff -Nru iproute2-4.3.0/man/man8/tc-prio.8 iproute2-4.9.0/man/man8/tc-prio.8 --- iproute2-4.3.0/man/man8/tc-prio.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-prio.8 2016-12-12 23:07:42.000000000 +0000 @@ -5,21 +5,21 @@ .B tc qdisc ... dev dev .B ( parent -classid -.B | root) [ handle -major: -.B ] prio [ bands +classid +.B | root) [ handle +major: +.B ] prio [ bands bands .B ] [ priomap band band band... -.B ] [ estimator +.B ] [ estimator interval timeconstant .B ] .SH DESCRIPTION The PRIO qdisc is a simple classful queueing discipline that contains an arbitrary number of classes of differing priority. The classes are -dequeued in numerical descending order of priority. PRIO is a scheduler +dequeued in numerical descending order of priority. PRIO is a scheduler and never delays packets - it is a work-conserving qdisc, though the qdiscs contained in the classes may not be. @@ -51,22 +51,22 @@ A process with sufficient privileges can encode the destination class directly with SO_PRIORITY, see .BR socket(7). -.TP +.TP with a tc filter A tc filter attached to the root qdisc can point traffic directly to a class -.TP +.TP with the priomap Based on the packet priority, which in turn is derived from the Type of Service assigned to the packet. .P -Only the priomap is specific to this qdisc. +Only the priomap is specific to this qdisc. .SH QDISC PARAMETERS .TP bands Number of bands. If changed from the default of 3, .B priomap must be updated as well. -.TP +.TP priomap The priomap maps the priority of a packet to a class. The priority can either be set directly from userspace, @@ -126,7 +126,7 @@ The second column contains the value of the relevant four TOS bits, followed by their translated meaning. For example, 15 stands for a packet wanting Minimal Monetary Cost, Maximum Reliability, Maximum -Throughput AND Minimum Delay. +Throughput AND Minimum Delay. The fourth column lists the way the Linux kernel interprets the TOS bits, by showing to which Priority they are mapped. @@ -151,7 +151,7 @@ TFTP 1000 (minimize delay) -SMTP +SMTP Command phase 1000 (minimize delay) DATA phase 0100 (maximize throughput) @@ -176,12 +176,10 @@ .SH BUGS Large amounts of traffic in the lower bands can cause starvation of higher -bands. Can be prevented by attaching a shaper (for example, +bands. Can be prevented by attaching a shaper (for example, .BR tc-tbf(8) to these bands to make sure they cannot dominate the link. .SH AUTHORS Alexey N. Kuznetsov, , J Hadi Salim . This manpage maintained by bert hubert - - diff -Nru iproute2-4.3.0/man/man8/tc-red.8 iproute2-4.9.0/man/man8/tc-red.8 --- iproute2-4.3.0/man/man8/tc-red.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-red.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,17 +1,17 @@ .TH RED 8 "13 December 2001" "iproute2" "Linux" .SH NAME -red \- Random Early Detection +red \- Random Early Detection .SH SYNOPSIS .B tc qdisc ... red -.B limit +.B limit +bytes +.B [ min +bytes +.B ] [ max bytes -.B [ min -bytes -.B ] [ max -bytes .B ] avpkt bytes -.B [ burst +.B [ burst packets .B ] [ ecn ] [ harddrop] [ bandwidth rate @@ -46,51 +46,51 @@ probability. This is calculated using an Exponential Weighted Moving Average, which can be more or less sensitive to bursts. -When the average queue size is below +When the average queue size is below .B min -bytes, no packet will ever be marked. When it exceeds -.B min, +bytes, no packet will ever be marked. When it exceeds +.B min, the probability of doing so climbs linearly up -to -.B probability, +to +.B probability, until the average queue size hits .B max -bytes. Because -.B probability +bytes. Because +.B probability is normally not set to 100%, the queue size might -conceivably rise above +conceivably rise above .B max -bytes, so the +bytes, so the .B limit parameter is provided to set a hard maximum for the size of the queue. .SH PARAMETERS -.TP +.TP min Average queue size at which marking becomes a possibility. Defaults to .B max /3 -.TP +.TP max At this average queue size, the marking probability is maximal. Should be at least twice .B min -to prevent synchronous retransmits, higher for low +to prevent synchronous retransmits, higher for low .B min. -Default to +Default to .B limit /4 -.TP +.TP probability Maximum probability for marking, specified as a floating point number from 0.0 to 1.0. Suggested values are 0.01 or 0.02 (1 or 2%, respectively). Default : 0.02 -.TP +.TP limit Hard limit on the real (not average) queue size in bytes. Further packets are dropped. Should be set higher than max+burst. It is advised to set this -a few times higher than +a few times higher than .B max. .TP burst @@ -98,7 +98,7 @@ real queue size. Larger values make the calculation more sluggish, allowing longer bursts of traffic before marking starts. Real life experiments support the following guideline: (min+min+max)/(3*avpkt). -.TP +.TP avpkt Specified in bytes. Used with burst to determine the time constant for average queue size calculations. 1000 is a good value. @@ -126,15 +126,15 @@ adaptive (Added in linux-3.3) Sets RED in adaptive mode as described in http://icir.org/floyd/papers/adaptiveRed.pdf .nf -Goal of Adaptive RED is to make 'probability' dynamic value between 1% and 50% to reach the target average queue : +Goal of Adaptive RED is to make 'probability' dynamic value between 1% and 50% to reach the target average queue : .B (max - min) / 2 .fi .SH EXAMPLE .P -# tc qdisc add dev eth0 parent 1:1 handle 10: red - limit 400000 min 30000 max 90000 avpkt 1000 +# tc qdisc add dev eth0 parent 1:1 handle 10: red + limit 400000 min 30000 max 90000 avpkt 1000 burst 55 ecn adaptive bandwidth 10Mbit .SH SEE ALSO @@ -142,11 +142,11 @@ .BR tc-choke (8) .SH SOURCES -.TP +.TP o Floyd, S., and Jacobson, V., Random Early Detection gateways for Congestion Avoidance. http://www.aciri.org/floyd/papers/red/red.html -.TP +.TP o Some changes to the algorithm by Alexey N. Kuznetsov. .TP @@ -156,7 +156,5 @@ .SH AUTHORS Alexey N. Kuznetsov, , Alexey Makarenko , J Hadi Salim , -Eric Dumazet . +Eric Dumazet . This manpage maintained by bert hubert - - diff -Nru iproute2-4.3.0/man/man8/tc-sfq.8 iproute2-4.9.0/man/man8/tc-sfq.8 --- iproute2-4.3.0/man/man8/tc-sfq.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-sfq.8 2016-12-12 23:07:42.000000000 +0000 @@ -33,11 +33,11 @@ .SH DESCRIPTION Stochastic Fairness Queueing is a classless queueing discipline available for -traffic control with the +traffic control with the .BR tc (8) command. -SFQ does not shape traffic but only schedules the transmission of packets, based on 'flows'. +SFQ does not shape traffic but only schedules the transmission of packets, based on 'flows'. The goal is to ensure fairness so that each flow is able to send data in turn, thus preventing any single flow from drowning out the rest. @@ -62,13 +62,13 @@ (iii) Source and Destination port .P -If these are available. SFQ knows about ipv4 and ipv6 and also UDP, TCP and ESP. -Packets with other protocols are hashed based on the 32bits representation of their +If these are available. SFQ knows about ipv4 and ipv6 and also UDP, TCP and ESP. +Packets with other protocols are hashed based on the 32bits representation of their destination and source. A flow corresponds mostly to a TCP/IP connection. Each of these buckets should represent a unique flow. Because multiple flows may -get hashed to the same bucket, sfqs internal hashing algorithm may be perturbed at configurable -intervals so that the unfairness lasts only for a short while. Perturbation may +get hashed to the same bucket, sfqs internal hashing algorithm may be perturbed at configurable +intervals so that the unfairness lasts only for a short while. Perturbation may however cause some inadvertent packet reordering to occur. After linux-3.3, there is no packet reordering problem, but possible packet drops if rehashing hits one limit (number of flows or packets per flow) @@ -88,7 +88,7 @@ Can be used to set a different hash table size, available from kernel 2.6.39 onwards. The specified divisor must be a power of two and cannot be larger than 65536. Default value: 1024. -.TP +.TP limit Upper limit of the SFQ. Can be used to reduce the default length of 127 packets. After linux-3.3, it can be raised. @@ -97,12 +97,12 @@ Limit of packets per flow (after linux-3.3). Default to 127 and can be lowered. .TP perturb -Interval in seconds for queue algorithm perturbation. Defaults to 0, which means that +Interval in seconds for queue algorithm perturbation. Defaults to 0, which means that no perturbation occurs. Do not set too low for each perturbation may cause some packet reordering or losses. Advised value: 60 This value has no effect when external flow classification is used. Its better to increase divisor value to lower risk of hash collisions. -.TP +.TP quantum Amount of bytes a flow is allowed to dequeue during a round of the round robin process. Defaults to the MTU of the interface which is also the advised value and the minimum value. @@ -142,7 +142,7 @@ burst Used for determining how fast the average queue size is influenced by the real queue size. .nf -Default value is : +Default value is : .B (2 * min + max) / (3 * avpkt) .fi .TP @@ -166,16 +166,16 @@ .P # tc qdisc add dev ppp0 root sfq .P -Please note that SFQ, like all non-shaping (work-conserving) qdiscs, is only useful +Please note that SFQ, like all non-shaping (work-conserving) qdiscs, is only useful if it owns the queue. -This is the case when the link speed equals the actually available bandwidth. This holds -for regular phone modems, ISDN connections and direct non-switched ethernet links. +This is the case when the link speed equals the actually available bandwidth. This holds +for regular phone modems, ISDN connections and direct non-switched ethernet links. .P -Most often, cable modems and DSL devices do not fall into this category. The same holds -for when connected to a switch and trying to send data to a congested segment also +Most often, cable modems and DSL devices do not fall into this category. The same holds +for when connected to a switch and trying to send data to a congested segment also connected to the switch. .P -In this case, the effective queue does not reside within Linux and is therefore not +In this case, the effective queue does not reside within Linux and is therefore not available for scheduling. .P Embed SFQ in a classful qdisc to make sure it owns the queue. @@ -191,11 +191,11 @@ .P Example of sfq with optional RED mode : .P -# tc qdisc add dev eth0 parent 1:1 handle 10: sfq limit 3000 flows 512 divisor 16384 +# tc qdisc add dev eth0 parent 1:1 handle 10: sfq limit 3000 flows 512 divisor 16384 redflowlimit 100000 min 8000 max 60000 probability 0.20 ecn headdrop .SH SOURCE -.TP +.TP o Paul E. McKenney "Stochastic Fairness Queuing", IEEE INFOCOMM'90 Proceedings, San Francisco, 1990. @@ -205,7 +205,7 @@ Paul E. McKenney "Stochastic Fairness Queuing", "Interworking: Research and Experience", v.2, 1991, p.113-131. -.TP +.TP o See also: M. Shreedhar and George Varghese "Efficient Fair @@ -220,5 +220,3 @@ Eric Dumazet . .P This manpage maintained by bert hubert - - diff -Nru iproute2-4.3.0/man/man8/tc-simple.8 iproute2-4.9.0/man/man8/tc-simple.8 --- iproute2-4.3.0/man/man8/tc-simple.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-simple.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,76 @@ +.TH "Simple action in tc" 8 "12 Jan 2015" "iproute2" "Linux" + +.SH NAME +simple - basic example action +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action simple" +.I STRING +.SH DESCRIPTION +This is a pedagogical example rather than an actually useful action. Upon every access, it prints the given +.I STRING +which may be of arbitrary length. +.SH OPTIONS +.TP +.I STRING +The actual string to print. +.SH EXAMPLES +The following example makes the kernel yell "Incoming ICMP!" every time it sees +an incoming ICMP on eth0. Steps are: +.IP 1) 4 +Add an ingress qdisc point to eth0 +.IP 2) 4 +Start a chain on ingress of eth0 that first matches ICMP then invokes the +simple action to shout. +.IP 3) 4 +display stats and show that no packet has been seen by the action +.IP 4) 4 +Send one ping packet to google (expect to receive a response back) +.IP 5) 4 +grep the logs to see the logged message +.IP 6) 4 +display stats again and observe increment by 1 + +.RE +.EX + hadi@noma1:$ tc qdisc add dev eth0 ingress + hadi@noma1:$tc filter add dev eth0 parent ffff: protocol ip prio 5 \\ + u32 match ip protocol 1 0xff flowid 1:1 action simple "Incoming ICMP" + + hadi@noma1:$ sudo tc -s filter ls dev eth0 parent ffff: + filter protocol ip pref 5 u32 + filter protocol ip pref 5 u32 fh 800: ht divisor 1 + filter protocol ip pref 5 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 + match 00010000/00ff0000 at 8 + action order 1: Simple + index 4 ref 1 bind 1 installed 29 sec used 29 sec + Action statistics: + Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) + backlog 0b 0p requeues 0 + + + hadi@noma1$ ping -c 1 www.google.ca + PING www.google.ca (74.125.225.120) 56(84) bytes of data. + 64 bytes from ord08s08-in-f24.1e100.net (74.125.225.120): icmp_req=1 ttl=53 time=31.3 ms + + --- www.google.ca ping statistics --- + 1 packets transmitted, 1 received, 0% packet loss, time 0ms + rtt min/avg/max/mdev = 31.316/31.316/31.316/0.000 ms + + hadi@noma1$ dmesg | grep simple + [135354.473951] simple: Incoming ICMP_1 + + hadi@noma1$ sudo tc/tc -s filter ls dev eth0 parent ffff: + filter protocol ip pref 5 u32 + filter protocol ip pref 5 u32 fh 800: ht divisor 1 + filter protocol ip pref 5 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 + match 00010000/00ff0000 at 8 + action order 1: Simple + index 4 ref 1 bind 1 installed 206 sec used 67 sec + Action statistics: + Sent 84 bytes 1 pkt (dropped 0, overlimits 0 requeues 0) + backlog 0b 0p requeues 0 +.EE +.SH SEE ALSO +.BR tc (8) diff -Nru iproute2-4.3.0/man/man8/tc-skbedit.8 iproute2-4.9.0/man/man8/tc-skbedit.8 --- iproute2-4.3.0/man/man8/tc-skbedit.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-skbedit.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,66 @@ +.TH "SKB editing action in tc" 8 "12 Jan 2015" "iproute2" "Linux" + +.SH NAME +skbedit - SKB editing action +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action skbedit " [ " queue_mapping +.IR QUEUE_MAPPING " ] [" +.B priority +.IR PRIORITY " ] [" +.B mark +.IR MARK " ]" +.B ptype +.IR PTYPE " ]" +.SH DESCRIPTION +The +.B skbedit +action allows to change a packet's associated meta data. It complements the +.B pedit +action, which in turn allows to change parts of the packet data itself. + +The most unique feature of +.B skbedit +is it's ability to decide over which queue of an interface with multiple +transmit queues the packet is to be sent out. The number of available transmit +queues is reflected by sysfs entries within +.I /sys/class/net//queues +with name +.I tx-N +(where +.I N +is the actual queue number). +.SH OPTIONS +.TP +.BI queue_mapping " QUEUE_MAPPING" +Override the packet's transmit queue. Useful when applied to packets transmitted +over MQ-capable network interfaces. +.I QUEUE_MAPPING +is an unsigned 16bit value in decimal format. +.TP +.BI priority " PRIORITY" +Override the packet classification decision. +.I PRIORITY +is either +.BR root ", " none +or a hexadecimal major class ID optionally followed by a colon +.RB ( : ) +and a hexadecimal minor class ID. +.TP +.BI mark " MARK" +Change the packet's firewall mark value. +.I MARK +is an unsigned 32bit value in automatically detected format (i.e., prefix with +.RB ' 0x ' +for hexadecimal interpretation, etc.). +.TP +.BI ptype " PTYPE" +Override the packet's type. Useful for setting packet type to host when +needing to allow ingressing packets with the wrong MAC address but +correct IP address. +.I PTYPE +is one of: host, otherhost, broadcast, multicast +.SH SEE ALSO +.BR tc (8), +.BR tc-pedit (8) diff -Nru iproute2-4.3.0/man/man8/tc-stab.8 iproute2-4.9.0/man/man8/tc-stab.8 --- iproute2-4.3.0/man/man8/tc-stab.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-stab.8 2016-12-12 23:07:42.000000000 +0000 @@ -156,8 +156,8 @@ .br \fB[2]\fR http://www.faqs.org/rfcs/rfc2684.html -Please direct bugreports and patches to: +Please direct bugreports and patches to: . .SH "AUTHOR" . -Manpage created by Michal Soltys (sol...@ziu.info) +Manpage created by Michal Soltys (soltys@ziu.info) diff -Nru iproute2-4.3.0/man/man8/tc-tbf.8 iproute2-4.9.0/man/man8/tc-tbf.8 --- iproute2-4.3.0/man/man8/tc-tbf.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-tbf.8 2016-12-12 23:07:42.000000000 +0000 @@ -6,11 +6,11 @@ rate .B burst bytes/cell -.B ( latency -ms +.B ( latency +ms .B | limit bytes -.B ) [ mpu +.B ) [ mpu bytes .B [ peakrate rate @@ -22,46 +22,46 @@ .SH DESCRIPTION The Token Bucket Filter is a classful queueing discipline available for -traffic control with the +traffic control with the .BR tc (8) command. TBF is a pure shaper and never schedules traffic. It is non-work-conserving and may throttle -itself, although packets are available, to ensure that the configured rate is not exceeded. -It is able to shape up to 1mbit/s of normal traffic with ideal minimal burstiness, +itself, although packets are available, to ensure that the configured rate is not exceeded. +It is able to shape up to 1mbit/s of normal traffic with ideal minimal burstiness, sending out data exactly at the configured rates. Much higher rates are possible but at the cost of losing the minimal burstiness. In that -case, data is on average dequeued at the configured rate but may be sent much faster at millisecond +case, data is on average dequeued at the configured rate but may be sent much faster at millisecond timescales. Because of further queues living in network adaptors, this is often not a problem. .SH ALGORITHM -As the name implies, traffic is filtered based on the expenditure of +As the name implies, traffic is filtered based on the expenditure of .B tokens. Tokens roughly correspond to bytes, with the additional constraint that each packet consumes some tokens, no matter how small it is. This reflects the fact that even a zero-sized packet occupies the link for some time. -On creation, the TBF is stocked with tokens which correspond to the amount of traffic that can be burst +On creation, the TBF is stocked with tokens which correspond to the amount of traffic that can be burst in one go. Tokens arrive at a steady rate, until the bucket is full. -If no tokens are available, packets are queued, up to a configured limit. The TBF now +If no tokens are available, packets are queued, up to a configured limit. The TBF now calculates the token deficit, and throttles until the first packet in the queue can be sent. -If it is not acceptable to burst out packets at maximum speed, a peakrate can be configured +If it is not acceptable to burst out packets at maximum speed, a peakrate can be configured to limit the speed at which the bucket empties. This peakrate is implemented as a second TBF with a very small bucket, so that it doesn't burst. -To achieve perfection, the second bucket may contain only a single packet, which leads to -the earlier mentioned 1mbit/s limit. +To achieve perfection, the second bucket may contain only a single packet, which leads to +the earlier mentioned 1mbit/s limit. This limit is caused by the fact that the kernel can only throttle for at minimum 1 'jiffy', which depends -on HZ as 1/HZ. For perfect shaping, only a single packet can get sent per jiffy - for HZ=100, this means 100 +on HZ as 1/HZ. For perfect shaping, only a single packet can get sent per jiffy - for HZ=100, this means 100 packets of on average 1000 bytes each, which roughly corresponds to 1mbit/s. .SH PARAMETERS -See +See .BR tc (8) for how to specify the units of these values. .TP @@ -71,30 +71,30 @@ latency parameter, which specifies the maximum amount of time a packet can sit in the TBF. The latter calculation takes into account the size of the bucket, the rate and possibly the peakrate (if set). These two parameters -are mutually exclusive. +are mutually exclusive. .TP burst Also known as buffer or maxburst. -Size of the bucket, in bytes. This is the maximum amount of bytes that tokens can be available for instantaneously. -In general, larger shaping rates require a larger buffer. For 10mbit/s on Intel, you need at least 10kbyte buffer +Size of the bucket, in bytes. This is the maximum amount of bytes that tokens can be available for instantaneously. +In general, larger shaping rates require a larger buffer. For 10mbit/s on Intel, you need at least 10kbyte buffer if you want to reach your configured rate! If your buffer is too small, packets may be dropped because more tokens arrive per timer tick than fit in your bucket. The minimum buffer size can be calculated by dividing the rate by HZ. -Token usage calculations are performed using a table which by default has a resolution of 8 packets. -This resolution can be changed by specifying the +Token usage calculations are performed using a table which by default has a resolution of 8 packets. +This resolution can be changed by specifying the .B cell size with the burst. For example, to specify a 6000 byte buffer with a 16 byte cell size, set a burst of 6000/16. You will probably never have to set this. Must be an integral power of 2. .TP mpu -A zero-sized packet does not use zero bandwidth. For ethernet, no packet uses less than 64 bytes. The Minimum Packet Unit +A zero-sized packet does not use zero bandwidth. For ethernet, no packet uses less than 64 bytes. The Minimum Packet Unit determines the minimal token usage (specified in bytes) for a packet. Defaults to zero. .TP rate -The speed knob. See remarks above about limits! See +The speed knob. See remarks above about limits! See .BR tc (8) for units. .PP @@ -112,7 +112,7 @@ If a peakrate is needed, but some burstiness is acceptable, this size can be raised. A 3000 byte minburst allows around 3mbit/s of peakrate, given 1000 byte packets. -Like the regular burstsize you can also specify a +Like the regular burstsize you can also specify a .B cell size. .SH EXAMPLE & USAGE @@ -139,5 +139,3 @@ .SH AUTHOR Alexey N. Kuznetsov, . This manpage maintained by bert hubert - - diff -Nru iproute2-4.3.0/man/man8/tc-u32.8 iproute2-4.9.0/man/man8/tc-u32.8 --- iproute2-4.3.0/man/man8/tc-u32.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-u32.8 2016-12-12 23:07:42.000000000 +0000 @@ -370,6 +370,7 @@ .RS .TP .BI src " ADDR" +.TQ .BI dst " ADDR" Compare Source or Destination Address fields against the value of .IR ADDR . diff -Nru iproute2-4.3.0/man/man8/tc-vlan.8 iproute2-4.9.0/man/man8/tc-vlan.8 --- iproute2-4.3.0/man/man8/tc-vlan.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-vlan.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,126 @@ +.TH "VLAN manipulation action in tc" 8 "12 Jan 2015" "iproute2" "Linux" + +.SH NAME +vlan - vlan manipulation module +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action vlan" " { " pop " |" +.IR PUSH " | " MODIFY " } [ " CONTROL " ]" + +.ti -8 +.IR PUSH " := " +.BR push " [ " protocol +.IR VLANPROTO " ]" +.BR " [ " priority +.IR VLANPRIO " ] " +.BI id " VLANID" + +.ti -8 +.IR MODIFY " := " +.BR modify " [ " protocol +.IR VLANPROTO " ]" +.BR " [ " priority +.IR VLANPRIO " ] " +.BI id " VLANID" + +.ti -8 +.IR CONTROL " := { " +.BR reclassify " | " pipe " | " drop " | " continue " | " pass " }" +.SH DESCRIPTION +The +.B vlan +action allows to perform 802.1Q en- or decapsulation on a packet, reflected by +the operation modes +.IR POP ", " PUSH " and " MODIFY . +The +.I POP +mode is simple, as no further information is required to just drop the +outer-most VLAN encapsulation. The +.IR PUSH " and " MODIFY +modes require at least a +.I VLANID +and allow to optionally choose the +.I VLANPROTO +to use. +.SH OPTIONS +.TP +.B pop +Decapsulation mode, no further arguments allowed. +.TP +.B push +Encapsulation mode. Requires at least +.B id +option. +.TP +.B modify +Replace mode. Existing 802.1Q tag is replaced. Requires at least +.B id +option. +.TP +.BI id " VLANID" +Specify the VLAN ID to encapsulate into. +.I VLANID +is an unsigned 16bit integer, the format is detected automatically (e.g. prefix +with +.RB ' 0x ' +for hexadecimal interpretation, etc.). +.TP +.BI protocol " VLANPROTO" +Choose the VLAN protocol to use. At the time of writing, the kernel accepts only +.BR 802.1Q " or " 802.1ad . +.TP +.BI priority " VLANPRIO" +Choose the VLAN priority to use. Decimal number in range of 0-7. +.TP +.I CONTROL +How to continue after executing this action. +.RS +.TP +.B reclassify +Restarts classification by jumping back to the first filter attached to this +action's parent. +.TP +.B pipe +Continue with the next action, this is the default. +.TP +.B drop +Packet will be dropped without running further actions. +.TP +.B continue +Continue classification with next filter in line. +.TP +.B pass +Return to calling qdisc for packet processing. This ends the classification +process. +.RE +.SH EXAMPLES +The following example encapsulates incoming ICMP packets on eth0 from 10.0.0.2 +into VLAN ID 123: + +.RS +.EX +#tc qdisc add dev eth0 handle ffff: ingress +#tc filter add dev eth0 parent ffff: pref 11 protocol ip \\ + u32 match ip protocol 1 0xff flowid 1:1 \\ + u32 match ip src 10.0.0.2 flowid 1:1 \\ + action vlan push id 123 +.EE +.RE + +Here is an example of the +.B pop +function: Incoming VLAN packets on eth0 are decapsulated and the classification +process then restarted for the plain packet: + +.RS +.EX +#tc qdisc add dev eth0 handle ffff: ingress +#tc filter add dev $ETH parent ffff: pref 1 protocol 802.1Q \\ + u32 match u32 0 0 flowid 1:1 \\ + action vlan pop reclassify +.EE +.RE + +.SH SEE ALSO +.BR tc (8) diff -Nru iproute2-4.3.0/man/man8/tc-xt.8 iproute2-4.9.0/man/man8/tc-xt.8 --- iproute2-4.3.0/man/man8/tc-xt.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tc-xt.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,42 @@ +.TH "iptables action in tc" 8 "3 Mar 2016" "iproute2" "Linux" + +.SH NAME +xt - tc iptables action +.SH SYNOPSIS +.in +8 +.ti -8 +.BR tc " ... " "action xt \-j" +.IR TARGET " [ " TARGET_OPTS " ]" +.SH DESCRIPTION +The +.B xt +action allows to call arbitrary iptables targets for packets matching the filter +this action is attached to. +.SH OPTIONS +.TP +.BI -j " TARGET \fR[\fI TARGET_OPTS \fR]" +Perform a jump to the given iptables target, optionally passing any target +specific options in +.IR TARGET_OPTS . +.SH EXAMPLES +The following will attach a +.B u32 +filter to the +.B ingress +qdisc matching ICMP replies and using the +.B xt +action to make the kernel yell 'PONG' each time: + +.RS +.EX +tc qdisc add dev eth0 ingress +tc filter add dev eth0 parent ffff: proto ip u32 \\ + match ip protocol 1 0xff \\ + match ip icmp_type 0 0xff \\ + action xt -j LOG --log-prefix PONG +.EE +.RE +.SH SEE ALSO +.BR tc (8), +.BR tc-u32 (8), +.BR iptables-extensions (8) diff -Nru iproute2-4.3.0/man/man8/tipc.8 iproute2-4.9.0/man/man8/tipc.8 --- iproute2-4.3.0/man/man8/tipc.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tipc.8 2016-12-12 23:07:42.000000000 +0000 @@ -87,6 +87,7 @@ .BR tipc-media (8), .BR tipc-nametable (8), .BR tipc-node (8), +.BR tipc-peer (8), .BR tipc-socket (8) .br .SH REPORTING BUGS diff -Nru iproute2-4.3.0/man/man8/tipc-bearer.8 iproute2-4.9.0/man/man8/tipc-bearer.8 --- iproute2-4.3.0/man/man8/tipc-bearer.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tipc-bearer.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,7 +1,7 @@ .TH TIPC-BEARER 8 "02 Jun 2015" "iproute2" "Linux" -./ For consistency, please keep padding right aligned. -./ For example '.B "foo " bar' and not '.B foo " bar"' +.\" For consistency, please keep padding right aligned. +.\" For example '.B "foo " bar' and not '.B foo " bar"' .SH NAME tipc-bearer \- show or modify TIPC bearers @@ -11,6 +11,11 @@ .in +8 .ti -8 +.B tipc bearer add media udp name +.IB "NAME " "remoteip " REMOTEIP +.br + +.ti -8 .B tipc bearer enable .RB "[ " domain .IR DOMAIN " ]" @@ -39,14 +44,12 @@ .B tipc bearer disable media .br .RB "{ { " eth " | " ib " } " device -.IR DEVICE +.IR "DEVICE " } .RB "|" .br .RB "{ " udp .B name -.IR NAME -.B localip -.IR LOCALIP " } }" +.IR NAME " }" .br .ti -8 @@ -65,14 +68,12 @@ .br .RB "{ " udp .B name -.IR NAME -.B localip -.IR LOCALIP " } }" +.IR NAME " }" .br .ti -8 .B tipc bearer get -.RB "{ " "priority" " | " tolerance " | " window " } " media +.RB "[ " "priority" " | " tolerance " | " window " ] " media .br .RB "{ { " eth " | " ib " } " device .IR "DEVICE" " }" @@ -81,8 +82,7 @@ .RB "{ " udp .B name .IR NAME -.B localip -.IR LOCALIP " } }" +.RB "[ " "localip " "| " "localport " "| " "remoteip " "| " "remoteport " "] }" .br .ti -8 @@ -202,6 +202,25 @@ .B udp bearer runs in point-to-point mode. +Multiple +.B remoteip +addresses can be added via the +.B bearer add +command. Adding one or more unicast +.B remoteip +addresses to an existing +.B udp +bearer puts the bearer in replicast mode where IP +multicast is emulated by sending multiple unicast messages to each configured +.B remoteip. +When a peer sees a TIPC discovery message from an unknown peer the peer address +is automatically added to the +.B remoteip +(replicast) list, thus only one side of +a link needs to be manually configured. A +.B remoteip +address cannot be added to a multicast bearer. + .TP .BI "remoteport " REMOTEPORT .br @@ -218,6 +237,7 @@ .BR tipc-media (8), .BR tipc-nametable (8), .BR tipc-node (8), +.BR tipc-peer (8), .BR tipc-socket (8) .br .SH REPORTING BUGS diff -Nru iproute2-4.3.0/man/man8/tipc-link.8 iproute2-4.9.0/man/man8/tipc-link.8 --- iproute2-4.3.0/man/man8/tipc-link.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tipc-link.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,7 +1,7 @@ .TH TIPC-LINK 8 "02 Jun 2015" "iproute2" "Linux" -./ For consistency, please keep padding right aligned. -./ For example '.B "foo " bar' and not '.B foo " bar"' +.\" For consistency, please keep padding right aligned. +.\" For example '.B "foo " bar' and not '.B foo " bar"' .SH NAME tipc-link \- show links or modify link properties @@ -33,12 +33,35 @@ .I LINK .RB "] | " "reset .BI "link " "LINK " -.R } +} .ti -8 .B tipc link list .br +.ti -8 +.B tipc link monitor set +.RB "{ " "threshold" " } " + +.ti -8 +.B tipc link monitor get +.RB "{ " "threshold" " } " + +.ti -8 +.B tipc link monitor summary +.br + +.ti -8 +.B tipc link monitor list +.br +.RB "[ " "media " " { " eth " | " ib " } " device +.IR "DEVICE" " ]" +.RB "|" +.br +.RB "[ " "media udp name" +.IR NAME " ]" +.br + .SH OPTIONS Options (flags) that can be passed anywhere in the command chain. .TP @@ -204,6 +227,87 @@ have in its transmit queue before TIPC's congestion control mechanism is activated. +.SS Monitor properties + +.TP +.B threshold +.br +The threshold specifies the cluster size exceeding which the link monitoring +algorithm will switch from "full-mesh" to "overlapping-ring". +If set of 0 the overlapping-ring monitoring is always on and if set to a +value larger than anticipated cluster size the overlapping-ring is disabled. +The default value is 32. + +.SS Monitor information + +.TP +.B table_generation +.br +Represents the event count in a node's local monitoring list. It steps every +time something changes in the local monitor list, including changes in the +local domain. + +.TP +.B cluster_size +.br +Represents the current count of cluster members. + +.TP +.B algorithm +.br +The current supervision algorithm used for neighbour monitoring for the bearer. +Possible values are full-mesh or overlapping-ring. + +.TP +.B status +.br +The node status derived by the local node. +Possible status are up or down. + +.TP +.B monitored +.br +Represent the type of monitoring chosen by the local node. +Possible values are direct or indirect. + +.TP +.B generation +.br +Represents the domain generation which is the event count in a node's local +domain. Every time something changes (peer add/remove/up/down) the domain +generation is stepped and a new version of node record is sent to inform +the neighbors about this change. The domain generation helps the receiver +of a domain record to know if it should ignore or process the record. + +.TP +.B applied_node_status +.br +The node status reported by the peer node for the succeeding peers in +the node list. The Node list is a circular list of ascending addresses +starting with the local node. +Possible status are: U or D. The status U implies up and D down. + +.TP +.B [non_applied_node:status] +.br +Represents the nodes and their status as reported by the peer node. +These nodes were not applied to the monitoring list for this peer node. +They are usually transient and occur during the cluster startup phase +or network reconfiguration. +Possible status are: U or D. The status U implies up and D down. + +.SH EXAMPLES +.PP +tipc link monitor list +.RS 4 +Shows the link monitoring information for cluster members on device data0. +.RE +.PP +tipc link monitor summary +.RS 4 +The monitor summary command prints the basic attributes. +.RE + .SH EXIT STATUS Exit status is 0 if command was successful or a positive integer upon failure. @@ -213,6 +317,7 @@ .BR tipc-bearer (8), .BR tipc-nametable (8), .BR tipc-node (8), +.BR tipc-peer (8), .BR tipc-socket (8) .br .SH REPORTING BUGS diff -Nru iproute2-4.3.0/man/man8/tipc-media.8 iproute2-4.9.0/man/man8/tipc-media.8 --- iproute2-4.3.0/man/man8/tipc-media.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tipc-media.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,7 +1,7 @@ .TH TIPC-MEDIA 8 "02 Jun 2015" "iproute2" "Linux" -./ For consistency, please keep padding right aligned. -./ For example '.B "foo " bar' and not '.B foo " bar"' +.\" For consistency, please keep padding right aligned. +.\" For example '.B "foo " bar' and not '.B foo " bar"' .SH NAME tipc-media \- list or modify media properties @@ -74,6 +74,7 @@ .BR tipc-link (8), .BR tipc-nametable (8), .BR tipc-node (8), +.BR tipc-peer (8), .BR tipc-socket (8) .br .SH REPORTING BUGS diff -Nru iproute2-4.3.0/man/man8/tipc-nametable.8 iproute2-4.9.0/man/man8/tipc-nametable.8 --- iproute2-4.3.0/man/man8/tipc-nametable.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tipc-nametable.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,7 +1,7 @@ .TH TIPC-NAMETABLE 8 "02 Jun 2015" "iproute2" "Linux" -./ For consistency, please keep padding right aligned. -./ For example '.B "foo " bar' and not '.B foo " bar"' +.\" For consistency, please keep padding right aligned. +.\" For example '.B "foo " bar' and not '.B foo " bar"' .SH NAME tipc-nametable \- show TIPC nametable @@ -87,6 +87,7 @@ .BR tipc-link (8), .BR tipc-media (8), .BR tipc-node (8), +.BR tipc-peer (8), .BR tipc-socket (8) .br .SH REPORTING BUGS diff -Nru iproute2-4.3.0/man/man8/tipc-node.8 iproute2-4.9.0/man/man8/tipc-node.8 --- iproute2-4.3.0/man/man8/tipc-node.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tipc-node.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,7 +1,7 @@ .TH TIPC-NODE 8 "02 Jun 2015" "iproute2" "Linux" -./ For consistency, please keep padding right aligned. -./ For example '.B "foo " bar' and not '.B foo " bar"' +.\" For consistency, please keep padding right aligned. +.\" For example '.B "foo " bar' and not '.B foo " bar"' .SH NAME tipc-node \- modify and show local node parameters or list peer nodes @@ -59,6 +59,7 @@ .BR tipc-link (8), .BR tipc-media (8), .BR tipc-nametable (8), +.BR tipc-peer (8), .BR tipc-socket (8) .br .SH REPORTING BUGS diff -Nru iproute2-4.3.0/man/man8/tipc-peer.8 iproute2-4.9.0/man/man8/tipc-peer.8 --- iproute2-4.3.0/man/man8/tipc-peer.8 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/man/man8/tipc-peer.8 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,52 @@ +.TH TIPC-PEER 8 "04 Dec 2015" "iproute2" "Linux" + +.\" For consistency, please keep padding right aligned. +.\" For example '.B "foo " bar' and not '.B foo " bar"' + +.SH NAME +tipc-peer \- modify peer information + +.SH SYNOPSIS +.ad l +.in +8 + +.ti -8 +.B tipc peer remove address +.IR ADDRESS + +.SH OPTIONS +Options (flags) that can be passed anywhere in the command chain. +.TP +.BR "\-h" , " --help" +Show help about last valid command. For example +.B tipc peer --help +will show peer help and +.B tipc --help +will show general help. The position of the option in the string is irrelevant. +.SH DESCRIPTION + +.SS Peer remove +Remove an offline peer node from the local data structures. The peer is +identified by its +.B address + +.SH EXIT STATUS +Exit status is 0 if command was successful or a positive integer upon failure. + +.SH SEE ALSO +.BR tipc (8), +.BR tipc-bearer (8), +.BR tipc-link (8), +.BR tipc-media (8), +.BR tipc-nametable (8), +.BR tipc-node (8), +.BR tipc-socket (8) +.br +.SH REPORTING BUGS +Report any bugs to the Network Developers mailing list +.B +where the development and maintenance is primarily done. +You do not have to be subscribed to the list to send a message there. + +.SH AUTHOR +Richard Alpe diff -Nru iproute2-4.3.0/man/man8/tipc-socket.8 iproute2-4.9.0/man/man8/tipc-socket.8 --- iproute2-4.3.0/man/man8/tipc-socket.8 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/man/man8/tipc-socket.8 2016-12-12 23:07:42.000000000 +0000 @@ -1,7 +1,7 @@ .TH TIPC-SOCKET 8 "02 Jun 2015" "iproute2" "Linux" -./ For consistency, please keep padding right aligned. -./ For example '.B "foo " bar' and not '.B foo " bar"' +.\" For consistency, please keep padding right aligned. +.\" For example '.B "foo " bar' and not '.B foo " bar"' .SH NAME tipc-socket \- show TIPC socket (port) information diff -Nru iproute2-4.3.0/misc/arpd.c iproute2-4.9.0/misc/arpd.c --- iproute2-4.3.0/misc/arpd.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/misc/arpd.c 2016-12-12 23:07:42.000000000 +0000 @@ -47,17 +47,16 @@ int *ifvec; char **ifnames; -struct dbkey -{ +struct dbkey { __u32 iface; __u32 addr; }; -#define IS_NEG(x) (((__u8*)(x))[0] == 0xFF) +#define IS_NEG(x) (((__u8 *)(x))[0] == 0xFF) #define NEG_TIME(x) (((x)[2]<<24)|((x)[3]<<16)|((x)[4]<<8)|(x)[5]) -#define NEG_AGE(x) ((__u32)time(NULL) - NEG_TIME((__u8*)x)) +#define NEG_AGE(x) ((__u32)time(NULL) - NEG_TIME((__u8 *)x)) #define NEG_VALID(x) (NEG_AGE(x) < negative_timeout) -#define NEG_CNT(x) (((__u8*)(x))[1]) +#define NEG_CNT(x) (((__u8 *)(x))[1]) struct rtnl_handle rth; @@ -96,8 +95,7 @@ static void usage(void) { fprintf(stderr, - "Usage: arpd [ -lkh? ] [ -a N ] [ -b dbase ] [ -B number ]" - " [ -f file ] [ -n time ] [-p interval ] [ -R rate ] [ interfaces ]\n"); + "Usage: arpd [ -lkh? ] [ -a N ] [ -b dbase ] [ -B number ] [ -f file ] [ -n time ] [-p interval ] [ -R rate ] [ interfaces ]\n"); exit(1); } @@ -108,7 +106,7 @@ if (ifnum == 0) return 1; - for (i=0; i=2 ? 1 : 3-active_probing); + sprintf(buf, "%d\n", active_probing >= 2 ? 1 : 3-active_probing); fputs(buf, fp); fclose(fp); } @@ -141,7 +139,7 @@ sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/app_solicit", ifnames[i]); if ((fp = fopen(buf, "w")) != NULL) { - sprintf(buf, "%d\n", active_probing<=1 ? 1 : active_probing); + sprintf(buf, "%d\n", active_probing <= 1 ? 1 : active_probing); fputs(buf, fp); fclose(fp); } @@ -156,7 +154,7 @@ if (!sysctl_adjusted) return; - for (i=0; iar_hrd = htons(ifr.ifr_hwaddr.sa_family); @@ -219,19 +220,16 @@ p += ah->ar_hln; memcpy(p, &dst.sin_addr, 4); - p+=4; + p += 4; - sll.sll_family = AF_PACKET; memset(sll.sll_addr, 0xFF, sizeof(sll.sll_addr)); - sll.sll_ifindex = ifindex; - sll.sll_protocol = htons(ETH_P_ARP); memcpy(p, &sll.sll_addr, ah->ar_hln); - p+=ah->ar_hln; + p += ah->ar_hln; memcpy(p, &addr, 4); - p+=4; + p += 4; - if (sendto(pset[0].fd, buf, p-buf, 0, (struct sockaddr*)&sll, sizeof(sll)) < 0) + if (sendto(pset[0].fd, buf, p-buf, 0, (struct sockaddr *)&sll, sizeof(sll)) < 0) return -1; stats.probes_sent++; return 0; @@ -248,6 +246,7 @@ gettimeofday(&now, NULL); if (prev.tv_sec) { int diff = (now.tv_sec-prev.tv_sec)*1000+(now.tv_usec-prev.tv_usec)/1000; + buckets += diff; } else { buckets = broadcast_burst; @@ -266,21 +265,18 @@ static int respond_to_kernel(int ifindex, __u32 addr, char *lla, int llalen) { struct { - struct nlmsghdr n; - struct ndmsg ndm; - char buf[256]; - } req; - - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_NEWNEIGH; - req.ndm.ndm_family = AF_INET; - req.ndm.ndm_state = NUD_STALE; - req.ndm.ndm_ifindex = ifindex; - req.ndm.ndm_type = RTN_UNICAST; + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_NEWNEIGH, + .ndm.ndm_family = AF_INET, + .ndm.ndm_state = NUD_STALE, + .ndm.ndm_ifindex = ifindex, + .ndm.ndm_type = RTN_UNICAST, + }; addattr_l(&req.n, sizeof(req), NDA_DST, &addr, 4); addattr_l(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); @@ -302,7 +298,7 @@ { struct ndmsg *ndm = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[NDA_MAX+1]; + struct rtattr *tb[NDA_MAX+1]; struct dbkey key; DBT dbkey, dbdat; int do_acct = 0; @@ -405,6 +401,7 @@ !IS_NEG(dbdat.data) || !NEG_VALID(dbdat.data)) { __u8 ndata[6]; + stats.kern_neg++; prepare_neg_entry(ndata, time(NULL)); dbdat.data = ndata; @@ -440,18 +437,16 @@ { int status; struct nlmsghdr *h; - struct sockaddr_nl nladdr; + struct sockaddr_nl nladdr = {}; struct iovec iov; char buf[8192]; struct msghdr msg = { - (void*)&nladdr, sizeof(nladdr), + (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; - memset(&nladdr, 0, sizeof(nladdr)); - iov.iov_base = buf; iov.iov_len = sizeof(buf); @@ -466,7 +461,7 @@ if (nladdr.nl_pid) return; - for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); @@ -477,7 +472,7 @@ return; status -= NLMSG_ALIGN(len); - h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } } @@ -487,13 +482,13 @@ unsigned char buf[1024]; struct sockaddr_ll sll; socklen_t sll_len = sizeof(sll); - struct arphdr *a = (struct arphdr*)buf; + struct arphdr *a = (struct arphdr *)buf; struct dbkey key; DBT dbkey, dbdat; int n; n = recvfrom(pset[0].fd, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr*)&sll, &sll_len); + (struct sockaddr *)&sll, &sll_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) syslog(LOG_ERR, "recvfrom: %m"); @@ -515,7 +510,7 @@ return; key.iface = sll.sll_ifindex; - memcpy(&key.addr, (char*)(a+1) + a->ar_hln, 4); + memcpy(&key.addr, (char *)(a+1) + a->ar_hln, 4); /* DAD message, ignore. */ if (key.addr == 0) @@ -539,10 +534,8 @@ static void catch_signal(int sig, void (*handler)(int)) { - struct sigaction sa; + struct sigaction sa = { .sa_handler = handler }; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handler; #ifdef SA_INTERRUPT sa.sa_flags = SA_INTERRUPT; #endif @@ -600,7 +593,7 @@ while ((opt = getopt(argc, argv, "h?b:lf:a:n:p:kR:B:")) != EOF) { switch (opt) { - case 'b': + case 'b': dbname = optarg; break; case 'f': @@ -624,7 +617,7 @@ break; case 'p': if ((poll_timeout = 1000 * strtod(optarg, NULL)) < 100) { - fprintf(stderr,"Invalid poll timeout\n"); + fprintf(stderr, "Invalid poll timeout\n"); exit(-1); } break; @@ -666,15 +659,15 @@ exit(-1); } - if (ifnum) { + if (ifnum) { int i; - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - for (i=0; iseq(dbase, &dbkey, &dbdat, R_NEXT) == 0) { struct dbkey *key = dbkey.data; + if (handle_if(key->iface)) { if (!IS_NEG(dbdat.data)) { char b1[18]; + printf("%-8d %-15s %s\n", key->iface, - inet_ntoa(*(struct in_addr*)&key->addr), + inet_ntoa(*(struct in_addr *)&key->addr), ll_addr_n2a(dbdat.data, 6, ARPHRD_ETHER, b1, 18)); } else { printf("%-8d %-15s FAILED: %dsec ago\n", key->iface, - inet_ntoa(*(struct in_addr*)&key->addr), + inet_ntoa(*(struct in_addr *)&key->addr), NEG_AGE(dbdat.data)); } } @@ -768,12 +764,13 @@ } if (1) { - struct sockaddr_ll sll; - memset(&sll, 0, sizeof(sll)); - sll.sll_family = AF_PACKET; - sll.sll_protocol = htons(ETH_P_ARP); - sll.sll_ifindex = (ifnum == 1 ? ifvec[0] : 0); - if (bind(pset[0].fd, (struct sockaddr*)&sll, sizeof(sll)) < 0) { + struct sockaddr_ll sll = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_ARP), + .sll_ifindex = (ifnum == 1 ? ifvec[0] : 0), + }; + + if (bind(pset[0].fd, (struct sockaddr *)&sll, sizeof(sll)) < 0) { perror("bind"); goto do_abort; } diff -Nru iproute2-4.3.0/misc/ifstat.c iproute2-4.9.0/misc/ifstat.c --- iproute2-4.3.0/misc/ifstat.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/misc/ifstat.c 2016-12-12 23:07:42.000000000 +0000 @@ -35,15 +35,15 @@ #include -int dump_zeros = 0; -int reset_history = 0; -int ignore_history = 0; -int no_output = 0; -int json_output = 0; -int no_update = 0; -int scan_interval = 0; -int time_constant = 0; -int show_errors = 0; +int dump_zeros; +int reset_history; +int ignore_history; +int no_output; +int json_output; +int no_update; +int scan_interval; +int time_constant; +int show_errors; int pretty; double W; char **patterns; @@ -54,8 +54,7 @@ #define MAXS (sizeof(struct rtnl_link_stats)/sizeof(__u32)) -struct ifstat_ent -{ +struct ifstat_ent { struct ifstat_ent *next; char *name; int ifindex; @@ -100,7 +99,7 @@ if (npatterns == 0) return 1; - for (i=0; inlmsg_len; struct ifstat_ent *n; int i; @@ -137,7 +136,7 @@ n->name = strdup(RTA_DATA(tb[IFLA_IFNAME])); memcpy(&n->ival, RTA_DATA(tb[IFLA_STATS]), sizeof(n->ival)); memset(&n->rate, 0, sizeof(n->rate)); - for (i=0; ival[i] = n->ival[i]; n->next = kern_db; kern_db = n; @@ -209,8 +208,9 @@ n->name = strdup(p); p = next; - for (i=0; inext) { + for (n = kern_db; n; n = n->next) { int i; unsigned long long *vals = n->val; double *rates = n->rate; + if (!match(n->name)) { struct ifstat_ent *h1; + if (!to_hist) continue; for (h1 = h; h1; h1 = h1->next) { @@ -273,19 +276,21 @@ jsonw_name(jw, n->name); jsonw_start_object(jw); - for (i=0; iifindex, n->name); - for (i=0; i mega) { - sprintf(temp, "%uM", (unsigned)(rates[i]/mega)); + sprintf(temp, "%uM", (unsigned int)(rates[i]/mega)); fprintf(fp, "%-6s ", temp); } else if (rates[i] > kilo) { - sprintf(temp, "%uK", (unsigned)(rates[i]/kilo)); + sprintf(temp, "%uK", (unsigned int)(rates[i]/kilo)); fprintf(fp, "%-6s ", temp); } else - fprintf(fp, "%-6u ", (unsigned)rates[i]); + fprintf(fp, "%-6u ", (unsigned int)rates[i]); } static void format_pair(FILE *fp, const unsigned long long *vals, int i, int k) { char temp[64]; + if (vals[i] > giga) fprintf(fp, "%7lluM ", vals[i]/mega); else if (vals[i] > mega) @@ -328,13 +334,13 @@ fprintf(fp, "%8llu ", vals[i]); if (vals[k] > giga) { - sprintf(temp, "%uM", (unsigned)(vals[k]/mega)); + sprintf(temp, "%uM", (unsigned int)(vals[k]/mega)); fprintf(fp, "%-6s ", temp); } else if (vals[k] > mega) { - sprintf(temp, "%uK", (unsigned)(vals[k]/kilo)); + sprintf(temp, "%uK", (unsigned int)(vals[k]/kilo)); fprintf(fp, "%-6s ", temp); } else - fprintf(fp, "%-6u ", (unsigned)vals[k]); + fprintf(fp, "%-6u ", (unsigned int)vals[k]); } static void print_head(FILE *fp) @@ -345,38 +351,38 @@ fprintf(fp, "%8s/%-6s ", "RX Pkts", "Rate"); fprintf(fp, "%8s/%-6s ", "TX Pkts", "Rate"); fprintf(fp, "%8s/%-6s ", "RX Data", "Rate"); - fprintf(fp, "%8s/%-6s\n","TX Data", "Rate"); + fprintf(fp, "%8s/%-6s\n", "TX Data", "Rate"); if (!show_errors) { fprintf(fp, "%-15s ", ""); fprintf(fp, "%8s/%-6s ", "RX Errs", "Drop"); fprintf(fp, "%8s/%-6s ", "TX Errs", "Drop"); fprintf(fp, "%8s/%-6s ", "RX Over", "Rate"); - fprintf(fp, "%8s/%-6s\n","TX Coll", "Rate"); + fprintf(fp, "%8s/%-6s\n", "TX Coll", "Rate"); } else { fprintf(fp, "%-15s ", ""); fprintf(fp, "%8s/%-6s ", "RX Errs", "Rate"); fprintf(fp, "%8s/%-6s ", "RX Drop", "Rate"); fprintf(fp, "%8s/%-6s ", "RX Over", "Rate"); - fprintf(fp, "%8s/%-6s\n","RX Leng", "Rate"); + fprintf(fp, "%8s/%-6s\n", "RX Leng", "Rate"); fprintf(fp, "%-15s ", ""); fprintf(fp, "%8s/%-6s ", "RX Crc", "Rate"); fprintf(fp, "%8s/%-6s ", "RX Frm", "Rate"); fprintf(fp, "%8s/%-6s ", "RX Fifo", "Rate"); - fprintf(fp, "%8s/%-6s\n","RX Miss", "Rate"); + fprintf(fp, "%8s/%-6s\n", "RX Miss", "Rate"); fprintf(fp, "%-15s ", ""); fprintf(fp, "%8s/%-6s ", "TX Errs", "Rate"); fprintf(fp, "%8s/%-6s ", "TX Drop", "Rate"); fprintf(fp, "%8s/%-6s ", "TX Coll", "Rate"); - fprintf(fp, "%8s/%-6s\n","TX Carr", "Rate"); + fprintf(fp, "%8s/%-6s\n", "TX Carr", "Rate"); fprintf(fp, "%-15s ", ""); fprintf(fp, "%8s/%-6s ", "TX Abrt", "Rate"); fprintf(fp, "%8s/%-6s ", "TX Fifo", "Rate"); fprintf(fp, "%8s/%-6s ", "TX Hear", "Rate"); - fprintf(fp, "%8s/%-6s\n","TX Wind", "Rate"); + fprintf(fp, "%8s/%-6s\n", "TX Wind", "Rate"); } } @@ -388,7 +394,7 @@ jsonw_name(jw, n->name); jsonw_start_object(jw); - for (i=0; i < m && stats[i]; i++) + for (i = 0; i < m && stats[i]; i++) jsonw_uint_field(jw, stats[i], vals[i]); jsonw_end_object(jw); @@ -400,7 +406,7 @@ int i; fprintf(fp, "%-15s ", n->name); - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) format_rate(fp, vals, n->rate, i); fprintf(fp, "\n"); @@ -448,13 +454,14 @@ struct ifstat_ent *n; if (jw) { + jsonw_start_object(jw); jsonw_pretty(jw, pretty); jsonw_name(jw, info_source); jsonw_start_object(jw); } else print_head(fp); - for (n=kern_db; n; n=n->next) { + for (n = kern_db; n; n = n->next) { if (!match(n->name)) continue; @@ -474,13 +481,14 @@ h = hist_db; if (jw) { + jsonw_start_object(jw); jsonw_pretty(jw, pretty); jsonw_name(jw, info_source); jsonw_start_object(jw); } else print_head(fp); - for (n=kern_db; n; n=n->next) { + for (n = kern_db; n; n = n->next) { int i; unsigned long long vals[MAXS]; struct ifstat_ent *h1; @@ -506,6 +514,8 @@ if (jw) { jsonw_end_object(jw); + + jsonw_end_object(jw); jsonw_destroy(&jw); } } @@ -530,9 +540,11 @@ for (n = kern_db; n; n = n->next) { struct ifstat_ent *h1; + for (h1 = h; h1; h1 = h1->next) { if (h1->ifindex == n->ifindex) { int i; + for (i = 0; i < MAXS; i++) { if ((long)(h1->ival[i] - n->ival[i]) < 0) { memset(n->ival, 0, sizeof(n->ival)); @@ -542,6 +554,7 @@ for (i = 0; i < MAXS; i++) { double sample; unsigned long incr = h1->ival[i] - n->ival[i]; + n->val[i] += incr; n->ival[i] = h1->ival[i]; sample = (double)(incr*1000)/interval; @@ -552,6 +565,7 @@ n->rate[i] = sample; } else { double w = W*(double)interval/scan_interval; + n->rate[i] += w*(sample-n->rate[i]); } } @@ -559,6 +573,7 @@ while (h != h1) { struct ifstat_ent *tmp = h; + h = h->next; free(tmp->name); free(tmp); @@ -572,13 +587,14 @@ } } -#define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) +#define T_DIFF(a, b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) static void server_loop(int fd) { struct timeval snaptime = { 0 }; struct pollfd p; + p.fd = fd; p.events = p.revents = POLLIN; @@ -589,7 +605,7 @@ for (;;) { int status; - int tdiff; + time_t tdiff; struct timeval now; gettimeofday(&now, NULL); @@ -600,24 +616,24 @@ tdiff = 0; } - if (poll(&p, 1, tdiff + scan_interval) > 0 + if (poll(&p, 1, scan_interval - tdiff) > 0 && (p.revents&POLLIN)) { int clnt = accept(fd, NULL, NULL); + if (clnt >= 0) { pid_t pid; + if (children >= 5) { close(clnt); } else if ((pid = fork()) != 0) { - if (pid>0) + if (pid > 0) children++; close(clnt); } else { FILE *fp = fdopen(clnt, "w"); - if (fp) { - if (tdiff > 0) - update_db(tdiff); + + if (fp) dump_raw_db(fp, 0); - } exit(0); } } @@ -632,7 +648,7 @@ struct ucred cred; socklen_t olen = sizeof(cred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *)&cred, &olen) || olen < sizeof(cred)) return -1; if (cred.uid == getuid() || cred.uid == 0) @@ -646,18 +662,18 @@ { fprintf(stderr, "Usage: ifstat [OPTION] [ PATTERN [ PATTERN ] ]\n" -" -h, --help this message\n" -" -a, --ignore ignore history\n" -" -d, --scan=SECS sample every statistics every SECS\n" -" -e, --errors show errors\n" -" -j, --json format output in JSON\n" -" -n, --nooutput do history only\n" -" -p, --pretty pretty print\n" -" -r, --reset reset history\n" -" -s, --noupdate don\'t update history\n" -" -t, --interval=SECS report average over the last SECS\n" -" -V, --version output version information\n" -" -z, --zeros show entries with zero activity\n"); +" -h, --help this message\n" +" -a, --ignore ignore history\n" +" -d, --scan=SECS sample every statistics every SECS\n" +" -e, --errors show errors\n" +" -j, --json format output in JSON\n" +" -n, --nooutput do history only\n" +" -p, --pretty pretty print\n" +" -r, --reset reset history\n" +" -s, --noupdate don't update history\n" +" -t, --interval=SECS report average over the last SECS\n" +" -V, --version output version information\n" +" -z, --zeros show entries with zero activity\n"); exit(-1); } @@ -688,7 +704,7 @@ while ((ch = getopt_long(argc, argv, "hjpvVzrnasd:t:e", longopts, NULL)) != EOF) { - switch(ch) { + switch (ch) { case 'z': dump_zeros = 1; break; @@ -754,7 +770,7 @@ perror("ifstat: socket"); exit(-1); } - if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { + if (bind(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { perror("ifstat: bind"); exit(-1); } @@ -812,6 +828,7 @@ if (!ignore_history) { FILE *tfp; long uptime = -1; + if ((tfp = fopen("/proc/uptime", "r")) != NULL) { if (fscanf(tfp, "%ld", &uptime) != 1) uptime = -1; @@ -819,7 +836,8 @@ } if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) { fprintf(stderr, "ifstat: history is aged out, resetting\n"); - ftruncate(fileno(hist_fp), 0); + if (ftruncate(fileno(hist_fp), 0)) + perror("ifstat: ftruncate"); } } @@ -830,11 +848,12 @@ } if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && - (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 + (connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0 || (strcpy(sun.sun_path+1, "ifstat0"), - connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) + connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) && verify_forging(fd) == 0) { FILE *sfp = fdopen(fd, "r"); + load_raw_table(sfp); if (hist_db && source_mismatch) { fprintf(stderr, "ifstat: history is stale, ignoring it.\n"); @@ -862,7 +881,8 @@ } if (!no_update) { - ftruncate(fileno(hist_fp), 0); + if (ftruncate(fileno(hist_fp), 0)) + perror("ifstat: ftruncate"); rewind(hist_fp); json_output = 0; diff -Nru iproute2-4.3.0/misc/lnstat.c iproute2-4.9.0/misc/lnstat.c --- iproute2-4.3.0/misc/lnstat.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/misc/lnstat.c 2016-12-12 23:07:42.000000000 +0000 @@ -56,10 +56,8 @@ static int usage(char *name, int exit_code) { fprintf(stderr, "%s Version %s\n", name, LNSTAT_VERSION); - fprintf(stderr, "Copyright (C) 2004 by Harald Welte " - "\n"); - fprintf(stderr, "This program is free software licensed under GNU GPLv2" - "\nwith ABSOLUTELY NO WARRANTY.\n\n"); + fprintf(stderr, "Copyright (C) 2004 by Harald Welte \n"); + fprintf(stderr, "This program is free software licensed under GNU GPLv2\nwith ABSOLUTELY NO WARRANTY.\n\n"); fprintf(stderr, "Parameters:\n"); fprintf(stderr, "\t-V --version\t\tPrint Version of Program\n"); fprintf(stderr, "\t-c --count \t" @@ -73,7 +71,10 @@ fprintf(stderr, "\t-i --interval \t" "Set interval to 'intv' seconds\n"); fprintf(stderr, "\t-k --keys k,k,k,...\tDisplay only keys specified\n"); - fprintf(stderr, "\t-s --subject [0-2]\t?\n"); + fprintf(stderr, "\t-s --subject [0-2]\tControl header printing:\n"); + fprintf(stderr, "\t\t\t\t0 = never\n"); + fprintf(stderr, "\t\t\t\t1 = once\n"); + fprintf(stderr, "\t\t\t\t2 = every 20 lines (default))\n"); fprintf(stderr, "\t-w --width n,n,n,...\tWidth for each field\n"); fprintf(stderr, "\n"); @@ -142,14 +143,13 @@ if (++j >= MAX_FIELDS - 1) { fprintf(stderr, - "WARN: MAX_FIELDS (%d) reached," - " truncating number of keys\n", + "WARN: MAX_FIELDS (%d) reached, truncating number of keys\n", MAX_FIELDS); goto full; } } } - full: +full: fps->num = j; return 1; } @@ -178,14 +178,12 @@ struct field_params *fps, int linewidth) { - int h,i; + int h, i; static struct table_hdr th; int ofs = 0; - for (i = 0; i < HDR_LINES; i++) { - th.hdr[i] = malloc(HDR_LINE_LENGTH); - memset(th.hdr[i], 0, HDR_LINE_LENGTH); - } + for (i = 0; i < HDR_LINES; i++) + th.hdr[i] = calloc(1, HDR_LINE_LENGTH); for (i = 0; i < fps->num; i++) { char *cname, *fname = fps->params[i].lf->name; @@ -266,7 +264,7 @@ num_req_files = 1; } - while ((c = getopt_long(argc, argv,"Vc:djpf:h?i:k:s:w:", + while ((c = getopt_long(argc, argv, "Vc:djpf:h?i:k:s:w:", opts, NULL)) != -1) { int len = 0; char *tmp, *tok; @@ -300,8 +298,7 @@ tok = strtok(NULL, ",")) { if (fp.num >= MAX_FIELDS) { fprintf(stderr, - "WARN: too many keys" - " requested: (%d max)\n", + "WARN: too many keys requested: (%d max)\n", MAX_FIELDS); break; } @@ -353,24 +350,22 @@ if (!header) exit(1); - if (interval < 1 ) + if (interval < 1) interval = 1; - for (i = 0; i < count || !count; ) { + for (i = 0; i < count || !count; i++) { lnstat_update(lnstat_files); if (mode == MODE_JSON) print_json(stdout, lnstat_files, &fp); else { - if ((hdr > 1 && - (! (i % 20))) || (hdr == 1 && i == 0)) + if ((hdr > 1 && !(i % 20)) || + (hdr == 1 && i == 0)) print_hdr(stdout, header); print_line(stdout, lnstat_files, &fp); } fflush(stdout); if (i < count - 1 || !count) sleep(interval); - if (count) - ++i; } break; } diff -Nru iproute2-4.3.0/misc/lnstat_util.c iproute2-4.9.0/misc/lnstat_util.c --- iproute2-4.3.0/misc/lnstat_util.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/misc/lnstat_util.c 2016-12-12 23:07:42.000000000 +0000 @@ -38,22 +38,27 @@ /* Read (and summarize for SMP) the different stats vars. */ static int scan_lines(struct lnstat_file *lf, int i) { + char buf[FGETS_BUF_SIZE]; int j, num_lines = 0; for (j = 0; j < lf->num_fields; j++) lf->fields[j].values[i] = 0; - while(!feof(lf->fp)) { - char buf[FGETS_BUF_SIZE]; + rewind(lf->fp); + /* skip first line */ + if (!lf->compat && !fgets(buf, sizeof(buf)-1, lf->fp)) + return -1; + + while (!feof(lf->fp) && fgets(buf, sizeof(buf)-1, lf->fp)) { char *ptr = buf; num_lines++; - fgets(buf, sizeof(buf)-1, lf->fp); gettimeofday(&lf->last_read, NULL); for (j = 0; j < lf->num_fields; j++) { unsigned long f = strtoul(ptr, &ptr, 16); + if (j == 0) lf->fields[j].values[i] = f; else @@ -81,7 +86,6 @@ int lnstat_update(struct lnstat_file *lnstat_files) { struct lnstat_file *lf; - char buf[FGETS_BUF_SIZE]; struct timeval tv; gettimeofday(&tv, NULL); @@ -91,11 +95,6 @@ int i; struct lnstat_field *lfi; - rewind(lf->fp); - if (!lf->compat) { - /* skip first line */ - fgets(buf, sizeof(buf)-1, lf->fp); - } scan_lines(lf, 1); for (i = 0, lfi = &lf->fields[i]; @@ -104,11 +103,9 @@ lfi->result = lfi->values[1]; else lfi->result = (lfi->values[1]-lfi->values[0]) - / lf->interval.tv_sec; + / lf->interval.tv_sec; } - rewind(lf->fp); - fgets(buf, sizeof(buf)-1, lf->fp); scan_lines(lf, 0); } } @@ -142,7 +139,8 @@ char buf[FGETS_BUF_SIZE]; rewind(lf->fp); - fgets(buf, sizeof(buf)-1, lf->fp); + if (!fgets(buf, sizeof(buf)-1, lf->fp)) + return -1; return __lnstat_scan_fields(lf, buf); } @@ -161,6 +159,7 @@ static int name_in_array(const int num, const char **arr, const char *name) { int i; + for (i = 0; i < num; i++) { if (!strcmp(arr[i], name)) return 1; @@ -174,13 +173,13 @@ struct lnstat_file *lf; /* allocate */ - lf = malloc(sizeof(*lf)); - if (!lf) + lf = calloc(1, sizeof(*lf)); + if (!lf) { + fprintf(stderr, "out of memory\n"); return NULL; + } /* initialize */ - memset(lf, 0, sizeof(*lf)); - /* de->d_name is guaranteed to be <= NAME_MAX */ strcpy(lf->basename, file); strcpy(lf->path, path); @@ -193,6 +192,7 @@ /* open */ lf->fp = fopen(lf->path, "r"); if (!lf->fp) { + perror(lf->path); free(lf); return NULL; } @@ -259,12 +259,16 @@ continue; lf = alloc_and_open(path, de->d_name); - if (!lf) + if (!lf) { + closedir(dir); return NULL; + } /* fill in field structure */ - if (lnstat_scan_fields(lf) < 0) + if (lnstat_scan_fields(lf) < 0) { + closedir(dir); return NULL; + } /* prepend to global list */ lf->next = lnstat_files; diff -Nru iproute2-4.3.0/misc/Makefile iproute2-4.9.0/misc/Makefile --- iproute2-4.3.0/misc/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/misc/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -10,8 +10,8 @@ endif ifeq ($(HAVE_SELINUX),y) - LDLIBS += $(shell pkg-config --libs libselinux) - CFLAGS += $(shell pkg-config --cflags libselinux) -DHAVE_SELINUX + LDLIBS += $(shell $(PKG_CONFIG) --libs libselinux) + CFLAGS += $(shell $(PKG_CONFIG) --cflags libselinux) -DHAVE_SELINUX endif ifeq ($(IP_CONFIG_SETNS),y) @@ -21,23 +21,25 @@ all: $(TARGETS) ss: $(SSOBJ) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ nstat: nstat.c - $(CC) $(CFLAGS) $(LDFLAGS) -o nstat nstat.c $(LIBNETLINK) -lm + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o nstat nstat.c $(LIBNETLINK) -lm ifstat: ifstat.c - $(CC) $(CFLAGS) $(LDFLAGS) -o ifstat ifstat.c $(LIBNETLINK) -lm + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o ifstat ifstat.c $(LIBNETLINK) -lm rtacct: rtacct.c - $(CC) $(CFLAGS) $(LDFLAGS) -o rtacct rtacct.c $(LIBNETLINK) -lm + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o rtacct rtacct.c $(LIBNETLINK) -lm arpd: arpd.c - $(CC) $(CFLAGS) -I$(DBM_INCLUDE) $(LDFLAGS) -o arpd arpd.c $(LIBNETLINK) -ldb -lpthread + $(QUIET_CC)$(CC) $(CFLAGS) -I$(DBM_INCLUDE) $(LDFLAGS) -o arpd arpd.c $(LIBNETLINK) -ldb -lpthread ssfilter.c: ssfilter.y - bison ssfilter.y -o ssfilter.c + $(QUIET_YACC)bison ssfilter.y -o ssfilter.c lnstat: $(LNSTATOBJ) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ install: all install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR) diff -Nru iproute2-4.3.0/misc/nstat.c iproute2-4.9.0/misc/nstat.c --- iproute2-4.3.0/misc/nstat.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/misc/nstat.c 2016-12-12 23:07:42.000000000 +0000 @@ -30,16 +30,17 @@ #include #include +#include "utils.h" -int dump_zeros = 0; -int reset_history = 0; -int ignore_history = 0; -int no_output = 0; -int json_output = 0; -int pretty = 0; -int no_update = 0; -int scan_interval = 0; -int time_constant = 0; +int dump_zeros; +int reset_history; +int ignore_history; +int no_output; +int json_output; +int pretty; +int no_update; +int scan_interval; +int time_constant; double W; char **patterns; int npatterns; @@ -51,6 +52,7 @@ { char store[128]; char *p = getenv(env); + if (!p) { p = getenv("PROC_ROOT") ? : "/proc"; snprintf(store, sizeof(store)-1, "%s/%s", p, name); @@ -74,8 +76,12 @@ return generic_proc_open("PROC_NET_SNMP6", "net/snmp6"); } -struct nstat_ent +static int net_sctp_snmp_open(void) { + return generic_proc_open("PROC_NET_SCTP_SNMP", "net/sctp/snmp"); +} + +struct nstat_ent { struct nstat_ent *next; char *id; unsigned long long val; @@ -94,7 +100,8 @@ static int useless_number(const char *id) { int i; - for (i=0; inext) { + for (n = kern_db; n; n = n->next) { unsigned long long val = n->val; + if (!dump_zeros && !val && !n->rate) continue; if (!match(n->id)) { struct nstat_ent *h1; + if (!to_hist) continue; for (h1 = h; h1; h1 = h1->next) { @@ -309,6 +334,8 @@ if (jw) { jsonw_end_object(jw); + + jsonw_end_object(jw); jsonw_destroy(&jw); } } @@ -320,16 +347,18 @@ h = hist_db; if (jw) { + jsonw_start_object(jw); jsonw_pretty(jw, pretty); jsonw_name(jw, info_source); jsonw_start_object(jw); } else fprintf(fp, "#%s\n", info_source); - for (n=kern_db; n; n=n->next) { + for (n = kern_db; n; n = n->next) { int ovfl = 0; unsigned long long val = n->val; struct nstat_ent *h1; + for (h1 = h; h1; h1 = h1->next) { if (strcmp(h1->id, n->id) == 0) { if (val < h1->val) { @@ -355,6 +384,8 @@ if (jw) { jsonw_end_object(jw); + + jsonw_end_object(jw); jsonw_destroy(&jw); } } @@ -375,12 +406,14 @@ load_netstat(); load_snmp6(); load_snmp(); + load_sctp_snmp(); h = kern_db; kern_db = n; for (n = kern_db; n; n = n->next) { struct nstat_ent *h1; + for (h1 = h; h1; h1 = h1->next) { if (strcmp(h1->id, n->id) == 0) { double sample; @@ -395,12 +428,14 @@ n->rate = sample; } else { double w = W*(double)interval/scan_interval; + n->rate += w*(sample-n->rate); } } while (h != h1) { struct nstat_ent *tmp = h; + h = h->next; free(tmp->id); free(tmp); @@ -414,13 +449,14 @@ } } -#define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) +#define T_DIFF(a, b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) static void server_loop(int fd) { struct timeval snaptime = { 0 }; struct pollfd p; + p.fd = fd; p.events = p.revents = POLLIN; @@ -430,11 +466,13 @@ load_netstat(); load_snmp6(); load_snmp(); + load_sctp_snmp(); for (;;) { int status; - int tdiff; + time_t tdiff; struct timeval now; + gettimeofday(&now, NULL); tdiff = T_DIFF(now, snaptime); if (tdiff >= scan_interval) { @@ -442,24 +480,24 @@ snaptime = now; tdiff = 0; } - if (poll(&p, 1, tdiff + scan_interval) > 0 + if (poll(&p, 1, scan_interval - tdiff) > 0 && (p.revents&POLLIN)) { int clnt = accept(fd, NULL, NULL); + if (clnt >= 0) { pid_t pid; + if (children >= 5) { close(clnt); } else if ((pid = fork()) != 0) { - if (pid>0) + if (pid > 0) children++; close(clnt); } else { FILE *fp = fdopen(clnt, "w"); - if (fp) { - if (tdiff > 0) - update_db(tdiff); + + if (fp) dump_kern_db(fp, 0); - } exit(0); } } @@ -474,7 +512,7 @@ struct ucred cred; socklen_t olen = sizeof(cred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *)&cred, &olen) || olen < sizeof(cred)) return -1; if (cred.uid == getuid() || cred.uid == 0) @@ -488,17 +526,17 @@ { fprintf(stderr, "Usage: nstat [OPTION] [ PATTERN [ PATTERN ] ]\n" -" -h, --help this message\n" -" -a, --ignore ignore history\n" -" -d, --scan=SECS sample every statistics every SECS\n" -" -j, --json format output in JSON\n" -" -n, --nooutput do history only\n" -" -p, --pretty pretty print\n" -" -r, --reset reset history\n" -" -s, --noupdate don\'t update history\n" -" -t, --interval=SECS report average over the last SECS\n" -" -V, --version output version information\n" -" -z, --zeros show entries with zero activity\n"); +" -h, --help this message\n" +" -a, --ignore ignore history\n" +" -d, --scan=SECS sample every statistics every SECS\n" +" -j, --json format output in JSON\n" +" -n, --nooutput do history only\n" +" -p, --pretty pretty print\n" +" -r, --reset reset history\n" +" -s, --noupdate don't update history\n" +" -t, --interval=SECS report average over the last SECS\n" +" -V, --version output version information\n" +" -z, --zeros show entries with zero activity\n"); exit(-1); } @@ -527,7 +565,7 @@ while ((ch = getopt_long(argc, argv, "h?vVzrnasd:t:jp", longopts, NULL)) != EOF) { - switch(ch) { + switch (ch) { case 'z': dump_zeros = 1; break; @@ -586,7 +624,7 @@ perror("nstat: socket"); exit(-1); } - if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { + if (bind(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { perror("nstat: bind"); exit(-1); } @@ -642,6 +680,7 @@ if (!ignore_history) { FILE *tfp; long uptime = -1; + if ((tfp = fopen("/proc/uptime", "r")) != NULL) { if (fscanf(tfp, "%ld", &uptime) != 1) uptime = -1; @@ -649,7 +688,8 @@ } if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) { fprintf(stderr, "nstat: history is aged out, resetting\n"); - ftruncate(fileno(hist_fp), 0); + if (ftruncate(fileno(hist_fp), 0) < 0) + perror("nstat: ftruncate"); } } @@ -660,11 +700,12 @@ } if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && - (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 + (connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0 || (strcpy(sun.sun_path+1, "nstat0"), - connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) + connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) && verify_forging(fd) == 0) { FILE *sfp = fdopen(fd, "r"); + load_good_table(sfp); if (hist_db && source_mismatch) { fprintf(stderr, "nstat: history is stale, ignoring it.\n"); @@ -682,6 +723,7 @@ load_netstat(); load_snmp6(); load_snmp(); + load_sctp_snmp(); if (info_source[0] == 0) strcpy(info_source, "kernel"); } @@ -693,7 +735,8 @@ dump_incr_db(stdout); } if (!no_update) { - ftruncate(fileno(hist_fp), 0); + if (ftruncate(fileno(hist_fp), 0) < 0) + perror("nstat: ftruncate"); rewind(hist_fp); json_output = 0; diff -Nru iproute2-4.3.0/misc/rtacct.c iproute2-4.9.0/misc/rtacct.c --- iproute2-4.3.0/misc/rtacct.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/misc/rtacct.c 2016-12-12 23:07:42.000000000 +0000 @@ -33,20 +33,21 @@ #include -int reset_history = 0; -int ignore_history = 0; -int no_output = 0; -int no_update = 0; -int scan_interval = 0; -int time_constant = 0; -int dump_zeros = 0; -unsigned long magic_number = 0; +int reset_history; +int ignore_history; +int no_output; +int no_update; +int scan_interval; +int time_constant; +int dump_zeros; +unsigned long magic_number; double W; static int generic_proc_open(const char *env, const char *name) { char store[1024]; char *p = getenv(env); + if (!p) { p = getenv("PROC_ROOT") ? : "/proc"; snprintf(store, sizeof(store)-1, "%s/%s", p, name); @@ -62,8 +63,7 @@ static __u32 rmap[256/4]; -struct rtacct_data -{ +struct rtacct_data { __u32 ival[256*4]; unsigned long long val[256*4]; @@ -82,6 +82,7 @@ while (count < tot) { int n = read(fd, buf+count, tot-count); + if (n < 0) { if (errno == EINTR) continue; @@ -121,7 +122,7 @@ fd = net_rtacct_open(); if (fd >= 0) { - nread(fd, (char*)tbl, 256*16); + nread(fd, (char *)tbl, 256*16); close(fd); } else { memset(tbl, 0, 256*16); @@ -134,13 +135,13 @@ char temp[64]; if (rate > 1024*1024) { - sprintf(temp, "%uM", (unsigned)rint(rate/(1024*1024))); + sprintf(temp, "%uM", (unsigned int)rint(rate/(1024*1024))); fprintf(fp, " %-10s", temp); } else if (rate > 1024) { - sprintf(temp, "%uK", (unsigned)rint(rate/1024)); + sprintf(temp, "%uK", (unsigned int)rint(rate/1024)); fprintf(fp, " %-10s", temp); } else - fprintf(fp, " %-10u", (unsigned)rate); + fprintf(fp, " %-10u", (unsigned int)rate); } static void format_count(FILE *fp, unsigned long long val) @@ -161,25 +162,19 @@ if (!no_output) { fprintf(fp, "#%s\n", kern_db->signature); fprintf(fp, -"%-10s " -"%-10s " -"%-10s " -"%-10s " -"%-10s " -"\n" +"%-10s %-10s " +"%-10s %-10s " +"%-10s \n" , "Realm", "BytesTo", "PktsTo", "BytesFrom", "PktsFrom"); fprintf(fp, -"%-10s " -"%-10s " -"%-10s " -"%-10s " -"%-10s " -"\n" +"%-10s %-10s " +"%-10s %-10s " +"%-10s \n" , "", "BPSTo", "PPSTo", "BPSFrom", "PPSFrom"); } - for (realm=0; realm<256; realm++) { + for (realm = 0; realm < 256; realm++) { int i; unsigned long long *val; double *rate; @@ -223,24 +218,18 @@ if (!no_output) { fprintf(fp, "#%s\n", kern_db->signature); fprintf(fp, -"%-10s " -"%-10s " -"%-10s " -"%-10s " -"%-10s " -"\n" +"%-10s %-10s " +"%-10s %-10s " +"%-10s \n" , "Realm", "BytesTo", "PktsTo", "BytesFrom", "PktsFrom"); fprintf(fp, -"%-10s " -"%-10s " -"%-10s " -"%-10s " -"%-10s " -"\n" +"%-10s %-10s " +"%-10s %-10s " +"%-10s \n" , "", "BPSTo", "PPSTo", "BPSFrom", "PPSFrom"); } - for (realm=0; realm<256; realm++) { + for (realm = 0; realm < 256; realm++) { int ovfl = 0; int i; unsigned long long *val; @@ -253,7 +242,7 @@ val = &kern_db->val[realm*4]; rate = &kern_db->rate[realm*4]; - for (k=0; k<4; k++) { + for (k = 0; k < 4; k++) { rval[k] = val[k]; if (rval[k] < hist_db->val[realm*4+k]) ovfl = 1; @@ -261,7 +250,7 @@ rval[k] -= hist_db->val[realm*4+k]; } if (ovfl) { - for (k=0; k<4; k++) + for (k = 0; k < 4; k++) rval[k] = val[k]; } if (hist_db) { @@ -306,7 +295,7 @@ ival = read_kern_table(_ival); - for (i=0; i<256*4; i++) { + for (i = 0; i < 256*4; i++) { double sample; __u32 incr = ival[i] - kern_db->ival[i]; @@ -324,6 +313,7 @@ kern_db->rate[i] = sample; } else { double w = W*(double)interval/scan_interval; + kern_db->rate[i] += w*(sample-kern_db->rate[i]); } } @@ -335,7 +325,8 @@ int tot = 0; while (tot < sizeof(*kern_db)) { - int n = write(fd, ((char*)kern_db) + tot, sizeof(*kern_db)-tot); + int n = write(fd, ((char *)kern_db) + tot, sizeof(*kern_db)-tot); + if (n < 0) { if (errno == EINTR) continue; @@ -347,16 +338,17 @@ -#define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) +#define T_DIFF(a, b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) static void pad_kern_table(struct rtacct_data *dat, __u32 *ival) { int i; + memset(dat->rate, 0, sizeof(dat->rate)); if (dat->ival != ival) memcpy(dat->ival, ival, sizeof(dat->ival)); - for (i=0; i<256*4; i++) + for (i = 0; i < 256*4; i++) dat->val[i] = ival[i]; } @@ -364,12 +356,13 @@ { struct timeval snaptime = { 0 }; struct pollfd p; + p.fd = fd; p.events = p.revents = POLLIN; sprintf(kern_db->signature, "%u.%lu sampling_interval=%d time_const=%d", - (unsigned) getpid(), (unsigned long)random(), + (unsigned int) getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000); pad_kern_table(kern_db, read_kern_table(kern_db->ival)); @@ -378,6 +371,7 @@ int status; int tdiff; struct timeval now; + gettimeofday(&now, NULL); tdiff = T_DIFF(now, snaptime); if (tdiff >= scan_interval) { @@ -388,12 +382,14 @@ if (poll(&p, 1, tdiff + scan_interval) > 0 && (p.revents&POLLIN)) { int clnt = accept(fd, NULL, NULL); + if (clnt >= 0) { pid_t pid; + if (children >= 5) { close(clnt); } else if ((pid = fork()) != 0) { - if (pid>0) + if (pid > 0) children++; close(clnt); } else { @@ -414,7 +410,7 @@ struct ucred cred; socklen_t olen = sizeof(cred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *)&cred, &olen) || olen < sizeof(cred)) return -1; if (cred.uid == getuid() || cred.uid == 0) @@ -440,7 +436,7 @@ int fd; while ((ch = getopt(argc, argv, "h?vVzrM:nasd:t:")) != EOF) { - switch(ch) { + switch (ch) { case 'z': dump_zeros = 1; break; @@ -489,6 +485,7 @@ if (argc) { while (argc > 0) { __u32 realm; + if (rtnl_rtrealm_a2n(&realm, argv[0])) { fprintf(stderr, "Warning: realm \"%s\" does not exist.\n", argv[0]); exit(-1); @@ -515,7 +512,7 @@ perror("rtacct: socket"); exit(-1); } - if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { + if (bind(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { perror("rtacct: bind"); exit(-1); } @@ -580,6 +577,7 @@ if (!ignore_history) { FILE *tfp; long uptime = -1; + if ((tfp = fopen("/proc/uptime", "r")) != NULL) { if (fscanf(tfp, "%ld", &uptime) != 1) uptime = -1; @@ -596,11 +594,11 @@ } if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && - (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 + (connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0 || (strcpy(sun.sun_path+1, "rtacct0"), - connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) + connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) && verify_forging(fd) == 0) { - nread(fd, (char*)kern_db, sizeof(*kern_db)); + nread(fd, (char *)kern_db, sizeof(*kern_db)); if (hist_db && hist_db->signature[0] && strcmp(kern_db->signature, hist_db->signature)) { fprintf(stderr, "rtacct: history is stale, ignoring it.\n"); diff -Nru iproute2-4.3.0/misc/ss.c iproute2-4.9.0/misc/ss.c --- iproute2-4.3.0/misc/ss.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/misc/ss.c 2016-12-12 23:07:42.000000000 +0000 @@ -26,6 +26,7 @@ #include #include #include +#include #include "utils.h" #include "rt_names.h" @@ -42,6 +43,7 @@ #include #include #include +#include #define MAGIC_SEQ 123456 @@ -86,20 +88,22 @@ } #endif -int resolve_hosts = 0; +int resolve_hosts; int resolve_services = 1; int preferred_family = AF_UNSPEC; -int show_options = 0; -int show_details = 0; -int show_users = 0; -int show_mem = 0; -int show_tcpinfo = 0; -int show_bpf = 0; -int show_proc_ctx = 0; -int show_sock_ctx = 0; +int show_options; +int show_details; +int show_users; +int show_mem; +int show_tcpinfo; +int show_bpf; +int show_proc_ctx; +int show_sock_ctx; +int show_header = 1; /* If show_users & show_proc_ctx only do user_ent_hash_build() once */ -int user_ent_hash_build_init = 0; -int follow_events = 0; +int user_ent_hash_build_init; +int follow_events; +int sctp_ino; int netid_width; int state_width; @@ -109,12 +113,12 @@ int screen_width; static const char *TCP_PROTO = "tcp"; +static const char *SCTP_PROTO = "sctp"; static const char *UDP_PROTO = "udp"; static const char *RAW_PROTO = "raw"; -static const char *dg_proto = NULL; +static const char *dg_proto; -enum -{ +enum { TCP_DB, DCCP_DB, UDP_DB, @@ -125,13 +129,15 @@ PACKET_DG_DB, PACKET_R_DB, NETLINK_DB, + SCTP_DB, MAX_DB }; #define PACKET_DBM ((1<states = (f->states | states) & states; + f->states = states; } static void filter_merge_defaults(struct filter *f) @@ -528,7 +550,8 @@ snprintf(tmp, sizeof(tmp), "%s/%d/stat", root, pid); if ((fp = fopen(tmp, "r")) != NULL) { - fscanf(fp, "%*d (%[^)])", p); + if (fscanf(fp, "%*d (%[^)])", p) < 1) + ; /* ignore */ fclose(fp); } } @@ -549,7 +572,7 @@ }; #define ENTRY_BUF_SIZE 512 -static int find_entry(unsigned ino, char **buf, int type) +static int find_entry(unsigned int ino, char **buf, int type) { struct user_ent *p; int cnt = 0; @@ -622,8 +645,7 @@ /* Get stats from slab */ -struct slabstat -{ +struct slabstat { int socks; int tcp_ports; int tcp_tws; @@ -633,8 +655,8 @@ static struct slabstat slabstat; -static const char *slabstat_ids[] = -{ +static const char *slabstat_ids[] = { + "sock", "tcp_bind_bucket", "tcp_tw_bucket", @@ -660,10 +682,14 @@ cnt = sizeof(*s)/sizeof(int); - fgets(buf, sizeof(buf), fp); - while(fgets(buf, sizeof(buf), fp) != NULL) { + if (!fgets(buf, sizeof(buf), fp)) { + fclose(fp); + return -1; + } + while (fgets(buf, sizeof(buf), fp) != NULL) { int i; - for (i=0; iino) + return false; + return true; +} + static void sock_state_print(struct sockstat *s, const char *sock_name) { if (netid_width) - printf("%-*s ", netid_width, sock_name); - if (state_width) - printf("%-*s ", state_width, sstate_name[s->state]); + printf("%-*s ", netid_width, + is_sctp_assoc(s, sock_name) ? "" : sock_name); + if (state_width) { + if (is_sctp_assoc(s, sock_name)) + printf("`- %-*s ", state_width - 3, + sctp_sstate_name[s->state]); + else + printf("%-*s ", state_width, sstate_name[s->state]); + } printf("%-6d %-6d ", s->rq, s->wq); } @@ -801,6 +860,9 @@ printf(" ino:%u", s->ino); printf(" sk:%llx", s->sk); + + if (s->mark) + printf(" fwmark:0x%x", s->mark); } static void sock_addr_print_width(int addr_len, const char *addr, char *delim, @@ -809,8 +871,7 @@ if (ifname) { printf("%*s%%%s%s%-*s ", addr_len, addr, ifname, delim, port_len, port); - } - else { + } else { printf("%*s%s%-*s ", addr_len, addr, delim, port_len, port); } } @@ -834,6 +895,7 @@ { static char buf[64]; int secs, msecs, minutes; + if (timeout < 0) timeout = 0; secs = timeout/1000; @@ -870,34 +932,41 @@ { char buf[128]; FILE *fp = popen("/usr/sbin/rpcinfo -p 2>/dev/null", "r"); - if (fp) { - fgets(buf, sizeof(buf), fp); - while (fgets(buf, sizeof(buf), fp) != NULL) { - unsigned int progn, port; - char proto[128], prog[128]; - if (sscanf(buf, "%u %*d %s %u %s", &progn, proto, - &port, prog+4) == 4) { - struct scache *c = malloc(sizeof(*c)); - if (c) { - c->port = port; - memcpy(prog, "rpc.", 4); - c->name = strdup(prog); - if (strcmp(proto, TCP_PROTO) == 0) - c->proto = TCP_PROTO; - else if (strcmp(proto, UDP_PROTO) == 0) - c->proto = UDP_PROTO; - else - c->proto = NULL; - c->next = rlist; - rlist = c; - } - } - } + + if (!fp) + return; + + if (!fgets(buf, sizeof(buf), fp)) { pclose(fp); + return; } -} + while (fgets(buf, sizeof(buf), fp) != NULL) { + unsigned int progn, port; + char proto[128], prog[128] = "rpc."; + struct scache *c; -static int ip_local_port_min, ip_local_port_max; + if (sscanf(buf, "%u %*d %s %u %s", + &progn, proto, &port, prog+4) != 4) + continue; + + if (!(c = malloc(sizeof(*c)))) + continue; + + c->port = port; + c->name = strdup(prog); + if (strcmp(proto, TCP_PROTO) == 0) + c->proto = TCP_PROTO; + else if (strcmp(proto, UDP_PROTO) == 0) + c->proto = UDP_PROTO; + else if (strcmp(proto, SCTP_PROTO) == 0) + c->proto = SCTP_PROTO; + else + c->proto = NULL; + c->next = rlist; + rlist = c; + } + pclose(fp); +} /* Even do not try default linux ephemeral port ranges: * default /etc/services contains so much of useless crap @@ -907,19 +976,19 @@ */ static int is_ephemeral(int port) { - if (!ip_local_port_min) { + static int min = 0, max; + + if (!min) { FILE *f = ephemeral_ports_open(); - if (f) { - fscanf(f, "%d %d", - &ip_local_port_min, &ip_local_port_max); - fclose(f); - } else { - ip_local_port_min = 1024; - ip_local_port_max = 4999; + + if (!f || fscanf(f, "%d %d", &min, &max) < 2) { + min = 1024; + max = 4999; } + if (f) + fclose(f); } - - return (port >= ip_local_port_min && port<= ip_local_port_max); + return port >= min && port <= max; } @@ -935,6 +1004,7 @@ if (!is_ephemeral(port)) { static int notfirst; struct servent *se; + if (!notfirst) { setservent(1); notfirst = 1; @@ -1008,10 +1078,10 @@ buf[0] = '*'; buf[1] = 0; } else { - ap = format_host(AF_INET, 4, a->data, buf, sizeof(buf)); + ap = format_host(AF_INET, 4, a->data); } } else { - ap = format_host(a->family, 16, a->data, buf, sizeof(buf)); + ap = format_host(a->family, 16, a->data); est_len = strlen(ap); if (est_len <= addr_width) est_len = addr_width; @@ -1030,10 +1100,12 @@ ifname); } -struct aafilter -{ +struct aafilter { inet_prefix addr; int port; + unsigned int iface; + __u32 mark; + __u32 mask; struct aafilter *next; }; @@ -1050,6 +1122,7 @@ if (a->data[0] == 0 && a->data[1] == 0 && a->data[2] == htonl(0xffff)) { inet_prefix tmp = *a; + tmp.data[0] = a->data[3]; return inet_addr_match(&tmp, p, plen); } @@ -1060,6 +1133,7 @@ static int unix_match(const inet_prefix *a, const inet_prefix *p) { char *addr, *pattern; + memcpy(&addr, a->data, sizeof(addr)); memcpy(&pattern, p->data, sizeof(pattern)); if (pattern == NULL) @@ -1074,10 +1148,9 @@ switch (f->type) { case SSF_S_AUTO: { - static int low, high=65535; - if (s->local.family == AF_UNIX) { char *p; + memcpy(&p, s->local.data, sizeof(p)); return p == NULL || (p[0] == '@' && strlen(p) == 6 && strspn(p+1, "0123456789abcdef") == 5); @@ -1087,18 +1160,12 @@ if (s->local.family == AF_NETLINK) return s->lport < 0; - if (!low) { - FILE *fp = ephemeral_ports_open(); - if (fp) { - fscanf(fp, "%d%d", &low, &high); - fclose(fp); - } - } - return s->lport >= low && s->lport <= high; + return is_ephemeral(s->lport); } case SSF_DCOND: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + if (a->addr.family == AF_UNIX) return unix_match(&s->remote, &a->addr); if (a->port != -1 && a->port != s->rport) @@ -1114,7 +1181,8 @@ } case SSF_SCOND: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + if (a->addr.family == AF_UNIX) return unix_match(&s->local, &a->addr); if (a->port != -1 && a->port != s->lport) @@ -1130,25 +1198,40 @@ } case SSF_D_GE: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + return s->rport >= a->port; } case SSF_D_LE: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + return s->rport <= a->port; } case SSF_S_GE: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + return s->lport >= a->port; } case SSF_S_LE: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + return s->lport <= a->port; } + case SSF_DEVCOND: + { + struct aafilter *a = (void *)f->pred; + + return s->iface == a->iface; + } + case SSF_MARKMASK: + { + struct aafilter *a = (void *)f->pred; + return (s->mark & a->mask) == a->mark; + } /* Yup. It is recursion. Sorry. */ case SSF_AND: return run_ssfilter(f->pred, s) && run_ssfilter(f->post, s); @@ -1165,7 +1248,8 @@ static void ssfilter_patch(char *a, int len, int reloc) { while (len > 0) { - struct inet_diag_bc_op *op = (struct inet_diag_bc_op*)a; + struct inet_diag_bc_op *op = (struct inet_diag_bc_op *)a; + if (op->no == len+4) op->no += reloc; len -= op->yes; @@ -1180,20 +1264,20 @@ switch (f->type) { case SSF_S_AUTO: { - if (!(*bytecode=malloc(4))) abort(); - ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_AUTO, 4, 8 }; + if (!(*bytecode = malloc(4))) abort(); + ((struct inet_diag_bc_op *)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_AUTO, 4, 8 }; return 4; } case SSF_DCOND: case SSF_SCOND: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; struct aafilter *b; char *ptr; int code = (f->type == SSF_DCOND ? INET_DIAG_BC_D_COND : INET_DIAG_BC_S_COND); int len = 0; - for (b=a; b; b=b->next) { + for (b = a; b; b = b->next) { len += 4 + sizeof(struct inet_diag_hostcond); if (a->addr.family == AF_INET6) len += 16; @@ -1204,11 +1288,11 @@ } if (!(ptr = malloc(len))) abort(); *bytecode = ptr; - for (b=a; b; b=b->next) { + for (b = a; b; b = b->next) { struct inet_diag_bc_op *op = (struct inet_diag_bc_op *)ptr; int alen = (a->addr.family == AF_INET6 ? 16 : 4); int oplen = alen + 4 + sizeof(struct inet_diag_hostcond); - struct inet_diag_hostcond *cond = (struct inet_diag_hostcond*)(ptr+4); + struct inet_diag_hostcond *cond = (struct inet_diag_hostcond *)(ptr+4); *op = (struct inet_diag_bc_op){ code, oplen, oplen+4 }; cond->family = a->addr.family; @@ -1226,43 +1310,53 @@ } case SSF_D_GE: { - struct aafilter *x = (void*)f->pred; - if (!(*bytecode=malloc(8))) abort(); - ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_D_GE, 8, 12 }; - ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; + struct aafilter *x = (void *)f->pred; + + if (!(*bytecode = malloc(8))) abort(); + ((struct inet_diag_bc_op *)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_D_GE, 8, 12 }; + ((struct inet_diag_bc_op *)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; return 8; } case SSF_D_LE: { - struct aafilter *x = (void*)f->pred; - if (!(*bytecode=malloc(8))) abort(); - ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_D_LE, 8, 12 }; - ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; + struct aafilter *x = (void *)f->pred; + + if (!(*bytecode = malloc(8))) abort(); + ((struct inet_diag_bc_op *)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_D_LE, 8, 12 }; + ((struct inet_diag_bc_op *)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; return 8; } case SSF_S_GE: { - struct aafilter *x = (void*)f->pred; - if (!(*bytecode=malloc(8))) abort(); - ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_S_GE, 8, 12 }; - ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; + struct aafilter *x = (void *)f->pred; + + if (!(*bytecode = malloc(8))) abort(); + ((struct inet_diag_bc_op *)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_S_GE, 8, 12 }; + ((struct inet_diag_bc_op *)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; return 8; } case SSF_S_LE: { - struct aafilter *x = (void*)f->pred; - if (!(*bytecode=malloc(8))) abort(); - ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_S_LE, 8, 12 }; - ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; + struct aafilter *x = (void *)f->pred; + + if (!(*bytecode = malloc(8))) abort(); + ((struct inet_diag_bc_op *)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_S_LE, 8, 12 }; + ((struct inet_diag_bc_op *)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; return 8; } case SSF_AND: { - char *a1, *a2, *a; + char *a1 = NULL, *a2 = NULL, *a; int l1, l2; + l1 = ssfilter_bytecompile(f->pred, &a1); l2 = ssfilter_bytecompile(f->post, &a2); + if (!l1 || !l2) { + free(a1); + free(a2); + return 0; + } if (!(a = malloc(l1+l2))) abort(); memcpy(a, a1, l1); memcpy(a+l1, a2, l2); @@ -1273,30 +1367,63 @@ } case SSF_OR: { - char *a1, *a2, *a; + char *a1 = NULL, *a2 = NULL, *a; int l1, l2; + l1 = ssfilter_bytecompile(f->pred, &a1); l2 = ssfilter_bytecompile(f->post, &a2); + if (!l1 || !l2) { + free(a1); + free(a2); + return 0; + } if (!(a = malloc(l1+l2+4))) abort(); memcpy(a, a1, l1); memcpy(a+l1+4, a2, l2); free(a1); free(a2); - *(struct inet_diag_bc_op*)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, l2+4 }; + *(struct inet_diag_bc_op *)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, l2+4 }; *bytecode = a; return l1+l2+4; } case SSF_NOT: { - char *a1, *a; + char *a1 = NULL, *a; int l1; + l1 = ssfilter_bytecompile(f->pred, &a1); + if (!l1) { + free(a1); + return 0; + } if (!(a = malloc(l1+4))) abort(); memcpy(a, a1, l1); free(a1); - *(struct inet_diag_bc_op*)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, 8 }; + *(struct inet_diag_bc_op *)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, 8 }; *bytecode = a; return l1+4; } + case SSF_DEVCOND: + { + /* bytecompile for SSF_DEVCOND not supported yet */ + return 0; + } + case SSF_MARKMASK: + { + struct aafilter *a = (void *)f->pred; + struct instr { + struct inet_diag_bc_op op; + struct inet_diag_markcond cond; + }; + int inslen = sizeof(struct instr); + + if (!(*bytecode = malloc(inslen))) abort(); + ((struct instr *)*bytecode)[0] = (struct instr) { + { INET_DIAG_BC_MARK_COND, inslen, inslen + 4 }, + { a->mark, a->mask}, + }; + + return inslen; + } default: abort(); } @@ -1317,6 +1444,7 @@ while (*ptr) { struct aafilter *b = a; + if (a->addr.bitlen) { if ((b = malloc(sizeof(*b))) == NULL) return cnt; @@ -1356,11 +1484,12 @@ return !cnt; } -static int xll_initted = 0; +static int xll_initted; static void xll_init(void) { struct rtnl_handle rth; + if (rtnl_open(&rth, 0) < 0) exit(1); @@ -1383,6 +1512,29 @@ return ll_name_to_index(dev); } +void *parse_devcond(char *name) +{ + struct aafilter a = { .iface = 0 }; + struct aafilter *res; + + a.iface = xll_name_to_index(name); + if (a.iface == 0) { + char *end; + unsigned long n; + + n = strtoul(name, &end, 0); + if (!end || end == name || *end || n > UINT_MAX) + return NULL; + + a.iface = n; + } + + res = malloc(sizeof(*res)); + *res = a; + + return res; +} + void *parse_hostcond(char *addr, bool is_port) { char *port = NULL; @@ -1393,9 +1545,10 @@ if (fam == AF_UNIX || strncmp(addr, "unix:", 5) == 0) { char *p; + a.addr.family = AF_UNIX; if (strncmp(addr, "unix:", 5) == 0) - addr+=5; + addr += 5; p = strdup(addr); a.addr.bitlen = 8*strlen(p); memcpy(a.addr.data, &p, sizeof(p)); @@ -1407,7 +1560,7 @@ a.addr.family = AF_PACKET; a.addr.bitlen = 0; if (strncmp(addr, "link:", 5) == 0) - addr+=5; + addr += 5; port = strchr(addr, ':'); if (port) { *port = 0; @@ -1420,6 +1573,7 @@ } if (addr[0] && strcmp(addr, "*")) { unsigned short tmp; + a.addr.bitlen = 32; if (ll_proto_a2n(&tmp, addr)) return NULL; @@ -1433,7 +1587,7 @@ a.addr.family = AF_NETLINK; a.addr.bitlen = 0; if (strncmp(addr, "netlink:", 8) == 0) - addr+=8; + addr += 8; port = strchr(addr, ':'); if (port) { *port = 0; @@ -1488,6 +1642,7 @@ if (get_integer(&a.port, port, 0)) { struct servent *se1 = NULL; struct servent *se2 = NULL; + if (current_filter.dbs&(1<s_port); } else { struct scache *s; + for (s = rlist; s; s = s->next) { if ((s->proto == UDP_PROTO && (current_filter.dbs&(1<states; f->families = 0; filter_af_set(f, fam); - filter_states_set(f, 0); + filter_states_set(f, states); + } + + res = malloc(sizeof(*res)); + if (res) + memcpy(res, &a, sizeof(a)); + return res; +} + +void *parse_markmask(const char *markmask) +{ + struct aafilter a, *res; + + if (strchr(markmask, '/')) { + if (sscanf(markmask, "%i/%i", &a.mark, &a.mask) != 2) + return NULL; + } else { + a.mask = 0xffffffff; + if (sscanf(markmask, "%i", &a.mark) != 1) + return NULL; } res = malloc(sizeof(*res)); @@ -1555,6 +1731,8 @@ return "udp"; case IPPROTO_TCP: return "tcp"; + case IPPROTO_SCTP: + return "sctp"; case IPPROTO_DCCP: return "dccp"; } @@ -1587,12 +1765,12 @@ } static int proc_parse_inet_addr(char *loc, char *rem, int family, struct - sockstat *s) + sockstat * s) { s->local.family = s->remote.family = family; if (family == AF_INET) { - sscanf(loc, "%x:%x", s->local.data, (unsigned*)&s->lport); - sscanf(rem, "%x:%x", s->remote.data, (unsigned*)&s->rport); + sscanf(loc, "%x:%x", s->local.data, (unsigned *)&s->lport); + sscanf(rem, "%x:%x", s->remote.data, (unsigned *)&s->rport); s->local.bytelen = s->remote.bytelen = 4; return 0; } else { @@ -1638,15 +1816,65 @@ static char *sprint_bw(char *buf, double bw) { if (bw > 1000000.) - sprintf(buf,"%.1fM", bw / 1000000.); + sprintf(buf, "%.1fM", bw / 1000000.); else if (bw > 1000.) - sprintf(buf,"%.1fK", bw / 1000.); + sprintf(buf, "%.1fK", bw / 1000.); else sprintf(buf, "%g", bw); return buf; } +static void sctp_stats_print(struct sctp_info *s) +{ + if (s->sctpi_tag) + printf(" tag:%x", s->sctpi_tag); + if (s->sctpi_state) + printf(" state:%s", sctp_sstate_name[s->sctpi_state]); + if (s->sctpi_rwnd) + printf(" rwnd:%d", s->sctpi_rwnd); + if (s->sctpi_unackdata) + printf(" unackdata:%d", s->sctpi_unackdata); + if (s->sctpi_penddata) + printf(" penddata:%d", s->sctpi_penddata); + if (s->sctpi_instrms) + printf(" instrms:%d", s->sctpi_instrms); + if (s->sctpi_outstrms) + printf(" outstrms:%d", s->sctpi_outstrms); + if (s->sctpi_inqueue) + printf(" inqueue:%d", s->sctpi_inqueue); + if (s->sctpi_outqueue) + printf(" outqueue:%d", s->sctpi_outqueue); + if (s->sctpi_overall_error) + printf(" overerr:%d", s->sctpi_overall_error); + if (s->sctpi_max_burst) + printf(" maxburst:%d", s->sctpi_max_burst); + if (s->sctpi_maxseg) + printf(" maxseg:%d", s->sctpi_maxseg); + if (s->sctpi_peer_rwnd) + printf(" prwnd:%d", s->sctpi_peer_rwnd); + if (s->sctpi_peer_tag) + printf(" ptag:%x", s->sctpi_peer_tag); + if (s->sctpi_peer_capable) + printf(" pcapable:%d", s->sctpi_peer_capable); + if (s->sctpi_peer_sack) + printf(" psack:%d", s->sctpi_peer_sack); + if (s->sctpi_s_autoclose) + printf(" autoclose:%d", s->sctpi_s_autoclose); + if (s->sctpi_s_adaptation_ind) + printf(" adapind:%d", s->sctpi_s_adaptation_ind); + if (s->sctpi_s_pd_point) + printf(" pdpoint:%d", s->sctpi_s_pd_point); + if (s->sctpi_s_nodelay) + printf(" nodealy:%d", s->sctpi_s_nodelay); + if (s->sctpi_s_disable_fragments) + printf(" nofrag:%d", s->sctpi_s_disable_fragments); + if (s->sctpi_s_v4mapped) + printf(" v4mapped:%d", s->sctpi_s_v4mapped); + if (s->sctpi_s_frag_interleave) + printf(" fraginl:%d", s->sctpi_s_frag_interleave); +} + static void tcp_stats_print(struct tcpstat *s) { char b1[64]; @@ -1682,7 +1910,7 @@ if (s->mss) printf(" mss:%d", s->mss); if (s->cwnd) - printf(" cwnd:%d", s->cwnd); + printf(" cwnd:%u", s->cwnd); if (s->ssthresh) printf(" ssthresh:%d", s->ssthresh); @@ -1694,6 +1922,10 @@ printf(" segs_out:%u", s->segs_out); if (s->segs_in) printf(" segs_in:%u", s->segs_in); + if (s->data_segs_out) + printf(" data_segs_out:%u", s->data_segs_out); + if (s->data_segs_in) + printf(" data_segs_in:%u", s->data_segs_in); if (s->dctcp && s->dctcp->enabled) { struct dctcpstat *dctcp = s->dctcp; @@ -1705,6 +1937,25 @@ printf(" dctcp:fallback_mode"); } + if (s->bbr_info) { + __u64 bw; + + bw = s->bbr_info->bbr_bw_hi; + bw <<= 32; + bw |= s->bbr_info->bbr_bw_lo; + + printf(" bbr:(bw:%sbps,mrtt:%g", + sprint_bw(b1, bw * 8.0), + (double)s->bbr_info->bbr_min_rtt / 1000.0); + if (s->bbr_info->bbr_pacing_gain) + printf(",pacing_gain:%g", + (double)s->bbr_info->bbr_pacing_gain / 256.0); + if (s->bbr_info->bbr_cwnd_gain) + printf(",cwnd_gain:%g", + (double)s->bbr_info->bbr_cwnd_gain / 256.0); + printf(")"); + } + if (s->send_bps) printf(" send %sbps", sprint_bw(b1, s->send_bps)); if (s->lastsnd) @@ -1737,6 +1988,10 @@ printf(" rcv_rtt:%g", s->rcv_rtt); if (s->rcv_space) printf(" rcv_space:%d", s->rcv_space); + if (s->not_sent) + printf(" notsent:%u", s->not_sent); + if (s->min_rtt) + printf(" minrtt:%g", s->min_rtt); } static void tcp_timer_print(struct tcpstat *s) @@ -1751,6 +2006,13 @@ } } +static void sctp_timer_print(struct tcpstat *s) +{ + if (s->timer) + printf(" timer:(T3_RTX,%s,%d)", + print_ms_timer(s->timeout), s->retrans); +} + static int tcp_show_line(char *line, const struct filter *f, int family) { int rto = 0, ato = 0; @@ -1764,6 +2026,7 @@ return -1; int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0'); + if (!(f->states & (1 << state))) return 0; @@ -1773,7 +2036,7 @@ return 0; opt[0] = 0; - n = sscanf(data, "%x %x:%x %x:%x %x %d %d %u %d %llx %d %d %d %d %d %[^\n]\n", + n = sscanf(data, "%x %x:%x %x:%x %x %d %d %u %d %llx %d %d %d %u %d %[^\n]\n", &s.ss.state, &s.ss.wq, &s.ss.rq, &s.timer, &s.timeout, &s.retrans, &s.ss.uid, &s.probes, &s.ss.ino, &s.ss.refcnt, &s.ss.sk, &rto, &ato, &s.qack, &s.cwnd, @@ -1827,6 +2090,7 @@ while (fgets(line, sizeof(line), fp) != NULL) { int n = strlen(line); + if (n == 0 || line[n-1] != '\n') { errno = -EINVAL; return -1; @@ -1877,6 +2141,10 @@ (SK_MEMINFO_BACKLOG + 1) * sizeof(__u32)) printf(",bl%u", skmeminfo[SK_MEMINFO_BACKLOG]); + if (RTA_PAYLOAD(tb[attrtype]) >= + (SK_MEMINFO_DROPS + 1) * sizeof(__u32)) + printf(",d%u", skmeminfo[SK_MEMINFO_DROPS]); + printf(")"); } @@ -1973,6 +2241,16 @@ s.dctcp = dctcp; } + if (tb[INET_DIAG_BBRINFO]) { + const void *bbr_info = RTA_DATA(tb[INET_DIAG_BBRINFO]); + int len = min(RTA_PAYLOAD(tb[INET_DIAG_BBRINFO]), + sizeof(*s.bbr_info)); + + s.bbr_info = calloc(1, sizeof(*s.bbr_info)); + if (s.bbr_info && bbr_info) + memcpy(s.bbr_info, bbr_info, len); + } + if (rtt > 0 && info->tcpi_snd_mss && info->tcpi_snd_cwnd) { s.send_bps = (double) info->tcpi_snd_cwnd * (double)info->tcpi_snd_mss * 8000000. / rtt; @@ -1990,47 +2268,121 @@ s.bytes_received = info->tcpi_bytes_received; s.segs_out = info->tcpi_segs_out; s.segs_in = info->tcpi_segs_in; + s.data_segs_out = info->tcpi_data_segs_out; + s.data_segs_in = info->tcpi_data_segs_in; + s.not_sent = info->tcpi_notsent_bytes; + if (info->tcpi_min_rtt && info->tcpi_min_rtt != ~0U) + s.min_rtt = (double) info->tcpi_min_rtt / 1000; tcp_stats_print(&s); free(s.dctcp); + free(s.bbr_info); + } +} + +static const char *format_host_sa(struct sockaddr_storage *sa) +{ + union { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } *saddr = (void *)sa; + + switch (sa->ss_family) { + case AF_INET: + return format_host(AF_INET, 4, &saddr->sin.sin_addr); + case AF_INET6: + return format_host(AF_INET6, 16, &saddr->sin6.sin6_addr); + default: + return ""; + } +} + +static void sctp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, + struct rtattr *tb[]) +{ + struct sockaddr_storage *sa; + int len; + + print_skmeminfo(tb, INET_DIAG_SKMEMINFO); + + if (tb[INET_DIAG_LOCALS]) { + len = RTA_PAYLOAD(tb[INET_DIAG_LOCALS]); + sa = RTA_DATA(tb[INET_DIAG_LOCALS]); + + printf("locals:%s", format_host_sa(sa)); + for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) + printf(",%s", format_host_sa(sa)); + + } + if (tb[INET_DIAG_PEERS]) { + len = RTA_PAYLOAD(tb[INET_DIAG_PEERS]); + sa = RTA_DATA(tb[INET_DIAG_PEERS]); + + printf(" peers:%s", format_host_sa(sa)); + for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) + printf(",%s", format_host_sa(sa)); + } + if (tb[INET_DIAG_INFO]) { + struct sctp_info *info; + len = RTA_PAYLOAD(tb[INET_DIAG_INFO]); + + /* workaround for older kernels with less fields */ + if (len < sizeof(*info)) { + info = alloca(sizeof(*info)); + memcpy(info, RTA_DATA(tb[INET_DIAG_INFO]), len); + memset((char *)info + len, 0, sizeof(*info) - len); + } else + info = RTA_DATA(tb[INET_DIAG_INFO]); + + sctp_stats_print(info); } } -static int inet_show_sock(struct nlmsghdr *nlh, struct filter *f, int protocol) +static void parse_diag_msg(struct nlmsghdr *nlh, struct sockstat *s) { - struct rtattr * tb[INET_DIAG_MAX+1]; + struct rtattr *tb[INET_DIAG_MAX+1]; struct inet_diag_msg *r = NLMSG_DATA(nlh); - struct sockstat s = {}; - parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr*)(r+1), + parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr *)(r+1), nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); - s.state = r->idiag_state; - s.local.family = s.remote.family = r->idiag_family; - s.lport = ntohs(r->id.idiag_sport); - s.rport = ntohs(r->id.idiag_dport); - s.wq = r->idiag_wqueue; - s.rq = r->idiag_rqueue; - s.ino = r->idiag_inode; - s.uid = r->idiag_uid; - s.iface = r->id.idiag_if; - s.sk = cookie_sk_get(&r->id.idiag_cookie[0]); + s->state = r->idiag_state; + s->local.family = s->remote.family = r->idiag_family; + s->lport = ntohs(r->id.idiag_sport); + s->rport = ntohs(r->id.idiag_dport); + s->wq = r->idiag_wqueue; + s->rq = r->idiag_rqueue; + s->ino = r->idiag_inode; + s->uid = r->idiag_uid; + s->iface = r->id.idiag_if; + s->sk = cookie_sk_get(&r->id.idiag_cookie[0]); + + s->mark = 0; + if (tb[INET_DIAG_MARK]) + s->mark = *(__u32 *) RTA_DATA(tb[INET_DIAG_MARK]); - if (s.local.family == AF_INET) { - s.local.bytelen = s.remote.bytelen = 4; - } else { - s.local.bytelen = s.remote.bytelen = 16; - } + if (s->local.family == AF_INET) + s->local.bytelen = s->remote.bytelen = 4; + else + s->local.bytelen = s->remote.bytelen = 16; - memcpy(s.local.data, r->id.idiag_src, s.local.bytelen); - memcpy(s.remote.data, r->id.idiag_dst, s.local.bytelen); + memcpy(s->local.data, r->id.idiag_src, s->local.bytelen); + memcpy(s->remote.data, r->id.idiag_dst, s->local.bytelen); +} - if (f && f->f && run_ssfilter(f->f, &s) == 0) - return 0; +static int inet_show_sock(struct nlmsghdr *nlh, + struct sockstat *s, + int protocol) +{ + struct rtattr *tb[INET_DIAG_MAX+1]; + struct inet_diag_msg *r = NLMSG_DATA(nlh); + + parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr *)(r+1), + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (tb[INET_DIAG_PROTOCOL]) protocol = *(__u8 *)RTA_DATA(tb[INET_DIAG_PROTOCOL]); - inet_stats_print(&s, protocol); + inet_stats_print(s, protocol); if (show_options) { struct tcpstat t = {}; @@ -2038,18 +2390,23 @@ t.timer = r->idiag_timer; t.timeout = r->idiag_expires; t.retrans = r->idiag_retrans; - tcp_timer_print(&t); + if (protocol == IPPROTO_SCTP) + sctp_timer_print(&t); + else + tcp_timer_print(&t); } if (show_details) { - sock_details_print(&s); - if (s.local.family == AF_INET6 && tb[INET_DIAG_SKV6ONLY]) { + sock_details_print(s); + if (s->local.family == AF_INET6 && tb[INET_DIAG_SKV6ONLY]) { unsigned char v6only; + v6only = *(__u8 *)RTA_DATA(tb[INET_DIAG_SKV6ONLY]); printf(" v6only:%u", v6only); } if (tb[INET_DIAG_SHUTDOWN]) { unsigned char mask; + mask = *(__u8 *)RTA_DATA(tb[INET_DIAG_SHUTDOWN]); printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>'); } @@ -2057,8 +2414,12 @@ if (show_mem || show_tcpinfo) { printf("\n\t"); - tcp_show_info(nlh, r, tb); + if (protocol == IPPROTO_SCTP) + sctp_show_info(nlh, r, tb); + else + tcp_show_info(nlh, r, tb); } + sctp_ino = s->ino; printf("\n"); return 0; @@ -2066,34 +2427,31 @@ static int tcpdiag_send(int fd, int protocol, struct filter *f) { - struct sockaddr_nl nladdr; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct { struct nlmsghdr nlh; struct inet_diag_req r; - } req; + } req = { + .nlh.nlmsg_len = sizeof(req), + .nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, + .nlh.nlmsg_seq = MAGIC_SEQ, + .r.idiag_family = AF_INET, + .r.idiag_states = f->states, + }; char *bc = NULL; int bclen; struct msghdr msg; struct rtattr rta; struct iovec iov[3]; + int iovlen = 1; if (protocol == IPPROTO_UDP) return -1; - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - - req.nlh.nlmsg_len = sizeof(req); if (protocol == IPPROTO_TCP) req.nlh.nlmsg_type = TCPDIAG_GETSOCK; else req.nlh.nlmsg_type = DCCPDIAG_GETSOCK; - req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; - req.nlh.nlmsg_pid = 0; - req.nlh.nlmsg_seq = MAGIC_SEQ; - memset(&req.r, 0, sizeof(req.r)); - req.r.idiag_family = AF_INET; - req.r.idiag_states = f->states; if (show_mem) { req.r.idiag_ext |= (1<<(INET_DIAG_MEMINFO-1)); req.r.idiag_ext |= (1<<(INET_DIAG_SKMEMINFO-1)); @@ -2111,18 +2469,21 @@ }; if (f->f) { bclen = ssfilter_bytecompile(f->f, &bc); - rta.rta_type = INET_DIAG_REQ_BYTECODE; - rta.rta_len = RTA_LENGTH(bclen); - iov[1] = (struct iovec){ &rta, sizeof(rta) }; - iov[2] = (struct iovec){ bc, bclen }; - req.nlh.nlmsg_len += RTA_LENGTH(bclen); + if (bclen) { + rta.rta_type = INET_DIAG_REQ_BYTECODE; + rta.rta_len = RTA_LENGTH(bclen); + iov[1] = (struct iovec){ &rta, sizeof(rta) }; + iov[2] = (struct iovec){ bc, bclen }; + req.nlh.nlmsg_len += RTA_LENGTH(bclen); + iovlen = 3; + } } msg = (struct msghdr) { - .msg_name = (void*)&nladdr, + .msg_name = (void *)&nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = iov, - .msg_iovlen = f->f ? 3 : 1, + .msg_iovlen = iovlen, }; if (sendmsg(fd, &msg, 0) < 0) { @@ -2135,20 +2496,18 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f) { - struct sockaddr_nl nladdr; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; DIAG_REQUEST(req, struct inet_diag_req_v2 r); char *bc = NULL; int bclen; struct msghdr msg; struct rtattr rta; struct iovec iov[3]; + int iovlen = 1; if (family == PF_UNSPEC) return tcpdiag_send(fd, protocol, f); - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - memset(&req.r, 0, sizeof(req.r)); req.r.sdiag_family = family; req.r.sdiag_protocol = protocol; @@ -2170,18 +2529,21 @@ }; if (f->f) { bclen = ssfilter_bytecompile(f->f, &bc); - rta.rta_type = INET_DIAG_REQ_BYTECODE; - rta.rta_len = RTA_LENGTH(bclen); - iov[1] = (struct iovec){ &rta, sizeof(rta) }; - iov[2] = (struct iovec){ bc, bclen }; - req.nlh.nlmsg_len += RTA_LENGTH(bclen); + if (bclen) { + rta.rta_type = INET_DIAG_REQ_BYTECODE; + rta.rta_len = RTA_LENGTH(bclen); + iov[1] = (struct iovec){ &rta, sizeof(rta) }; + iov[2] = (struct iovec){ bc, bclen }; + req.nlh.nlmsg_len += RTA_LENGTH(bclen); + iovlen = 3; + } } msg = (struct msghdr) { - .msg_name = (void*)&nladdr, + .msg_name = (void *)&nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = iov, - .msg_iovlen = f->f ? 3 : 1, + .msg_iovlen = iovlen, }; if (sendmsg(fd, &msg, 0) < 0) { @@ -2195,18 +2557,55 @@ struct inet_diag_arg { struct filter *f; int protocol; + struct rtnl_handle *rth; }; +static int kill_inet_sock(struct nlmsghdr *h, void *arg) +{ + struct inet_diag_msg *d = NLMSG_DATA(h); + struct inet_diag_arg *diag_arg = arg; + struct rtnl_handle *rth = diag_arg->rth; + + DIAG_REQUEST(req, struct inet_diag_req_v2 r); + + req.nlh.nlmsg_type = SOCK_DESTROY; + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nlh.nlmsg_seq = ++rth->seq; + req.r.sdiag_family = d->idiag_family; + req.r.sdiag_protocol = diag_arg->protocol; + req.r.id = d->id; + + return rtnl_talk(rth, &req.nlh, NULL, 0); +} + static int show_one_inet_sock(const struct sockaddr_nl *addr, struct nlmsghdr *h, void *arg) { int err; struct inet_diag_arg *diag_arg = arg; struct inet_diag_msg *r = NLMSG_DATA(h); + struct sockstat s = {}; if (!(diag_arg->f->families & (1 << r->idiag_family))) return 0; - if ((err = inet_show_sock(h, diag_arg->f, diag_arg->protocol)) < 0) + + parse_diag_msg(h, &s); + + if (diag_arg->f->f && run_ssfilter(diag_arg->f->f, &s) == 0) + return 0; + + if (diag_arg->f->kill && kill_inet_sock(h, arg) != 0) { + if (errno == EOPNOTSUPP || errno == ENOENT) { + /* Socket can't be closed, or is already closed. */ + return 0; + } else { + perror("SOCK_DESTROY answers"); + return -1; + } + } + + err = inet_show_sock(h, &s, diag_arg->protocol); + if (err < 0) return err; return 0; @@ -2215,12 +2614,21 @@ static int inet_show_netlink(struct filter *f, FILE *dump_fp, int protocol) { int err = 0; - struct rtnl_handle rth; + struct rtnl_handle rth, rth2; int family = PF_INET; struct inet_diag_arg arg = { .f = f, .protocol = protocol }; if (rtnl_open_byproto(&rth, 0, NETLINK_SOCK_DIAG)) return -1; + + if (f->kill) { + if (rtnl_open_byproto(&rth2, 0, NETLINK_SOCK_DIAG)) { + rtnl_close(&rth); + return -1; + } + arg.rth = &rth2; + } + rth.dump = MAGIC_SEQ; rth.dump_fp = dump_fp; if (preferred_family == PF_INET6) @@ -2244,6 +2652,8 @@ Exit: rtnl_close(&rth); + if (arg.rth) + rtnl_close(arg.rth); return err; } @@ -2259,7 +2669,8 @@ while (1) { int status, err; - struct nlmsghdr *h = (struct nlmsghdr*)buf; + struct nlmsghdr *h = (struct nlmsghdr *)buf; + struct sockstat s = {}; status = fread(buf, 1, sizeof(*h), fp); if (status < 0) { @@ -2287,7 +2698,8 @@ return 0; if (h->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { fprintf(stderr, "ERROR truncated\n"); } else { @@ -2297,7 +2709,12 @@ return -1; } - err = inet_show_sock(h, f, IPPROTO_TCP); + parse_diag_msg(h, &s); + + if (f && f->f && run_ssfilter(f->f, &s) == 0) + continue; + + err = inet_show_sock(h, &s, IPPROTO_TCP); if (err < 0) return err; } @@ -2334,6 +2751,7 @@ get_slabstat(&slabstat); int guess = slabstat.socks+slabstat.tcp_syns; + if (f->states&(1< (16*1024*1024)/128) @@ -2376,6 +2794,7 @@ outerr: do { int saved_errno = errno; + free(buf); if (fp) fclose(fp); @@ -2384,6 +2803,17 @@ } while (0); } +static int sctp_show(struct filter *f) +{ + if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6)) + return 0; + + if (!getenv("PROC_NET_SCTP") && !getenv("PROC_ROOT") + && inet_show_netlink(f, NULL, IPPROTO_SCTP) == 0) + return 0; + + return 0; +} static int dgram_show_line(char *line, const struct filter *f, int family) { @@ -2396,6 +2826,7 @@ return -1; int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0'); + if (!(f->states & (1 << state))) return 0; @@ -2454,6 +2885,7 @@ outerr: do { int saved_errno = errno; + if (fp) fclose(fp); errno = saved_errno; @@ -2489,6 +2921,7 @@ outerr: do { int saved_errno = errno; + if (fp) fclose(fp); errno = saved_errno; @@ -2581,13 +3014,13 @@ } if (use_proc && f->f) { - struct sockstat st; - st.local.family = AF_UNIX; - st.remote.family = AF_UNIX; + struct sockstat st = { + .local.family = AF_UNIX, + .remote.family = AF_UNIX, + }; + memcpy(st.local.data, &s->name, sizeof(s->name)); - if (strcmp(peer, "*") == 0) - memset(st.remote.data, 0, sizeof(peer)); - else + if (strcmp(peer, "*")) memcpy(st.remote.data, &peer, sizeof(peer)); if (run_ssfilter(f->f, &st) == 0) continue; @@ -2626,7 +3059,7 @@ char name[128]; struct sockstat stat = { .name = "*", .peer_name = "*" }; - parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr*)(r+1), + parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr *)(r+1), nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); stat.type = r->udiag_type; @@ -2639,6 +3072,7 @@ if (tb[UNIX_DIAG_RQLEN]) { struct unix_diag_rqlen *rql = RTA_DATA(tb[UNIX_DIAG_RQLEN]); + stat.rq = rql->udiag_rqueue; stat.wq = rql->udiag_wqueue; } @@ -2647,8 +3081,12 @@ memcpy(name, RTA_DATA(tb[UNIX_DIAG_NAME]), len); name[len] = '\0'; - if (name[0] == '\0') - name[0] = '@'; + if (name[0] == '\0') { + int i; + for (i = 0; i < len; i++) + if (name[i] == '\0') + name[i] = '@'; + } stat.name = &name[0]; memcpy(stat.local.data, &stat.name, sizeof(stat.name)); } @@ -2667,6 +3105,7 @@ if (show_details) { if (tb[UNIX_DIAG_SHUTDOWN]) { unsigned char mask; + mask = *(__u8 *)RTA_DATA(tb[UNIX_DIAG_SHUTDOWN]); printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>'); } @@ -2730,13 +3169,16 @@ if ((fp = net_unix_open()) == NULL) return -1; - fgets(buf, sizeof(buf)-1, fp); + if (!fgets(buf, sizeof(buf), fp)) { + fclose(fp); + return -1; + } if (memcmp(buf, "Peer", 4) == 0) newformat = 1; cnt = 0; - while (fgets(buf, sizeof(buf)-1, fp)) { + while (fgets(buf, sizeof(buf), fp)) { struct sockstat *u, **insp; int flags; @@ -2872,7 +3314,7 @@ uint32_t fanout = 0; bool has_fanout = false; - parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr*)(r+1), + parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr *)(r+1), nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); /* use /proc/net/packet if all info are not available */ @@ -2887,6 +3329,7 @@ if (tb[PACKET_DIAG_MEMINFO]) { __u32 *skmeminfo = RTA_DATA(tb[PACKET_DIAG_MEMINFO]); + stat.rq = skmeminfo[SK_MEMINFO_RMEM_ALLOC]; } @@ -3050,12 +3493,13 @@ } static int netlink_show_one(struct filter *f, - int prot, int pid, unsigned groups, - int state, int dst_pid, unsigned dst_group, + int prot, int pid, unsigned int groups, + int state, int dst_pid, unsigned int dst_group, int rq, int wq, unsigned long long sk, unsigned long long cb) { struct sockstat st; + SPRINT_BUF(prot_buf) = {}; const char *prot_name; char procname[64] = {}; @@ -3085,11 +3529,13 @@ procname[0] = '*'; } else if (resolve_services) { int done = 0; + if (!pid) { done = 1; strncpy(procname, "kernel", 6); } else if (pid > 0) { FILE *fp; + snprintf(procname, sizeof(procname), "%s/%d/stat", getenv("PROC_ROOT") ? : "/proc", pid); if ((fp = fopen(procname, "r")) != NULL) { @@ -3113,6 +3559,7 @@ if (state == NETLINK_CONNECTED) { char dst_group_buf[30]; char dst_pid_buf[30]; + sock_addr_print(int_to_str(dst_group, dst_group_buf), ":", int_to_str(dst_pid, dst_pid_buf), NULL); } else { @@ -3120,6 +3567,7 @@ } char *pid_context = NULL; + if (show_proc_ctx) { /* The pid value will either be: * 0 if destination kernel - show kernel initial context. @@ -3157,7 +3605,7 @@ int rq = 0, wq = 0; unsigned long groups = 0; - parse_rtattr(tb, NETLINK_DIAG_MAX, (struct rtattr*)(r+1), + parse_rtattr(tb, NETLINK_DIAG_MAX, (struct rtattr *)(r+1), nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (tb[NETLINK_DIAG_GROUPS] && RTA_PAYLOAD(tb[NETLINK_DIAG_GROUPS])) @@ -3165,6 +3613,7 @@ if (tb[NETLINK_DIAG_MEMINFO]) { const __u32 *skmeminfo; + skmeminfo = RTA_DATA(tb[NETLINK_DIAG_MEMINFO]); rq = skmeminfo[SK_MEMINFO_RMEM_ALLOC]; @@ -3202,7 +3651,7 @@ FILE *fp; char buf[256]; int prot, pid; - unsigned groups; + unsigned int groups; int rq, wq, rc; unsigned long long sk, cb; @@ -3215,9 +3664,12 @@ if ((fp = net_netlink_open()) == NULL) return -1; - fgets(buf, sizeof(buf)-1, fp); + if (!fgets(buf, sizeof(buf), fp)) { + fclose(fp); + return -1; + } - while (fgets(buf, sizeof(buf)-1, fp)) { + while (fgets(buf, sizeof(buf), fp)) { sscanf(buf, "%llx %d %d %x %d %d %llx %d", &sk, &prot, &pid, &groups, &rq, &wq, &cb, &rc); @@ -3287,8 +3739,7 @@ return ret; } -struct snmpstat -{ +struct snmpstat { int tcp_estab; }; @@ -3307,6 +3758,7 @@ while (fgets(buf, sizeof(buf), fp) != NULL) { char *p = buf; int pos = 0; + if (memcmp(buf, proto, protolen)) continue; while ((p = strchr(p, ' ')) != NULL) { @@ -3339,8 +3791,7 @@ /* Get stats from sockstat */ -struct ssummary -{ +struct ssummary { int socks; int tcp_mem; int tcp_total; @@ -3396,13 +3847,13 @@ if ((fp = net_sockstat_open()) == NULL) return -1; - while(fgets(buf, sizeof(buf), fp) != NULL) + while (fgets(buf, sizeof(buf), fp) != NULL) get_sockstat_line(buf, s); fclose(fp); if ((fp = net_sockstat6_open()) == NULL) return 0; - while(fgets(buf, sizeof(buf), fp) != NULL) + while (fgets(buf, sizeof(buf), fp) != NULL) get_sockstat_line(buf, s); fclose(fp); @@ -3478,11 +3929,16 @@ " -6, --ipv6 display only IP version 6 sockets\n" " -0, --packet display PACKET sockets\n" " -t, --tcp display only TCP sockets\n" +" -S, --sctp display only SCTP sockets\n" " -u, --udp display only UDP sockets\n" " -d, --dccp display only DCCP sockets\n" " -w, --raw display only RAW sockets\n" " -x, --unix display only Unix domain sockets\n" " -f, --family=FAMILY display sockets of type FAMILY\n" +" FAMILY := {inet|inet6|link|unix|netlink|help}\n" +"\n" +" -K, --kill forcibly close sockets, display what was closed\n" +" -H, --no-header Suppress header line\n" "\n" " -A, --query=QUERY, --socket=QUERY\n" " QUERY := {all|inet|tcp|udp|raw|unix|unix_dgram|unix_stream|unix_seqpacket|packet|netlink}[,QUERY]\n" @@ -3517,6 +3973,7 @@ static int scan_state(const char *state) { int i; + if (strcasecmp(state, "close") == 0 || strcasecmp(state, "closed") == 0) return (1< @@ -20,4 +22,5 @@ int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp); void *parse_hostcond(char *addr, bool is_port); - +void *parse_devcond(char *name); +void *parse_markmask(const char *markmask); diff -Nru iproute2-4.3.0/misc/ssfilter.y iproute2-4.9.0/misc/ssfilter.y --- iproute2-4.3.0/misc/ssfilter.y 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/misc/ssfilter.y 2016-12-12 23:07:42.000000000 +0000 @@ -36,7 +36,7 @@ %} -%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND +%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND DEVCOND DEVNAME MARKMASK FWMARK %left '|' %left '&' %nonassoc '!' @@ -108,7 +108,22 @@ { $$ = alloc_node(SSF_NOT, alloc_node(SSF_SCOND, $3)); } - + | DEVNAME '=' DEVCOND + { + $$ = alloc_node(SSF_DEVCOND, $3); + } + | DEVNAME NEQ DEVCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_DEVCOND, $3)); + } + | FWMARK '=' MARKMASK + { + $$ = alloc_node(SSF_MARKMASK, $3); + } + | FWMARK NEQ MARKMASK + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_MARKMASK, $3)); + } | AUTOBOUND { $$ = alloc_node(SSF_S_AUTO, NULL); @@ -237,6 +252,14 @@ tok_type = SPORT; return SPORT; } + if (strcmp(curtok, "dev") == 0) { + tok_type = DEVNAME; + return DEVNAME; + } + if (strcmp(curtok, "fwmark") == 0) { + tok_type = FWMARK; + return FWMARK; + } if (strcmp(curtok, ">=") == 0 || strcmp(curtok, "ge") == 0 || strcmp(curtok, "geq") == 0) @@ -263,6 +286,22 @@ tok_type = AUTOBOUND; return AUTOBOUND; } + if (tok_type == DEVNAME) { + yylval = (void*)parse_devcond(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse device.\n"); + exit(1); + } + return DEVCOND; + } + if (tok_type == FWMARK) { + yylval = (void*)parse_markmask(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse mark %s.\n", curtok); + exit(1); + } + return MARKMASK; + } yylval = (void*)parse_hostcond(curtok, tok_type == SPORT || tok_type == DPORT); if (yylval == NULL) { fprintf(stderr, "Cannot parse dst/src address.\n"); diff -Nru iproute2-4.3.0/netem/maketable.c iproute2-4.9.0/netem/maketable.c --- iproute2-4.3.0/netem/maketable.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/netem/maketable.c 2016-12-12 23:07:42.000000000 +0000 @@ -210,7 +210,7 @@ } } else { fp = stdin; - } + } x = readdoubles(fp, &limit); if (limit <= 0) { fprintf(stderr, "Nothing much read!\n"); @@ -221,7 +221,7 @@ fprintf(stderr, "%d values, mu %10.4f, sigma %10.4f, rho %10.4f\n", limit, mu, sigma, rho); #endif - + table = makedist(x, limit, mu, sigma); free((void *) x); cumulativedist(table, DISTTABLESIZE, &total); diff -Nru iproute2-4.3.0/netem/normal.c iproute2-4.9.0/netem/normal.c --- iproute2-4.3.0/netem/normal.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/netem/normal.c 2016-12-12 23:07:42.000000000 +0000 @@ -33,7 +33,7 @@ table[i] = x; } - + printf("# This is the distribution table for the normal distribution.\n"); for (i = n = 0; i < TABLESIZE; i += 4) { int value = (int) rint(table[i]*TABLEFACTOR); diff -Nru iproute2-4.3.0/netem/pareto.c iproute2-4.9.0/netem/pareto.c --- iproute2-4.3.0/netem/pareto.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/netem/pareto.c 2016-12-12 23:07:42.000000000 +0000 @@ -36,6 +36,6 @@ n = 0; } } - + return 0; -} +} diff -Nru iproute2-4.3.0/netem/paretonormal.c iproute2-4.9.0/netem/paretonormal.c --- iproute2-4.3.0/netem/paretonormal.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/netem/paretonormal.c 2016-12-12 23:07:42.000000000 +0000 @@ -44,7 +44,7 @@ if (dvalue > 32767) dvalue = 32767; return (int)rint(dvalue); -} +} int main(int argc, char **argv) diff -Nru iproute2-4.3.0/schema/bridge_fdb_schema.json iproute2-4.9.0/schema/bridge_fdb_schema.json --- iproute2-4.3.0/schema/bridge_fdb_schema.json 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/schema/bridge_fdb_schema.json 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,62 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "bridge fdb show", + "type": "array", + "items": { + "type": "object", + "properties": { + "dev": { + "type": "string" + }, + "dst": { + "description" : "host name or ip address", + "type": "string" + }, + "flags": { + "type": "array", + "items": { + "enum": ["self", "master", "router", "offload"] + }, + "uniqueItems": true + }, + "linkNetNsId": { + "type": "integer" + }, + "mac": { + "type": "string" + }, + "master": { + "type": "string" + }, + "opCode": { + "description" : "used to indicate fdb entry del", + "enum": ["deleted"] + }, + "port": { + "type": "integer" + }, + "state": { + "description" : "permanent, static, stale, state=#x", + "type": "string" + }, + "updated": { + "type": "integer" + }, + "used": { + "type": "integer" + }, + "viaIf": { + "type": "string" + }, + "viaIfIndex": { + "type": "integer" + }, + "vlan": { + "type": "integer" + }, + "vni": { + "type": "integer" + } + } + } +} diff -Nru iproute2-4.3.0/tc/e_bpf.c iproute2-4.9.0/tc/e_bpf.c --- iproute2-4.3.0/tc/e_bpf.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/e_bpf.c 2016-12-12 23:07:42.000000000 +0000 @@ -26,10 +26,19 @@ static void explain(void) { - fprintf(stderr, "Usage: ... bpf [ import UDS_FILE ] [ run CMD ]\n\n"); + fprintf(stderr, "Usage: ... bpf [ import UDS_FILE ] [ run CMD ]\n"); + fprintf(stderr, " ... bpf [ debug ]\n"); + fprintf(stderr, " ... bpf [ graft MAP_FILE ] [ key KEY ]\n"); + fprintf(stderr, " `... [ object-file OBJ_FILE ] [ type TYPE ] [ section NAME ] [ verbose ]\n"); + fprintf(stderr, " `... [ object-pinned PROG_FILE ]\n"); + fprintf(stderr, "\n"); fprintf(stderr, "Where UDS_FILE provides the name of a unix domain socket file\n"); fprintf(stderr, "to import eBPF maps and the optional CMD denotes the command\n"); fprintf(stderr, "to be executed (default: \'%s\').\n", BPF_DEFAULT_CMD); + fprintf(stderr, "Where MAP_FILE points to a pinned map, OBJ_FILE to an object file\n"); + fprintf(stderr, "and PROG_FILE to a pinned program. TYPE can be {cls, act}, where\n"); + fprintf(stderr, "\'cls\' is default. KEY is optional and can be inferred from the\n"); + fprintf(stderr, "section name, otherwise it needs to be provided.\n"); } static int bpf_num_env_entries(void) @@ -47,8 +56,8 @@ char **argv_run = argv_default, **envp_run, *tmp; int ret, i, env_old, env_num, env_map; const char *bpf_uds_name = NULL; - int fds[BPF_SCM_MAX_FDS]; - struct bpf_map_aux aux; + int fds[BPF_SCM_MAX_FDS] = {}; + struct bpf_map_aux aux = {}; if (argc == 0) return 0; @@ -58,17 +67,40 @@ NEXT_ARG(); argv_run = argv; break; - } else if (matches(*argv, "import") == 0 || - matches(*argv, "imp") == 0) { + } else if (matches(*argv, "import") == 0) { NEXT_ARG(); bpf_uds_name = *argv; + } else if (matches(*argv, "debug") == 0 || + matches(*argv, "dbg") == 0) { + if (bpf_trace_pipe()) + fprintf(stderr, + "No trace pipe, tracefs not mounted?\n"); + return -1; + } else if (matches(*argv, "graft") == 0) { + const char *bpf_map_path; + bool has_key = false; + uint32_t key; + + NEXT_ARG(); + bpf_map_path = *argv; + NEXT_ARG(); + if (matches(*argv, "key") == 0) { + NEXT_ARG(); + if (get_unsigned(&key, *argv, 0)) { + fprintf(stderr, "Illegal \"key\"\n"); + return -1; + } + has_key = true; + NEXT_ARG(); + } + return bpf_graft_map(bpf_map_path, has_key ? + &key : NULL, argc, argv); } else { explain(); return -1; } - argc--; - argv++; + NEXT_ARG_FWD(); } if (!bpf_uds_name) { @@ -83,9 +115,6 @@ return -1; } - memset(fds, 0, sizeof(fds)); - memset(&aux, 0, sizeof(aux)); - ret = bpf_recv_map_fds(bpf_uds_name, fds, &aux, ARRAY_SIZE(fds)); if (ret < 0) { fprintf(stderr, "bpf: Could not receive fds!\n"); @@ -142,6 +171,6 @@ } struct exec_util bpf_exec_util = { - .id = "bpf", - .parse_eopt = parse_bpf, + .id = "bpf", + .parse_eopt = parse_bpf, }; diff -Nru iproute2-4.3.0/tc/em_canid.c iproute2-4.9.0/tc/em_canid.c --- iproute2-4.3.0/tc/em_canid.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/em_canid.c 2016-12-12 23:07:42.000000000 +0000 @@ -106,8 +106,8 @@ if (args == NULL) return PARSE_ERR(args, "canid: missing arguments"); - rules.rules_raw = malloc(sizeof(struct can_filter) * rules.rules_capacity); - memset(rules.rules_raw, 0, sizeof(struct can_filter) * rules.rules_capacity); + rules.rules_raw = calloc(rules.rules_capacity, + sizeof(struct can_filter)); do { if (!bstrcmp(args, "sff")) { diff -Nru iproute2-4.3.0/tc/em_cmp.c iproute2-4.9.0/tc/em_cmp.c --- iproute2-4.3.0/tc/em_cmp.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/em_cmp.c 2016-12-12 23:07:42.000000000 +0000 @@ -44,12 +44,10 @@ int align, opnd = 0; unsigned long offset = 0, layer = TCF_LAYER_NETWORK, mask = 0, value = 0; int offset_present = 0, value_present = 0; - struct tcf_em_cmp cmp; - - memset(&cmp, 0, sizeof(cmp)); + struct tcf_em_cmp cmp = {}; #define PARSE_ERR(CARG, FMT, ARGS...) \ - em_parse_error(EINVAL, args, CARG, &cmp_ematch_util, FMT ,##ARGS) + em_parse_error(EINVAL, args, CARG, &cmp_ematch_util, FMT, ##ARGS) if (args == NULL) return PARSE_ERR(args, "cmp: missing arguments"); diff -Nru iproute2-4.3.0/tc/em_ipset.c iproute2-4.9.0/tc/em_ipset.c --- iproute2-4.3.0/tc/em_ipset.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/em_ipset.c 2016-12-12 23:07:42.000000000 +0000 @@ -52,8 +52,8 @@ #define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ struct ip_set_req_get_set { - unsigned op; - unsigned version; + unsigned int op; + unsigned int version; union ip_set_name_index set; }; @@ -62,14 +62,14 @@ #define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ struct ip_set_req_version { - unsigned op; - unsigned version; + unsigned int op; + unsigned int version; }; #endif /* IPSET_INVALID_ID */ extern struct ematch_util ipset_ematch_util; -static int get_version(unsigned *version) +static int get_version(unsigned int *version) { int res, sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); struct ip_set_req_version req_version; @@ -95,6 +95,7 @@ { int sockfd, res; socklen_t size = sizeof(struct ip_set_req_get_set); + sockfd = get_version(&req->version); if (sockfd < 0) return -1; @@ -107,8 +108,7 @@ if (size != sizeof(struct ip_set_req_get_set)) { fprintf(stderr, - "Incorrect return size from kernel during ipset lookup, " - "(want %zu, got %zu)\n", + "Incorrect return size from kernel during ipset lookup, (want %zu, got %zu)\n", sizeof(struct ip_set_req_get_set), (size_t)size); return -1; } @@ -158,29 +158,29 @@ static int parse_dirs(const char *opt_arg, struct xt_set_info *info) { - char *saved = strdup(opt_arg); - char *ptr, *tmp = saved; + char *saved = strdup(opt_arg); + char *ptr, *tmp = saved; if (!tmp) { perror("strdup"); return -1; } - while (info->dim < IPSET_DIM_MAX && tmp != NULL) { - info->dim++; - ptr = strsep(&tmp, ","); - if (strncmp(ptr, "src", 3) == 0) - info->flags |= (1 << info->dim); - else if (strncmp(ptr, "dst", 3) != 0) { - fputs("You must specify (the comma separated list of) 'src' or 'dst'\n", stderr); + while (info->dim < IPSET_DIM_MAX && tmp != NULL) { + info->dim++; + ptr = strsep(&tmp, ","); + if (strncmp(ptr, "src", 3) == 0) + info->flags |= (1 << info->dim); + else if (strncmp(ptr, "dst", 3) != 0) { + fputs("You must specify (the comma separated list of) 'src' or 'dst'\n", stderr); free(saved); return -1; } - } + } - if (tmp) - fprintf(stderr, "Can't be more src/dst options than %u", IPSET_DIM_MAX); - free(saved); + if (tmp) + fprintf(stderr, "Can't be more src/dst options than %u", IPSET_DIM_MAX); + free(saved); return tmp ? -1 : 0; } @@ -198,13 +198,11 @@ static int ipset_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, struct bstr *args) { - struct xt_set_info set_info; + struct xt_set_info set_info = {}; int ret; - memset(&set_info, 0, sizeof(set_info)); - #define PARSE_ERR(CARG, FMT, ARGS...) \ - em_parse_error(EINVAL, args, CARG, &ipset_ematch_util, FMT ,##ARGS) + em_parse_error(EINVAL, args, CARG, &ipset_ematch_util, FMT, ##ARGS) if (args == NULL) return PARSE_ERR(args, "ipset: missing set name"); @@ -238,7 +236,7 @@ int data_len) { int i; - char setname[IPSET_MAXNAMELEN]; + char setname[IPSET_MAXNAMELEN]; const struct xt_set_info *set_info = data; if (data_len != sizeof(*set_info)) { @@ -246,7 +244,7 @@ return -1; } - if (get_set_byid(setname, set_info->index)) + if (get_set_byid(setname, set_info->index)) return -1; fputs(setname, fd); for (i = 1; i <= set_info->dim; i++) { diff -Nru iproute2-4.3.0/tc/em_meta.c iproute2-4.9.0/tc/em_meta.c --- iproute2-4.3.0/tc/em_meta.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/em_meta.c 2016-12-12 23:07:42.000000000 +0000 @@ -41,9 +41,9 @@ struct meta_entry { int id; - char * kind; - char * mask; - char * desc; + char *kind; + char *mask; + char *desc; } meta_table[] = { #define TCF_META_ID_SECTION 0 #define __A(id, name, mask, desc) { TCF_META_ID_##id, name, mask, desc } @@ -102,7 +102,7 @@ __A(SK_RMEM_ALLOC, "sk_rmem", "i", "RMEM"), __A(SK_WMEM_ALLOC, "sk_wmem", "i", "WMEM"), __A(SK_OMEM_ALLOC, "sk_omem", "i", "OMEM"), - __A(SK_WMEM_QUEUED, "sk_wmem_queue","i", "WMEM queue"), + __A(SK_WMEM_QUEUED, "sk_wmem_queue", "i", "WMEM queue"), __A(SK_SND_QLEN, "sk_snd_queue", "i", "Send queue length"), __A(SK_RCV_QLEN, "sk_rcv_queue", "i", "Receive queue length"), __A(SK_ERR_QLEN, "sk_err_queue", "i", "Error queue length"), @@ -122,11 +122,11 @@ return INT_MAX; } -static struct meta_entry * lookup_meta_entry(struct bstr *kind) +static struct meta_entry *lookup_meta_entry(struct bstr *kind) { int i; - for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) + for (i = 0; i < ARRAY_SIZE(meta_table); i++) if (!bstrcmp(kind, meta_table[i].kind) && meta_table[i].id != 0) return &meta_table[i]; @@ -134,11 +134,11 @@ return NULL; } -static struct meta_entry * lookup_meta_entry_byid(int id) +static struct meta_entry *lookup_meta_entry_byid(int id) { int i; - for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) + for (i = 0; i < ARRAY_SIZE(meta_table); i++) if (meta_table[i].id == id) return &meta_table[i]; @@ -159,6 +159,7 @@ case TCF_META_TYPE_VAR: if (TCF_META_ID(hdr->kind) == TCF_META_ID_VALUE) { struct bstr *a = (struct bstr *) val; + addattr_l(n, MAX_MSG, tlv, a->data, a->len); } break; @@ -192,7 +193,7 @@ " ID Type Description\n" \ "--------------------------------------------------------"); - for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) { + for (i = 0; i < ARRAY_SIZE(meta_table); i++) { if (meta_table[i].id == TCF_META_ID_SECTION) { fprintf(fd, "\n%s:\n", meta_table[i].kind); } else { @@ -231,7 +232,7 @@ #define PARSE_FAILURE ((void *) (-1)) #define PARSE_ERR(CARG, FMT, ARGS...) \ - em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT ,##ARGS) + em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT, ##ARGS) static inline int can_adopt(struct tcf_meta_val *val) { @@ -308,7 +309,7 @@ a = bstr_next(arg); - while(a) { + while (a) { if (!bstrcmp(a, "shift")) { unsigned long shift; @@ -360,11 +361,9 @@ { int opnd; struct bstr *a; - struct tcf_meta_hdr meta_hdr; + struct tcf_meta_hdr meta_hdr = {}; unsigned long lvalue = 0, rvalue = 0; - memset(&meta_hdr, 0, sizeof(meta_hdr)); - if (args == NULL) return PARSE_ERR(args, "meta: missing arguments"); @@ -441,7 +440,7 @@ return -1; } - switch(type) { + switch (type) { case TCF_META_TYPE_INT: if (RTA_PAYLOAD(rta) < sizeof(__u32)) { fprintf(stderr, "meta int type value TLV " \ @@ -484,8 +483,9 @@ if (RTA_PAYLOAD(rta) < sizeof(__u32)) goto size_mismatch; - fprintf(fd, " mask 0x%08x", - rta_getattr_u32(rta)); + if (rta_getattr_u32(rta)) + fprintf(fd, " mask 0x%08x", + rta_getattr_u32(rta)); } break; } diff -Nru iproute2-4.3.0/tc/em_nbyte.c iproute2-4.9.0/tc/em_nbyte.c --- iproute2-4.3.0/tc/em_nbyte.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/em_nbyte.c 2016-12-12 23:07:42.000000000 +0000 @@ -44,12 +44,10 @@ struct bstr *needle = args; unsigned long offset = 0, layer = TCF_LAYER_NETWORK; int offset_present = 0; - struct tcf_em_nbyte nb; - - memset(&nb, 0, sizeof(nb)); + struct tcf_em_nbyte nb = {}; #define PARSE_ERR(CARG, FMT, ARGS...) \ - em_parse_error(EINVAL, args, CARG, &nbyte_ematch_util, FMT ,##ARGS) + em_parse_error(EINVAL, args, CARG, &nbyte_ematch_util, FMT, ##ARGS) if (args == NULL) return PARSE_ERR(args, "nbyte: missing arguments"); diff -Nru iproute2-4.3.0/tc/em_u32.c iproute2-4.9.0/tc/em_u32.c --- iproute2-4.3.0/tc/em_u32.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/em_u32.c 2016-12-12 23:07:42.000000000 +0000 @@ -39,12 +39,10 @@ struct bstr *a; int align, nh_len; unsigned long key, mask, offmask = 0, offset; - struct tc_u32_key u_key; - - memset(&u_key, 0, sizeof(u_key)); + struct tc_u32_key u_key = {}; #define PARSE_ERR(CARG, FMT, ARGS...) \ - em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT ,##ARGS) + em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT, ##ARGS) if (args == NULL) return PARSE_ERR(args, "u32: missing arguments"); @@ -85,6 +83,7 @@ nh_len = strlen("nexthdr+"); if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) { char buf[a->len - nh_len + 1]; + offmask = -1; memcpy(buf, a->data + nh_len, a->len - nh_len); offset = strtoul(buf, NULL, 0); diff -Nru iproute2-4.3.0/tc/f_basic.c iproute2-4.9.0/tc/f_basic.c --- iproute2-4.3.0/tc/f_basic.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_basic.c 2016-12-12 23:07:42.000000000 +0000 @@ -27,7 +27,7 @@ static void explain(void) { - fprintf(stderr, "Usage: ... basic [ match EMATCH_TREE ] \n"); + fprintf(stderr, "Usage: ... basic [ match EMATCH_TREE ]\n"); fprintf(stderr, " [ action ACTION_SPEC ] [ classid CLASSID ]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where: SELECTOR := SAMPLE SAMPLE ...\n"); @@ -56,7 +56,7 @@ if (argc == 0) return 0; - tail = (struct rtattr*)(((void*)n)+NLMSG_ALIGN(n->nlmsg_len)); + tail = (struct rtattr *)(((void *)n)+NLMSG_ALIGN(n->nlmsg_len)); addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); while (argc > 0) { @@ -69,7 +69,8 @@ continue; } else if (matches(*argv, "classid") == 0 || strcmp(*argv, "flowid") == 0) { - unsigned handle; + unsigned int handle; + NEXT_ARG(); if (get_tc_classid(&handle, *argv)) { fprintf(stderr, "Illegal \"classid\"\n"); @@ -102,7 +103,7 @@ argc--; argv++; } - tail->rta_len = (((void*)n)+n->nlmsg_len) - (void*)tail; + tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail; return 0; } diff -Nru iproute2-4.3.0/tc/f_bpf.c iproute2-4.9.0/tc/f_bpf.c --- iproute2-4.3.0/tc/f_bpf.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_bpf.c 2016-12-12 23:07:42.000000000 +0000 @@ -11,19 +11,8 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + +#include #include "utils.h" #include "tc_util.h" @@ -31,6 +20,13 @@ static const enum bpf_prog_type bpf_type = BPF_PROG_TYPE_SCHED_CLS; +static const int nla_tbl[BPF_NLA_MAX] = { + [BPF_NLA_OPS_LEN] = TCA_BPF_OPS_LEN, + [BPF_NLA_OPS] = TCA_BPF_OPS, + [BPF_NLA_FD] = TCA_BPF_FD, + [BPF_NLA_NAME] = TCA_BPF_NAME, +}; + static void explain(void) { fprintf(stderr, "Usage: ... bpf ...\n"); @@ -41,7 +37,8 @@ fprintf(stderr, "\n"); fprintf(stderr, "eBPF use case:\n"); fprintf(stderr, " object-file FILE [ section CLS_NAME ] [ export UDS_FILE ]"); - fprintf(stderr, " [ verbose ]\n"); + fprintf(stderr, " [ verbose ] [ direct-action ] [ skip_hw | skip_sw ]\n"); + fprintf(stderr, " object-pinned FILE [ direct-action ] [ skip_hw | skip_sw ]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Common remaining options:\n"); fprintf(stderr, " [ action ACTION_SPEC ]\n"); @@ -51,7 +48,8 @@ fprintf(stderr, "c,t,f,k and s are decimals; s denotes number of 4-tuples\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where FILE points to a file containing the BPF_BYTECODE string,\n"); - fprintf(stderr, "an ELF file containing eBPF map definitions and bytecode.\n"); + fprintf(stderr, "an ELF file containing eBPF map definitions and bytecode, or a\n"); + fprintf(stderr, "pinned eBPF program.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where CLS_NAME refers to the section name containing the\n"); fprintf(stderr, "classifier (default \'%s\').\n", bpf_default_section(bpf_type)); @@ -66,115 +64,39 @@ static int bpf_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { + const char *bpf_obj = NULL, *bpf_uds_name = NULL; struct tcmsg *t = NLMSG_DATA(n); - const char *bpf_uds_name = NULL; - const char *bpf_sec_name = NULL; - char *bpf_obj = NULL; - struct rtattr *tail; + unsigned int bpf_gen_flags = 0; + unsigned int bpf_flags = 0; bool seen_run = false; - long h = 0; + struct rtattr *tail; int ret = 0; - if (argc == 0) - return 0; - if (handle) { - h = strtol(handle, NULL, 0); - if (h == LONG_MIN || h == LONG_MAX) { - fprintf(stderr, "Illegal handle \"%s\", must be " - "numeric.\n", handle); + if (get_u32(&t->tcm_handle, handle, 0)) { + fprintf(stderr, "Illegal \"handle\"\n"); return -1; } } - t->tcm_handle = h; + if (argc == 0) + return 0; tail = (struct rtattr *)(((void *)n) + NLMSG_ALIGN(n->nlmsg_len)); addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); while (argc > 0) { if (matches(*argv, "run") == 0) { - struct sock_filter bpf_ops[BPF_MAXINSNS]; - bool from_file, ebpf, bpf_verbose; - int ret; - NEXT_ARG(); opt_bpf: - bpf_sec_name = bpf_default_section(bpf_type); - bpf_verbose = false; - ebpf = false; seen_run = true; - - if (strcmp(*argv, "bytecode-file") == 0 || - strcmp(*argv, "bcf") == 0) { - from_file = true; - } else if (strcmp(*argv, "bytecode") == 0 || - strcmp(*argv, "bc") == 0) { - from_file = false; - } else if (strcmp(*argv, "object-file") == 0 || - strcmp(*argv, "obj") == 0) { - ebpf = true; - } else { - fprintf(stderr, "What is \"%s\"?\n", *argv); - explain(); - return -1; - } - - NEXT_ARG(); - if (ebpf) { - bpf_uds_name = getenv(BPF_ENV_UDS); - bpf_obj = *argv; - NEXT_ARG(); - - if (strcmp(*argv, "section") == 0 || - strcmp(*argv, "sec") == 0) { - NEXT_ARG(); - bpf_sec_name = *argv; - NEXT_ARG(); - } - if (!bpf_uds_name && - (strcmp(*argv, "export") == 0 || - strcmp(*argv, "exp") == 0)) { - NEXT_ARG(); - bpf_uds_name = *argv; - NEXT_ARG(); - } - if (strcmp(*argv, "verbose") == 0 || - strcmp(*argv, "verb") == 0) { - bpf_verbose = true; - NEXT_ARG(); - } - - PREV_ARG(); - } - - ret = ebpf ? bpf_open_object(bpf_obj, bpf_type, bpf_sec_name, - bpf_verbose) : - bpf_parse_ops(argc, argv, bpf_ops, from_file); - if (ret < 0) { - fprintf(stderr, "%s\n", ebpf ? - "Could not load object" : - "Illegal \"bytecode\""); + if (bpf_parse_common(&argc, &argv, nla_tbl, bpf_type, + &bpf_obj, &bpf_uds_name, n)) { + fprintf(stderr, "Failed to retrieve (e)BPF data!\n"); return -1; } - - if (ebpf) { - char bpf_name[256]; - - bpf_obj = basename(bpf_obj); - - snprintf(bpf_name, sizeof(bpf_name), "%s:[%s]", - bpf_obj, bpf_sec_name); - - addattr32(n, MAX_MSG, TCA_BPF_FD, ret); - addattrstrz(n, MAX_MSG, TCA_BPF_NAME, bpf_name); - } else { - addattr16(n, MAX_MSG, TCA_BPF_OPS_LEN, ret); - addattr_l(n, MAX_MSG, TCA_BPF_OPS, &bpf_ops, - ret * sizeof(struct sock_filter)); - } } else if (matches(*argv, "classid") == 0 || - strcmp(*argv, "flowid") == 0) { + matches(*argv, "flowid") == 0) { unsigned int handle; NEXT_ARG(); @@ -182,7 +104,14 @@ fprintf(stderr, "Illegal \"classid\"\n"); return -1; } - addattr_l(n, MAX_MSG, TCA_BPF_CLASSID, &handle, 4); + addattr32(n, MAX_MSG, TCA_BPF_CLASSID, handle); + } else if (matches(*argv, "direct-action") == 0 || + matches(*argv, "da") == 0) { + bpf_flags |= TCA_BPF_FLAG_ACT_DIRECT; + } else if (matches(*argv, "skip_hw") == 0) { + bpf_gen_flags |= TCA_CLS_FLAGS_SKIP_HW; + } else if (matches(*argv, "skip_sw") == 0) { + bpf_gen_flags |= TCA_CLS_FLAGS_SKIP_SW; } else if (matches(*argv, "action") == 0) { NEXT_ARG(); if (parse_action(&argc, &argv, TCA_BPF_ACT, n)) { @@ -197,7 +126,7 @@ return -1; } continue; - } else if (strcmp(*argv, "help") == 0) { + } else if (matches(*argv, "help") == 0) { explain(); return -1; } else { @@ -208,10 +137,15 @@ explain(); return -1; } - argc--; - argv++; + + NEXT_ARG_FWD(); } + if (bpf_gen_flags) + addattr32(n, MAX_MSG, TCA_BPF_FLAGS_GEN, bpf_gen_flags); + if (bpf_obj && bpf_flags) + addattr32(n, MAX_MSG, TCA_BPF_FLAGS, bpf_flags); + tail->rta_len = (((void *)n) + n->nlmsg_len) - (void *)tail; if (bpf_uds_name) @@ -244,6 +178,23 @@ else if (tb[TCA_BPF_FD]) fprintf(f, "pfd %u ", rta_getattr_u32(tb[TCA_BPF_FD])); + if (tb[TCA_BPF_FLAGS]) { + unsigned int flags = rta_getattr_u32(tb[TCA_BPF_FLAGS]); + + if (flags & TCA_BPF_FLAG_ACT_DIRECT) + fprintf(f, "direct-action "); + } + + if (tb[TCA_BPF_FLAGS_GEN]) { + unsigned int flags = + rta_getattr_u32(tb[TCA_BPF_FLAGS_GEN]); + + if (flags & TCA_CLS_FLAGS_SKIP_HW) + fprintf(f, "skip_hw "); + if (flags & TCA_CLS_FLAGS_SKIP_SW) + fprintf(f, "skip_sw "); + } + if (tb[TCA_BPF_OPS] && tb[TCA_BPF_OPS_LEN]) { bpf_print_ops(f, tb[TCA_BPF_OPS], rta_getattr_u16(tb[TCA_BPF_OPS_LEN])); @@ -263,7 +214,7 @@ } struct filter_util bpf_filter_util = { - .id = "bpf", - .parse_fopt = bpf_parse_opt, - .print_fopt = bpf_print_opt, + .id = "bpf", + .parse_fopt = bpf_parse_opt, + .print_fopt = bpf_print_opt, }; diff -Nru iproute2-4.3.0/tc/f_cgroup.c iproute2-4.9.0/tc/f_cgroup.c --- iproute2-4.3.0/tc/f_cgroup.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_cgroup.c 2016-12-12 23:07:42.000000000 +0000 @@ -40,7 +40,7 @@ t->tcm_handle = h; - tail = (struct rtattr*)(((void*)n)+NLMSG_ALIGN(n->nlmsg_len)); + tail = (struct rtattr *)(((void *)n)+NLMSG_ALIGN(n->nlmsg_len)); addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); while (argc > 0) { @@ -76,7 +76,7 @@ } } - tail->rta_len = (((void*)n)+n->nlmsg_len) - (void*)tail; + tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail; return 0; } diff -Nru iproute2-4.3.0/tc/f_flow.c iproute2-4.9.0/tc/f_flow.c --- iproute2-4.3.0/tc/f_flow.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_flow.c 2016-12-12 23:07:42.000000000 +0000 @@ -30,8 +30,8 @@ " [ action ACTION_SPEC ]\n" "\n" "KEY-LIST := [ KEY-LIST , ] KEY\n" -"KEY := [ src | dst | proto | proto-src | proto-dst | iif | priority | \n" -" mark | nfct | nfct-src | nfct-dst | nfct-proto-src | \n" +"KEY := [ src | dst | proto | proto-src | proto-dst | iif | priority |\n" +" mark | nfct | nfct-src | nfct-dst | nfct-proto-src |\n" " nfct-proto-dst | rt-classid | sk-uid | sk-gid |\n" " vlan-tag | rxhash ]\n" "OPS := [ or NUM | and NUM | xor NUM | rshift NUM | addend NUM ]\n" @@ -133,7 +133,6 @@ static int flow_parse_opt(struct filter_util *fu, char *handle, int argc, char **argv, struct nlmsghdr *n) { - struct tc_police tp; struct tcmsg *t = NLMSG_DATA(n); struct rtattr *tail; __u32 mask = ~0U, xor = 0; @@ -141,8 +140,6 @@ __u32 mode = FLOW_MODE_MAP; __u32 tmp; - memset(&tp, 0, sizeof(tp)); - if (handle) { if (get_u32(&t->tcm_handle, handle, 0)) { fprintf(stderr, "Illegal \"handle\"\n"); @@ -270,6 +267,7 @@ __u32 handle) { struct rtattr *tb[TCA_FLOW_MAX+1]; + SPRINT_BUF(b1); unsigned int i; __u32 mask = ~0, val = 0; diff -Nru iproute2-4.3.0/tc/f_flower.c iproute2-4.9.0/tc/f_flower.c --- iproute2-4.3.0/tc/f_flower.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_flower.c 2016-12-12 23:07:42.000000000 +0000 @@ -17,6 +17,7 @@ #include #include #include +#include #include "utils.h" #include "tc_util.h" @@ -25,22 +26,25 @@ static void explain(void) { fprintf(stderr, "Usage: ... flower [ MATCH-LIST ]\n"); + fprintf(stderr, " [ skip_sw | skip_hw ]\n"); fprintf(stderr, " [ action ACTION-SPEC ] [ classid CLASSID ]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n"); - fprintf(stderr, " MATCH := { indev DEV-NAME | \n"); - fprintf(stderr, " dst_mac MAC-ADDR | \n"); - fprintf(stderr, " src_mac MAC-ADDR | \n"); - fprintf(stderr, " eth_type [ipv4 | ipv6 | ETH-TYPE ] | \n"); - fprintf(stderr, " ip_proto [tcp | udp | IP-PROTO ] | \n"); - fprintf(stderr, " dst_ip [ IPV4-ADDR | IPV6-ADDR ] | \n"); - fprintf(stderr, " src_ip [ IPV4-ADDR | IPV6-ADDR ] | \n"); - fprintf(stderr, " dst_port PORT-NUMBER | \n"); + fprintf(stderr, " MATCH := { indev DEV-NAME |\n"); + fprintf(stderr, " vlan_id VID |\n"); + fprintf(stderr, " vlan_prio PRIORITY |\n"); + fprintf(stderr, " vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n"); + fprintf(stderr, " dst_mac MAC-ADDR |\n"); + fprintf(stderr, " src_mac MAC-ADDR |\n"); + fprintf(stderr, " ip_proto [tcp | udp | IP-PROTO ] |\n"); + fprintf(stderr, " dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"); + fprintf(stderr, " src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"); + fprintf(stderr, " dst_port PORT-NUMBER |\n"); fprintf(stderr, " src_port PORT-NUMBER }\n"); fprintf(stderr, " FILTERID := X:Y:Z\n"); fprintf(stderr, " ACTION-SPEC := ... look at individual actions\n"); fprintf(stderr, "\n"); - fprintf(stderr, "NOTE: CLASSID, ETH-TYPE, IP-PROTO are parsed as hexadecimal input.\n"); + fprintf(stderr, "NOTE: CLASSID, IP-PROTO are parsed as hexadecimal input.\n"); fprintf(stderr, "NOTE: There can be only used one mask per one prio. If user needs\n"); fprintf(stderr, " to specify different mask, he has to use different prio.\n"); } @@ -60,26 +64,20 @@ return 0; } -static int flower_parse_eth_type(char *str, int type, __be16 *p_eth_type, - struct nlmsghdr *n) +static int flower_parse_vlan_eth_type(char *str, __be16 eth_type, int type, + __be16 *p_vlan_eth_type, struct nlmsghdr *n) { - int ret; - __be16 eth_type; + __be16 vlan_eth_type; - if (matches(str, "ipv4") == 0) { - eth_type = htons(ETH_P_IP); - } else if (matches(str, "ipv6") == 0) { - eth_type = htons(ETH_P_IPV6); - } else { - __u16 tmp; - - ret = get_u16(&tmp, str, 16); - if (ret) - return -1; - eth_type = htons(tmp); + if (eth_type != htons(ETH_P_8021Q)) { + fprintf(stderr, "Can't set \"vlan_ethtype\" if ethertype isn't 802.1Q\n"); + return -1; } - addattr16(n, MAX_MSG, type, eth_type); - *p_eth_type = eth_type; + + if (ll_proto_a2n(&vlan_eth_type, str)) + invarg("invalid vlan_ethtype", str); + addattr16(n, MAX_MSG, type, vlan_eth_type); + *p_vlan_eth_type = vlan_eth_type; return 0; } @@ -173,11 +171,11 @@ return -1; } - ret = get_u16(&port, str, 10); + ret = get_be16(&port, str, 10); if (ret) return -1; - addattr16(n, MAX_MSG, type, htons(port)); + addattr16(n, MAX_MSG, type, port); return 0; } @@ -188,11 +186,10 @@ int ret; struct tcmsg *t = NLMSG_DATA(n); struct rtattr *tail; - __be16 eth_type = 0; + __be16 eth_type = TC_H_MIN(t->tcm_info); + __be16 vlan_ethtype = 0; __u8 ip_proto = 0xff; - - if (argc == 0) - return 0; + __u32 flags = 0; if (handle) { ret = get_u32(&t->tcm_handle, handle, 0); @@ -205,10 +202,15 @@ tail = (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)); addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); + if (argc == 0) { + /*at minimal we will match all ethertype packets */ + goto parse_done; + } + while (argc > 0) { if (matches(*argv, "classid") == 0 || matches(*argv, "flowid") == 0) { - unsigned handle; + unsigned int handle; NEXT_ARG(); ret = get_tc_classid(&handle, *argv); @@ -217,13 +219,51 @@ return -1; } addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4); + } else if (matches(*argv, "skip_hw") == 0) { + flags |= TCA_CLS_FLAGS_SKIP_HW; + } else if (matches(*argv, "skip_sw") == 0) { + flags |= TCA_CLS_FLAGS_SKIP_SW; } else if (matches(*argv, "indev") == 0) { - char ifname[IFNAMSIZ]; + char ifname[IFNAMSIZ] = {}; NEXT_ARG(); - memset(ifname, 0, sizeof(ifname)); strncpy(ifname, *argv, sizeof(ifname) - 1); addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, ifname); + } else if (matches(*argv, "vlan_id") == 0) { + __u16 vid; + + NEXT_ARG(); + if (eth_type != htons(ETH_P_8021Q)) { + fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q\n"); + return -1; + } + ret = get_u16(&vid, *argv, 10); + if (ret < 0 || vid & ~0xfff) { + fprintf(stderr, "Illegal \"vlan_id\"\n"); + return -1; + } + addattr16(n, MAX_MSG, TCA_FLOWER_KEY_VLAN_ID, vid); + } else if (matches(*argv, "vlan_prio") == 0) { + __u8 vlan_prio; + + NEXT_ARG(); + if (eth_type != htons(ETH_P_8021Q)) { + fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q\n"); + return -1; + } + ret = get_u8(&vlan_prio, *argv, 10); + if (ret < 0 || vlan_prio & ~0x7) { + fprintf(stderr, "Illegal \"vlan_prio\"\n"); + return -1; + } + addattr8(n, MAX_MSG, TCA_FLOWER_KEY_VLAN_PRIO, vlan_prio); + } else if (matches(*argv, "vlan_ethtype") == 0) { + NEXT_ARG(); + ret = flower_parse_vlan_eth_type(*argv, eth_type, + TCA_FLOWER_KEY_VLAN_ETH_TYPE, + &vlan_ethtype, n); + if (ret < 0) + return -1; } else if (matches(*argv, "dst_mac") == 0) { NEXT_ARG(); ret = flower_parse_eth_addr(*argv, @@ -244,18 +284,10 @@ fprintf(stderr, "Illegal \"src_mac\"\n"); return -1; } - } else if (matches(*argv, "eth_type") == 0) { - NEXT_ARG(); - ret = flower_parse_eth_type(*argv, - TCA_FLOWER_KEY_ETH_TYPE, - ð_type, n); - if (ret < 0) { - fprintf(stderr, "Illegal \"eth_type\"\n"); - return -1; - } } else if (matches(*argv, "ip_proto") == 0) { NEXT_ARG(); - ret = flower_parse_ip_proto(*argv, eth_type, + ret = flower_parse_ip_proto(*argv, vlan_ethtype ? + vlan_ethtype : eth_type, TCA_FLOWER_KEY_IP_PROTO, &ip_proto, n); if (ret < 0) { @@ -264,7 +296,8 @@ } } else if (matches(*argv, "dst_ip") == 0) { NEXT_ARG(); - ret = flower_parse_ip_addr(*argv, eth_type, + ret = flower_parse_ip_addr(*argv, vlan_ethtype ? + vlan_ethtype : eth_type, TCA_FLOWER_KEY_IPV4_DST, TCA_FLOWER_KEY_IPV4_DST_MASK, TCA_FLOWER_KEY_IPV6_DST, @@ -276,7 +309,8 @@ } } else if (matches(*argv, "src_ip") == 0) { NEXT_ARG(); - ret = flower_parse_ip_addr(*argv, eth_type, + ret = flower_parse_ip_addr(*argv, vlan_ethtype ? + vlan_ethtype : eth_type, TCA_FLOWER_KEY_IPV4_SRC, TCA_FLOWER_KEY_IPV4_SRC_MASK, TCA_FLOWER_KEY_IPV6_SRC, @@ -323,7 +357,17 @@ argc--; argv++; } - tail->rta_len = (((void*)n)+n->nlmsg_len) - (void*)tail; +parse_done: + addattr32(n, MAX_MSG, TCA_FLOWER_FLAGS, flags); + + ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type); + if (ret) { + fprintf(stderr, "Illegal \"eth_type\"(0x%x)\n", + ntohs(eth_type)); + return -1; + } + + tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail; return 0; } @@ -416,7 +460,6 @@ struct rtattr *addr6_attr, struct rtattr *mask6_attr) { - SPRINT_BUF(b1); struct rtattr *addr_attr; struct rtattr *mask_attr; int family; @@ -438,18 +481,12 @@ } if (!addr_attr || RTA_PAYLOAD(addr_attr) != len) return; - fprintf(f, "\n %s %s", name, rt_addr_n2a(family, - RTA_PAYLOAD(addr_attr), - RTA_DATA(addr_attr), - b1, sizeof(b1))); + fprintf(f, "\n %s %s", name, rt_addr_n2a_rta(family, addr_attr)); if (!mask_attr || RTA_PAYLOAD(mask_attr) != len) return; bits = __mask_bits(RTA_DATA(mask_attr), len); if (bits < 0) - fprintf(f, "/%s", rt_addr_n2a(family, - RTA_PAYLOAD(mask_attr), - RTA_DATA(mask_attr), - b1, sizeof(b1))); + fprintf(f, "/%s", rt_addr_n2a_rta(family, mask_attr)); else if (bits < len * 8) fprintf(f, "/%d", bits); } @@ -489,7 +526,8 @@ if (tb[TCA_FLOWER_CLASSID]) { SPRINT_BUF(b1); fprintf(f, "classid %s ", - sprint_tc_classid(rta_getattr_u32(tb[TCA_FLOWER_CLASSID]), b1)); + sprint_tc_classid(rta_getattr_u32(tb[TCA_FLOWER_CLASSID]), + b1)); } if (tb[TCA_FLOWER_INDEV]) { @@ -498,6 +536,18 @@ fprintf(f, "\n indev %s", rta_getattr_str(attr)); } + if (tb[TCA_FLOWER_KEY_VLAN_ID]) { + struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_ID]; + + fprintf(f, "\n vlan_id %d", rta_getattr_u16(attr)); + } + + if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) { + struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_PRIO]; + + fprintf(f, "\n vlan_prio %d", rta_getattr_u8(attr)); + } + flower_print_eth_addr(f, "dst_mac", tb[TCA_FLOWER_KEY_ETH_DST], tb[TCA_FLOWER_KEY_ETH_DST_MASK]); flower_print_eth_addr(f, "src_mac", tb[TCA_FLOWER_KEY_ETH_SRC], @@ -526,6 +576,15 @@ tb[TCA_FLOWER_KEY_TCP_SRC], tb[TCA_FLOWER_KEY_UDP_SRC]); + if (tb[TCA_FLOWER_FLAGS]) { + __u32 flags = rta_getattr_u32(tb[TCA_FLOWER_FLAGS]); + + if (flags & TCA_CLS_FLAGS_SKIP_HW) + fprintf(f, "\n skip_hw"); + if (flags & TCA_CLS_FLAGS_SKIP_SW) + fprintf(f, "\n skip_sw"); + } + if (tb[TCA_FLOWER_ACT]) { tc_print_action(f, tb[TCA_FLOWER_ACT]); } diff -Nru iproute2-4.3.0/tc/f_fw.c iproute2-4.9.0/tc/f_fw.c --- iproute2-4.3.0/tc/f_fw.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_fw.c 2016-12-12 23:07:42.000000000 +0000 @@ -25,24 +25,31 @@ static void explain(void) { - fprintf(stderr, "Usage: ... fw [ classid CLASSID ] [ action ACTION_SPEC ]\n"); - fprintf(stderr, " ACTION_SPEC := ... look at individual actions\n"); - fprintf(stderr, " CLASSID := X:Y\n"); - fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n"); + fprintf(stderr, + "Usage: ... fw [ classid CLASSID ] [ indev DEV ] [ action ACTION_SPEC ]\n"); + fprintf(stderr, + " CLASSID := Push matching packets to the class identified by CLASSID with format X:Y\n"); + fprintf(stderr, + " CLASSID is parsed as hexadecimal input.\n"); + fprintf(stderr, + " DEV := specify device for incoming device classification.\n"); + fprintf(stderr, + " ACTION_SPEC := Apply an action on matching packets.\n"); + fprintf(stderr, + " NOTE: handle is represented as HANDLE[/FWMASK].\n"); + fprintf(stderr, " FWMASK is 0xffffffff by default.\n"); } static int fw_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { - struct tc_police tp; struct tcmsg *t = NLMSG_DATA(n); struct rtattr *tail; __u32 mask = 0; int mask_set = 0; - memset(&tp, 0, sizeof(tp)); - if (handle) { char *slash; + if ((slash = strchr(handle, '/')) != NULL) *slash = '\0'; if (get_u32(&t->tcm_handle, handle, 0)) { @@ -70,7 +77,8 @@ while (argc > 0) { if (matches(*argv, "classid") == 0 || matches(*argv, "flowid") == 0) { - unsigned handle; + unsigned int handle; + NEXT_ARG(); if (get_tc_classid(&handle, *argv)) { fprintf(stderr, "Illegal \"classid\"\n"); @@ -92,15 +100,15 @@ } continue; } else if (strcmp(*argv, "indev") == 0) { - char d[IFNAMSIZ+1]; - memset(d, 0, sizeof (d)); + char d[IFNAMSIZ+1] = {}; + argc--; argv++; if (argc < 1) { fprintf(stderr, "Illegal indev\n"); return -1; } - strncpy(d, *argv, sizeof (d) - 1); + strncpy(d, *argv, sizeof(d) - 1); addattr_l(n, MAX_MSG, TCA_FW_INDEV, d, strlen(d) + 1); } else if (strcmp(*argv, "help") == 0) { explain(); @@ -127,9 +135,10 @@ if (handle || tb[TCA_FW_MASK]) { __u32 mark = 0, mask = 0; - if(handle) + + if (handle) mark = handle; - if(tb[TCA_FW_MASK] && + if (tb[TCA_FW_MASK] && (mask = rta_getattr_u32(tb[TCA_FW_MASK])) != 0xFFFFFFFF) fprintf(f, "handle 0x%x/0x%x ", mark, mask); else @@ -145,7 +154,8 @@ tc_print_police(f, tb[TCA_FW_POLICE]); if (tb[TCA_FW_INDEV]) { struct rtattr *idev = tb[TCA_FW_INDEV]; - fprintf(f, "input dev %s ",rta_getattr_str(idev)); + + fprintf(f, "input dev %s ", rta_getattr_str(idev)); } if (tb[TCA_FW_ACT]) { diff -Nru iproute2-4.3.0/tc/f_matchall.c iproute2-4.9.0/tc/f_matchall.c --- iproute2-4.3.0/tc/f_matchall.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/tc/f_matchall.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,143 @@ +/* + * f_matchall.c Match-all Classifier + * + * This program is free software; you can distribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Jiri Pirko , Yotam Gigi + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... matchall [skip_sw | skip_hw]\n"); + fprintf(stderr, " [ action ACTION_SPEC ] [ classid CLASSID ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Where: SELECTOR := SAMPLE SAMPLE ...\n"); + fprintf(stderr, " FILTERID := X:Y:Z\n"); + fprintf(stderr, " ACTION_SPEC := ... look at individual actions\n"); + fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n"); +} + +static int matchall_parse_opt(struct filter_util *qu, char *handle, + int argc, char **argv, struct nlmsghdr *n) +{ + struct tcmsg *t = NLMSG_DATA(n); + struct rtattr *tail; + __u32 flags = 0; + long h = 0; + + if (handle) { + h = strtol(handle, NULL, 0); + if (h == LONG_MIN || h == LONG_MAX) { + fprintf(stderr, "Illegal handle \"%s\", must be numeric.\n", + handle); + return -1; + } + } + t->tcm_handle = h; + + if (argc == 0) + return 0; + + tail = (struct rtattr *)(((void *)n)+NLMSG_ALIGN(n->nlmsg_len)); + addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); + + while (argc > 0) { + if (matches(*argv, "classid") == 0 || + strcmp(*argv, "flowid") == 0) { + unsigned int handle; + + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, MAX_MSG, TCA_MATCHALL_CLASSID, &handle, 4); + } else if (matches(*argv, "action") == 0) { + NEXT_ARG(); + if (parse_action(&argc, &argv, TCA_MATCHALL_ACT, n)) { + fprintf(stderr, "Illegal \"action\"\n"); + return -1; + } + continue; + + } else if (strcmp(*argv, "skip_hw") == 0) { + NEXT_ARG(); + flags |= TCA_CLS_FLAGS_SKIP_HW; + continue; + } else if (strcmp(*argv, "skip_sw") == 0) { + NEXT_ARG(); + flags |= TCA_CLS_FLAGS_SKIP_SW; + continue; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (flags) { + if (!(flags ^ (TCA_CLS_FLAGS_SKIP_HW | + TCA_CLS_FLAGS_SKIP_SW))) { + fprintf(stderr, + "skip_hw and skip_sw are mutually exclusive\n"); + return -1; + } + addattr_l(n, MAX_MSG, TCA_MATCHALL_FLAGS, &flags, 4); + } + + tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail; + return 0; +} + +static int matchall_print_opt(struct filter_util *qu, FILE *f, + struct rtattr *opt, __u32 handle) +{ + struct rtattr *tb[TCA_MATCHALL_MAX+1]; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_MATCHALL_MAX, opt); + + if (handle) + fprintf(f, "handle 0x%x ", handle); + + if (tb[TCA_MATCHALL_CLASSID]) { + SPRINT_BUF(b1); + fprintf(f, "flowid %s ", + sprint_tc_classid(rta_getattr_u32(tb[TCA_MATCHALL_CLASSID]), b1)); + } + + if (tb[TCA_MATCHALL_ACT]) + tc_print_action(f, tb[TCA_MATCHALL_ACT]); + + return 0; +} + +struct filter_util matchall_filter_util = { + .id = "matchall", + .parse_fopt = matchall_parse_opt, + .print_fopt = matchall_print_opt, +}; diff -Nru iproute2-4.3.0/tc/f_route.c iproute2-4.9.0/tc/f_route.c --- iproute2-4.3.0/tc/f_route.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_route.c 2016-12-12 23:07:42.000000000 +0000 @@ -36,14 +36,11 @@ static int route_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { - struct tc_police tp; struct tcmsg *t = NLMSG_DATA(n); struct rtattr *tail; __u32 fh = 0xFFFF8000; __u32 order = 0; - memset(&tp, 0, sizeof(tp)); - if (handle) { if (get_u32(&t->tcm_handle, handle, 0)) { fprintf(stderr, "Illegal \"handle\"\n"); @@ -60,6 +57,7 @@ while (argc > 0) { if (matches(*argv, "to") == 0) { __u32 id; + NEXT_ARG(); if (rtnl_rtrealm_a2n(&id, *argv)) { fprintf(stderr, "Illegal \"to\"\n"); @@ -70,6 +68,7 @@ fh |= id&0xFF; } else if (matches(*argv, "from") == 0) { __u32 id; + NEXT_ARG(); if (rtnl_rtrealm_a2n(&id, *argv)) { fprintf(stderr, "Illegal \"from\"\n"); @@ -80,9 +79,10 @@ fh |= id<<16; } else if (matches(*argv, "fromif") == 0) { __u32 id; + NEXT_ARG(); ll_init_map(&rth); - if ((id=ll_name_to_index(*argv)) <= 0) { + if ((id = ll_name_to_index(*argv)) <= 0) { fprintf(stderr, "Illegal \"fromif\"\n"); return -1; } @@ -91,7 +91,8 @@ fh |= (0x8000|id)<<16; } else if (matches(*argv, "classid") == 0 || strcmp(*argv, "flowid") == 0) { - unsigned handle; + unsigned int handle; + NEXT_ARG(); if (get_tc_classid(&handle, *argv)) { fprintf(stderr, "Illegal \"classid\"\n"); @@ -141,6 +142,7 @@ static int route_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_ROUTE4_MAX+1]; + SPRINT_BUF(b1); if (opt == NULL) @@ -162,7 +164,7 @@ if (tb[TCA_ROUTE4_FROM]) fprintf(f, "from %s ", rtnl_rtrealm_n2a(rta_getattr_u32(tb[TCA_ROUTE4_FROM]), b1, sizeof(b1))); if (tb[TCA_ROUTE4_IIF]) - fprintf(f, "fromif %s", ll_index_to_name(*(int*)RTA_DATA(tb[TCA_ROUTE4_IIF]))); + fprintf(f, "fromif %s", ll_index_to_name(*(int *)RTA_DATA(tb[TCA_ROUTE4_IIF]))); if (tb[TCA_ROUTE4_POLICE]) tc_print_police(f, tb[TCA_ROUTE4_POLICE]); if (tb[TCA_ROUTE4_ACT]) diff -Nru iproute2-4.3.0/tc/f_rsvp.c iproute2-4.9.0/tc/f_rsvp.c --- iproute2-4.3.0/tc/f_rsvp.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_rsvp.c 2016-12-12 23:07:42.000000000 +0000 @@ -37,7 +37,7 @@ fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n"); } -static int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix * addr, +static int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix *addr, struct tc_rsvp_pinfo *pinfo, int dir, int family) { int argc = *argc_p; @@ -76,6 +76,7 @@ if (strcmp(*argv, "spi/ah") == 0 || strcmp(*argv, "gpi/ah") == 0) { __u32 gpi; + NEXT_ARG(); if (get_u32(&gpi, *argv, 0)) return -1; @@ -88,6 +89,7 @@ } else if (strcmp(*argv, "spi/esp") == 0 || strcmp(*argv, "gpi/esp") == 0) { __u32 gpi; + NEXT_ARG(); if (get_u32(&gpi, *argv, 0)) return -1; @@ -99,6 +101,7 @@ argc--; argv++; } else if (strcmp(*argv, "flowlabel") == 0) { __u32 flabel; + NEXT_ARG(); if (get_u32(&flabel, *argv, 0)) return -1; @@ -114,6 +117,7 @@ int sz = 1; __u32 tmp; __u32 mask = 0xff; + if (strcmp(*argv, "u32") == 0) { sz = 4; mask = 0xffff; @@ -169,15 +173,11 @@ static int rsvp_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; - struct tc_rsvp_pinfo pinfo; - struct tc_police tp; + struct tc_rsvp_pinfo pinfo = {}; struct tcmsg *t = NLMSG_DATA(n); int pinfo_ok = 0; struct rtattr *tail; - memset(&pinfo, 0, sizeof(pinfo)); - memset(&tp, 0, sizeof(tp)); - if (handle) { if (get_u32(&t->tcm_handle, handle, 0)) { fprintf(stderr, "Illegal \"handle\"\n"); @@ -194,6 +194,7 @@ while (argc > 0) { if (matches(*argv, "session") == 0) { inet_prefix addr; + NEXT_ARG(); if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 1, family)) { fprintf(stderr, "Illegal \"session\"\n"); @@ -206,6 +207,7 @@ } else if (matches(*argv, "sender") == 0 || matches(*argv, "flowspec") == 0) { inet_prefix addr; + NEXT_ARG(); if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 0, family)) { fprintf(stderr, "Illegal \"sender\"\n"); @@ -217,6 +219,7 @@ continue; } else if (matches("ipproto", *argv) == 0) { int num; + NEXT_ARG(); num = inet_proto_a2n(*argv); if (num < 0) { @@ -227,7 +230,8 @@ pinfo_ok++; } else if (matches(*argv, "classid") == 0 || strcmp(*argv, "flowid") == 0) { - unsigned handle; + unsigned int handle; + NEXT_ARG(); if (get_tc_classid(&handle, *argv)) { fprintf(stderr, "Illegal \"classid\"\n"); @@ -235,7 +239,8 @@ } addattr_l(n, 4096, TCA_RSVP_CLASSID, &handle, 4); } else if (strcmp(*argv, "tunnelid") == 0) { - unsigned tid; + unsigned int tid; + NEXT_ARG(); if (get_unsigned(&tid, *argv, 0)) { fprintf(stderr, "Illegal \"tunnelid\"\n"); @@ -244,7 +249,8 @@ pinfo.tunnelid = tid; pinfo_ok++; } else if (strcmp(*argv, "tunnel") == 0) { - unsigned tid; + unsigned int tid; + NEXT_ARG(); if (get_unsigned(&tid, *argv, 0)) { fprintf(stderr, "Illegal \"tunnel\"\n"); @@ -292,7 +298,7 @@ return 0; } -static char * sprint_spi(struct tc_rsvp_gpi *pi, int dir, char *buf) +static char *sprint_spi(struct tc_rsvp_gpi *pi, int dir, char *buf) { if (pi->offset == 0) { if (dir && pi->mask == htonl(0xFFFF)) { @@ -351,6 +357,7 @@ if (tb[TCA_RSVP_DST]) { char buf[128]; + fprintf(f, "session "); if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_DST]), buf, sizeof(buf)) == 0) fprintf(f, " [INVALID DADDR] "); @@ -377,6 +384,7 @@ fprintf(f, "tunnelid %d ", pinfo->tunnelid); if (tb[TCA_RSVP_SRC]) { char buf[128]; + fprintf(f, "sender "); if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_SRC]), buf, sizeof(buf)) == 0) { fprintf(f, "[BAD]"); diff -Nru iproute2-4.3.0/tc/f_tcindex.c iproute2-4.9.0/tc/f_tcindex.c --- iproute2-4.3.0/tc/f_tcindex.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_tcindex.c 2016-12-12 23:07:42.000000000 +0000 @@ -17,22 +17,20 @@ static void explain(void) { - fprintf(stderr," Usage: ... tcindex [ hash SIZE ] [ mask MASK ]" - " [ shift SHIFT ]\n"); - fprintf(stderr," [ pass_on | fall_through ]\n"); - fprintf(stderr," [ classid CLASSID ] " - "[ action ACTION_SPEC ]\n"); + fprintf(stderr," Usage: ... tcindex [ hash SIZE ] [ mask MASK ] [ shift SHIFT ]\n"); + fprintf(stderr, " [ pass_on | fall_through ]\n"); + fprintf(stderr," [ classid CLASSID ] [ action ACTION_SPEC ]\n"); } static int tcindex_parse_opt(struct filter_util *qu, char *handle, int argc, - char **argv, struct nlmsghdr *n) + char **argv, struct nlmsghdr *n) { struct tcmsg *t = NLMSG_DATA(n); struct rtattr *tail; char *end; if (handle) { - t->tcm_handle = strtoul(handle,&end,0); + t->tcm_handle = strtoul(handle, &end, 0); if (*end) { fprintf(stderr, "Illegal filter ID\n"); return -1; @@ -40,81 +38,75 @@ } if (!argc) return 0; tail = NLMSG_TAIL(n); - addattr_l(n,4096,TCA_OPTIONS,NULL,0); + addattr_l(n, 4096, TCA_OPTIONS, NULL, 0); while (argc) { - if (!strcmp(*argv,"hash")) { + if (!strcmp(*argv, "hash")) { int hash; NEXT_ARG(); - hash = strtoul(*argv,&end,0); + hash = strtoul(*argv, &end, 0); if (*end || !hash || hash > 0x10000) { explain(); return -1; } - addattr_l(n,4096,TCA_TCINDEX_HASH,&hash,sizeof(hash)); - } - else if (!strcmp(*argv,"mask")) { + addattr_l(n, 4096, TCA_TCINDEX_HASH, &hash, + sizeof(hash)); + } else if (!strcmp(*argv,"mask")) { __u16 mask; NEXT_ARG(); - mask = strtoul(*argv,&end,0); + mask = strtoul(*argv, &end, 0); if (*end) { explain(); return -1; } - addattr_l(n,4096,TCA_TCINDEX_MASK,&mask,sizeof(mask)); - } - else if (!strcmp(*argv,"shift")) { + addattr_l(n, 4096, TCA_TCINDEX_MASK, &mask, + sizeof(mask)); + } else if (!strcmp(*argv,"shift")) { int shift; NEXT_ARG(); - shift = strtoul(*argv,&end,0); + shift = strtoul(*argv, &end, 0); if (*end) { explain(); return -1; } - addattr_l(n,4096,TCA_TCINDEX_SHIFT,&shift, + addattr_l(n, 4096, TCA_TCINDEX_SHIFT, &shift, sizeof(shift)); - } - else if (!strcmp(*argv,"fall_through")) { + } else if (!strcmp(*argv,"fall_through")) { int value = 1; - addattr_l(n,4096,TCA_TCINDEX_FALL_THROUGH,&value, + addattr_l(n, 4096, TCA_TCINDEX_FALL_THROUGH, &value, sizeof(value)); - } - else if (!strcmp(*argv,"pass_on")) { + } else if (!strcmp(*argv,"pass_on")) { int value = 0; - addattr_l(n,4096,TCA_TCINDEX_FALL_THROUGH,&value, + addattr_l(n, 4096, TCA_TCINDEX_FALL_THROUGH, &value, sizeof(value)); - } - else if (!strcmp(*argv,"classid")) { + } else if (!strcmp(*argv,"classid")) { __u32 handle; NEXT_ARG(); - if (get_tc_classid(&handle,*argv)) { + if (get_tc_classid(&handle, *argv)) { fprintf(stderr, "Illegal \"classid\"\n"); return -1; } addattr_l(n, 4096, TCA_TCINDEX_CLASSID, &handle, 4); - } - else if (!strcmp(*argv,"police")) { + } else if (!strcmp(*argv,"police")) { NEXT_ARG(); if (parse_police(&argc, &argv, TCA_TCINDEX_POLICE, n)) { fprintf(stderr, "Illegal \"police\"\n"); return -1; } continue; - } - else if (!strcmp(*argv,"action")) { + } else if (!strcmp(*argv,"action")) { NEXT_ARG(); - if (parse_police(&argc, &argv, TCA_TCINDEX_ACT, n)) { + if (parse_action(&argc, &argv, TCA_TCINDEX_ACT, n)) { fprintf(stderr, "Illegal \"action\"\n"); return -1; } continue; - } - else { + } else { explain(); return -1; } @@ -127,7 +119,7 @@ static int tcindex_print_opt(struct filter_util *qu, FILE *f, - struct rtattr *opt, __u32 handle) + struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_TCINDEX_MAX+1]; @@ -136,14 +128,14 @@ parse_rtattr_nested(tb, TCA_TCINDEX_MAX, opt); - if (handle != ~0) fprintf(f,"handle 0x%04x ",handle); + if (handle != ~0) fprintf(f, "handle 0x%04x ", handle); if (tb[TCA_TCINDEX_HASH]) { __u16 hash; if (RTA_PAYLOAD(tb[TCA_TCINDEX_HASH]) < sizeof(hash)) return -1; hash = rta_getattr_u16(tb[TCA_TCINDEX_HASH]); - fprintf(f,"hash %d ",hash); + fprintf(f, "hash %d ", hash); } if (tb[TCA_TCINDEX_MASK]) { __u16 mask; @@ -151,7 +143,7 @@ if (RTA_PAYLOAD(tb[TCA_TCINDEX_MASK]) < sizeof(mask)) return -1; mask = rta_getattr_u16(tb[TCA_TCINDEX_MASK]); - fprintf(f,"mask 0x%04x ",mask); + fprintf(f, "mask 0x%04x ", mask); } if (tb[TCA_TCINDEX_SHIFT]) { int shift; @@ -159,7 +151,7 @@ if (RTA_PAYLOAD(tb[TCA_TCINDEX_SHIFT]) < sizeof(shift)) return -1; shift = *(int *) RTA_DATA(tb[TCA_TCINDEX_SHIFT]); - fprintf(f,"shift %d ",shift); + fprintf(f, "shift %d ", shift); } if (tb[TCA_TCINDEX_FALL_THROUGH]) { int fall_through; @@ -168,11 +160,11 @@ sizeof(fall_through)) return -1; fall_through = *(int *) RTA_DATA(tb[TCA_TCINDEX_FALL_THROUGH]); - fprintf(f,fall_through ? "fall_through " : "pass_on "); + fprintf(f, fall_through ? "fall_through " : "pass_on "); } if (tb[TCA_TCINDEX_CLASSID]) { SPRINT_BUF(b1); - fprintf(f, "classid %s ",sprint_tc_classid(*(__u32 *) + fprintf(f, "classid %s ", sprint_tc_classid(*(__u32 *) RTA_DATA(tb[TCA_TCINDEX_CLASSID]), b1)); } if (tb[TCA_TCINDEX_POLICE]) { @@ -181,7 +173,7 @@ } if (tb[TCA_TCINDEX_ACT]) { fprintf(f, "\n"); - tc_print_police(f, tb[TCA_TCINDEX_ACT]); + tc_print_action(f, tb[TCA_TCINDEX_ACT]); } return 0; } diff -Nru iproute2-4.3.0/tc/f_u32.c iproute2-4.9.0/tc/f_u32.c --- iproute2-4.3.0/tc/f_u32.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/f_u32.c 2016-12-12 23:07:42.000000000 +0000 @@ -30,24 +30,23 @@ static void explain(void) { - fprintf(stderr, "Usage: ... u32 [ match SELECTOR ... ] [ link HTID ]" - " [ classid CLASSID ]\n"); - fprintf(stderr, " [ action ACTION_SPEC ]" - " [ offset OFFSET_SPEC ]\n"); - fprintf(stderr, " [ ht HTID ] [ hashkey HASHKEY_SPEC ]\n"); - fprintf(stderr, " [ sample SAMPLE ]\n"); - fprintf(stderr, "or u32 divisor DIVISOR\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Where: SELECTOR := SAMPLE SAMPLE ...\n"); - fprintf(stderr, " SAMPLE := { ip | ip6 | udp | tcp | icmp |" - " u{32|16|8} | mark } SAMPLE_ARGS [divisor DIVISOR]\n"); - fprintf(stderr, " FILTERID := X:Y:Z\n"); - fprintf(stderr, "\nNOTE: CLASSID is parsed at hexadecimal input.\n"); + fprintf(stderr, + "Usage: ... u32 [ match SELECTOR ... ] [ link HTID ] [ classid CLASSID ]\n" + " [ action ACTION_SPEC ] [ offset OFFSET_SPEC ]\n" + " [ ht HTID ] [ hashkey HASHKEY_SPEC ]\n" + " [ sample SAMPLE ] [skip-hw | skip-sw]\n" + "or u32 divisor DIVISOR\n" + "\n" + "Where: SELECTOR := SAMPLE SAMPLE ...\n" + " SAMPLE := { ip | ip6 | udp | tcp | icmp | u{32|16|8} | mark }\n" + " SAMPLE_ARGS [ divisor DIVISOR ]\n" + " FILTERID := X:Y:Z\n" + "\nNOTE: CLASSID is parsed at hexadecimal input.\n"); } static int get_u32_handle(__u32 *handle, const char *str) { - __u32 htid=0, hash=0, nodeid=0; + __u32 htid = 0, hash = 0, nodeid = 0; char *tmp = strchr(str, ':'); if (tmp == NULL) { @@ -58,21 +57,21 @@ htid = strtoul(str, &tmp, 16); if (tmp == str && *str != ':' && *str != 0) return -1; - if (htid>=0x1000) + if (htid >= 0x1000) return -1; if (*tmp) { str = tmp + 1; hash = strtoul(str, &tmp, 16); if (tmp == str && *str != ':' && *str != 0) return -1; - if (hash>=0x100) + if (hash >= 0x100) return -1; if (*tmp) { str = tmp + 1; nodeid = strtoul(str, &tmp, 16); if (tmp == str && *str != 0) return -1; - if (nodeid>=0x1000) + if (nodeid >= 0x1000) return -1; } } @@ -80,7 +79,7 @@ return 0; } -static char * sprint_u32_handle(__u32 handle, char *buf) +static char *sprint_u32_handle(__u32 handle, char *buf) { int bsize = SPRINT_BSIZE-1; __u32 htid = TC_U32_HTID(handle); @@ -94,17 +93,20 @@ } if (htid) { int l = snprintf(b, bsize, "%x:", htid>>20); + bsize -= l; b += l; } if (nodeid|hash) { if (hash) { int l = snprintf(b, bsize, "%x", hash); + bsize -= l; b += l; } if (nodeid) { int l = snprintf(b, bsize, ":%x", nodeid); + bsize -= l; b += l; } @@ -122,7 +124,7 @@ key &= mask; - for (i=0; ikeys[i].off == off && sel->keys[i].offmask == offmask) { __u32 intersect = mask & sel->keys[i].mask; @@ -171,7 +173,8 @@ return pack_key(sel, key, mask, off, offmask); } -static int pack_key8(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask) +static int pack_key8(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, + int offmask) { if (key > 0xFF || mask > 0xFF) return -1; @@ -382,7 +385,7 @@ plen = addr.bitlen; for (i = 0; i < plen; i += 32) { -// if (((i + 31) & ~0x1F) <= plen) { + /* if (((i + 31) & ~0x1F) <= plen) { */ if (i + 31 <= plen) { res = pack_key(sel, addr.data[i / 32], 0xFFFFFFFF, off + 4 * (i / 32), offmask); @@ -390,6 +393,7 @@ return -1; } else if (i < plen) { __u32 mask = htonl(0xFFFFFFFF << (32 - (plen - i))); + res = pack_key(sel, addr.data[i / 32], mask, off + 4 * (i / 32), offmask); if (res < 0) @@ -712,7 +716,7 @@ } else if (matches(*argv, "ip") == 0) { NEXT_ARG(); res = parse_ip(&argc, &argv, sel); - } else if (matches(*argv, "ip6") == 0) { + } else if (matches(*argv, "ip6") == 0) { NEXT_ARG(); res = parse_ip6(&argc, &argv, sel); } else if (matches(*argv, "udp") == 0) { @@ -746,6 +750,7 @@ while (argc > 0) { if (matches(*argv, "plus") == 0) { int off; + NEXT_ARG(); if (get_integer(&off, *argv, 0)) return -1; @@ -753,6 +758,7 @@ sel->flags |= TC_U32_OFFSET; } else if (matches(*argv, "at") == 0) { int off; + NEXT_ARG(); if (get_integer(&off, *argv, 0)) return -1; @@ -763,14 +769,13 @@ } sel->flags |= TC_U32_VAROFFSET; } else if (matches(*argv, "mask") == 0) { - __u16 mask; NEXT_ARG(); - if (get_u16(&mask, *argv, 16)) + if (get_be16(&sel->offmask, *argv, 16)) return -1; - sel->offmask = htons(mask); sel->flags |= TC_U32_VAROFFSET; } else if (matches(*argv, "shift") == 0) { int shift; + NEXT_ARG(); if (get_integer(&shift, *argv, 0)) return -1; @@ -796,13 +801,12 @@ while (argc > 0) { if (matches(*argv, "mask") == 0) { - __u32 mask; NEXT_ARG(); - if (get_u32(&mask, *argv, 16)) + if (get_be32(&sel->hmask, *argv, 16)) return -1; - sel->hmask = htonl(mask); } else if (matches(*argv, "at") == 0) { int num; + NEXT_ARG(); if (get_integer(&num, *argv, 0)) return -1; @@ -828,22 +832,26 @@ case 0: switch (ntohl(key->mask)) { case 0x0f000000: - fprintf(f, "\n match IP ihl %u", ntohl(key->val) >> 24); + fprintf(f, "\n match IP ihl %u", + ntohl(key->val) >> 24); return; case 0x00ff0000: - fprintf(f, "\n match IP dsfield %#x", ntohl(key->val) >> 16); + fprintf(f, "\n match IP dsfield %#x", + ntohl(key->val) >> 16); return; } break; case 8: if (ntohl(key->mask) == 0x00ff0000) { - fprintf(f, "\n match IP protocol %d", ntohl(key->val) >> 16); + fprintf(f, "\n match IP protocol %d", + ntohl(key->val) >> 16); return; } break; case 12: case 16: { int bits = mask2bits(key->mask); + if (bits >= 0) { fprintf(f, "\n %s %s/%d", key->off == 12 ? "match IP src" : "match IP dst", @@ -884,22 +892,26 @@ case 0: switch (ntohl(key->mask)) { case 0x0f000000: - fprintf(f, "\n match IP ihl %u", ntohl(key->val) >> 24); + fprintf(f, "\n match IP ihl %u", + ntohl(key->val) >> 24); return; case 0x00ff0000: - fprintf(f, "\n match IP dsfield %#x", ntohl(key->val) >> 16); + fprintf(f, "\n match IP dsfield %#x", + ntohl(key->val) >> 16); return; } break; case 8: if (ntohl(key->mask) == 0x00ff0000) { - fprintf(f, "\n match IP protocol %d", ntohl(key->val) >> 16); + fprintf(f, "\n match IP protocol %d", + ntohl(key->val) >> 16); return; } break; case 12: case 16: { int bits = mask2bits(key->mask); + if (bits >= 0) { fprintf(f, "\n %s %s/%d", key->off == 12 ? "match IP src" : "match IP dst", @@ -946,7 +958,7 @@ __u16 pad; void (*pprinter)(FILE *f, const struct tc_u32_key *key); } u32_pprinters[] = { - {0, 0, print_raw}, + {0, 0, print_raw}, {ETH_P_IP, 0, print_ipv4}, {ETH_P_IPV6, 0, print_ipv6}, }; @@ -958,7 +970,7 @@ if (!show_pretty) goto show_k; - for (i = 0; i < sizeof(u32_pprinters) / sizeof(u32_pprinters[0]); i++) { + for (i = 0; i < ARRAY_SIZE(u32_pprinters); i++) { if (u32_pprinters[i].proto == ntohs(f_proto)) { show_k: u32_pprinters[i].pprinter(f, key); @@ -976,15 +988,14 @@ struct { struct tc_u32_sel sel; struct tc_u32_key keys[128]; - } sel; + } sel = {}; struct tcmsg *t = NLMSG_DATA(n); struct rtattr *tail; int sel_ok = 0, terminal_ok = 0; int sample_ok = 0; __u32 htid = 0; __u32 order = 0; - - memset(&sel, 0, sizeof(sel)); + __u32 flags = 0; if (handle && get_u32_handle(&t->tcm_handle, handle)) { fprintf(stderr, "Illegal filter ID\n"); @@ -1022,16 +1033,18 @@ continue; } else if (matches(*argv, "classid") == 0 || strcmp(*argv, "flowid") == 0) { - unsigned handle; + unsigned int flowid; + NEXT_ARG(); - if (get_tc_classid(&handle, *argv)) { + if (get_tc_classid(&flowid, *argv)) { fprintf(stderr, "Illegal \"classid\"\n"); return -1; } - addattr_l(n, MAX_MSG, TCA_U32_CLASSID, &handle, 4); + addattr_l(n, MAX_MSG, TCA_U32_CLASSID, &flowid, 4); sel.sel.flags |= TC_U32_TERMINAL; } else if (matches(*argv, "divisor") == 0) { - unsigned divisor; + unsigned int divisor; + NEXT_ARG(); if (get_unsigned(&divisor, *argv, 0) || divisor == 0 || @@ -1047,55 +1060,56 @@ return -1; } } else if (strcmp(*argv, "link") == 0) { - unsigned handle; + unsigned int linkid; + NEXT_ARG(); - if (get_u32_handle(&handle, *argv)) { + if (get_u32_handle(&linkid, *argv)) { fprintf(stderr, "Illegal \"link\"\n"); return -1; } - if (handle && TC_U32_NODE(handle)) { + if (linkid && TC_U32_NODE(linkid)) { fprintf(stderr, "\"link\" must be a hash table.\n"); return -1; } - addattr_l(n, MAX_MSG, TCA_U32_LINK, &handle, 4); + addattr_l(n, MAX_MSG, TCA_U32_LINK, &linkid, 4); } else if (strcmp(*argv, "ht") == 0) { - unsigned handle; + unsigned int ht; + NEXT_ARG(); - if (get_u32_handle(&handle, *argv)) { + if (get_u32_handle(&ht, *argv)) { fprintf(stderr, "Illegal \"ht\"\n"); return -1; } - if (handle && TC_U32_NODE(handle)) { + if (handle && TC_U32_NODE(ht)) { fprintf(stderr, "\"ht\" must be a hash table.\n"); return -1; } if (sample_ok) - htid = (htid & 0xFF000) | (handle & 0xFFF00000); + htid = (htid & 0xFF000) | (ht & 0xFFF00000); else - htid = (handle & 0xFFFFF000); + htid = (ht & 0xFFFFF000); } else if (strcmp(*argv, "sample") == 0) { __u32 hash; - unsigned divisor = 0x100; - + unsigned int divisor = 0x100; struct { struct tc_u32_sel sel; struct tc_u32_key keys[4]; - } sel2; - memset(&sel2, 0, sizeof(sel2)); + } sel2 = {}; + NEXT_ARG(); if (parse_selector(&argc, &argv, &sel2.sel, n)) { fprintf(stderr, "Illegal \"sample\"\n"); return -1; } if (sel2.sel.nkeys != 1) { - fprintf(stderr, "\"sample\" must contain" - " exactly ONE key.\n"); + fprintf(stderr, "\"sample\" must contain exactly ONE key.\n"); return -1; } if (*argv != 0 && strcmp(*argv, "divisor") == 0) { NEXT_ARG(); - if (get_unsigned(&divisor, *argv, 0) || divisor == 0 || - divisor > 0x100 || ((divisor - 1) & divisor)) { + if (get_unsigned(&divisor, *argv, 0) || + divisor == 0 || divisor > 0x100 || + ((divisor - 1) & divisor)) { fprintf(stderr, "Illegal sample \"divisor\"\n"); return -1; } @@ -1108,16 +1122,17 @@ sample_ok = 1; continue; } else if (strcmp(*argv, "indev") == 0) { - char ind[IFNAMSIZ + 1]; - memset(ind, 0, sizeof (ind)); + char ind[IFNAMSIZ + 1] = {}; + argc--; argv++; if (argc < 1) { fprintf(stderr, "Illegal indev\n"); return -1; } - strncpy(ind, *argv, sizeof (ind) - 1); - addattr_l(n, MAX_MSG, TCA_U32_INDEV, ind, strlen(ind) + 1); + strncpy(ind, *argv, sizeof(ind) - 1); + addattr_l(n, MAX_MSG, TCA_U32_INDEV, ind, + strlen(ind) + 1); } else if (matches(*argv, "action") == 0) { NEXT_ARG(); @@ -1136,6 +1151,14 @@ } terminal_ok++; continue; + } else if (strcmp(*argv, "skip_hw") == 0) { + NEXT_ARG(); + flags |= TCA_CLS_FLAGS_SKIP_HW; + continue; + } else if (strcmp(*argv, "skip_sw") == 0) { + NEXT_ARG(); + flags |= TCA_CLS_FLAGS_SKIP_SW; + continue; } else if (strcmp(*argv, "help") == 0) { explain(); return -1; @@ -1152,7 +1175,8 @@ sel.sel.flags |= TC_U32_TERMINAL; if (order) { - if (TC_U32_NODE(t->tcm_handle) && order != TC_U32_NODE(t->tcm_handle)) { + if (TC_U32_NODE(t->tcm_handle) && + order != TC_U32_NODE(t->tcm_handle)) { fprintf(stderr, "\"order\" contradicts \"handle\"\n"); return -1; } @@ -1163,7 +1187,18 @@ addattr_l(n, MAX_MSG, TCA_U32_HASH, &htid, 4); if (sel_ok) addattr_l(n, MAX_MSG, TCA_U32_SEL, &sel, - sizeof(sel.sel) + sel.sel.nkeys * sizeof(struct tc_u32_key)); + sizeof(sel.sel) + + sel.sel.nkeys * sizeof(struct tc_u32_key)); + if (flags) { + if (!(flags ^ (TCA_CLS_FLAGS_SKIP_HW | + TCA_CLS_FLAGS_SKIP_SW))) { + fprintf(stderr, + "skip_hw and skip_sw are mutually exclusive\n"); + return -1; + } + addattr_l(n, MAX_MSG, TCA_U32_FLAGS, &flags, 4); + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; return 0; } @@ -1184,9 +1219,9 @@ SPRINT_BUF(b1); fprintf(f, "fh %s ", sprint_u32_handle(handle, b1)); } - if (TC_U32_NODE(handle)) { + + if (TC_U32_NODE(handle)) fprintf(f, "order %d ", TC_U32_NODE(handle)); - } if (tb[TCA_U32_SEL]) { if (RTA_PAYLOAD(tb[TCA_U32_SEL]) < sizeof(*sel)) @@ -1196,9 +1231,11 @@ } if (tb[TCA_U32_DIVISOR]) { - fprintf(f, "ht divisor %d ", rta_getattr_u32(tb[TCA_U32_DIVISOR])); + fprintf(f, "ht divisor %d ", + rta_getattr_u32(tb[TCA_U32_DIVISOR])); } else if (tb[TCA_U32_HASH]) { __u32 htid = rta_getattr_u32(tb[TCA_U32_HASH]); + fprintf(f, "key ht %x bkt %x ", TC_U32_USERHTID(htid), TC_U32_HASH(htid)); } else { @@ -1208,19 +1245,30 @@ SPRINT_BUF(b1); fprintf(f, "%sflowid %s ", !sel || !(sel->flags & TC_U32_TERMINAL) ? "*" : "", - sprint_tc_classid(rta_getattr_u32(tb[TCA_U32_CLASSID]), b1)); + sprint_tc_classid(rta_getattr_u32(tb[TCA_U32_CLASSID]), + b1)); } else if (sel && sel->flags & TC_U32_TERMINAL) { fprintf(f, "terminal flowid ??? "); } if (tb[TCA_U32_LINK]) { SPRINT_BUF(b1); fprintf(f, "link %s ", - sprint_u32_handle(rta_getattr_u32(tb[TCA_U32_LINK]), b1)); + sprint_u32_handle(rta_getattr_u32(tb[TCA_U32_LINK]), + b1)); + } + + if (tb[TCA_U32_FLAGS]) { + __u32 flags = rta_getattr_u32(tb[TCA_U32_FLAGS]); + + if (flags & TCA_CLS_FLAGS_SKIP_HW) + fprintf(f, "skip_hw "); + if (flags & TCA_CLS_FLAGS_SKIP_SW) + fprintf(f, "skip_sw "); } if (tb[TCA_U32_PCNT]) { if (RTA_PAYLOAD(tb[TCA_U32_PCNT]) < sizeof(*pf)) { - fprintf(f, "Broken perf counters \n"); + fprintf(f, "Broken perf counters\n"); return -1; } pf = RTA_DATA(tb[TCA_U32_PCNT]); @@ -1233,6 +1281,7 @@ if (tb[TCA_U32_MARK]) { struct tc_u32_mark *mark = RTA_DATA(tb[TCA_U32_MARK]); + if (RTA_PAYLOAD(tb[TCA_U32_MARK]) < sizeof(*mark)) { fprintf(f, "\n Invalid mark (kernel&iproute2 mismatch)\n"); } else { @@ -1244,7 +1293,8 @@ if (sel) { if (sel->nkeys) { int i; - for (i=0; inkeys; i++) { + + for (i = 0; i < sel->nkeys; i++) { show_keys(f, sel->keys + i); if (show_stats && NULL != pf) fprintf(f, " (success %llu ) ", @@ -1274,13 +1324,15 @@ fprintf(f, "\n"); tc_print_police(f, tb[TCA_U32_POLICE]); } + if (tb[TCA_U32_INDEV]) { struct rtattr *idev = tb[TCA_U32_INDEV]; + fprintf(f, "\n input dev %s\n", rta_getattr_str(idev)); } - if (tb[TCA_U32_ACT]) { + + if (tb[TCA_U32_ACT]) tc_print_action(f, tb[TCA_U32_ACT]); - } return 0; } diff -Nru iproute2-4.3.0/tc/m_action.c iproute2-4.9.0/tc/m_action.c --- iproute2-4.3.0/tc/m_action.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_action.c 2016-12-12 23:07:42.000000000 +0000 @@ -28,11 +28,11 @@ #include "tc_common.h" #include "tc_util.h" -static struct action_util * action_list; +static struct action_util *action_list; #ifdef CONFIG_GACT -int gact_ld = 0 ; //fuckin backward compatibility +int gact_ld; /* f*ckin backward compatibility */ #endif -int tab_flush = 0; +int tab_flush; static void act_usage(void) { @@ -43,10 +43,10 @@ * does that, they would know how to fix this .. * */ - fprintf (stderr, "usage: tc actions *\n"); + fprintf(stderr, "usage: tc actions *\n"); fprintf(stderr, "Where: \tACTSPECOP := ACR | GD | FL\n" - "\tACR := add | change | replace * \n" + "\tACR := add | change | replace *\n" "\tGD := get | delete | *\n" "\tFL := ls | list | flush | \n" "\tACTNAMESPEC := action \n" @@ -65,7 +65,7 @@ { if (opt && RTA_PAYLOAD(opt)) fprintf(f, "[Unknown action, optlen=%u] ", - (unsigned) RTA_PAYLOAD(opt)); + (unsigned int) RTA_PAYLOAD(opt)); return 0; } @@ -122,13 +122,12 @@ #ifdef CONFIG_GACT if (!looked4gact) { looked4gact = 1; - strcpy(str,"gact"); + strcpy(str, "gact"); goto restart_s; } #endif - a = malloc(sizeof(*a)); + a = calloc(1, sizeof(*a)); if (a) { - memset(a, 0, sizeof(*a)); strncpy(a->id, "noact", 15); a->parse_aopt = parse_noaopt; a->print_aopt = print_noaopt; @@ -141,9 +140,9 @@ new_cmd(char **argv) { if ((matches(*argv, "change") == 0) || - (matches(*argv, "replace") == 0)|| - (matches(*argv, "delete") == 0)|| - (matches(*argv, "get") == 0)|| + (matches(*argv, "replace") == 0) || + (matches(*argv, "delete") == 0) || + (matches(*argv, "get") == 0) || (matches(*argv, "add") == 0)) return 1; @@ -173,9 +172,9 @@ while (argc > 0) { - memset(k, 0, sizeof (k)); + memset(k, 0, sizeof(k)); - if (strcmp(*argv, "action") == 0 ) { + if (strcmp(*argv, "action") == 0) { argc--; argv++; eap = 1; @@ -195,9 +194,10 @@ goto done0; } else { struct action_util *a = NULL; - strncpy(k, *argv, sizeof (k) - 1); + + strncpy(k, *argv, sizeof(k) - 1); eap = 0; - if (argc > 0 ) { + if (argc > 0) { a = get_action_kind(k); } else { done0: @@ -207,7 +207,7 @@ goto done; } - if (NULL == a) { + if (a == NULL) { goto bad_val; } @@ -215,10 +215,10 @@ addattr_l(n, MAX_MSG, ++prio, NULL, 0); addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); - ret = a->parse_aopt(a,&argc, &argv, TCA_ACT_OPTIONS, n); + ret = a->parse_aopt(a, &argc, &argv, TCA_ACT_OPTIONS, n); if (ret < 0) { - fprintf(stderr,"bad action parsing\n"); + fprintf(stderr, "bad action parsing\n"); goto bad_val; } tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; @@ -228,7 +228,7 @@ } if (eap > 0) { - fprintf(stderr,"bad action empty %d\n",eap); + fprintf(stderr, "bad action empty %d\n", eap); goto bad_val; } @@ -241,12 +241,12 @@ bad_val: /* no need to undo things, returning from here should * cause enough pain */ - fprintf(stderr, "parse_action: bad value (%d:%s)!\n",argc,*argv); + fprintf(stderr, "parse_action: bad value (%d:%s)!\n", argc, *argv); return -1; } static int -tc_print_one_action(FILE * f, struct rtattr *arg) +tc_print_one_action(FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_ACT_MAX + 1]; @@ -265,12 +265,12 @@ a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND])); - if (NULL == a) + if (a == NULL) return err; err = a->print_aopt(a, f, tb[TCA_ACT_OPTIONS]); - if (0 > err) + if (err < 0) return err; if (show_stats && tb[TCA_ACT_STATS]) { @@ -299,11 +299,11 @@ } a = get_action_kind(RTA_DATA(tb[TCA_KIND])); - if (NULL == a) + if (a == NULL) return err; delete_count = RTA_DATA(tb[TCA_FCNT]); - fprintf(f," %s (%d entries)\n", a->id, *delete_count); + fprintf(f, " %s (%d entries)\n", a->id, *delete_count); tab_flush = 0; return 0; } @@ -326,7 +326,7 @@ for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { if (tb[i]) { fprintf(f, "\n\taction order %d: ", i); - if (0 > tc_print_one_action(f, tb[i])) { + if (tc_print_one_action(f, tb[i]) < 0) { fprintf(f, "Error printing action\n"); } } @@ -340,10 +340,10 @@ struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct tcamsg *t = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[TCAA_MAX+1]; + struct rtattr *tb[TCAA_MAX+1]; len -= NLMSG_LENGTH(sizeof(*t)); @@ -354,7 +354,7 @@ parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len); - if (NULL == tb[TCA_ACT_TAB]) { + if (tb[TCA_ACT_TAB] == NULL) { if (n->nlmsg_type != RTM_GETACTION) fprintf(stderr, "print_action: NULL kind\n"); return -1; @@ -376,7 +376,7 @@ return 0; } -static int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p) +static int tc_action_gd(int cmd, unsigned int flags, int *argc_p, char ***argv_p) { char k[16]; struct action_util *a = NULL; @@ -385,7 +385,6 @@ int prio = 0; int ret = 0; __u32 i; - struct sockaddr_nl nladdr; struct rtattr *tail; struct rtattr *tail2; struct nlmsghdr *ans = NULL; @@ -394,27 +393,22 @@ struct nlmsghdr n; struct tcamsg t; char buf[MAX_MSG]; - } req; - - req.t.tca_family = AF_UNSPEC; - - memset(&req, 0, sizeof(req)); - - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .t.tca_family = AF_UNSPEC, + }; - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - argc -=1; - argv +=1; + argc -= 1; + argv += 1; tail = NLMSG_TAIL(&req.n); addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); while (argc > 0) { - if (strcmp(*argv, "action") == 0 ) { + if (strcmp(*argv, "action") == 0) { argc--; argv++; continue; @@ -422,23 +416,23 @@ return -1; } - strncpy(k, *argv, sizeof (k) - 1); + strncpy(k, *argv, sizeof(k) - 1); a = get_action_kind(k); - if (NULL == a) { - fprintf(stderr, "Error: non existent action: %s\n",k); + if (a == NULL) { + fprintf(stderr, "Error: non existent action: %s\n", k); ret = -1; goto bad_val; } if (strcmp(a->id, k) != 0) { - fprintf(stderr, "Error: non existent action: %s\n",k); + fprintf(stderr, "Error: non existent action: %s\n", k); ret = -1; goto bad_val; } - argc -=1; - argv +=1; + argc -= 1; + argv += 1; if (argc <= 0) { - fprintf(stderr, "Error: no index specified action: %s\n",k); + fprintf(stderr, "Error: no index specified action: %s\n", k); ret = -1; goto bad_val; } @@ -450,10 +444,10 @@ ret = -1; goto bad_val; } - argc -=1; - argv +=1; + argc -= 1; + argv += 1; } else { - fprintf(stderr, "Error: no index specified action: %s\n",k); + fprintf(stderr, "Error: no index specified action: %s\n", k); ret = -1; goto bad_val; } @@ -477,7 +471,7 @@ return 1; } - if (ans && print_action(NULL, &req.n, (void*)stdout) < 0) { + if (ans && print_action(NULL, &req.n, (void *)stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return 1; } @@ -488,29 +482,25 @@ return ret; } -static int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p) +static int tc_action_modify(int cmd, unsigned int flags, int *argc_p, char ***argv_p) { int argc = *argc_p; char **argv = *argv_p; int ret = 0; - - struct rtattr *tail; struct { struct nlmsghdr n; struct tcamsg t; char buf[MAX_MSG]; - } req; - - req.t.tca_family = AF_UNSPEC; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .t.tca_family = AF_UNSPEC, + }; + struct rtattr *tail = NLMSG_TAIL(&req.n); - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - tail = NLMSG_TAIL(&req.n); - argc -=1; - argv +=1; + argc -= 1; + argv += 1; if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) { fprintf(stderr, "Illegal \"action\"\n"); return -1; @@ -532,40 +522,37 @@ { int ret = 0, prio = 0, msg_size = 0; char k[16]; - struct rtattr *tail,*tail2; + struct rtattr *tail, *tail2; struct action_util *a = NULL; struct { struct nlmsghdr n; struct tcamsg t; char buf[MAX_MSG]; - } req; - - req.t.tca_family = AF_UNSPEC; - - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)), + .t.tca_family = AF_UNSPEC, + }; tail = NLMSG_TAIL(&req.n); addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); tail2 = NLMSG_TAIL(&req.n); - strncpy(k, *argv, sizeof (k) - 1); + strncpy(k, *argv, sizeof(k) - 1); #ifdef CONFIG_GACT if (!gact_ld) { get_action_kind("gact"); } #endif a = get_action_kind(k); - if (NULL == a) { - fprintf(stderr,"bad action %s\n",k); + if (a == NULL) { + fprintf(stderr, "bad action %s\n", k); goto bad_val; } if (strcmp(a->id, k) != 0) { - fprintf(stderr,"bad action %s\n",k); + fprintf(stderr, "bad action %s\n", k); goto bad_val; } - strncpy(k, *argv, sizeof (k) - 1); + strncpy(k, *argv, sizeof(k) - 1); addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); @@ -612,12 +599,12 @@ matches(*argv, "replace") == 0) { ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv); } else if (matches(*argv, "delete") == 0) { - argc -=1; - argv +=1; + argc -= 1; + argv += 1; ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv); } else if (matches(*argv, "get") == 0) { - argc -=1; - argv +=1; + argc -= 1; + argv += 1; ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv); } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 || matches(*argv, "lst") == 0) { @@ -636,14 +623,12 @@ act_usage(); return -1; } else { - - ret = -1; - } - - if (ret < 0) { fprintf(stderr, "Command \"%s\" is unknown, try \"tc actions help\".\n", *argv); return -1; } + + if (ret < 0) + return -1; } return 0; diff -Nru iproute2-4.3.0/tc/Makefile iproute2-4.9.0/tc/Makefile --- iproute2-4.3.0/tc/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -43,6 +43,7 @@ TCMODULES += m_mirred.o TCMODULES += m_nat.o TCMODULES += m_pedit.o +TCMODULES += m_ife.o TCMODULES += m_skbedit.o TCMODULES += m_csum.o TCMODULES += m_simple.o @@ -64,30 +65,31 @@ TCMODULES += q_fq.o TCMODULES += q_pie.o TCMODULES += q_hhf.o +TCMODULES += q_clsact.o TCMODULES += e_bpf.o - -ifeq ($(TC_CONFIG_IPSET), y) - ifeq ($(TC_CONFIG_XT), y) - TCMODULES += em_ipset.o - endif -endif +TCMODULES += f_matchall.o TCSO := ifeq ($(TC_CONFIG_ATM),y) TCSO += q_atm.so endif -ifeq ($(TC_CONFIG_XT),y) - TCSO += m_xt.so -else - ifeq ($(TC_CONFIG_XT_OLD),y) - TCSO += m_xt_old.so +ifneq ($(TC_CONFIG_NO_XT),y) + ifeq ($(TC_CONFIG_XT),y) + TCSO += m_xt.so + ifeq ($(TC_CONFIG_IPSET),y) + TCMODULES += em_ipset.o + endif else - ifeq ($(TC_CONFIG_XT_OLD_H),y) - CFLAGS += -DTC_CONFIG_XT_H - TCSO += m_xt_old.so + ifeq ($(TC_CONFIG_XT_OLD),y) + TCSO += m_xt_old.so else - TCMODULES += m_ipt.o + ifeq ($(TC_CONFIG_XT_OLD_H),y) + CFLAGS += -DTC_CONFIG_XT_H + TCSO += m_xt_old.so + else + TCMODULES += m_ipt.o + endif endif endif endif @@ -123,15 +125,16 @@ MODDESTDIR := $(DESTDIR)$(LIBDIR)/tc %.so: %.c - $(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic $< -o $@ + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic $< -o $@ all: libtc.a tc $(TCSO) tc: $(TCOBJ) $(TCLIB) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ libtc.a: $(TCLIB) - $(AR) rcs $@ $(TCLIB) + $(QUIET_AR)$(AR) rcs $@ $(TCLIB) install: all mkdir -p $(MODDESTDIR) @@ -152,21 +155,21 @@ rm -f emp_ematch.yacc.* q_atm.so: q_atm.c - $(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o q_atm.so q_atm.c -latm + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o q_atm.so q_atm.c -latm m_xt.so: m_xt.c - $(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o m_xt.so m_xt.c $$($(PKG_CONFIG) xtables --cflags --libs) + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o m_xt.so m_xt.c $$($(PKG_CONFIG) xtables --cflags --libs) m_xt_old.so: m_xt_old.c - $(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o m_xt_old.so m_xt_old.c $$($(PKG_CONFIG) xtables --cflags --libs) + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o m_xt_old.so m_xt_old.c $$($(PKG_CONFIG) xtables --cflags --libs) em_ipset.o: CFLAGS += $$($(PKG_CONFIG) xtables --cflags) %.yacc.c: %.y - $(YACC) $(YACCFLAGS) -o $@ $< + $(QUIET_YACC)$(YACC) $(YACCFLAGS) -o $@ $< %.lex.c: %.l - $(LEX) $(LEXFLAGS) -o$@ $< + $(QUIET_LEX)$(LEX) $(LEXFLAGS) -o$@ $< # our lexer includes the header from yacc, so make sure # we don't attempt to compile it before the header has diff -Nru iproute2-4.3.0/tc/m_bpf.c iproute2-4.9.0/tc/m_bpf.c --- iproute2-4.3.0/tc/m_bpf.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_bpf.c 2016-12-12 23:07:42.000000000 +0000 @@ -12,20 +12,23 @@ #include #include -#include -#include -#include -#include + #include #include #include "utils.h" -#include "rt_names.h" #include "tc_util.h" #include "tc_bpf.h" static const enum bpf_prog_type bpf_type = BPF_PROG_TYPE_SCHED_ACT; +static const int nla_tbl[BPF_NLA_MAX] = { + [BPF_NLA_OPS_LEN] = TCA_ACT_BPF_OPS_LEN, + [BPF_NLA_OPS] = TCA_ACT_BPF_OPS, + [BPF_NLA_FD] = TCA_ACT_BPF_FD, + [BPF_NLA_NAME] = TCA_ACT_BPF_NAME, +}; + static void explain(void) { fprintf(stderr, "Usage: ... bpf ... [ index INDEX ]\n"); @@ -37,12 +40,14 @@ fprintf(stderr, "eBPF use case:\n"); fprintf(stderr, " object-file FILE [ section ACT_NAME ] [ export UDS_FILE ]"); fprintf(stderr, " [ verbose ]\n"); + fprintf(stderr, " object-pinned FILE\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where BPF_BYTECODE := \'s,c t f k,c t f k,c t f k,...\'\n"); fprintf(stderr, "c,t,f,k and s are decimals; s denotes number of 4-tuples\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where FILE points to a file containing the BPF_BYTECODE string,\n"); - fprintf(stderr, "an ELF file containing eBPF map definitions and bytecode.\n"); + fprintf(stderr, "an ELF file containing eBPF map definitions and bytecode, or a\n"); + fprintf(stderr, "pinned eBPF program.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where ACT_NAME refers to the section name containing the\n"); fprintf(stderr, "action (default \'%s\').\n", bpf_default_section(bpf_type)); @@ -54,111 +59,40 @@ fprintf(stderr, "explicitly specifies an action index upon creation.\n"); } -static void usage(void) -{ - explain(); - exit(-1); -} - -static int parse_bpf(struct action_util *a, int *argc_p, char ***argv_p, - int tca_id, struct nlmsghdr *n) +static int bpf_parse_opt(struct action_util *a, int *ptr_argc, char ***ptr_argv, + int tca_id, struct nlmsghdr *n) { - char **argv = *argv_p, bpf_name[256]; + const char *bpf_obj = NULL, *bpf_uds_name = NULL; + struct tc_act_bpf parm = { .action = TC_ACT_PIPE }; + bool seen_run = false; struct rtattr *tail; - struct tc_act_bpf parm = { 0 }; - struct sock_filter bpf_ops[BPF_MAXINSNS]; - bool ebpf_fill = false, bpf_fill = false; - bool ebpf = false, seen_run = false; - const char *bpf_uds_name = NULL; - const char *bpf_sec_name = NULL; - char *bpf_obj = NULL; - int argc = *argc_p, ret = 0; - __u16 bpf_len = 0; - __u32 bpf_fd = 0; + int argc, ret = 0; + char **argv; + + argv = *ptr_argv; + argc = *ptr_argc; if (matches(*argv, "bpf") != 0) return -1; NEXT_ARG(); + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + while (argc > 0) { if (matches(*argv, "run") == 0) { - bool from_file, bpf_verbose; - int ret; - NEXT_ARG(); opt_bpf: - bpf_sec_name = bpf_default_section(bpf_type); - bpf_verbose = false; seen_run = true; - - if (strcmp(*argv, "bytecode-file") == 0 || - strcmp(*argv, "bcf") == 0) { - from_file = true; - } else if (strcmp(*argv, "bytecode") == 0 || - strcmp(*argv, "bc") == 0) { - from_file = false; - } else if (strcmp(*argv, "object-file") == 0 || - strcmp(*argv, "obj") == 0) { - ebpf = true; - } else { - fprintf(stderr, "unexpected \"%s\"\n", *argv); - explain(); - return -1; - } - - NEXT_ARG(); - if (ebpf) { - bpf_uds_name = getenv(BPF_ENV_UDS); - bpf_obj = *argv; - NEXT_ARG(); - - if (strcmp(*argv, "section") == 0 || - strcmp(*argv, "sec") == 0) { - NEXT_ARG(); - bpf_sec_name = *argv; - NEXT_ARG(); - } - if (!bpf_uds_name && - (strcmp(*argv, "export") == 0 || - strcmp(*argv, "exp") == 0)) { - NEXT_ARG(); - bpf_uds_name = *argv; - NEXT_ARG(); - } - if (strcmp(*argv, "verbose") == 0 || - strcmp(*argv, "verb") == 0) { - bpf_verbose = true; - NEXT_ARG(); - } - - PREV_ARG(); - } - - ret = ebpf ? bpf_open_object(bpf_obj, bpf_type, bpf_sec_name, - bpf_verbose) : - bpf_parse_ops(argc, argv, bpf_ops, from_file); - if (ret < 0) { - fprintf(stderr, "%s\n", ebpf ? - "Could not load object" : - "Illegal \"bytecode\""); + if (bpf_parse_common(&argc, &argv, nla_tbl, bpf_type, + &bpf_obj, &bpf_uds_name, n)) { + fprintf(stderr, "Failed to retrieve (e)BPF data!\n"); return -1; } - - if (ebpf) { - bpf_obj = basename(bpf_obj); - - snprintf(bpf_name, sizeof(bpf_name), "%s:[%s]", - bpf_obj, bpf_sec_name); - - bpf_fd = ret; - ebpf_fill = true; - } else { - bpf_len = ret; - bpf_fill = true; - } } else if (matches(*argv, "help") == 0) { - usage(); + explain(); + return -1; } else if (matches(*argv, "index") == 0) { break; } else { @@ -166,36 +100,13 @@ goto opt_bpf; break; } - argc--; - argv++; - } - parm.action = TC_ACT_PIPE; - if (argc) { - if (matches(*argv, "reclassify") == 0) { - parm.action = TC_ACT_RECLASSIFY; - argc--; - argv++; - } else if (matches(*argv, "pipe") == 0) { - parm.action = TC_ACT_PIPE; - argc--; - argv++; - } else if (matches(*argv, "drop") == 0 || - matches(*argv, "shot") == 0) { - parm.action = TC_ACT_SHOT; - argc--; - argv++; - } else if (matches(*argv, "continue") == 0) { - parm.action = TC_ACT_UNSPEC; - argc--; - argv++; - } else if (matches(*argv, "pass") == 0) { - parm.action = TC_ACT_OK; - argc--; - argv++; - } + NEXT_ARG_FWD(); } + if (argc && !action_a2n(*argv, &parm.action, false)) + NEXT_ARG_FWD(); + if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); @@ -203,41 +114,27 @@ fprintf(stderr, "bpf: Illegal \"index\"\n"); return -1; } - argc--; - argv++; + + NEXT_ARG_FWD(); } } - tail = NLMSG_TAIL(n); - - addattr_l(n, MAX_MSG, tca_id, NULL, 0); addattr_l(n, MAX_MSG, TCA_ACT_BPF_PARMS, &parm, sizeof(parm)); - - if (ebpf_fill) { - addattr32(n, MAX_MSG, TCA_ACT_BPF_FD, bpf_fd); - addattrstrz(n, MAX_MSG, TCA_ACT_BPF_NAME, bpf_name); - } else if (bpf_fill) { - addattr16(n, MAX_MSG, TCA_ACT_BPF_OPS_LEN, bpf_len); - addattr_l(n, MAX_MSG, TCA_ACT_BPF_OPS, &bpf_ops, - bpf_len * sizeof(struct sock_filter)); - } - tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail; - *argc_p = argc; - *argv_p = argv; - if (bpf_uds_name) ret = bpf_send_map_fds(bpf_uds_name, bpf_obj); + *ptr_argc = argc; + *ptr_argv = argv; + return ret; } -static int print_bpf(struct action_util *au, FILE *f, struct rtattr *arg) +static int bpf_print_opt(struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_ACT_BPF_MAX + 1]; struct tc_act_bpf *parm; - SPRINT_BUF(action_buf); if (arg == NULL) return -1; @@ -250,7 +147,6 @@ } parm = RTA_DATA(tb[TCA_ACT_BPF_PARMS]); - fprintf(f, "bpf "); if (tb[TCA_ACT_BPF_NAME]) @@ -264,25 +160,24 @@ fprintf(f, " "); } - fprintf(f, "default-action %s\n", action_n2a(parm->action, action_buf, - sizeof(action_buf))); + fprintf(f, "default-action %s\n", action_n2a(parm->action)); fprintf(f, "\tindex %d ref %d bind %d", parm->index, parm->refcnt, parm->bindcnt); if (show_stats) { if (tb[TCA_ACT_BPF_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_ACT_BPF_TM]); + print_tm(f, tm); } } fprintf(f, "\n "); - return 0; } struct action_util bpf_action_util = { - .id = "bpf", - .parse_aopt = parse_bpf, - .print_aopt = print_bpf, + .id = "bpf", + .parse_aopt = bpf_parse_opt, + .print_aopt = bpf_print_opt, }; diff -Nru iproute2-4.3.0/tc/m_connmark.c iproute2-4.9.0/tc/m_connmark.c --- iproute2-4.3.0/tc/m_connmark.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_connmark.c 2016-12-12 23:07:42.000000000 +0000 @@ -27,10 +27,10 @@ static void explain(void) { - fprintf(stderr, "Usage: ... connmark [ZONE] [BRANCH] [index ]\n"); + fprintf(stderr, "Usage: ... connmark [zone ZONE] [CONTROL] [index ]\n"); fprintf(stderr, "where :\n" "\tZONE is the conntrack zone\n" - "\tBRANCH := reclassify|pipe|drop|continue|ok\n"); + "\tCONTROL := reclassify|pipe|drop|continue|ok\n"); } static void @@ -81,30 +81,8 @@ } sel.action = TC_ACT_PIPE; - if (argc) { - if (matches(*argv, "reclassify") == 0) { - sel.action = TC_ACT_RECLASSIFY; - argc--; - argv++; - } else if (matches(*argv, "pipe") == 0) { - sel.action = TC_ACT_PIPE; - argc--; - argv++; - } else if (matches(*argv, "drop") == 0 || - matches(*argv, "shot") == 0) { - sel.action = TC_ACT_SHOT; - argc--; - argv++; - } else if (matches(*argv, "continue") == 0) { - sel.action = TC_ACT_UNSPEC; - argc--; - argv++; - } else if (matches(*argv, "pass") == 0) { - sel.action = TC_ACT_OK; - argc--; - argv++; - } - } + if (argc && !action_a2n(*argv, &sel.action, false)) + NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { @@ -151,6 +129,7 @@ if (show_stats) { if (tb[TCA_CONNMARK_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_CONNMARK_TM]); + print_tm(f, tm); } } diff -Nru iproute2-4.3.0/tc/m_csum.c iproute2-4.9.0/tc/m_csum.c --- iproute2-4.3.0/tc/m_csum.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_csum.c 2016-12-12 23:07:42.000000000 +0000 @@ -24,8 +24,7 @@ { fprintf(stderr, "Usage: ... csum \n" "Where: UPDATE := []\n" - " TARGET := { ip4h | icmp | igmp |" - " tcp | udp | udplite | }\n" + " TARGET := { ip4h | icmp | igmp | tcp | udp | udplite | }\n" " SWEETS := { and | or | \'+\' }\n"); } @@ -45,7 +44,7 @@ if (argc <= 0) return -1; - while(argc > 0) { + while (argc > 0) { if ((matches(*argv, "iph") == 0) || (matches(*argv, "ip4h") == 0) || (matches(*argv, "ipv4h") == 0)) @@ -86,15 +85,13 @@ parse_csum(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { - struct tc_csum sel; + struct tc_csum sel = {}; int argc = *argc_p; char **argv = *argv_p; int ok = 0; struct rtattr *tail; - memset(&sel, 0, sizeof(sel)); - while (argc > 0) { if (matches(*argv, "csum") == 0) { NEXT_ARG(); @@ -108,8 +105,7 @@ continue; } else if (matches(*argv, "help") == 0) { usage(); - } - else { + } else { break; } } @@ -124,30 +120,8 @@ return -1; } - if (argc) { - if (matches(*argv, "reclassify") == 0) { - sel.action = TC_ACT_RECLASSIFY; - argc--; - argv++; - } else if (matches(*argv, "pipe") == 0) { - sel.action = TC_ACT_PIPE; - argc--; - argv++; - } else if (matches(*argv, "drop") == 0 || - matches(*argv, "shot") == 0) { - sel.action = TC_ACT_SHOT; - argc--; - argv++; - } else if (matches(*argv, "continue") == 0) { - sel.action = TC_ACT_UNSPEC; - argc--; - argv++; - } else if (matches(*argv, "pass") == 0) { - sel.action = TC_ACT_OK; - argc--; - argv++; - } - } + if (argc && !action_a2n(*argv, &sel.action, false)) + NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { @@ -174,7 +148,7 @@ } static int -print_csum(struct action_util *au, FILE * f, struct rtattr *arg) +print_csum(struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_csum *sel; @@ -186,7 +160,6 @@ char *uflag_4 = ""; char *uflag_5 = ""; char *uflag_6 = ""; - SPRINT_BUF(action_buf); int uflag_count = 0; @@ -212,7 +185,7 @@ ", " flag_string : flag_string; \ uflag_count++; \ } \ - } while(0) + } while (0) CSUM_UFLAG_BUFFER(uflag_2, TCA_CSUM_UPDATE_FLAG_ICMP, "icmp"); CSUM_UFLAG_BUFFER(uflag_3, TCA_CSUM_UPDATE_FLAG_IGMP, "igmp"); CSUM_UFLAG_BUFFER(uflag_4, TCA_CSUM_UPDATE_FLAG_TCP, "tcp"); @@ -225,13 +198,14 @@ fprintf(f, "csum (%s%s%s%s%s%s) action %s\n", uflag_1, uflag_2, uflag_3, uflag_4, uflag_5, uflag_6, - action_n2a(sel->action, action_buf, sizeof(action_buf))); + action_n2a(sel->action)); fprintf(f, "\tindex %d ref %d bind %d", sel->index, sel->refcnt, sel->bindcnt); if (show_stats) { if (tb[TCA_CSUM_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_CSUM_TM]); - print_tm(f,tm); + + print_tm(f, tm); } } fprintf(f, "\n"); diff -Nru iproute2-4.3.0/tc/m_ematch.c iproute2-4.9.0/tc/m_ematch.c --- iproute2-4.3.0/tc/m_ematch.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_ematch.c 2016-12-12 23:07:42.000000000 +0000 @@ -33,7 +33,7 @@ /* export to bison parser */ int ematch_argc; char **ematch_argv; -char *ematch_err = NULL; +char *ematch_err; struct ematch *ematch_root; static int begin_argc; @@ -177,9 +177,7 @@ for (t = tree; t; t = t->next) { struct rtattr *tail = NLMSG_TAIL(n); - struct tcf_ematch_hdr hdr = { - .flags = t->relation - }; + struct tcf_ematch_hdr hdr = { .flags = t->relation }; if (t->inverted) hdr.flags |= TCF_EM_INVERT; @@ -188,6 +186,7 @@ if (t->child) { __u32 r = t->child_ref; + addraw_l(n, MAX_MSG, &hdr, sizeof(hdr)); addraw_l(n, MAX_MSG, &r, sizeof(r)); } else { @@ -198,7 +197,7 @@ if (t->args == NULL) return -1; - strncpy(buf, (char*) t->args->data, sizeof(buf)-1); + strncpy(buf, (char *) t->args->data, sizeof(buf)-1); e = get_ematch_kind(buf); if (e == NULL) { fprintf(stderr, "Unknown ematch \"%s\"\n", @@ -218,7 +217,7 @@ return -1; } - tail->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail; + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; } return 0; @@ -353,8 +352,8 @@ if (parse_tree(n, ematch_root) < 0) return -1; - tail_list->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail_list; - tail->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail; + tail_list->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail_list; + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; } *argc_p = ematch_argc; @@ -492,7 +491,7 @@ return print_ematch_list(fd, hdr, tb[TCA_EMATCH_TREE_LIST]); } -struct bstr * bstr_alloc(const char *text) +struct bstr *bstr_alloc(const char *text) { struct bstr *b = calloc(1, sizeof(*b)); @@ -558,6 +557,7 @@ printf(")"); } else { struct bstr *b; + for (b = t->args; b; b = b->next) printf("%s%s", b->data, b->next ? " " : ""); } diff -Nru iproute2-4.3.0/tc/m_estimator.c iproute2-4.9.0/tc/m_estimator.c --- iproute2-4.3.0/tc/m_estimator.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_estimator.c 2016-12-12 23:07:42.000000000 +0000 @@ -38,7 +38,7 @@ { int argc = *p_argc; char **argv = *p_argv; - unsigned A, time_const; + unsigned int A, time_const; NEXT_ARG(); if (est->ewma_log) diff -Nru iproute2-4.3.0/tc/m_gact.c iproute2-4.9.0/tc/m_gact.c --- iproute2-4.3.0/tc/m_gact.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_gact.c 2016-12-12 23:07:42.000000000 +0000 @@ -45,16 +45,16 @@ #ifdef CONFIG_GACT_PROB fprintf(stderr, "Usage: ... gact [RAND] [INDEX]\n"); fprintf(stderr, - "Where: \tACTION := reclassify | drop | continue | pass \n" - "\tRAND := random \n" - "\tRANDTYPE := netrand | determ\n" + "Where: \tACTION := reclassify | drop | continue | pass | pipe\n" + "\tRAND := random \n" + "\tRANDTYPE := netrand | determ\n" "\tVAL : = value not exceeding 10000\n" "\tINDEX := index value used\n" "\n"); #else fprintf(stderr, "Usage: ... gact [INDEX]\n"); fprintf(stderr, - "Where: \tACTION := reclassify | drop | continue | pass \n" + "Where: \tACTION := reclassify | drop | continue | pass | pipe\n" "\tINDEX := index value used\n" "\n"); #endif @@ -71,22 +71,13 @@ static int get_act(char ***argv_p) { - char **argv = *argv_p; + int n; - if (matches(*argv, "reclassify") == 0) { - return TC_ACT_RECLASSIFY; - } else if (matches(*argv, "drop") == 0 || matches(*argv, "shot") == 0) { - return TC_ACT_SHOT; - } else if (matches(*argv, "continue") == 0) { - return TC_ACT_UNSPEC; - } else if (matches(*argv, "pipe") == 0) { - return TC_ACT_PIPE; - } else if (matches(*argv, "pass") == 0 || matches(*argv, "ok") == 0) { - return TC_ACT_OK; - } else { - fprintf(stderr,"bad action type %s\n",*argv); + if (action_a2n(**argv_p, &n, false)) { + fprintf(stderr, "bad action type %s\n", **argv_p); return -10; } + return n; } static int @@ -97,16 +88,13 @@ char **argv = *argv_p; int ok = 0; int action = TC_POLICE_RECLASSIFY; - struct tc_gact p; + struct tc_gact p = { .action = TC_POLICE_RECLASSIFY }; #ifdef CONFIG_GACT_PROB int rd = 0; struct tc_gact_p pp; #endif struct rtattr *tail; - memset(&p, 0, sizeof (p)); - p.action = TC_POLICE_RECLASSIFY; - if (argc < 0) return -1; @@ -155,11 +143,11 @@ argc--; argv++; if (get_u16(&pp.pval, *argv, 10)) { - fprintf(stderr, "Illegal probability val 0x%x\n",pp.pval); + fprintf(stderr, "Illegal probability val 0x%x\n", pp.pval); return -1; } if (pp.pval > 10000) { - fprintf(stderr, "Illegal probability val 0x%x\n",pp.pval); + fprintf(stderr, "Illegal probability val 0x%x\n", pp.pval); return -1; } argc--; @@ -190,10 +178,10 @@ tail = NLMSG_TAIL(n); addattr_l(n, MAX_MSG, tca_id, NULL, 0); - addattr_l(n, MAX_MSG, TCA_GACT_PARMS, &p, sizeof (p)); + addattr_l(n, MAX_MSG, TCA_GACT_PARMS, &p, sizeof(p)); #ifdef CONFIG_GACT_PROB if (rd) { - addattr_l(n, MAX_MSG, TCA_GACT_PROB, &pp, sizeof (pp)); + addattr_l(n, MAX_MSG, TCA_GACT_PROB, &pp, sizeof(pp)); } #endif tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; @@ -204,11 +192,9 @@ } static int -print_gact(struct action_util *au,FILE * f, struct rtattr *arg) +print_gact(struct action_util *au, FILE * f, struct rtattr *arg) { - SPRINT_BUF(b1); #ifdef CONFIG_GACT_PROB - SPRINT_BUF(b2); struct tc_gact_p *pp = NULL; struct tc_gact_p pp_dummy; #endif @@ -226,22 +212,24 @@ } p = RTA_DATA(tb[TCA_GACT_PARMS]); - fprintf(f, "gact action %s", action_n2a(p->action, b1, sizeof (b1))); + fprintf(f, "gact action %s", action_n2a(p->action)); #ifdef CONFIG_GACT_PROB - if (NULL != tb[TCA_GACT_PROB]) { + if (tb[TCA_GACT_PROB] != NULL) { pp = RTA_DATA(tb[TCA_GACT_PROB]); } else { /* need to keep consistent output */ - memset(&pp_dummy, 0, sizeof (pp_dummy)); + memset(&pp_dummy, 0, sizeof(pp_dummy)); pp = &pp_dummy; } - fprintf(f, "\n\t random type %s %s val %d",prob_n2a(pp->ptype), action_n2a(pp->paction, b2, sizeof (b2)), pp->pval); + fprintf(f, "\n\t random type %s %s val %d", + prob_n2a(pp->ptype), action_n2a(pp->paction), pp->pval); #endif - fprintf(f, "\n\t index %d ref %d bind %d",p->index, p->refcnt, p->bindcnt); + fprintf(f, "\n\t index %d ref %d bind %d", p->index, p->refcnt, p->bindcnt); if (show_stats) { if (tb[TCA_GACT_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_GACT_TM]); - print_tm(f,tm); + + print_tm(f, tm); } } fprintf(f, "\n "); diff -Nru iproute2-4.3.0/tc/m_ife.c iproute2-4.9.0/tc/m_ife.c --- iproute2-4.3.0/tc/m_ife.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/tc/m_ife.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,334 @@ +/* + * m_ife.c IFE actions module + * + * This program is free software; you can distribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: J Hadi Salim (jhs@mojatatu.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "tc_util.h" +#include + +static void ife_explain(void) +{ + fprintf(stderr, + "Usage:... ife {decode|encode} [{ALLOW|USE} ATTR] [dst DMAC] [src SMAC] [type TYPE] [CONTROL] [index INDEX]\n"); + fprintf(stderr, + "\tALLOW := Encode direction. Allows encoding specified metadata\n" + "\t\t e.g \"allow mark\"\n" + "\tUSE := Encode direction. Enforce Static encoding of specified metadata\n" + "\t\t e.g \"use mark 0x12\"\n" + "\tATTR := mark (32-bit), prio (32-bit), tcindex (16-bit)\n" + "\tDMAC := 6 byte Destination MAC address to encode\n" + "\tSMAC := optional 6 byte Source MAC address to encode\n" + "\tTYPE := optional 16 bit ethertype to encode\n" + "\tCONTROL := reclassify|pipe|drop|continue|ok\n" + "\tINDEX := optional IFE table index value used\n"); + fprintf(stderr, "encode is used for sending IFE packets\n"); + fprintf(stderr, "decode is used for receiving IFE packets\n"); +} + +static void ife_usage(void) +{ + ife_explain(); + exit(-1); +} + +static int parse_ife(struct action_util *a, int *argc_p, char ***argv_p, + int tca_id, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + int ok = 0; + struct tc_ife p = { .action = TC_ACT_PIPE }; /* good default */ + struct rtattr *tail; + struct rtattr *tail2; + char dbuf[ETH_ALEN]; + char sbuf[ETH_ALEN]; + __u16 ife_type = 0; + __u32 ife_prio = 0; + __u32 ife_prio_v = 0; + __u32 ife_mark = 0; + __u32 ife_mark_v = 0; + __u16 ife_tcindex = 0; + __u16 ife_tcindex_v = 0; + char *daddr = NULL; + char *saddr = NULL; + + if (argc <= 0) + return -1; + + while (argc > 0) { + if (matches(*argv, "ife") == 0) { + NEXT_ARG(); + continue; + } else if (matches(*argv, "decode") == 0) { + p.flags = IFE_DECODE; /* readability aid */ + ok++; + } else if (matches(*argv, "encode") == 0) { + p.flags = IFE_ENCODE; + ok++; + } else if (matches(*argv, "allow") == 0) { + NEXT_ARG(); + if (matches(*argv, "mark") == 0) { + ife_mark = IFE_META_SKBMARK; + } else if (matches(*argv, "prio") == 0) { + ife_prio = IFE_META_PRIO; + } else if (matches(*argv, "tcindex") == 0) { + ife_prio = IFE_META_TCINDEX; + } else { + fprintf(stderr, "Illegal meta define <%s>\n", + *argv); + return -1; + } + } else if (matches(*argv, "use") == 0) { + NEXT_ARG(); + if (matches(*argv, "mark") == 0) { + NEXT_ARG(); + if (get_u32(&ife_mark_v, *argv, 0)) + invarg("ife mark val is invalid", + *argv); + } else if (matches(*argv, "prio") == 0) { + NEXT_ARG(); + if (get_u32(&ife_prio_v, *argv, 0)) + invarg("ife prio val is invalid", + *argv); + } else if (matches(*argv, "tcindex") == 0) { + NEXT_ARG(); + if (get_u16(&ife_tcindex_v, *argv, 0)) + invarg("ife tcindex val is invalid", + *argv); + } else { + fprintf(stderr, "Illegal meta use type <%s>\n", + *argv); + return -1; + } + } else if (matches(*argv, "type") == 0) { + NEXT_ARG(); + if (get_u16(&ife_type, *argv, 0)) + invarg("ife type is invalid", *argv); + fprintf(stderr, "IFE type 0x%x\n", ife_type); + } else if (matches(*argv, "dst") == 0) { + NEXT_ARG(); + daddr = *argv; + if (sscanf(daddr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + dbuf, dbuf + 1, dbuf + 2, + dbuf + 3, dbuf + 4, dbuf + 5) != 6) { + fprintf(stderr, "Invalid mac address %s\n", + daddr); + } + fprintf(stderr, "dst MAC address <%s>\n", daddr); + + } else if (matches(*argv, "src") == 0) { + NEXT_ARG(); + saddr = *argv; + if (sscanf(saddr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + sbuf, sbuf + 1, sbuf + 2, + sbuf + 3, sbuf + 4, sbuf + 5) != 6) { + fprintf(stderr, "Invalid mac address %s\n", + saddr); + } + fprintf(stderr, "src MAC address <%s>\n", saddr); + } else if (matches(*argv, "help") == 0) { + ife_usage(); + } else { + break; + } + + argc--; + argv++; + } + + if (argc && !action_a2n(*argv, &p.action, false)) + NEXT_ARG_FWD(); + + if (argc) { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 0)) { + fprintf(stderr, "ife: Illegal \"index\"\n"); + return -1; + } + ok++; + argc--; + argv++; + } + } + + if (!ok) { + fprintf(stderr, "IFE requires decode/encode specified\n"); + ife_usage(); + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_IFE_PARMS, &p, sizeof(p)); + + if (!(p.flags & IFE_ENCODE)) + goto skip_encode; + + if (daddr) + addattr_l(n, MAX_MSG, TCA_IFE_DMAC, dbuf, ETH_ALEN); + if (ife_type) + addattr_l(n, MAX_MSG, TCA_IFE_TYPE, &ife_type, 2); + if (saddr) + addattr_l(n, MAX_MSG, TCA_IFE_SMAC, sbuf, ETH_ALEN); + + tail2 = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, TCA_IFE_METALST, NULL, 0); + if (ife_mark || ife_mark_v) { + if (ife_mark_v) + addattr_l(n, MAX_MSG, IFE_META_SKBMARK, &ife_mark_v, 4); + else + addattr_l(n, MAX_MSG, IFE_META_SKBMARK, NULL, 0); + } + if (ife_prio || ife_prio_v) { + if (ife_prio_v) + addattr_l(n, MAX_MSG, IFE_META_PRIO, &ife_prio_v, 4); + else + addattr_l(n, MAX_MSG, IFE_META_PRIO, NULL, 0); + } + if (ife_tcindex || ife_tcindex_v) { + if (ife_tcindex_v) + addattr_l(n, MAX_MSG, IFE_META_TCINDEX, &ife_tcindex_v, + 2); + else + addattr_l(n, MAX_MSG, IFE_META_TCINDEX, NULL, 0); + } + + tail2->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail2; + +skip_encode: + tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +static int print_ife(struct action_util *au, FILE *f, struct rtattr *arg) +{ + struct tc_ife *p = NULL; + struct rtattr *tb[TCA_IFE_MAX + 1]; + __u16 ife_type = 0; + __u32 mmark = 0; + __u16 mtcindex = 0; + __u32 mprio = 0; + int has_optional = 0; + SPRINT_BUF(b2); + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_IFE_MAX, arg); + + if (tb[TCA_IFE_PARMS] == NULL) { + fprintf(f, "[NULL ife parameters]"); + return -1; + } + p = RTA_DATA(tb[TCA_IFE_PARMS]); + + fprintf(f, "ife %s action %s ", + (p->flags & IFE_ENCODE) ? "encode" : "decode", + action_n2a(p->action)); + + if (tb[TCA_IFE_TYPE]) { + ife_type = rta_getattr_u16(tb[TCA_IFE_TYPE]); + has_optional = 1; + fprintf(f, "type 0x%X ", ife_type); + } + + if (has_optional) + fprintf(f, "\n\t "); + + if (tb[TCA_IFE_METALST]) { + struct rtattr *metalist[IFE_META_MAX + 1]; + int len = 0; + + parse_rtattr_nested(metalist, IFE_META_MAX, + tb[TCA_IFE_METALST]); + + if (metalist[IFE_META_SKBMARK]) { + len = RTA_PAYLOAD(metalist[IFE_META_SKBMARK]); + if (len) { + mmark = rta_getattr_u32(metalist[IFE_META_SKBMARK]); + fprintf(f, "use mark %u ", mmark); + } else + fprintf(f, "allow mark "); + } + + if (metalist[IFE_META_TCINDEX]) { + len = RTA_PAYLOAD(metalist[IFE_META_TCINDEX]); + if (len) { + mtcindex = + rta_getattr_u16(metalist[IFE_META_TCINDEX]); + fprintf(f, "use tcindex %d ", mtcindex); + } else + fprintf(f, "allow tcindex "); + } + + if (metalist[IFE_META_PRIO]) { + len = RTA_PAYLOAD(metalist[IFE_META_PRIO]); + if (len) { + mprio = rta_getattr_u32(metalist[IFE_META_PRIO]); + fprintf(f, "use prio %u ", mprio); + } else + fprintf(f, "allow prio "); + } + + } + + if (tb[TCA_IFE_DMAC]) { + has_optional = 1; + fprintf(f, "dst %s ", + ll_addr_n2a(RTA_DATA(tb[TCA_IFE_DMAC]), + RTA_PAYLOAD(tb[TCA_IFE_DMAC]), 0, b2, + sizeof(b2))); + + } + + if (tb[TCA_IFE_SMAC]) { + has_optional = 1; + fprintf(f, "src %s ", + ll_addr_n2a(RTA_DATA(tb[TCA_IFE_SMAC]), + RTA_PAYLOAD(tb[TCA_IFE_SMAC]), 0, b2, + sizeof(b2))); + } + + fprintf(f, "\n\t index %d ref %d bind %d", p->index, p->refcnt, + p->bindcnt); + if (show_stats) { + if (tb[TCA_IFE_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_IFE_TM]); + + print_tm(f, tm); + } + } + + fprintf(f, "\n"); + + return 0; +} + +struct action_util ife_action_util = { + .id = "ife", + .parse_aopt = parse_ife, + .print_aopt = print_ife, +}; diff -Nru iproute2-4.3.0/tc/m_ipt.c iproute2-4.9.0/tc/m_ipt.c --- iproute2-4.3.0/tc/m_ipt.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_ipt.c 2016-12-12 23:07:42.000000000 +0000 @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -51,40 +50,29 @@ {0, 0, 0, 0} }; -static struct iptables_target *t_list = NULL; +static struct xtables_target *t_list; static struct option *opts = original_opts; -static unsigned int global_option_offset = 0; +static unsigned int global_option_offset; #define OPTION_OFFSET 256 char *lib_dir; void -register_target(struct iptables_target *me) +xtables_register_target(struct xtables_target *me) { -/* fprintf(stderr, "\nDummy register_target %s \n", me->name); -*/ me->next = t_list; t_list = me; } -void -xtables_register_target(struct iptables_target *me) -{ - me->next = t_list; - t_list = me; -} - -void -exit_tryhelp(int status) +static void exit_tryhelp(int status) { fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", pname, pname); exit(status); } -void -exit_error(enum exittype status, char *msg, ...) +static void exit_error(enum xtables_exittype status, char *msg, ...) { va_list args; @@ -106,61 +94,6 @@ Email them next time i remember */ -char * -addr_to_dotted(const struct in_addr *addrp) -{ - static char buf[20]; - const unsigned char *bytep; - - bytep = (const unsigned char *) &(addrp->s_addr); - sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); - return buf; -} - -int string_to_number_ll(const char *s, unsigned long long min, - unsigned long long max, - unsigned long long *ret) -{ - unsigned long long number; - char *end; - - /* Handle hex, octal, etc. */ - errno = 0; - number = strtoull(s, &end, 0); - if (*end == '\0' && end != s) { - /* we parsed a number, let's see if we want this */ - if (errno != ERANGE && min <= number && (!max || number <= max)) { - *ret = number; - return 0; - } - } - return -1; -} - -int string_to_number_l(const char *s, unsigned long min, unsigned long max, - unsigned long *ret) -{ - int result; - unsigned long long number; - - result = string_to_number_ll(s, min, max, &number); - *ret = (unsigned long)number; - - return result; -} - -int string_to_number(const char *s, unsigned int min, unsigned int max, - unsigned int *ret) -{ - int result; - unsigned long number; - - result = string_to_number_l(s, min, max, &number); - *ret = (unsigned int)number; - - return result; -} - static void free_opts(struct option *local_opts) { if (local_opts != original_opts) { @@ -177,18 +110,18 @@ struct option *merge; unsigned int num_old, num_new, i; - for (num_old = 0; oldopts[num_old].name; num_old++) ; - for (num_new = 0; newopts[num_new].name; num_new++) ; + for (num_old = 0; oldopts[num_old].name; num_old++); + for (num_new = 0; newopts[num_new].name; num_new++); *option_offset = global_option_offset + OPTION_OFFSET; - merge = malloc(sizeof (struct option) * (num_new + num_old + 1)); - memcpy(merge, oldopts, num_old * sizeof (struct option)); + merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); + memcpy(merge, oldopts, num_old * sizeof(struct option)); for (i = 0; i < num_new; i++) { merge[num_old + i] = newopts[i]; merge[num_old + i].val += *option_offset; } - memset(merge + num_old + num_new, 0, sizeof (struct option)); + memset(merge + num_old + num_new, 0, sizeof(struct option)); return merge; } @@ -205,10 +138,11 @@ return p; } -static struct iptables_target * +static struct xtables_target * find_t(char *name) { - struct iptables_target *m; + struct xtables_target *m; + for (m = t_list; m; m = m->next) { if (strcmp(m->name, name) == 0) return m; @@ -217,29 +151,24 @@ return NULL; } -static struct iptables_target * +static struct xtables_target * get_target_name(const char *name) { void *handle; char *error; char *new_name, *lname; - struct iptables_target *m; - char path[strlen(lib_dir) + sizeof ("/libipt_.so") + strlen(name)]; + struct xtables_target *m; + char path[strlen(lib_dir) + sizeof("/libipt_.so") + strlen(name)]; #ifdef NO_SHARED_LIBS return NULL; #endif - new_name = malloc(strlen(name) + 1); - lname = malloc(strlen(name) + 1); - if (new_name) - memset(new_name, '\0', strlen(name) + 1); - else + new_name = calloc(1, strlen(name) + 1); + lname = calloc(1, strlen(name) + 1); + if (!new_name) exit_error(PARAMETER_PROBLEM, "get_target_name"); - - if (lname) - memset(lname, '\0', strlen(name) + 1); - else + if (!lname) exit_error(PARAMETER_PROBLEM, "get_target_name"); strcpy(new_name, name); @@ -247,6 +176,7 @@ if (isupper(lname[0])) { int i; + for (i = 0; i < strlen(name); i++) { lname[i] = tolower(lname[i]); } @@ -254,6 +184,7 @@ if (islower(new_name[0])) { int i; + for (i = 0; i < strlen(new_name); i++) { new_name[i] = toupper(new_name[i]); } @@ -268,12 +199,12 @@ handle = dlopen(path, RTLD_LAZY); if (!handle) { - sprintf(path, "%s/libxt_%s.so", lib_dir , lname); + sprintf(path, "%s/libxt_%s.so", lib_dir, lname); handle = dlopen(path, RTLD_LAZY); } if (!handle) { - sprintf(path, "%s/libipt_%s.so", lib_dir , lname); + sprintf(path, "%s/libipt_%s.so", lib_dir, lname); handle = dlopen(path, RTLD_LAZY); } /* ok, lets give up .. */ @@ -288,12 +219,12 @@ m = dlsym(handle, new_name); if ((error = dlerror()) != NULL) { - m = (struct iptables_target *) dlsym(handle, lname); + m = (struct xtables_target *) dlsym(handle, lname); if ((error = dlerror()) != NULL) { m = find_t(new_name); - if (NULL == m) { + if (m == NULL) { m = find_t(lname); - if (NULL == m) { + if (m == NULL) { fputs(error, stderr); fprintf(stderr, "\n"); dlclose(handle); @@ -310,42 +241,6 @@ return m; } - -struct in_addr *dotted_to_addr(const char *dotted) -{ - static struct in_addr addr; - unsigned char *addrp; - char *p, *q; - unsigned int onebyte; - int i; - char buf[20]; - - /* copy dotted string, because we need to modify it */ - strncpy(buf, dotted, sizeof (buf) - 1); - addrp = (unsigned char *) &(addr.s_addr); - - p = buf; - for (i = 0; i < 3; i++) { - if ((q = strchr(p, '.')) == NULL) - return (struct in_addr *) NULL; - - *q = '\0'; - if (string_to_number(p, 0, 255, &onebyte) == -1) - return (struct in_addr *) NULL; - - addrp[i] = (unsigned char) onebyte; - p = q + 1; - } - - /* we've checked 3 bytes, now we check the last one */ - if (string_to_number(p, 0, 255, &onebyte) == -1) - return (struct in_addr *) NULL; - - addrp[3] = (unsigned char) onebyte; - - return &addr; -} - static void set_revision(char *name, u_int8_t revision) { /* Old kernel sources don't have ".revision" field, @@ -357,23 +252,20 @@ /* * we may need to check for version mismatch */ -int -build_st(struct iptables_target *target, struct ipt_entry_target *t) +static int build_st(struct xtables_target *target, struct ipt_entry_target *t) { - unsigned int nfcache = 0; - if (target) { size_t size; size = - IPT_ALIGN(sizeof (struct ipt_entry_target)) + target->size; + XT_ALIGN(sizeof(struct ipt_entry_target)) + target->size; - if (NULL == t) { + if (t == NULL) { target->t = fw_calloc(1, size); target->t->u.target_size = size; if (target->init != NULL) - target->init(target->t, &nfcache); + target->init(target->t); set_revision(target->t->u.user.name, target->revision); } else { target->t = t; @@ -385,10 +277,10 @@ return -1; } -static int parse_ipt(struct action_util *a,int *argc_p, +static int parse_ipt(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { - struct iptables_target *m = NULL; + struct xtables_target *m = NULL; struct ipt_entry fw; struct rtattr *tail; int c; @@ -406,6 +298,7 @@ { int i; + for (i = 0; i < rargc; i++) { if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { break; @@ -415,7 +308,7 @@ } if (argc <= 2) { - fprintf(stderr,"bad arguments to ipt %d vs %d \n", argc, rargc); + fprintf(stderr, "bad arguments to ipt %d vs %d\n", argc, rargc); return -1; } @@ -426,29 +319,29 @@ switch (c) { case 'j': m = get_target_name(optarg); - if (NULL != m) { + if (m != NULL) { - if (0 > build_st(m, NULL)) { - printf(" %s error \n", m->name); + if (build_st(m, NULL) < 0) { + printf(" %s error\n", m->name); return -1; } opts = merge_options(opts, m->extra_opts, &m->option_offset); } else { - fprintf(stderr," failed to find target %s\n\n", optarg); + fprintf(stderr, " failed to find target %s\n\n", optarg); return -1; } ok++; break; default: - memset(&fw, 0, sizeof (fw)); + memset(&fw, 0, sizeof(fw)); if (m) { m->parse(c - m->option_offset, argv, 0, &m->tflags, NULL, &m->t); } else { - fprintf(stderr," failed to find target %s\n\n", optarg); + fprintf(stderr, " failed to find target %s\n\n", optarg); return -1; } @@ -472,7 +365,7 @@ } if (!ok && !iok) { - fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv); + fprintf(stderr, " ipt Parser BAD!! (%s)\n", *argv); return -1; } @@ -482,6 +375,7 @@ { struct tcmsg *t = NLMSG_DATA(n); + if (t->tcm_parent != TC_H_ROOT && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) { hook = NF_IP_PRE_ROUTING; @@ -522,11 +416,11 @@ optind = 0; free_opts(opts); /* Clear flags if target will be used again */ - m->tflags=0; - m->used=0; + m->tflags = 0; + m->used = 0; /* Free allocated memory */ - if (m->t) - free(m->t); + if (m->t) + free(m->t); return 0; @@ -534,7 +428,7 @@ } static int -print_ipt(struct action_util *au,FILE * f, struct rtattr *arg) +print_ipt(struct action_util *au, FILE * f, struct rtattr *arg) { struct rtattr *tb[TCA_IPT_MAX + 1]; struct ipt_entry_target *t = NULL; @@ -560,20 +454,22 @@ return -1; } else { __u32 hook; + hook = rta_getattr_u32(tb[TCA_IPT_HOOK]); - fprintf(f, " hook: %s \n", ipthooks[hook]); + fprintf(f, " hook: %s\n", ipthooks[hook]); } if (tb[TCA_IPT_TARG] == NULL) { - fprintf(f, "\t[NULL ipt target parameters ] \n"); + fprintf(f, "\t[NULL ipt target parameters ]\n"); return -1; } else { - struct iptables_target *m = NULL; + struct xtables_target *m = NULL; + t = RTA_DATA(tb[TCA_IPT_TARG]); m = get_target_name(t->u.user.name); - if (NULL != m) { - if (0 > build_st(m, t)) { - fprintf(stderr, " %s error \n", m->name); + if (m != NULL) { + if (build_st(m, t) < 0) { + fprintf(stderr, " %s error\n", m->name); return -1; } @@ -591,21 +487,24 @@ fprintf(f, " [NULL ipt target index ]\n"); } else { __u32 index; + index = rta_getattr_u32(tb[TCA_IPT_INDEX]); - fprintf(f, " \n\tindex %d", index); + fprintf(f, "\n\tindex %d", index); } if (tb[TCA_IPT_CNT]) { - struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);; + struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]); + fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); } if (show_stats) { if (tb[TCA_IPT_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); - print_tm(f,tm); + + print_tm(f, tm); } } - fprintf(f, " \n"); + fprintf(f, "\n"); } free_opts(opts); @@ -614,7 +513,7 @@ } struct action_util ipt_action_util = { - .id = "ipt", - .parse_aopt = parse_ipt, - .print_aopt = print_ipt, + .id = "ipt", + .parse_aopt = parse_ipt, + .print_aopt = print_ipt, }; diff -Nru iproute2-4.3.0/tc/m_mirred.c iproute2-4.9.0/tc/m_mirred.c --- iproute2-4.3.0/tc/m_mirred.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_mirred.c 2016-12-12 23:07:42.000000000 +0000 @@ -29,12 +29,12 @@ static void explain(void) { - fprintf(stderr, "Usage: mirred [index INDEX] \n"); - fprintf(stderr, "where: \n"); + fprintf(stderr, "Usage: mirred [index INDEX] \n"); + fprintf(stderr, "where:\n"); fprintf(stderr, "\tDIRECTION := \n"); fprintf(stderr, "\tACTION := \n"); fprintf(stderr, "\tINDEX is the specific policy instance id\n"); - fprintf(stderr, "\tDEVICENAME is the devicename \n"); + fprintf(stderr, "\tDEVICENAME is the devicename\n"); } @@ -68,13 +68,10 @@ int argc = *argc_p; char **argv = *argv_p; - int ok = 0, iok = 0, mirror=0,redir=0; - struct tc_mirred p; + int ok = 0, iok = 0, mirror = 0, redir = 0; + struct tc_mirred p = {}; struct rtattr *tail; - char d[16]; - - memset(d,0,sizeof(d)-1); - memset(&p,0,sizeof(struct tc_mirred)); + char d[16] = {}; while (argc > 0) { @@ -98,12 +95,12 @@ argv++; break; } - } else if(!ok) { + } else if (!ok) { fprintf(stderr, "was expecting egress (%s)\n", *argv); break; } else if (!mirror && matches(*argv, "mirror") == 0) { - mirror=1; + mirror = 1; if (redir) { fprintf(stderr, "Can't have both mirror and redir\n"); return -1; @@ -112,7 +109,7 @@ p.action = TC_ACT_PIPE; ok++; } else if (!redir && matches(*argv, "redirect") == 0) { - redir=1; + redir = 1; if (mirror) { fprintf(stderr, "Can't have both mirror and redir\n"); return -1; @@ -145,6 +142,7 @@ if (d[0]) { int idx; + ll_init_map(&rth); if ((idx = ll_name_to_index(d)) == 0) { @@ -156,27 +154,9 @@ } - if (argc && p.eaction == TCA_EGRESS_MIRROR) { - - if (matches(*argv, "reclassify") == 0) { - p.action = TC_POLICE_RECLASSIFY; - NEXT_ARG(); - } else if (matches(*argv, "pipe") == 0) { - p.action = TC_POLICE_PIPE; - NEXT_ARG(); - } else if (matches(*argv, "drop") == 0 || - matches(*argv, "shot") == 0) { - p.action = TC_POLICE_SHOT; - NEXT_ARG(); - } else if (matches(*argv, "continue") == 0) { - p.action = TC_POLICE_UNSPEC; - NEXT_ARG(); - } else if (matches(*argv, "pass") == 0) { - p.action = TC_POLICE_OK; - NEXT_ARG(); - } - - } + if (argc && p.eaction == TCA_EGRESS_MIRROR + && !action_a2n(*argv, &p.action, false)) + NEXT_ARG(); if (argc) { if (iok && matches(*argv, "index") == 0) { @@ -197,7 +177,7 @@ tail = NLMSG_TAIL(n); addattr_l(n, MAX_MSG, tca_id, NULL, 0); - addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof (p)); + addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof(p)); tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; *argc_p = argc; @@ -215,20 +195,21 @@ char **argv = *argv_p; if (argc < 0) { - fprintf(stderr,"mirred bad argument count %d\n", argc); + fprintf(stderr, "mirred bad argument count %d\n", argc); return -1; } if (matches(*argv, "mirred") == 0) { NEXT_ARG(); } else { - fprintf(stderr,"mirred bad argument %s\n", *argv); + fprintf(stderr, "mirred bad argument %s\n", *argv); return -1; } if (matches(*argv, "egress") == 0 || matches(*argv, "index") == 0) { int ret = parse_egress(a, &argc, &argv, tca_id, n); + if (ret == 0) { *argc_p = argc; *argv_p = argv; @@ -236,11 +217,11 @@ } } else if (matches(*argv, "ingress") == 0) { - fprintf(stderr,"mirred ingress not supported at the moment\n"); + fprintf(stderr, "mirred ingress not supported at the moment\n"); } else if (matches(*argv, "help") == 0) { usage(); } else { - fprintf(stderr,"mirred option not supported %s\n", *argv); + fprintf(stderr, "mirred option not supported %s\n", *argv); } return -1; @@ -248,12 +229,11 @@ } static int -print_mirred(struct action_util *au,FILE * f, struct rtattr *arg) +print_mirred(struct action_util *au, FILE * f, struct rtattr *arg) { struct tc_mirred *p; struct rtattr *tb[TCA_MIRRED_MAX + 1]; const char *dev; - SPRINT_BUF(b1); if (arg == NULL) return -1; @@ -276,15 +256,17 @@ return -1; } - fprintf(f, "mirred (%s to device %s) %s", mirred_n2a(p->eaction), dev,action_n2a(p->action, b1, sizeof (b1))); + fprintf(f, "mirred (%s to device %s) %s", + mirred_n2a(p->eaction), dev, action_n2a(p->action)); fprintf(f, "\n "); - fprintf(f, "\tindex %d ref %d bind %d",p->index,p->refcnt,p->bindcnt); + fprintf(f, "\tindex %d ref %d bind %d", p->index, p->refcnt, p->bindcnt); if (show_stats) { if (tb[TCA_MIRRED_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_MIRRED_TM]); - print_tm(f,tm); + + print_tm(f, tm); } } fprintf(f, "\n "); diff -Nru iproute2-4.3.0/tc/m_nat.c iproute2-4.9.0/tc/m_nat.c --- iproute2-4.3.0/tc/m_nat.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_nat.c 2016-12-12 23:07:42.000000000 +0000 @@ -41,7 +41,7 @@ } static int -parse_nat_args(int *argc_p, char ***argv_p,struct tc_nat *sel) +parse_nat_args(int *argc_p, char ***argv_p, struct tc_nat *sel) { int argc = *argc_p; char **argv = *argv_p; @@ -84,20 +84,18 @@ static int parse_nat(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { - struct tc_nat sel; + struct tc_nat sel = {}; int argc = *argc_p; char **argv = *argv_p; int ok = 0; struct rtattr *tail; - memset(&sel, 0, sizeof(sel)); - while (argc > 0) { if (matches(*argv, "nat") == 0) { NEXT_ARG(); if (parse_nat_args(&argc, &argv, &sel)) { - fprintf(stderr, "Illegal nat construct (%s) \n", + fprintf(stderr, "Illegal nat construct (%s)\n", *argv); explain(); return -1; @@ -117,30 +115,8 @@ return -1; } - if (argc) { - if (matches(*argv, "reclassify") == 0) { - sel.action = TC_ACT_RECLASSIFY; - argc--; - argv++; - } else if (matches(*argv, "pipe") == 0) { - sel.action = TC_ACT_PIPE; - argc--; - argv++; - } else if (matches(*argv, "drop") == 0 || - matches(*argv, "shot") == 0) { - sel.action = TC_ACT_SHOT; - argc--; - argv++; - } else if (matches(*argv, "continue") == 0) { - sel.action = TC_ACT_UNSPEC; - argc--; - argv++; - } else if (matches(*argv, "pass") == 0) { - sel.action = TC_ACT_OK; - argc--; - argv++; - } - } + if (argc && !action_a2n(*argv, &sel.action, false)) + NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { @@ -165,13 +141,13 @@ } static int -print_nat(struct action_util *au,FILE * f, struct rtattr *arg) +print_nat(struct action_util *au, FILE * f, struct rtattr *arg) { struct tc_nat *sel; struct rtattr *tb[TCA_NAT_MAX + 1]; char buf1[256]; char buf2[256]; - SPRINT_BUF(buf3); + int len; if (arg == NULL) @@ -190,15 +166,16 @@ fprintf(f, " nat %s %s/%d %s %s", sel->flags & TCA_NAT_FLAG_EGRESS ? "egress" : "ingress", - format_host(AF_INET, 4, &sel->old_addr, buf1, sizeof(buf1)), + format_host_r(AF_INET, 4, &sel->old_addr, buf1, sizeof(buf1)), len, - format_host(AF_INET, 4, &sel->new_addr, buf2, sizeof(buf2)), - action_n2a(sel->action, buf3, sizeof (buf3))); + format_host_r(AF_INET, 4, &sel->new_addr, buf2, sizeof(buf2)), + action_n2a(sel->action)); if (show_stats) { if (tb[TCA_NAT_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_NAT_TM]); - print_tm(f,tm); + + print_tm(f, tm); } } diff -Nru iproute2-4.3.0/tc/m_pedit.c iproute2-4.9.0/tc/m_pedit.c --- iproute2-4.3.0/tc/m_pedit.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_pedit.c 2016-12-12 23:07:42.000000000 +0000 @@ -32,41 +32,41 @@ static struct m_pedit_util *pedit_list; static int pedit_debug; -static void -explain(void) +static void explain(void) { - fprintf(stderr, "Usage: ... pedit munge \n"); + fprintf(stderr, "Usage: ... pedit munge [CONTROL]\n"); fprintf(stderr, "Where: MUNGE := |\n" - "\t:= [ATC]\n " - "\t\tOFFSETC:= offset \n " - "\t\tATC:= at offmask shift \n " - "\t\tNOTE: offval is byte offset, must be multiple of 4\n " - "\t\tNOTE: maskval is a 32 bit hex number\n " - "\t\tNOTE: shiftval is a is a shift value\n " - "\t\tCMD:= clear | invert | set | retain\n " - "\t:= ip | ip6 \n " - " \t\t| udp | tcp | icmp \n" + "\t:= [ATC]\n \t\tOFFSETC:= offset \n" + "\t\tATC:= at offmask shift \n" + "\t\tNOTE: offval is byte offset, must be multiple of 4\n" + "\t\tNOTE: maskval is a 32 bit hex number\n \t\tNOTE: shiftval is a is a shift value\n" + "\t\tCMD:= clear | invert | set | retain\n" + "\t:= ip | ip6 \n" + " \t\t| udp | tcp | icmp \n" + "\tCONTROL:= reclassify | pipe | drop | continue | pass\n" "For Example usage look at the examples directory\n"); } -static void -usage(void) +static void usage(void) { explain(); exit(-1); } -static int -pedit_parse_nopopt (int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +static int pedit_parse_nopopt(int *argc_p, char ***argv_p, + struct tc_pedit_sel *sel, + struct tc_pedit_key *tkey) { int argc = *argc_p; char **argv = *argv_p; if (argc) { - fprintf(stderr, "Unknown action hence option \"%s\" is unparsable\n", *argv); - return -1; + fprintf(stderr, + "Unknown action hence option \"%s\" is unparsable\n", + *argv); + return -1; } return 0; @@ -78,7 +78,7 @@ static void *pBODY; void *dlh; char buf[256]; - struct m_pedit_util *p; + struct m_pedit_util *p; for (p = pedit_list; p; p = p->next) { if (strcmp(p->id, str) == 0) @@ -107,18 +107,16 @@ return p; noexist: - p = malloc(sizeof(*p)); + p = calloc(1, sizeof(*p)); if (p) { - memset(p, 0, sizeof(*p)); - strncpy(p->id, str, sizeof(p->id)-1); + strncpy(p->id, str, sizeof(p->id) - 1); p->parse_peopt = pedit_parse_nopopt; goto reg; } return p; } -int -pack_key(struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +int pack_key(struct tc_pedit_sel *sel, struct tc_pedit_key *tkey) { int hwm = sel->nkeys; @@ -140,9 +138,8 @@ return 0; } - -int -pack_key32(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +int pack_key32(__u32 retain, struct tc_pedit_sel *sel, + struct tc_pedit_key *tkey) { if (tkey->off > (tkey->off & ~3)) { fprintf(stderr, @@ -152,16 +149,14 @@ tkey->val = htonl(tkey->val & retain); tkey->mask = htonl(tkey->mask | ~retain); - /* jamal remove this - it is not necessary given the if check above */ - tkey->off &= ~3; - return pack_key(sel,tkey); + return pack_key(sel, tkey); } -int -pack_key16(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +int pack_key16(__u32 retain, struct tc_pedit_sel *sel, + struct tc_pedit_key *tkey) { int ind, stride; - __u32 m[4] = {0xFFFF0000,0xFF0000FF,0x0000FFFF}; + __u32 m[4] = { 0x0000FFFF, 0xFF0000FF, 0xFFFF0000 }; if (tkey->val > 0xFFFF || tkey->mask > 0xFFFF) { fprintf(stderr, "pack_key16 bad value\n"); @@ -171,53 +166,49 @@ ind = tkey->off & 3; if (ind == 3) { - fprintf(stderr, "pack_key16 bad index value %d\n",ind); + fprintf(stderr, "pack_key16 bad index value %d\n", ind); return -1; } - stride = 8 * ind; - tkey->val = htons(tkey->val); - tkey->val <<= stride; - tkey->mask <<= stride; - retain <<= stride; - tkey->mask = retain|m[ind]; + stride = 8 * (2 - ind); + tkey->val = htonl((tkey->val & retain) << stride); + tkey->mask = htonl(((tkey->mask | ~retain) << stride) | m[ind]); tkey->off &= ~3; if (pedit_debug) - printf("pack_key16: Final val %08x mask %08x \n",tkey->val,tkey->mask); - return pack_key(sel,tkey); + printf("pack_key16: Final val %08x mask %08x\n", + tkey->val, tkey->mask); + return pack_key(sel, tkey); } -int -pack_key8(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +int pack_key8(__u32 retain, struct tc_pedit_sel *sel, struct tc_pedit_key *tkey) { int ind, stride; - __u32 m[4] = {0xFFFFFF00,0xFFFF00FF,0xFF00FFFF,0x00FFFFFF}; + __u32 m[4] = { 0x00FFFFFF, 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00 }; if (tkey->val > 0xFF || tkey->mask > 0xFF) { - fprintf(stderr, "pack_key8 bad value (val %x mask %x\n", tkey->val, tkey->mask); + fprintf(stderr, "pack_key8 bad value (val %x mask %x\n", + tkey->val, tkey->mask); return -1; } ind = tkey->off & 3; - stride = 8 * ind; - tkey->val <<= stride; - tkey->mask <<= stride; - retain <<= stride; - tkey->mask = retain|m[ind]; + stride = 8 * (3 - ind); + tkey->val = htonl((tkey->val & retain) << stride); + tkey->mask = htonl(((tkey->mask | ~retain) << stride) | m[ind]); tkey->off &= ~3; if (pedit_debug) - printf("pack_key8: Final word off %d val %08x mask %08x \n",tkey->off , tkey->val,tkey->mask); - return pack_key(sel,tkey); + printf("pack_key8: Final word off %d val %08x mask %08x\n", + tkey->off, tkey->val, tkey->mask); + return pack_key(sel, tkey); } -int -parse_val(int *argc_p, char ***argv_p, __u32 * val, int type) +int parse_val(int *argc_p, char ***argv_p, __u32 *val, int type) { int argc = *argc_p; char **argv = *argv_p; @@ -225,30 +216,30 @@ if (argc <= 0) return -1; - if (TINT == type) - return get_integer((int *) val, *argv, 0); + if (type == TINT) + return get_integer((int *)val, *argv, 0); - if (TU32 == type) + if (type == TU32) return get_u32(val, *argv, 0); - if (TIPV4 == type) { + if (type == TIPV4) { inet_prefix addr; - if (get_prefix_1(&addr, *argv, AF_INET)) { + + if (get_prefix_1(&addr, *argv, AF_INET)) return -1; - } - *val=addr.data[0]; + + *val = addr.data[0]; return 0; } - if (TIPV6 == type) { - /* not implemented yet */ - return -1; - } + + if (type == TIPV6) + return -1; /* not implemented yet */ return -1; } -int -parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type, __u32 retain, + struct tc_pedit_sel *sel, struct tc_pedit_key *tkey) { __u32 mask = 0, val = 0; __u32 o = 0xFF; @@ -260,7 +251,8 @@ return -1; if (pedit_debug) - printf("parse_cmd argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len); + printf("parse_cmd argc %d %s offset %d length %d\n", + argc, *argv, tkey->off, len); if (len == 2) o = 0xFFFF; @@ -268,57 +260,61 @@ o = 0xFFFFFFFF; if (matches(*argv, "invert") == 0) { - retain = val = mask = o; + val = mask = o; } else if (matches(*argv, "set") == 0) { NEXT_ARG(); if (parse_val(&argc, &argv, &val, type)) return -1; } else if (matches(*argv, "preserve") == 0) { - retain = mask = o; + retain = 0; } else { if (matches(*argv, "clear") != 0) return -1; } - argc--; argv++; + argc--; + argv++; if (argc && matches(*argv, "retain") == 0) { NEXT_ARG(); if (parse_val(&argc, &argv, &retain, TU32)) return -1; - argc--; argv++; + argc--; + argv++; } tkey->val = val; + tkey->mask = mask; + + if (type == TIPV4) + tkey->val = ntohl(tkey->val); if (len == 1) { - tkey->mask = 0xFF; - res = pack_key8(retain,sel,tkey); + res = pack_key8(retain, sel, tkey); goto done; } if (len == 2) { - tkey->mask = mask; - res = pack_key16(retain,sel,tkey); + res = pack_key16(retain, sel, tkey); goto done; } if (len == 4) { - tkey->mask = mask; - res = pack_key32(retain,sel,tkey); + res = pack_key32(retain, sel, tkey); goto done; } return -1; done: if (pedit_debug) - printf("parse_cmd done argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len); + printf("parse_cmd done argc %d %s offset %d length %d\n", + argc, *argv, tkey->off, len); *argc_p = argc; *argv_p = argv; return res; } -int -parse_offset(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +int parse_offset(int *argc_p, char ***argv_p, struct tc_pedit_sel *sel, + struct tc_pedit_key *tkey) { int off; __u32 len, retain; @@ -339,7 +335,6 @@ if (argc <= 0) return -1; - if (matches(*argv, "u32") == 0) { len = 4; retain = 0xFFFFFFFF; @@ -347,12 +342,12 @@ } if (matches(*argv, "u16") == 0) { len = 2; - retain = 0x0; + retain = 0xffff; goto done; } if (matches(*argv, "u8") == 0) { len = 1; - retain = 0x0; + retain = 0xff; goto done; } @@ -365,7 +360,7 @@ /* [at offmask shift ] */ if (matches(*argv, "at") == 0) { - __u32 atv=0,offmask=0x0,shift=0; + __u32 atv = 0, offmask = 0x0, shift = 0; NEXT_ARG(); if (get_u32(&atv, *argv, 0)) @@ -387,17 +382,16 @@ NEXT_ARG(); } - res = parse_cmd(&argc, &argv, len, TU32,retain,sel,tkey); + res = parse_cmd(&argc, &argv, len, TU32, retain, sel, tkey); *argc_p = argc; *argv_p = argv; return res; } -static int -parse_munge(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel) +static int parse_munge(int *argc_p, char ***argv_p, struct tc_pedit_sel *sel) { - struct tc_pedit_key tkey; + struct tc_pedit_key tkey = {}; int argc = *argc_p; char **argv = *argv_p; int res = -1; @@ -405,25 +399,24 @@ if (argc <= 0) return -1; - memset(&tkey, 0, sizeof(tkey)); - if (matches(*argv, "offset") == 0) { NEXT_ARG(); - res = parse_offset(&argc, &argv,sel,&tkey); + res = parse_offset(&argc, &argv, sel, &tkey); goto done; } else { char k[16]; struct m_pedit_util *p = NULL; - strncpy(k, *argv, sizeof (k) - 1); + strncpy(k, *argv, sizeof(k) - 1); - if (argc > 0 ) { + if (argc > 0) { p = get_pedit_kind(k); - if (NULL == p) + if (p == NULL) goto bad_val; - res = p->parse_peopt(&argc, &argv, sel,&tkey); + NEXT_ARG(); + res = p->parse_peopt(&argc, &argv, sel, &tkey); if (res < 0) { - fprintf(stderr,"bad pedit parsing\n"); + fprintf(stderr, "bad pedit parsing\n"); goto bad_val; } goto done; @@ -440,24 +433,22 @@ return res; } -int -parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, + struct nlmsghdr *n) { struct { struct tc_pedit_sel sel; struct tc_pedit_key keys[MAX_OFFS]; - } sel; + } sel = {}; int argc = *argc_p; char **argv = *argv_p; int ok = 0, iok = 0; struct rtattr *tail; - memset(&sel, 0, sizeof(sel)); - while (argc > 0) { if (pedit_debug > 1) - fprintf(stderr, "while pedit (%d:%s)\n",argc, *argv); + fprintf(stderr, "while pedit (%d:%s)\n", argc, *argv); if (matches(*argv, "pedit") == 0) { NEXT_ARG(); ok++; @@ -466,13 +457,15 @@ usage(); } else if (matches(*argv, "munge") == 0) { if (!ok) { - fprintf(stderr, "Illegal pedit construct (%s) \n", *argv); + fprintf(stderr, "Bad pedit construct (%s)\n", + *argv); explain(); return -1; } NEXT_ARG(); - if (parse_munge(&argc, &argv,&sel.sel)) { - fprintf(stderr, "Illegal pedit construct (%s) \n", *argv); + if (parse_munge(&argc, &argv, &sel.sel)) { + fprintf(stderr, "Bad pedit construct (%s)\n", + *argv); explain(); return -1; } @@ -488,25 +481,8 @@ return -1; } - if (argc) { - if (matches(*argv, "reclassify") == 0) { - sel.sel.action = TC_ACT_RECLASSIFY; - NEXT_ARG(); - } else if (matches(*argv, "pipe") == 0) { - sel.sel.action = TC_ACT_PIPE; - NEXT_ARG(); - } else if (matches(*argv, "drop") == 0 || - matches(*argv, "shot") == 0) { - sel.sel.action = TC_ACT_SHOT; - NEXT_ARG(); - } else if (matches(*argv, "continue") == 0) { - sel.sel.action = TC_ACT_UNSPEC; - NEXT_ARG(); - } else if (matches(*argv, "pass") == 0) { - sel.sel.action = TC_ACT_OK; - NEXT_ARG(); - } - } + if (argc && !action_a2n(*argv, &sel.sel.action, false)) + NEXT_ARG(); if (argc) { if (matches(*argv, "index") == 0) { @@ -523,20 +499,20 @@ tail = NLMSG_TAIL(n); addattr_l(n, MAX_MSG, tca_id, NULL, 0); - addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS,&sel, sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_pedit_key)); - tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS, &sel, + sizeof(sel.sel) + + sel.sel.nkeys * sizeof(struct tc_pedit_key)); + tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail; *argc_p = argc; *argv_p = argv; return 0; } -int -print_pedit(struct action_util *au,FILE * f, struct rtattr *arg) +int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_pedit_sel *sel; struct rtattr *tb[TCA_PEDIT_MAX + 1]; - SPRINT_BUF(b1); if (arg == NULL) return -1; @@ -549,37 +525,39 @@ } sel = RTA_DATA(tb[TCA_PEDIT_PARMS]); - fprintf(f, " pedit action %s keys %d\n ", action_n2a(sel->action, b1, sizeof (b1)),sel->nkeys); - fprintf(f, "\t index %d ref %d bind %d", sel->index,sel->refcnt, sel->bindcnt); + fprintf(f, " pedit action %s keys %d\n ", + action_n2a(sel->action), sel->nkeys); + fprintf(f, "\t index %d ref %d bind %d", sel->index, sel->refcnt, + sel->bindcnt); if (show_stats) { if (tb[TCA_PEDIT_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]); - print_tm(f,tm); + + print_tm(f, tm); } } if (sel->nkeys) { int i; struct tc_pedit_key *key = sel->keys; - for (i=0; inkeys; i++, key++) { - fprintf(f, "\n\t key #%d",i); + for (i = 0; i < sel->nkeys; i++, key++) { + fprintf(f, "\n\t key #%d", i); fprintf(f, " at %d: val %08x mask %08x", - (unsigned int)key->off, - (unsigned int)ntohl(key->val), - (unsigned int)ntohl(key->mask)); + (unsigned int)key->off, + (unsigned int)ntohl(key->val), + (unsigned int)ntohl(key->mask)); } } else { - fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,sel->nkeys); + fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index, + sel->nkeys); } - fprintf(f, "\n "); return 0; } -int -pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats) +int pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats) { return 0; } diff -Nru iproute2-4.3.0/tc/m_police.c iproute2-4.9.0/tc/m_police.c --- iproute2-4.3.0/tc/m_police.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_police.c 2016-12-12 23:07:42.000000000 +0000 @@ -36,11 +36,12 @@ { fprintf(stderr, "Usage: ... police rate BPS burst BYTES[/BYTES] [ mtu BYTES[/BYTES] ]\n"); fprintf(stderr, " [ peakrate BPS ] [ avrate BPS ] [ overhead BYTES ]\n"); - fprintf(stderr, " [ linklayer TYPE ] [ ACTIONTERM ]\n"); + fprintf(stderr, " [ linklayer TYPE ] [ CONTROL ]\n"); - fprintf(stderr, "New Syntax ACTIONTERM := conform-exceed [/NOTEXCEEDACT] \n"); - fprintf(stderr, "Where: *EXCEEDACT := pipe | ok | reclassify | drop | continue \n"); - fprintf(stderr, "Where: pipe is only valid for new syntax \n"); + fprintf(stderr, "Where: CONTROL := conform-exceed [/NOTEXCEEDACT]\n"); + fprintf(stderr, " Define how to handle packets which exceed ()\n"); + fprintf(stderr, " or conform () the configured bandwidth limit.\n"); + fprintf(stderr, " EXCEEDACT/NOTEXCEEDACT := { pipe | ok | reclassify | drop | continue }\n"); exit(-1); } @@ -49,56 +50,6 @@ fprintf(stderr, "Illegal \"%s\"\n", arg); } -static const char *police_action_n2a(int action, char *buf, int len) -{ - switch (action) { - case -1: - return "continue"; - break; - case TC_POLICE_OK: - return "pass"; - break; - case TC_POLICE_SHOT: - return "drop"; - break; - case TC_POLICE_RECLASSIFY: - return "reclassify"; - case TC_POLICE_PIPE: - return "pipe"; - default: - snprintf(buf, len, "%d", action); - return buf; - } -} - -static int police_action_a2n(const char *arg, int *result) -{ - int res; - - if (matches(arg, "continue") == 0) - res = -1; - else if (matches(arg, "drop") == 0) - res = TC_POLICE_SHOT; - else if (matches(arg, "shot") == 0) - res = TC_POLICE_SHOT; - else if (matches(arg, "pass") == 0) - res = TC_POLICE_OK; - else if (strcmp(arg, "ok") == 0) - res = TC_POLICE_OK; - else if (matches(arg, "reclassify") == 0) - res = TC_POLICE_RECLASSIFY; - else if (matches(arg, "pipe") == 0) - res = TC_POLICE_PIPE; - else { - char dummy; - if (sscanf(arg, "%d%c", &res, &dummy) != 1) - return -1; - } - *result = res; - return 0; -} - - static int get_police_result(int *action, int *result, char *arg) { char *p = strchr(arg, '/'); @@ -106,7 +57,7 @@ if (p) *p = 0; - if (police_action_a2n(arg, action)) { + if (action_a2n(arg, action, true)) { if (p) *p = '/'; return -1; @@ -114,33 +65,30 @@ if (p) { *p = '/'; - if (police_action_a2n(p+1, result)) + if (action_a2n(p+1, result, true)) return -1; } return 0; } - -int act_parse_police(struct action_util *a,int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p, + int tca_id, struct nlmsghdr *n) { int argc = *argc_p; char **argv = *argv_p; int res = -1; - int ok=0; - struct tc_police p; + int ok = 0; + struct tc_police p = { .action = TC_POLICE_RECLASSIFY }; __u32 rtab[256]; __u32 ptab[256]; __u32 avrate = 0; int presult = 0; - unsigned buffer=0, mtu=0, mpu=0; - unsigned short overhead=0; + unsigned buffer = 0, mtu = 0, mpu = 0; + unsigned short overhead = 0; unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */ - int Rcell_log=-1, Pcell_log = -1; + int Rcell_log = -1, Pcell_log = -1; struct rtattr *tail; - memset(&p, 0, sizeof(p)); - p.action = TC_POLICE_RECLASSIFY; - if (a) /* new way of doing things */ NEXT_ARG(); @@ -257,10 +205,21 @@ if (!ok) return -1; - if (p.rate.rate && !buffer) { + if (p.rate.rate && avrate) + return -1; + + /* Must at least do late binding, use TB or ewma policing */ + if (!p.rate.rate && !avrate && !p.index) { + fprintf(stderr, "\"rate\" or \"avrate\" MUST be specified.\n"); + return -1; + } + + /* When the TB policer is used, burst is required */ + if (p.rate.rate && !buffer && !avrate) { fprintf(stderr, "\"burst\" requires \"rate\".\n"); return -1; } + if (p.peakrate.rate) { if (!p.rate.rate) { fprintf(stderr, "\"peakrate\" requires \"rate\".\n"); @@ -275,8 +234,9 @@ if (p.rate.rate) { p.rate.mpu = mpu; p.rate.overhead = overhead; - if (tc_calc_rtable(&p.rate, rtab, Rcell_log, mtu, linklayer) < 0) { - fprintf(stderr, "TBF: failed to calculate rate table.\n"); + if (tc_calc_rtable(&p.rate, rtab, Rcell_log, mtu, + linklayer) < 0) { + fprintf(stderr, "POLICE: failed to calculate rate table.\n"); return -1; } p.burst = tc_calc_xmittime(p.rate.rate, buffer); @@ -285,7 +245,8 @@ if (p.peakrate.rate) { p.peakrate.mpu = mpu; p.peakrate.overhead = overhead; - if (tc_calc_rtable(&p.peakrate, ptab, Pcell_log, mtu, linklayer) < 0) { + if (tc_calc_rtable(&p.peakrate, ptab, Pcell_log, mtu, + linklayer) < 0) { fprintf(stderr, "POLICE: failed to calculate peak rate table.\n"); return -1; } @@ -297,7 +258,7 @@ if (p.rate.rate) addattr_l(n, MAX_MSG, TCA_POLICE_RATE, rtab, 1024); if (p.peakrate.rate) - addattr_l(n, MAX_MSG, TCA_POLICE_PEAKRATE, ptab, 1024); + addattr_l(n, MAX_MSG, TCA_POLICE_PEAKRATE, ptab, 1024); if (avrate) addattr32(n, MAX_MSG, TCA_POLICE_AVRATE, avrate); if (presult) @@ -313,17 +274,16 @@ int parse_police(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { - return act_parse_police(NULL,argc_p,argv_p,tca_id,n); + return act_parse_police(NULL, argc_p, argv_p, tca_id, n); } -int -print_police(struct action_util *a, FILE *f, struct rtattr *arg) +int print_police(struct action_util *a, FILE *f, struct rtattr *arg) { SPRINT_BUF(b1); SPRINT_BUF(b2); struct tc_police *p; struct rtattr *tb[TCA_POLICE_MAX+1]; - unsigned buffer; + unsigned int buffer; unsigned int linklayer; if (arg == NULL) @@ -350,25 +310,42 @@ fprintf(f, "mtu %s ", sprint_size(p->mtu, b1)); if (show_raw) fprintf(f, "[%08x] ", p->burst); + if (p->peakrate.rate) fprintf(f, "peakrate %s ", sprint_rate(p->peakrate.rate, b1)); + if (tb[TCA_POLICE_AVRATE]) - fprintf(f, "avrate %s ", sprint_rate(rta_getattr_u32(tb[TCA_POLICE_AVRATE]), b1)); - fprintf(f, "action %s", police_action_n2a(p->action, b1, sizeof(b1))); + fprintf(f, "avrate %s ", + sprint_rate(rta_getattr_u32(tb[TCA_POLICE_AVRATE]), + b1)); + fprintf(f, "action %s", action_n2a(p->action)); + if (tb[TCA_POLICE_RESULT]) { - fprintf(f, "/%s ", police_action_n2a(*(int*)RTA_DATA(tb[TCA_POLICE_RESULT]), b1, sizeof(b1))); + __u32 action = rta_getattr_u32(tb[TCA_POLICE_RESULT]); + + fprintf(f, "/%s ", action_n2a(action)); } else fprintf(f, " "); + fprintf(f, "overhead %ub ", p->rate.overhead); linklayer = (p->rate.linklayer & TC_LINKLAYER_MASK); if (linklayer > TC_LINKLAYER_ETHERNET || show_details) fprintf(f, "linklayer %s ", sprint_linklayer(linklayer, b2)); - fprintf(f, "\nref %d bind %d\n",p->refcnt, p->bindcnt); + fprintf(f, "\n\tref %d bind %d", p->refcnt, p->bindcnt); + if (show_stats) { + if (tb[TCA_POLICE_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_POLICE_TM]); + + print_tm(f, tm); + } + } + fprintf(f, "\n"); + return 0; } -int -tc_print_police(FILE *f, struct rtattr *arg) { - return print_police(&police_action_util,f,arg); +int tc_print_police(FILE *f, struct rtattr *arg) +{ + return print_police(&police_action_util, f, arg); } diff -Nru iproute2-4.3.0/tc/m_simple.c iproute2-4.9.0/tc/m_simple.c --- iproute2-4.3.0/tc/m_simple.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_simple.c 2016-12-12 23:07:42.000000000 +0000 @@ -81,9 +81,10 @@ #endif static void explain(void) { - fprintf(stderr, "Usage: ... simple STRING\n" - "STRING being an arbitrary string\n" - "example: \"simple blah\"\n"); + fprintf(stderr, "Usage:... simple [sdata STRING] [CONTROL] [index INDEX]\n"); + fprintf(stderr, "\tSTRING being an arbitrary string\n" + "\tCONTROL := reclassify|pipe|drop|continue|ok\n" + "\tINDEX := optional index value used\n"); } static void usage(void) @@ -103,42 +104,46 @@ struct rtattr *tail; char *simpdata = NULL; - while (argc > 0) { if (matches(*argv, "simple") == 0) { NEXT_ARG(); + } else if (matches(*argv, "sdata") == 0) { + NEXT_ARG(); + ok += 1; simpdata = *argv; - ok = 1; argc--; argv++; - break; } else if (matches(*argv, "help") == 0) { usage(); } else { break; } - } - if (!ok) { - explain(); - return -1; - } + if (argc && !action_a2n(*argv, &sel.action, false)) + NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); if (get_u32(&sel.index, *argv, 10)) { - fprintf(stderr, "simple: Illegal \"index\"\n"); + fprintf(stderr, "simple: Illegal \"index\" (%s)\n", + *argv); return -1; } + ok += 1; argc--; argv++; } } - if (strlen(simpdata) > (SIMP_MAX_DATA - 1)) { - fprintf(stderr, "simple: Illegal string len %zu <%s> \n", + if (!ok) { + explain(); + return -1; + } + + if (simpdata && (strlen(simpdata) > (SIMP_MAX_DATA - 1))) { + fprintf(stderr, "simple: Illegal string len %zu <%s>\n", strlen(simpdata), simpdata); return -1; } @@ -148,7 +153,8 @@ tail = NLMSG_TAIL(n); addattr_l(n, MAX_MSG, tca_id, NULL, 0); addattr_l(n, MAX_MSG, TCA_DEF_PARMS, &sel, sizeof(sel)); - addattr_l(n, MAX_MSG, TCA_DEF_DATA, simpdata, SIMP_MAX_DATA); + if (simpdata) + addattr_l(n, MAX_MSG, TCA_DEF_DATA, simpdata, SIMP_MAX_DATA); tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail; *argc_p = argc; @@ -156,7 +162,7 @@ return 0; } -static int print_simple(struct action_util *au, FILE * f, struct rtattr *arg) +static int print_simple(struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_defact *sel; struct rtattr *tb[TCA_DEF_MAX + 1]; @@ -187,6 +193,7 @@ if (show_stats) { if (tb[TCA_DEF_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_DEF_TM]); + print_tm(f, tm); } } diff -Nru iproute2-4.3.0/tc/m_skbedit.c iproute2-4.9.0/tc/m_skbedit.c --- iproute2-4.3.0/tc/m_skbedit.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_skbedit.c 2016-12-12 23:07:42.000000000 +0000 @@ -26,14 +26,17 @@ #include "utils.h" #include "tc_util.h" #include +#include -static void -explain(void) +static void explain(void) { - fprintf(stderr, "Usage: ... skbedit <[QM] [PM] [MM]>\n" + fprintf(stderr, "Usage: ... skbedit <[QM] [PM] [MM] [PT]>\n" "QM = queue_mapping QUEUE_MAPPING\n" - "PM = priority PRIORITY \n" - "MM = mark MARK \n" + "PM = priority PRIORITY\n" + "MM = mark MARK\n" + "PT = ptype PACKETYPE\n" + "PACKETYPE = is one of:\n" + " host, otherhost, broadcast, multicast\n" "QUEUE_MAPPING = device transmit queue to use\n" "PRIORITY = classID to assign to priority field\n" "MARK = firewall mark to set\n"); @@ -55,7 +58,7 @@ int ok = 0; struct rtattr *tail; unsigned int tmp; - __u16 queue_mapping; + __u16 queue_mapping, ptype; __u32 flags = 0, priority, mark; struct tc_skbedit sel = { 0 }; @@ -90,6 +93,24 @@ return -1; } ok++; + } else if (matches(*argv, "ptype") == 0) { + + NEXT_ARG(); + if (matches(*argv, "host") == 0) { + ptype = PACKET_HOST; + } else if (matches(*argv, "broadcast") == 0) { + ptype = PACKET_BROADCAST; + } else if (matches(*argv, "multicast") == 0) { + ptype = PACKET_MULTICAST; + } else if (matches(*argv, "otherhost") == 0) { + ptype = PACKET_OTHERHOST; + } else { + fprintf(stderr, "Illegal ptype (%s)\n", + *argv); + return -1; + } + flags |= SKBEDIT_F_PTYPE; + ok++; } else if (matches(*argv, "help") == 0) { usage(); } else { @@ -100,25 +121,8 @@ } sel.action = TC_ACT_PIPE; - if (argc) { - if (matches(*argv, "reclassify") == 0) { - sel.action = TC_ACT_RECLASSIFY; - NEXT_ARG(); - } else if (matches(*argv, "pipe") == 0) { - sel.action = TC_ACT_PIPE; - NEXT_ARG(); - } else if (matches(*argv, "drop") == 0 || - matches(*argv, "shot") == 0) { - sel.action = TC_ACT_SHOT; - NEXT_ARG(); - } else if (matches(*argv, "continue") == 0) { - sel.action = TC_ACT_UNSPEC; - NEXT_ARG(); - } else if (matches(*argv, "pass") == 0) { - sel.action = TC_ACT_OK; - NEXT_ARG(); - } - } + if (argc && !action_a2n(*argv, &sel.action, false)) + NEXT_ARG(); if (argc) { if (matches(*argv, "index") == 0) { @@ -151,6 +155,9 @@ if (flags & SKBEDIT_F_MARK) addattr_l(n, MAX_MSG, TCA_SKBEDIT_MARK, &mark, sizeof(mark)); + if (flags & SKBEDIT_F_PTYPE) + addattr_l(n, MAX_MSG, TCA_SKBEDIT_PTYPE, + &ptype, sizeof(ptype)); tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail; *argc_p = argc; @@ -161,10 +168,11 @@ static int print_skbedit(struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_SKBEDIT_MAX + 1]; + SPRINT_BUF(b1); __u32 *priority; __u32 *mark; - __u16 *queue_mapping; + __u16 *queue_mapping, *ptype; struct tc_skbedit *p = NULL; if (arg == NULL) @@ -192,12 +200,27 @@ mark = RTA_DATA(tb[TCA_SKBEDIT_MARK]); fprintf(f, " mark %d", *mark); } + if (tb[TCA_SKBEDIT_PTYPE] != NULL) { + ptype = RTA_DATA(tb[TCA_SKBEDIT_PTYPE]); + if (*ptype == PACKET_HOST) + fprintf(f, " ptype host"); + else if (*ptype == PACKET_BROADCAST) + fprintf(f, " ptype broadcast"); + else if (*ptype == PACKET_MULTICAST) + fprintf(f, " ptype multicast"); + else if (*ptype == PACKET_OTHERHOST) + fprintf(f, " ptype otherhost"); + else + fprintf(f, " ptype %d", *ptype); + } - fprintf(f, "\n\t index %d ref %d bind %d", p->index, p->refcnt, p->bindcnt); + fprintf(f, "\n\t index %d ref %d bind %d", + p->index, p->refcnt, p->bindcnt); if (show_stats) { if (tb[TCA_SKBEDIT_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_SKBEDIT_TM]); + print_tm(f, tm); } } diff -Nru iproute2-4.3.0/tc/m_skbmod.c iproute2-4.9.0/tc/m_skbmod.c --- iproute2-4.3.0/tc/m_skbmod.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/tc/m_skbmod.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,259 @@ +/* + * m_skbmod.c skb modifier action module + * + * This program is free software; you can distribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: J Hadi Salim (jhs@mojatatu.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "tc_util.h" +#include + +static void skbmod_explain(void) +{ + fprintf(stderr, + "Usage:... skbmod {[set ] [swap ]} [CONTROL] [index INDEX]\n" + "where SETTABLE is: [dmac DMAC] [smac SMAC] [etype ETYPE]\n" + "where SWAPABLE is: \"mac\" to swap mac addresses\n" + "note: \"swap mac\" is done after any outstanding D/SMAC change\n" + "\tDMAC := 6 byte Destination MAC address\n" + "\tSMAC := optional 6 byte Source MAC address\n" + "\tETYPE := optional 16 bit ethertype\n" + "\tCONTROL := reclassify|pipe|drop|continue|ok\n" + "\tINDEX := skbmod index value to use\n"); +} + +static void skbmod_usage(void) +{ + skbmod_explain(); + exit(-1); +} + +static int parse_skbmod(struct action_util *a, int *argc_p, char ***argv_p, + int tca_id, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + int ok = 0; + struct tc_skbmod p; + struct rtattr *tail; + char dbuf[ETH_ALEN]; + char sbuf[ETH_ALEN]; + __u16 skbmod_etype = 0; + char *daddr = NULL; + char *saddr = NULL; + + memset(&p, 0, sizeof(p)); + p.action = TC_ACT_PIPE; /* good default */ + + if (argc <= 0) + return -1; + + while (argc > 0) { + if (matches(*argv, "skbmod") == 0) { + NEXT_ARG(); + continue; + } else if (matches(*argv, "swap") == 0) { + NEXT_ARG(); + continue; + } else if (matches(*argv, "mac") == 0) { + p.flags |= SKBMOD_F_SWAPMAC; + ok += 1; + } else if (matches(*argv, "set") == 0) { + NEXT_ARG(); + continue; + } else if (matches(*argv, "etype") == 0) { + NEXT_ARG(); + if (get_u16(&skbmod_etype, *argv, 0)) + invarg("ethertype is invalid", *argv); + fprintf(stderr, "skbmod etype 0x%x\n", skbmod_etype); + p.flags |= SKBMOD_F_ETYPE; + ok += 1; + } else if (matches(*argv, "dmac") == 0) { + NEXT_ARG(); + daddr = *argv; + if (sscanf(daddr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + dbuf, dbuf + 1, dbuf + 2, + dbuf + 3, dbuf + 4, dbuf + 5) != 6) { + fprintf(stderr, "Invalid dst mac address %s\n", + daddr); + return -1; + } + p.flags |= SKBMOD_F_DMAC; + fprintf(stderr, "dst MAC address <%s>\n", daddr); + ok += 1; + + } else if (matches(*argv, "smac") == 0) { + NEXT_ARG(); + saddr = *argv; + if (sscanf(saddr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + sbuf, sbuf + 1, sbuf + 2, + sbuf + 3, sbuf + 4, sbuf + 5) != 6) { + fprintf(stderr, "Invalid smac address %s\n", + saddr); + return -1; + } + p.flags |= SKBMOD_F_SMAC; + fprintf(stderr, "src MAC address <%s>\n", saddr); + ok += 1; + } else if (matches(*argv, "help") == 0) { + skbmod_usage(); + } else { + break; + } + + argc--; + argv++; + } + + if (argc) { + if (matches(*argv, "reclassify") == 0) { + p.action = TC_ACT_RECLASSIFY; + argc--; + argv++; + } else if (matches(*argv, "pipe") == 0) { + p.action = TC_ACT_PIPE; + argc--; + argv++; + } else if (matches(*argv, "drop") == 0 || + matches(*argv, "shot") == 0) { + p.action = TC_ACT_SHOT; + argc--; + argv++; + } else if (matches(*argv, "continue") == 0) { + p.action = TC_ACT_UNSPEC; + argc--; + argv++; + } else if (matches(*argv, "pass") == 0 || + matches(*argv, "ok") == 0) { + p.action = TC_ACT_OK; + argc--; + argv++; + } + } + + if (argc) { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 0)) { + fprintf(stderr, "skbmod: Illegal \"index\"\n"); + return -1; + } + ok++; + argc--; + argv++; + } + } + + if (!ok) { + fprintf(stderr, "skbmod requires at least one option\n"); + skbmod_usage(); + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_SKBMOD_PARMS, &p, sizeof(p)); + + if (daddr) + addattr_l(n, MAX_MSG, TCA_SKBMOD_DMAC, dbuf, ETH_ALEN); + if (skbmod_etype) + addattr16(n, MAX_MSG, TCA_SKBMOD_ETYPE, skbmod_etype); + if (saddr) + addattr_l(n, MAX_MSG, TCA_SKBMOD_SMAC, sbuf, ETH_ALEN); + + tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +static int print_skbmod(struct action_util *au, FILE *f, struct rtattr *arg) +{ + struct tc_skbmod *p = NULL; + struct rtattr *tb[TCA_SKBMOD_MAX + 1]; + __u16 skbmod_etype = 0; + int has_optional = 0; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_SKBMOD_MAX, arg); + + if (tb[TCA_SKBMOD_PARMS] == NULL) { + fprintf(f, "[NULL skbmod parameters]"); + return -1; + } + + p = RTA_DATA(tb[TCA_SKBMOD_PARMS]); + + fprintf(f, "skbmod action %s ", action_n2a(p->action)); + + if (tb[TCA_SKBMOD_ETYPE]) { + skbmod_etype = rta_getattr_u16(tb[TCA_SKBMOD_ETYPE]); + has_optional = 1; + fprintf(f, "set etype 0x%X ", skbmod_etype); + } + + if (has_optional) + fprintf(f, "\n\t "); + + if (tb[TCA_SKBMOD_DMAC]) { + has_optional = 1; + fprintf(f, "set dmac %s ", + ll_addr_n2a(RTA_DATA(tb[TCA_SKBMOD_DMAC]), + RTA_PAYLOAD(tb[TCA_SKBMOD_DMAC]), 0, b1, + sizeof(b1))); + + } + + if (tb[TCA_SKBMOD_SMAC]) { + has_optional = 1; + fprintf(f, "set smac %s ", + ll_addr_n2a(RTA_DATA(tb[TCA_SKBMOD_SMAC]), + RTA_PAYLOAD(tb[TCA_SKBMOD_SMAC]), 0, b2, + sizeof(b2))); + } + + if (p->flags & SKBMOD_F_SWAPMAC) + fprintf(f, "swap mac "); + + fprintf(f, "\n\t index %d ref %d bind %d", p->index, p->refcnt, + p->bindcnt); + if (show_stats) { + if (tb[TCA_SKBMOD_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_SKBMOD_TM]); + + print_tm(f, tm); + } + } + + fprintf(f, "\n"); + + return 0; +} + +struct action_util skbmod_action_util = { + .id = "skbmod", + .parse_aopt = parse_skbmod, + .print_aopt = print_skbmod, +}; diff -Nru iproute2-4.3.0/tc/m_vlan.c iproute2-4.9.0/tc/m_vlan.c --- iproute2-4.3.0/tc/m_vlan.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_vlan.c 2016-12-12 23:07:42.000000000 +0000 @@ -19,12 +19,20 @@ #include "tc_util.h" #include +static const char * const action_names[] = { + [TCA_VLAN_ACT_POP] = "pop", + [TCA_VLAN_ACT_PUSH] = "push", + [TCA_VLAN_ACT_MODIFY] = "modify", +}; + static void explain(void) { fprintf(stderr, "Usage: vlan pop\n"); - fprintf(stderr, " vlan push [ protocol VLANPROTO ] id VLANID\n"); + fprintf(stderr, " vlan push [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n"); + fprintf(stderr, " vlan modify [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n"); fprintf(stderr, " VLANPROTO is one of 802.1Q or 802.1AD\n"); fprintf(stderr, " with default: 802.1Q\n"); + fprintf(stderr, " CONTROL := reclassify | pipe | drop | continue | pass\n"); } static void usage(void) @@ -33,6 +41,11 @@ exit(-1); } +static bool has_push_attribs(int action) +{ + return action == TCA_VLAN_ACT_PUSH || action == TCA_VLAN_ACT_MODIFY; +} + static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { @@ -44,6 +57,8 @@ int id_set = 0; __u16 proto; int proto_set = 0; + __u8 prio; + int prio_set = 0; struct tc_vlan parm = { 0 }; if (matches(*argv, "vlan") != 0) @@ -68,9 +83,17 @@ return -1; } action = TCA_VLAN_ACT_PUSH; + } else if (matches(*argv, "modify") == 0) { + if (action) { + fprintf(stderr, "unexpected \"%s\" - action already specified\n", + *argv); + explain(); + return -1; + } + action = TCA_VLAN_ACT_MODIFY; } else if (matches(*argv, "id") == 0) { - if (action != TCA_VLAN_ACT_PUSH) { - fprintf(stderr, "\"%s\" is only valid for push\n", + if (!has_push_attribs(action)) { + fprintf(stderr, "\"%s\" is only valid for push/modify\n", *argv); explain(); return -1; @@ -80,8 +103,8 @@ invarg("id is invalid", *argv); id_set = 1; } else if (matches(*argv, "protocol") == 0) { - if (action != TCA_VLAN_ACT_PUSH) { - fprintf(stderr, "\"%s\" is only valid for push\n", + if (!has_push_attribs(action)) { + fprintf(stderr, "\"%s\" is only valid for push/modify\n", *argv); explain(); return -1; @@ -90,6 +113,17 @@ if (ll_proto_a2n(&proto, *argv)) invarg("protocol is invalid", *argv); proto_set = 1; + } else if (matches(*argv, "priority") == 0) { + if (!has_push_attribs(action)) { + fprintf(stderr, "\"%s\" is only valid for push/modify\n", + *argv); + explain(); + return -1; + } + NEXT_ARG(); + if (get_u8(&prio, *argv, 0) || (prio & ~0x7)) + invarg("prio is invalid", *argv); + prio_set = 1; } else if (matches(*argv, "help") == 0) { usage(); } else { @@ -100,30 +134,8 @@ } parm.action = TC_ACT_PIPE; - if (argc) { - if (matches(*argv, "reclassify") == 0) { - parm.action = TC_ACT_RECLASSIFY; - argc--; - argv++; - } else if (matches(*argv, "pipe") == 0) { - parm.action = TC_ACT_PIPE; - argc--; - argv++; - } else if (matches(*argv, "drop") == 0 || - matches(*argv, "shot") == 0) { - parm.action = TC_ACT_SHOT; - argc--; - argv++; - } else if (matches(*argv, "continue") == 0) { - parm.action = TC_ACT_UNSPEC; - argc--; - argv++; - } else if (matches(*argv, "pass") == 0) { - parm.action = TC_ACT_OK; - argc--; - argv++; - } - } + if (argc && !action_a2n(*argv, &parm.action, false)) + NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { @@ -137,8 +149,9 @@ } } - if (action == TCA_VLAN_ACT_PUSH && !id_set) { - fprintf(stderr, "id needs to be set for push\n"); + if (has_push_attribs(action) && !id_set) { + fprintf(stderr, "id needs to be set for %s\n", + action_names[action]); explain(); return -1; } @@ -159,6 +172,9 @@ addattr_l(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_PROTOCOL, &proto, 2); } + if (prio_set) + addattr8(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_PRIORITY, prio); + tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail; *argc_p = argc; @@ -186,12 +202,13 @@ fprintf(f, " vlan"); - switch(parm->v_action) { + switch (parm->v_action) { case TCA_VLAN_ACT_POP: fprintf(f, " pop"); break; case TCA_VLAN_ACT_PUSH: - fprintf(f, " push"); + case TCA_VLAN_ACT_MODIFY: + fprintf(f, " %s", action_names[parm->v_action]); if (tb[TCA_VLAN_PUSH_VLAN_ID]) { val = rta_getattr_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); fprintf(f, " id %u", val); @@ -201,9 +218,13 @@ ll_proto_n2a(rta_getattr_u16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]), b1, sizeof(b1))); } + if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY]) { + val = rta_getattr_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]); + fprintf(f, " priority %u", val); + } break; } - fprintf(f, " %s", action_n2a(parm->action, b1, sizeof (b1))); + fprintf(f, " %s", action_n2a(parm->action)); fprintf(f, "\n\t index %d ref %d bind %d", parm->index, parm->refcnt, parm->bindcnt); @@ -211,6 +232,7 @@ if (show_stats) { if (tb[TCA_VLAN_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_VLAN_TM]); + print_tm(f, tm); } } diff -Nru iproute2-4.3.0/tc/m_xt.c iproute2-4.9.0/tc/m_xt.c --- iproute2-4.3.0/tc/m_xt.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_xt.c 2016-12-12 23:07:42.000000000 +0000 @@ -39,19 +39,21 @@ #endif #ifndef __ALIGN_KERNEL -#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) -#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) +#define __ALIGN_KERNEL(x, a) \ + __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) +#define __ALIGN_KERNEL_MASK(x, mask) \ + (((x) + (mask)) & ~(mask)) #endif #ifndef ALIGN -#define ALIGN(x,a) __ALIGN_KERNEL((x), (a)) +#define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) #endif static const char *tname = "mangle"; char *lib_dir; -static const char *ipthooks[] = { +static const char * const ipthooks[] = { "NF_IP_PRE_ROUTING", "NF_IP_LOCAL_IN", "NF_IP_FORWARD", @@ -85,9 +87,9 @@ { size_t size = - XT_ALIGN(sizeof (struct xt_entry_target)) + target->size; + XT_ALIGN(sizeof(struct xt_entry_target)) + target->size; - if (NULL == t) { + if (t == NULL) { target->t = xtables_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, target->name); @@ -109,95 +111,107 @@ if (!lib_dir) { lib_dir = getenv("IPTABLES_LIB_DIR"); if (lib_dir) - fprintf(stderr, "using deprecated IPTABLES_LIB_DIR \n"); + fprintf(stderr, "using deprecated IPTABLES_LIB_DIR\n"); } if (lib_dir == NULL) lib_dir = XT_LIB_DIR; } -static int parse_ipt(struct action_util *a,int *argc_p, +static int get_xtables_target_opts(struct xtables_globals *globals, + struct xtables_target *m) +{ + struct option *opts; + +#if (XTABLES_VERSION_CODE >= 6) + opts = xtables_options_xfrm(globals->orig_opts, + globals->opts, + m->x6_options, + &m->option_offset); +#else + opts = xtables_merge_options(globals->opts, + m->extra_opts, + &m->option_offset); +#endif + if (!opts) + return -1; + globals->opts = opts; + return 0; +} + +static int parse_ipt(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct xtables_target *m = NULL; - struct ipt_entry fw; struct rtattr *tail; int c; - int rargc = *argc_p; char **argv = *argv_p; - int argc = 0, iargc = 0; + int argc; char k[16]; int size = 0; int iok = 0, ok = 0; __u32 hook = 0, index = 0; - struct option *opts = NULL; - xtables_init_all(&tcipt_globals, NFPROTO_IPV4); + /* copy tcipt_globals because .opts will be modified by iptables */ + struct xtables_globals tmp_tcipt_globals = tcipt_globals; + + xtables_init_all(&tmp_tcipt_globals, NFPROTO_IPV4); set_lib_dir(); - { - int i; - for (i = 0; i < rargc; i++) { - if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { - break; - } - } - iargc = argc = i; + /* parse only up until the next action */ + for (argc = 0; argc < *argc_p; argc++) { + if (!argv[argc] || !strcmp(argv[argc], "action")) + break; } if (argc <= 2) { - fprintf(stderr,"bad arguments to ipt %d vs %d \n", argc, rargc); + fprintf(stderr, + "too few arguments for xt, need at least '-j '\n"); return -1; } while (1) { - c = getopt_long(argc, argv, "j:", tcipt_globals.opts, NULL); + c = getopt_long(argc, argv, "j:", tmp_tcipt_globals.opts, NULL); if (c == -1) break; switch (c) { case 'j': m = xtables_find_target(optarg, XTF_TRY_LOAD); - if (NULL != m) { + if (!m) { + fprintf(stderr, + " failed to find target %s\n\n", + optarg); + return -1; + } - if (0 > build_st(m, NULL)) { - printf(" %s error \n", m->name); - return -1; - } -#if (XTABLES_VERSION_CODE >= 6) - opts = xtables_options_xfrm(tcipt_globals.orig_opts, - tcipt_globals.opts, - m->x6_options, - &m->option_offset); -#else - opts = xtables_merge_options(tcipt_globals.opts, - m->extra_opts, - &m->option_offset); -#endif - if (opts == NULL) { - fprintf(stderr, " failed to find additional options for target %s\n\n", optarg); + if (build_st(m, NULL) < 0) { + printf(" %s error\n", m->name); return -1; - } else - tcipt_globals.opts = opts; - } else { - fprintf(stderr," failed to find target %s\n\n", optarg); + } + + if (get_xtables_target_opts(&tmp_tcipt_globals, + m) < 0) { + fprintf(stderr, + " failed to find additional options for target %s\n\n", + optarg); return -1; } ok++; break; default: - memset(&fw, 0, sizeof (fw)); #if (XTABLES_VERSION_CODE >= 6) - if (m != NULL && m->x6_parse != NULL ) { - xtables_option_tpcall(c, argv, 0 , m, NULL); + if (m != NULL && m->x6_parse != NULL) { + xtables_option_tpcall(c, argv, 0, m, NULL); #else - if (m != NULL && m->parse != NULL ) { - m->parse(c - m->option_offset, argv, 0, &m->tflags, - NULL, &m->t); + if (m != NULL && m->parse != NULL) { + m->parse(c - m->option_offset, argv, 0, + &m->tflags, NULL, &m->t); #endif } else { - fprintf(stderr,"failed to find target %s\n\n", optarg); + fprintf(stderr, + "failed to find target %s\n\n", optarg); return -1; } @@ -206,7 +220,7 @@ } } - if (iargc > optind) { + if (argc > optind) { if (matches(argv[optind], "index") == 0) { if (get_u32(&index, argv[optind + 1], 10)) { fprintf(stderr, "Illegal \"index\"\n"); @@ -220,7 +234,7 @@ } if (!ok && !iok) { - fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv); + fprintf(stderr, " ipt Parser BAD!! (%s)\n", *argv); return -1; } @@ -235,6 +249,7 @@ { struct tcmsg *t = NLMSG_DATA(n); + if (t->tcm_parent != TC_H_ROOT && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) { hook = NF_IP_PRE_ROUTING; @@ -248,8 +263,12 @@ fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]); fprintf(stdout, "\ttarget: "); - if (m) - m->print(NULL, m->t, 0); + if (m) { + if (m->print) + m->print(NULL, m->t, 0); + else + printf("%s ", m->name); + } fprintf(stdout, " index %d\n", index); if (strlen(tname) > 16) { @@ -267,9 +286,8 @@ addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size); tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; - argc -= optind; argv += optind; - *argc_p = rargc - iargc; + *argc_p -= argc; *argv_p = argv; optind = 0; @@ -289,11 +307,11 @@ } static int -print_ipt(struct action_util *au,FILE * f, struct rtattr *arg) +print_ipt(struct action_util *au, FILE *f, struct rtattr *arg) { + struct xtables_target *m; struct rtattr *tb[TCA_IPT_MAX + 1]; struct xt_entry_target *t = NULL; - struct option *opts = NULL; if (arg == NULL) return -1; @@ -318,73 +336,66 @@ return -1; } else { __u32 hook; + hook = rta_getattr_u32(tb[TCA_IPT_HOOK]); - fprintf(f, " hook: %s \n", ipthooks[hook]); + fprintf(f, " hook: %s\n", ipthooks[hook]); } if (tb[TCA_IPT_TARG] == NULL) { - fprintf(f, "\t[NULL ipt target parameters ] \n"); + fprintf(f, "\t[NULL ipt target parameters ]\n"); return -1; - } else { - struct xtables_target *m = NULL; - t = RTA_DATA(tb[TCA_IPT_TARG]); - m = xtables_find_target(t->u.user.name, XTF_TRY_LOAD); - if (NULL != m) { - if (0 > build_st(m, t)) { - fprintf(stderr, " %s error \n", m->name); - return -1; - } + } -#if (XTABLES_VERSION_CODE >= 6) - opts = xtables_options_xfrm(tmp_tcipt_globals.orig_opts, - tmp_tcipt_globals.opts, - m->x6_options, - &m->option_offset); -#else - opts = xtables_merge_options(tmp_tcipt_globals.opts, - m->extra_opts, - &m->option_offset); -#endif - if (opts == NULL) { - fprintf(stderr, " failed to find additional options for target %s\n\n", optarg); + t = RTA_DATA(tb[TCA_IPT_TARG]); + m = xtables_find_target(t->u.user.name, XTF_TRY_LOAD); + if (!m) { + fprintf(stderr, " failed to find target %s\n\n", + t->u.user.name); return -1; - } else - tmp_tcipt_globals.opts = opts; - } else { - fprintf(stderr, " failed to find target %s\n\n", - t->u.user.name); - return -1; - } - fprintf(f, "\ttarget "); - m->print(NULL, m->t, 0); - if (tb[TCA_IPT_INDEX] == NULL) { - fprintf(f, " [NULL ipt target index ]\n"); - } else { - __u32 index; - index = rta_getattr_u32(tb[TCA_IPT_INDEX]); - fprintf(f, " \n\tindex %d", index); - } + } + if (build_st(m, t) < 0) { + fprintf(stderr, " %s error\n", m->name); + return -1; + } - if (tb[TCA_IPT_CNT]) { - struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);; - fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); - } - if (show_stats) { - if (tb[TCA_IPT_TM]) { - struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); - print_tm(f,tm); - } - } - fprintf(f, " \n"); + if (get_xtables_target_opts(&tmp_tcipt_globals, m) < 0) { + fprintf(stderr, + " failed to find additional options for target %s\n\n", + t->u.user.name); + return -1; + } + fprintf(f, "\ttarget "); + m->print(NULL, m->t, 0); + if (tb[TCA_IPT_INDEX] == NULL) { + fprintf(f, " [NULL ipt target index ]\n"); + } else { + __u32 index; + index = rta_getattr_u32(tb[TCA_IPT_INDEX]); + fprintf(f, "\n\tindex %d", index); } + + if (tb[TCA_IPT_CNT]) { + struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]); + + fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); + } + if (show_stats) { + if (tb[TCA_IPT_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); + + print_tm(f, tm); + } + } + fprintf(f, "\n"); + xtables_free_opts(1); return 0; } struct action_util xt_action_util = { - .id = "xt", - .parse_aopt = parse_ipt, - .print_aopt = print_ipt, + .id = "xt", + .parse_aopt = parse_ipt, + .print_aopt = print_ipt, }; diff -Nru iproute2-4.3.0/tc/m_xt_old.c iproute2-4.9.0/tc/m_xt_old.c --- iproute2-4.3.0/tc/m_xt_old.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/m_xt_old.c 2016-12-12 23:07:42.000000000 +0000 @@ -41,8 +41,8 @@ #endif #ifndef ALIGN -#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) -#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) +#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) +#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) #endif static const char *pname = "tc-ipt"; @@ -63,7 +63,7 @@ }; static struct option *opts = original_opts; -static unsigned int global_option_offset = 0; +static unsigned int global_option_offset; char *lib_dir; const char *program_version = XTABLES_VERSION; const char *program_name = "tc-ipt"; @@ -96,18 +96,18 @@ struct option *merge; unsigned int num_old, num_new, i; - for (num_old = 0; oldopts[num_old].name; num_old++) ; - for (num_new = 0; newopts[num_new].name; num_new++) ; + for (num_old = 0; oldopts[num_old].name; num_old++); + for (num_new = 0; newopts[num_new].name; num_new++); *option_offset = global_option_offset + OPTION_OFFSET; - merge = malloc(sizeof (struct option) * (num_new + num_old + 1)); - memcpy(merge, oldopts, num_old * sizeof (struct option)); + merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); + memcpy(merge, oldopts, num_old * sizeof(struct option)); for (i = 0; i < num_new; i++) { merge[num_old + i] = newopts[i]; merge[num_old + i].val += *option_offset; } - memset(merge + num_old + num_new, 0, sizeof (struct option)); + memset(merge + num_old + num_new, 0, sizeof(struct option)); return merge; } @@ -125,35 +125,35 @@ int check_inverse(const char option[], int *invert, int *my_optind, int argc) { - if (option && strcmp(option, "!") == 0) { - if (*invert) - exit_error(PARAMETER_PROBLEM, - "Multiple `!' flags not allowed"); - *invert = TRUE; - if (my_optind != NULL) { - ++*my_optind; - if (argc && *my_optind > argc) - exit_error(PARAMETER_PROBLEM, - "no argument following `!'"); - } - - return TRUE; - } - return FALSE; + if (option && strcmp(option, "!") == 0) { + if (*invert) + exit_error(PARAMETER_PROBLEM, + "Multiple `!' flags not allowed"); + *invert = TRUE; + if (my_optind != NULL) { + ++*my_optind; + if (argc && *my_optind > argc) + exit_error(PARAMETER_PROBLEM, + "no argument following `!'"); + } + + return TRUE; + } + return FALSE; } /*XXX: TC_CONFIG_XT_H */ void exit_error(enum exittype status, const char *msg, ...) { - va_list args; + va_list args; - va_start(args, msg); - fprintf(stderr, "%s v%s: ", pname, pversion); - vfprintf(stderr, msg, args); - va_end(args); - fprintf(stderr, "\n"); - /* On error paths, make sure that we don't leak memory */ - exit(status); + va_start(args, msg); + fprintf(stderr, "%s v%s: ", pname, pversion); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + /* On error paths, make sure that we don't leak memory */ + exit(status); } /*XXX: TC_CONFIG_XT_H */ @@ -173,9 +173,9 @@ { size_t size = - XT_ALIGN(sizeof (struct xt_entry_target)) + target->size; + XT_ALIGN(sizeof(struct xt_entry_target)) + target->size; - if (NULL == t) { + if (t == NULL) { target->t = fw_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, target->name); @@ -197,14 +197,14 @@ if (!lib_dir) { lib_dir = getenv("IPTABLES_LIB_DIR"); if (lib_dir) - fprintf(stderr, "using deprecated IPTABLES_LIB_DIR \n"); + fprintf(stderr, "using deprecated IPTABLES_LIB_DIR\n"); } if (lib_dir == NULL) lib_dir = XT_LIB_DIR; } -static int parse_ipt(struct action_util *a,int *argc_p, +static int parse_ipt(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct xtables_target *m = NULL; @@ -223,6 +223,7 @@ { int i; + for (i = 0; i < rargc; i++) { if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { break; @@ -232,7 +233,7 @@ } if (argc <= 2) { - fprintf(stderr,"bad arguments to ipt %d vs %d \n", argc, rargc); + fprintf(stderr, "bad arguments to ipt %d vs %d\n", argc, rargc); return -1; } @@ -243,29 +244,29 @@ switch (c) { case 'j': m = find_target(optarg, TRY_LOAD); - if (NULL != m) { + if (m != NULL) { - if (0 > build_st(m, NULL)) { - printf(" %s error \n", m->name); + if (build_st(m, NULL) < 0) { + printf(" %s error\n", m->name); return -1; } opts = merge_options(opts, m->extra_opts, &m->option_offset); } else { - fprintf(stderr," failed to find target %s\n\n", optarg); + fprintf(stderr, " failed to find target %s\n\n", optarg); return -1; } ok++; break; default: - memset(&fw, 0, sizeof (fw)); + memset(&fw, 0, sizeof(fw)); if (m) { m->parse(c - m->option_offset, argv, 0, &m->tflags, NULL, &m->t); } else { - fprintf(stderr," failed to find target %s\n\n", optarg); + fprintf(stderr, " failed to find target %s\n\n", optarg); return -1; } @@ -289,7 +290,7 @@ } if (!ok && !iok) { - fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv); + fprintf(stderr, " ipt Parser BAD!! (%s)\n", *argv); return -1; } @@ -299,6 +300,7 @@ { struct tcmsg *t = NLMSG_DATA(n); + if (t->tcm_parent != TC_H_ROOT && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) { hook = NF_IP_PRE_ROUTING; @@ -339,11 +341,11 @@ optind = 0; free_opts(opts); /* Clear flags if target will be used again */ - m->tflags=0; - m->used=0; + m->tflags = 0; + m->used = 0; /* Free allocated memory */ - if (m->t) - free(m->t); + if (m->t) + free(m->t); return 0; @@ -351,7 +353,7 @@ } static int -print_ipt(struct action_util *au,FILE * f, struct rtattr *arg) +print_ipt(struct action_util *au, FILE * f, struct rtattr *arg) { struct rtattr *tb[TCA_IPT_MAX + 1]; struct xt_entry_target *t = NULL; @@ -375,20 +377,22 @@ return -1; } else { __u32 hook; + hook = rta_getattr_u32(tb[TCA_IPT_HOOK]); - fprintf(f, " hook: %s \n", ipthooks[hook]); + fprintf(f, " hook: %s\n", ipthooks[hook]); } if (tb[TCA_IPT_TARG] == NULL) { - fprintf(f, "\t[NULL ipt target parameters ] \n"); + fprintf(f, "\t[NULL ipt target parameters ]\n"); return -1; } else { struct xtables_target *m = NULL; + t = RTA_DATA(tb[TCA_IPT_TARG]); m = find_target(t->u.user.name, TRY_LOAD); - if (NULL != m) { - if (0 > build_st(m, t)) { - fprintf(stderr, " %s error \n", m->name); + if (m != NULL) { + if (build_st(m, t) < 0) { + fprintf(stderr, " %s error\n", m->name); return -1; } @@ -406,21 +410,24 @@ fprintf(f, " [NULL ipt target index ]\n"); } else { __u32 index; + index = rta_getattr_u32(tb[TCA_IPT_INDEX]); - fprintf(f, " \n\tindex %d", index); + fprintf(f, "\n\tindex %d", index); } if (tb[TCA_IPT_CNT]) { - struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);; + struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]); + fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); } if (show_stats) { if (tb[TCA_IPT_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); - print_tm(f,tm); + + print_tm(f, tm); } } - fprintf(f, " \n"); + fprintf(f, "\n"); } free_opts(opts); @@ -429,7 +436,7 @@ } struct action_util ipt_action_util = { - .id = "ipt", - .parse_aopt = parse_ipt, - .print_aopt = print_ipt, + .id = "ipt", + .parse_aopt = parse_ipt, + .print_aopt = print_ipt, }; diff -Nru iproute2-4.3.0/tc/p_icmp.c iproute2-4.9.0/tc/p_icmp.c --- iproute2-4.3.0/tc/p_icmp.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/p_icmp.c 2016-12-12 23:07:42.000000000 +0000 @@ -25,7 +25,7 @@ static int -parse_icmp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +parse_icmp(int *argc_p, char ***argv_p, struct tc_pedit_sel *sel, struct tc_pedit_key *tkey) { int res = -1; #if 0 @@ -47,7 +47,7 @@ } return -1; - done: +done: *argc_p = argc; *argv_p = argv; #endif diff -Nru iproute2-4.3.0/tc/p_ip.c iproute2-4.9.0/tc/p_ip.c --- iproute2-4.3.0/tc/p_ip.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/p_ip.c 2016-12-12 23:07:42.000000000 +0000 @@ -24,7 +24,8 @@ #include "m_pedit.h" static int -parse_ip(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +parse_ip(int *argc_p, char ***argv_p, + struct tc_pedit_sel *sel, struct tc_pedit_key *tkey) { int res = -1; int argc = *argc_p; @@ -36,13 +37,13 @@ if (strcmp(*argv, "src") == 0) { NEXT_ARG(); tkey->off = 12; - res = parse_cmd(&argc, &argv, 4, TIPV4,RU32,sel,tkey); + res = parse_cmd(&argc, &argv, 4, TIPV4, RU32, sel, tkey); goto done; } if (strcmp(*argv, "dst") == 0) { NEXT_ARG(); tkey->off = 16; - res = parse_cmd(&argc, &argv, 4, TIPV4,RU32,sel,tkey); + res = parse_cmd(&argc, &argv, 4, TIPV4, RU32, sel, tkey); goto done; } /* jamal - look at these and make them either old or new @@ -52,94 +53,95 @@ if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { NEXT_ARG(); tkey->off = 1; - res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, RU8, sel, tkey); goto done; } if (strcmp(*argv, "ihl") == 0) { NEXT_ARG(); tkey->off = 0; - res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, 0x0f, sel, tkey); goto done; } if (strcmp(*argv, "protocol") == 0) { NEXT_ARG(); tkey->off = 9; - res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, RU8, sel, tkey); goto done; } /* jamal - fix this */ if (matches(*argv, "precedence") == 0) { NEXT_ARG(); tkey->off = 1; - res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, RU8, sel, tkey); goto done; } /* jamal - validate this at some point */ if (strcmp(*argv, "nofrag") == 0) { NEXT_ARG(); tkey->off = 6; - res = parse_cmd(&argc, &argv, 1, TU32,0x3F,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, 0x3F, sel, tkey); goto done; } /* jamal - validate this at some point */ if (strcmp(*argv, "firstfrag") == 0) { NEXT_ARG(); tkey->off = 6; - res = parse_cmd(&argc, &argv, 1, TU32,0x1F,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, 0x1F, sel, tkey); goto done; } if (strcmp(*argv, "ce") == 0) { NEXT_ARG(); tkey->off = 6; - res = parse_cmd(&argc, &argv, 1, TU32,0x80,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, 0x80, sel, tkey); goto done; } if (strcmp(*argv, "df") == 0) { NEXT_ARG(); tkey->off = 6; - res = parse_cmd(&argc, &argv, 1, TU32,0x40,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, 0x40, sel, tkey); goto done; } if (strcmp(*argv, "mf") == 0) { NEXT_ARG(); tkey->off = 6; - res = parse_cmd(&argc, &argv, 1, TU32,0x20,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, 0x20, sel, tkey); goto done; } if (strcmp(*argv, "dport") == 0) { NEXT_ARG(); tkey->off = 22; - res = parse_cmd(&argc, &argv, 2, TU32,RU16,sel,tkey); + res = parse_cmd(&argc, &argv, 2, TU32, RU16, sel, tkey); goto done; } if (strcmp(*argv, "sport") == 0) { NEXT_ARG(); tkey->off = 20; - res = parse_cmd(&argc, &argv, 2, TU32,RU16,sel,tkey); + res = parse_cmd(&argc, &argv, 2, TU32, RU16, sel, tkey); goto done; } if (strcmp(*argv, "icmp_type") == 0) { NEXT_ARG(); tkey->off = 20; - res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, RU8, sel, tkey); goto done; } if (strcmp(*argv, "icmp_code") == 0) { NEXT_ARG(); tkey->off = 20; - res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + res = parse_cmd(&argc, &argv, 1, TU32, RU8, sel, tkey); goto done; } return -1; - done: +done: *argc_p = argc; *argv_p = argv; return res; } static int -parse_ip6(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +parse_ip6(int *argc_p, char ***argv_p, + struct tc_pedit_sel *sel, struct tc_pedit_key *tkey) { int res = -1; return res; diff -Nru iproute2-4.3.0/tc/p_tcp.c iproute2-4.9.0/tc/p_tcp.c --- iproute2-4.3.0/tc/p_tcp.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/p_tcp.c 2016-12-12 23:07:42.000000000 +0000 @@ -24,7 +24,7 @@ #include "m_pedit.h" static int -parse_tcp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +parse_tcp(int *argc_p, char ***argv_p, struct tc_pedit_sel *sel, struct tc_pedit_key *tkey) { int res = -1; return res; diff -Nru iproute2-4.3.0/tc/p_udp.c iproute2-4.9.0/tc/p_udp.c --- iproute2-4.3.0/tc/p_udp.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/p_udp.c 2016-12-12 23:07:42.000000000 +0000 @@ -24,7 +24,7 @@ #include "m_pedit.h" static int -parse_udp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +parse_udp(int *argc_p, char ***argv_p, struct tc_pedit_sel *sel, struct tc_pedit_key *tkey) { int res = -1; return res; diff -Nru iproute2-4.3.0/tc/q_atm.c iproute2-4.9.0/tc/q_atm.c --- iproute2-4.3.0/tc/q_atm.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_atm.c 2016-12-12 23:07:42.000000000 +0000 @@ -30,7 +30,7 @@ static int atm_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { if (argc) { - fprintf(stderr,"Usage: atm\n"); + fprintf(stderr, "Usage: atm\n"); return -1; } return 0; @@ -39,17 +39,15 @@ static void explain(void) { - fprintf(stderr, "Usage: ... atm ( pvc ADDR | svc ADDR [ sap SAP ] ) " - "[ qos QOS ] [ sndbuf BYTES ]\n"); - fprintf(stderr, " [ hdr HEX... ] [ excess ( CLASSID | clp ) ] " - "[ clip ]\n"); + fprintf(stderr, "Usage: ... atm ( pvc ADDR | svc ADDR [ sap SAP ] ) [ qos QOS ] [ sndbuf BYTES ]\n"); + fprintf(stderr, " [ hdr HEX... ] [ excess ( CLASSID | clp ) ] [ clip ]\n"); } static int atm_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - struct sockaddr_atmsvc addr; + struct sockaddr_atmsvc addr = {}; struct atm_qos qos; struct atm_sap sap; unsigned char hdr[MAX_HDR_LEN]; @@ -60,52 +58,46 @@ int set_clip = 0; int s; - memset(&addr,0,sizeof(addr)); - (void) text2qos("aal5,ubr:sdu=9180,rx:none",&qos,0); - (void) text2sap("blli:l2=iso8802",&sap,0); + (void) text2qos("aal5,ubr:sdu=9180,rx:none", &qos, 0); + (void) text2sap("blli:l2=iso8802", &sap, 0); while (argc > 0) { - if (!strcmp(*argv,"pvc")) { + if (!strcmp(*argv, "pvc")) { NEXT_ARG(); - if (text2atm(*argv,(struct sockaddr *) &addr, - sizeof(addr),T2A_PVC | T2A_NAME) < 0) { + if (text2atm(*argv, (struct sockaddr *) &addr, + sizeof(addr), T2A_PVC | T2A_NAME) < 0) { explain(); return -1; } - } - else if (!strcmp(*argv,"svc")) { + } else if (!strcmp(*argv,"svc")) { NEXT_ARG(); - if (text2atm(*argv,(struct sockaddr *) &addr, - sizeof(addr),T2A_SVC | T2A_NAME) < 0) { + if (text2atm(*argv, (struct sockaddr *) &addr, + sizeof(addr), T2A_SVC | T2A_NAME) < 0) { explain(); return -1; } - } - else if (!strcmp(*argv,"qos")) { + } else if (!strcmp(*argv,"qos")) { NEXT_ARG(); - if (text2qos(*argv,&qos,0) < 0) { + if (text2qos(*argv, &qos, 0) < 0) { explain(); return -1; } - } - else if (!strcmp(*argv,"sndbuf")) { + } else if (!strcmp(*argv,"sndbuf")) { char *end; NEXT_ARG(); - sndbuf = strtol(*argv,&end,0); + sndbuf = strtol(*argv, &end, 0); if (*end) { explain(); return -1; } - } - else if (!strcmp(*argv,"sap")) { + } else if (!strcmp(*argv,"sap")) { NEXT_ARG(); if (addr.sas_family != AF_ATMSVC || - text2sap(*argv,&sap,T2A_NAME) < 0) { + text2sap(*argv, &sap, T2A_NAME) < 0) { explain(); return -1; } - } - else if (!strcmp(*argv,"hdr")) { + } else if (!strcmp(*argv,"hdr")) { unsigned char *ptr; char *walk; @@ -115,7 +107,7 @@ int tmp; if (ptr == hdr+MAX_HDR_LEN) { - fprintf(stderr,"header is too long\n"); + fprintf(stderr, "header is too long\n"); return -1; } if (*walk == '.') continue; @@ -124,64 +116,61 @@ explain(); return -1; } - sscanf(walk,"%2x",&tmp); + sscanf(walk, "%2x", &tmp); *ptr++ = tmp; walk++; } hdr_len = ptr-hdr; - } - else if (!strcmp(*argv,"excess")) { + } else if (!strcmp(*argv,"excess")) { NEXT_ARG(); - if (!strcmp(*argv,"clp")) excess = 0; - else if (get_tc_classid(&excess,*argv)) { + if (!strcmp(*argv, "clp")) excess = 0; + else if (get_tc_classid(&excess, *argv)) { explain(); return -1; } - } - else if (!strcmp(*argv,"clip")) { + } else if (!strcmp(*argv,"clip")) { set_clip = 1; - } - else { + } else { explain(); return 1; } argc--; argv++; } - s = socket(addr.sas_family,SOCK_DGRAM,0); + s = socket(addr.sas_family, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return -1; } - if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) { + if (setsockopt(s, SOL_ATM, SO_ATMQOS, &qos, sizeof(qos)) < 0) { perror("SO_ATMQOS"); return -1; } if (sndbuf) - if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) { perror("SO_SNDBUF"); return -1; } - if (addr.sas_family == AF_ATMSVC && setsockopt(s,SOL_ATM,SO_ATMSAP, - &sap,sizeof(sap)) < 0) { + if (addr.sas_family == AF_ATMSVC && setsockopt(s, SOL_ATM, SO_ATMSAP, + &sap, sizeof(sap)) < 0) { perror("SO_ATMSAP"); return -1; } - if (connect(s,(struct sockaddr *) &addr,addr.sas_family == AF_ATMPVC ? + if (connect(s, (struct sockaddr *) &addr, addr.sas_family == AF_ATMPVC ? sizeof(struct sockaddr_atmpvc) : sizeof(addr)) < 0) { perror("connect"); return -1; } if (set_clip) - if (ioctl(s,ATMARP_MKIP,0) < 0) { + if (ioctl(s, ATMARP_MKIP, 0) < 0) { perror("ioctl ATMARP_MKIP"); return -1; } tail = NLMSG_TAIL(n); - addattr_l(n,1024,TCA_OPTIONS,NULL,0); - addattr_l(n,1024,TCA_ATM_FD,&s,sizeof(s)); - if (excess) addattr_l(n,1024,TCA_ATM_EXCESS,&excess,sizeof(excess)); - if (hdr_len != -1) addattr_l(n,1024,TCA_ATM_HDR,hdr,hdr_len); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_ATM_FD, &s, sizeof(s)); + if (excess) addattr_l(n, 1024, TCA_ATM_EXCESS, &excess, sizeof(excess)); + if (hdr_len != -1) addattr_l(n, 1024, TCA_ATM_HDR, hdr, hdr_len); tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; return 0; } @@ -200,37 +189,37 @@ if (tb[TCA_ATM_ADDR]) { if (RTA_PAYLOAD(tb[TCA_ATM_ADDR]) < sizeof(struct sockaddr_atmpvc)) - fprintf(stderr,"ATM: address too short\n"); + fprintf(stderr, "ATM: address too short\n"); else { - if (atm2text(buffer,MAX_ATM_ADDR_LEN, - RTA_DATA(tb[TCA_ATM_ADDR]),A2T_PRETTY | A2T_NAME) < - 0) fprintf(stderr,"atm2text error\n"); - fprintf(f,"pvc %s ",buffer); + if (atm2text(buffer, MAX_ATM_ADDR_LEN, + RTA_DATA(tb[TCA_ATM_ADDR]), A2T_PRETTY | A2T_NAME) < + 0) fprintf(stderr, "atm2text error\n"); + fprintf(f, "pvc %s ", buffer); } } if (tb[TCA_ATM_HDR]) { int i; const __u8 *hdr = RTA_DATA(tb[TCA_ATM_HDR]); - fprintf(f,"hdr"); + fprintf(f, "hdr"); for (i = 0; i < RTA_PAYLOAD(tb[TCA_ATM_HDR]); i++) - fprintf(f,"%c%02x", i ? '.' : ' ', hdr[i]); - if (!i) fprintf(f," ."); - fprintf(f," "); + fprintf(f, "%c%02x", i ? '.' : ' ', hdr[i]); + if (!i) fprintf(f, " ."); + fprintf(f, " "); } if (tb[TCA_ATM_EXCESS]) { __u32 excess; if (RTA_PAYLOAD(tb[TCA_ATM_EXCESS]) < sizeof(excess)) - fprintf(stderr,"ATM: excess class ID too short\n"); + fprintf(stderr, "ATM: excess class ID too short\n"); else { excess = rta_getattr_u32(tb[TCA_ATM_EXCESS]); - if (!excess) fprintf(f,"excess clp "); + if (!excess) fprintf(f, "excess clp "); else { char buf[64]; - print_tc_classid(buf,sizeof(buf),excess); - fprintf(f,"excess %s ",buf); + print_tc_classid(buf, sizeof(buf), excess); + fprintf(f, "excess %s ", buf); } } } @@ -239,10 +228,10 @@ int state; if (RTA_PAYLOAD(tb[TCA_ATM_STATE]) < sizeof(state)) - fprintf(stderr,"ATM: state field too short\n"); + fprintf(stderr, "ATM: state field too short\n"); else { state = *(int *) RTA_DATA(tb[TCA_ATM_STATE]); - fprintf(f,"%s ",map[state]); + fprintf(f, "%s ", map[state]); } } return 0; @@ -250,7 +239,7 @@ struct qdisc_util atm_qdisc_util = { - .id = "atm", + .id = "atm", .parse_qopt = atm_parse_opt, .print_qopt = atm_print_opt, .parse_copt = atm_parse_class_opt, diff -Nru iproute2-4.3.0/tc/q_cbq.c iproute2-4.9.0/tc/q_cbq.c --- iproute2-4.3.0/tc/q_cbq.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_cbq.c 2016-12-12 23:07:42.000000000 +0000 @@ -49,19 +49,16 @@ static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - struct tc_ratespec r; - struct tc_cbq_lssopt lss; + struct tc_ratespec r = {}; + struct tc_cbq_lssopt lss = {}; __u32 rtab[256]; - unsigned mpu=0, avpkt=0, allot=0; - unsigned short overhead=0; + unsigned mpu = 0, avpkt = 0, allot = 0; + unsigned short overhead = 0; unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */ - int cell_log=-1; - int ewma_log=-1; + int cell_log = -1; + int ewma_log = -1; struct rtattr *tail; - memset(&lss, 0, sizeof(lss)); - memset(&r, 0, sizeof(r)); - while (argc > 0) { if (matches(*argv, "bandwidth") == 0 || matches(*argv, "rate") == 0) { @@ -81,17 +78,18 @@ return -1; } } else if (matches(*argv, "cell") == 0) { - unsigned cell; + unsigned int cell; int i; + NEXT_ARG(); if (get_size(&cell, *argv)) { explain1("cell"); return -1; } - for (i=0; i<32; i++) + for (i = 0; i < 32; i++) if ((1<=32) { + if (i >= 32) { fprintf(stderr, "cell must be 2^n\n"); return -1; } @@ -170,7 +168,8 @@ addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024); if (show_raw) { int i; - for (i=0; i<256; i++) + + for (i = 0; i < 256; i++) printf("%u ", rtab[i]); printf("\n"); } @@ -180,28 +179,21 @@ static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - int wrr_ok=0, fopt_ok=0; - struct tc_ratespec r; - struct tc_cbq_lssopt lss; - struct tc_cbq_wrropt wrr; - struct tc_cbq_fopt fopt; - struct tc_cbq_ovl ovl; + int wrr_ok = 0, fopt_ok = 0; + struct tc_ratespec r = {}; + struct tc_cbq_lssopt lss = {}; + struct tc_cbq_wrropt wrr = {}; + struct tc_cbq_fopt fopt = {}; __u32 rtab[256]; - unsigned mpu=0; - int cell_log=-1; - int ewma_log=-1; - unsigned bndw = 0; - unsigned minburst=0, maxburst=0; - unsigned short overhead=0; + unsigned mpu = 0; + int cell_log = -1; + int ewma_log = -1; + unsigned int bndw = 0; + unsigned minburst = 0, maxburst = 0; + unsigned short overhead = 0; unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */ struct rtattr *tail; - memset(&r, 0, sizeof(r)); - memset(&lss, 0, sizeof(lss)); - memset(&wrr, 0, sizeof(wrr)); - memset(&fopt, 0, sizeof(fopt)); - memset(&ovl, 0, sizeof(ovl)); - while (argc > 0) { if (matches(*argv, "rate") == 0) { NEXT_ARG(); @@ -260,23 +252,25 @@ } lss.change |= TCF_CBQ_LSS_EWMA; } else if (matches(*argv, "cell") == 0) { - unsigned cell; + unsigned int cell; int i; + NEXT_ARG(); if (get_size(&cell, *argv)) { explain1("cell"); return -1; } - for (i=0; i<32; i++) + for (i = 0; i < 32; i++) if ((1<=32) { + if (i >= 32) { fprintf(stderr, "cell must be 2^n\n"); return -1; } cell_log = i; } else if (matches(*argv, "prio") == 0) { - unsigned prio; + unsigned int prio; + NEXT_ARG(); if (get_u32(&prio, *argv, 0)) { explain1("prio"); @@ -323,6 +317,7 @@ fopt_ok++; } else if (matches(*argv, "defmap") == 0) { int err; + NEXT_ARG(); err = sscanf(*argv, "%08x/%08x", &fopt.defmap, &fopt.defchange); if (err < 1) { @@ -357,7 +352,8 @@ /* 1. Prepare link sharing scheduler parameters */ if (r.rate) { - unsigned pktsize = wrr.allot; + unsigned int pktsize = wrr.allot; + if (wrr.allot < (lss.avpkt*3)/2) wrr.allot = (lss.avpkt*3)/2; r.mpu = mpu; @@ -375,7 +371,7 @@ fprintf(stderr, "CBQ: avpkt is required for max/minburst.\n"); return -1; } - if (bndw==0 || r.rate == 0) { + if (bndw == 0 || r.rate == 0) { fprintf(stderr, "CBQ: bandwidth&rate are required for max/minburst.\n"); return -1; } @@ -424,7 +420,8 @@ addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024); if (show_raw) { int i; - for (i=0; i<256; i++) + + for (i = 0; i < 256; i++) printf("%u ", rtab[i]); printf("\n"); } @@ -443,6 +440,7 @@ struct tc_cbq_fopt *fopt = NULL; struct tc_cbq_ovl *ovl = NULL; unsigned int linklayer; + SPRINT_BUF(b1); SPRINT_BUF(b2); @@ -478,14 +476,15 @@ if (tb[TCA_CBQ_OVL_STRATEGY]) { if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl)) fprintf(stderr, "CBQ: too short overlimit strategy %u/%u\n", - (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]), - (unsigned) sizeof(*ovl)); + (unsigned int) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]), + (unsigned int) sizeof(*ovl)); else ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]); } if (r) { char buf[64]; + print_rate(buf, sizeof(buf), r->rate); fprintf(f, "rate %s ", buf); linklayer = (r->linklayer & TC_LINKLAYER_MASK); @@ -500,11 +499,12 @@ } } if (lss && lss->flags) { - int comma=0; + int comma = 0; + fprintf(f, "("); if (lss->flags&TCF_CBQ_LSS_BOUNDED) { fprintf(f, "bounded"); - comma=1; + comma = 1; } if (lss->flags&TCF_CBQ_LSS_ISOLATED) { if (comma) @@ -520,6 +520,7 @@ fprintf(f, "prio no-transmit"); if (show_details) { char buf[64]; + fprintf(f, "/%u ", wrr->cpriority); if (wrr->weight != 1) { print_rate(buf, sizeof(buf), wrr->weight); @@ -536,7 +537,7 @@ if (show_raw) fprintf(f, "[%08x] ", lss->maxidle); } - if (lss->minidle!=0x7fffffff) { + if (lss->minidle != 0x7fffffff) { fprintf(f, "minidle %s ", sprint_ticks(lss->minidle>>lss->ewma_log, b1)); if (show_raw) fprintf(f, "[%08x] ", lss->minidle); @@ -549,6 +550,7 @@ } if (fopt && show_details) { char buf[64]; + print_tc_classid(buf, sizeof(buf), fopt->split); fprintf(f, "\nsplit %s ", buf); if (fopt->defmap) { diff -Nru iproute2-4.3.0/tc/q_choke.c iproute2-4.9.0/tc/q_choke.c --- iproute2-4.3.0/tc/q_choke.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_choke.c 2016-12-12 23:07:42.000000000 +0000 @@ -34,19 +34,17 @@ static int choke_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - struct tc_red_qopt opt; - unsigned burst = 0; - unsigned avpkt = 1000; + struct tc_red_qopt opt = {}; + unsigned int burst = 0; + unsigned int avpkt = 1000; double probability = 0.02; - unsigned rate = 0; + unsigned int rate = 0; int ecn_ok = 0; int wlog; __u8 sbuf[256]; __u32 max_P; struct rtattr *tail; - memset(&opt, 0, sizeof(opt)); - while (argc > 0) { if (strcmp(*argv, "limit") == 0) { NEXT_ARG(); diff -Nru iproute2-4.3.0/tc/q_clsact.c iproute2-4.9.0/tc/q_clsact.c --- iproute2-4.3.0/tc/q_clsact.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/tc/q_clsact.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,33 @@ +#include +#include + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... clsact\n"); +} + +static int clsact_parse_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + if (argc > 0) { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + + return 0; +} + +static int clsact_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + return 0; +} + +struct qdisc_util clsact_qdisc_util = { + .id = "clsact", + .parse_qopt = clsact_parse_opt, + .print_qopt = clsact_print_opt, +}; diff -Nru iproute2-4.3.0/tc/q_codel.c iproute2-4.9.0/tc/q_codel.c --- iproute2-4.3.0/tc/q_codel.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_codel.c 2016-12-12 23:07:42.000000000 +0000 @@ -53,7 +53,7 @@ static void explain(void) { - fprintf(stderr, "Usage: ... codel [ limit PACKETS ] [ target TIME]\n"); + fprintf(stderr, "Usage: ... codel [ limit PACKETS ] [ target TIME ]\n"); fprintf(stderr, " [ interval TIME ] [ ecn | noecn ]\n"); fprintf(stderr, " [ ce_threshold TIME ]\n"); } @@ -61,10 +61,10 @@ static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - unsigned limit = 0; - unsigned target = 0; - unsigned interval = 0; - unsigned ce_threshold = ~0U; + unsigned int limit = 0; + unsigned int target = 0; + unsigned int interval = 0; + unsigned int ce_threshold = ~0U; int ecn = -1; struct rtattr *tail; @@ -129,11 +129,12 @@ static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_CODEL_MAX + 1]; - unsigned limit; - unsigned interval; - unsigned target; - unsigned ecn; - unsigned ce_threshold; + unsigned int limit; + unsigned int interval; + unsigned int target; + unsigned int ecn; + unsigned int ce_threshold; + SPRINT_BUF(b1); if (opt == NULL) @@ -174,7 +175,8 @@ static int codel_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { - struct tc_codel_xstats _st, *st; + struct tc_codel_xstats _st = {}, *st; + SPRINT_BUF(b1); if (xstats == NULL) @@ -182,7 +184,6 @@ st = RTA_DATA(xstats); if (RTA_PAYLOAD(xstats) < sizeof(*st)) { - memset(&_st, 0, sizeof(_st)); memcpy(&_st, st, RTA_PAYLOAD(xstats)); st = &_st; } diff -Nru iproute2-4.3.0/tc/q_drr.c iproute2-4.9.0/tc/q_drr.c --- iproute2-4.3.0/tc/q_drr.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_drr.c 2016-12-12 23:07:42.000000000 +0000 @@ -84,6 +84,7 @@ static int drr_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_DRR_MAX + 1]; + SPRINT_BUF(b1); if (opt == NULL) @@ -100,6 +101,7 @@ static int drr_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_drr_stats *x; + SPRINT_BUF(b1); if (xstats == NULL) diff -Nru iproute2-4.3.0/tc/q_dsmark.c iproute2-4.9.0/tc/q_dsmark.c --- iproute2-4.3.0/tc/q_dsmark.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_dsmark.c 2016-12-12 23:07:42.000000000 +0000 @@ -21,8 +21,7 @@ static void explain(void) { - fprintf(stderr,"Usage: dsmark indices INDICES [ default_index " - "DEFAULT_INDEX ] [ set_tc_index ]\n"); + fprintf(stderr,"Usage: dsmark indices INDICES [ default_index DEFAULT_INDEX ] [ set_tc_index ]\n"); } @@ -32,32 +31,29 @@ struct rtattr *tail; __u16 ind; char *end; - int dflt,set_tc_index; + int dflt, set_tc_index; ind = set_tc_index = 0; dflt = -1; while (argc > 0) { - if (!strcmp(*argv,"indices")) { + if (!strcmp(*argv, "indices")) { NEXT_ARG(); - ind = strtoul(*argv,&end,0); + ind = strtoul(*argv, &end, 0); if (*end) { explain(); return -1; } - } - else if (!strcmp(*argv,"default_index") || !strcmp(*argv, + } else if (!strcmp(*argv,"default_index") || !strcmp(*argv, "default")) { NEXT_ARG(); - dflt = strtoul(*argv,&end,0); + dflt = strtoul(*argv, &end, 0); if (*end) { explain(); return -1; } - } - else if (!strcmp(*argv,"set_tc_index")) { + } else if (!strcmp(*argv,"set_tc_index")) { set_tc_index = 1; - } - else { + } else { explain(); return -1; } @@ -69,14 +65,14 @@ return -1; } tail = NLMSG_TAIL(n); - addattr_l(n,1024,TCA_OPTIONS,NULL,0); - addattr_l(n,1024,TCA_DSMARK_INDICES,&ind,sizeof(ind)); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_DSMARK_INDICES, &ind, sizeof(ind)); if (dflt != -1) { __u16 tmp = dflt; - addattr_l(n,1024,TCA_DSMARK_DEFAULT_INDEX,&tmp,sizeof(tmp)); + addattr_l(n, 1024, TCA_DSMARK_DEFAULT_INDEX, &tmp, sizeof(tmp)); } - if (set_tc_index) addattr_l(n,1024,TCA_DSMARK_SET_TC_INDEX,NULL,0); + if (set_tc_index) addattr_l(n, 1024, TCA_DSMARK_SET_TC_INDEX, NULL, 0); tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; return 0; } @@ -96,27 +92,25 @@ char *end; tail = NLMSG_TAIL(n); - addattr_l(n,1024,TCA_OPTIONS,NULL,0); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); while (argc > 0) { - if (!strcmp(*argv,"mask")) { + if (!strcmp(*argv, "mask")) { NEXT_ARG(); - tmp = strtoul(*argv,&end,0); + tmp = strtoul(*argv, &end, 0); if (*end) { explain_class(); return -1; } - addattr_l(n,1024,TCA_DSMARK_MASK,&tmp,1); - } - else if (!strcmp(*argv,"value")) { + addattr_l(n, 1024, TCA_DSMARK_MASK, &tmp, 1); + } else if (!strcmp(*argv,"value")) { NEXT_ARG(); - tmp = strtoul(*argv,&end,0); + tmp = strtoul(*argv, &end, 0); if (*end) { explain_class(); return -1; } - addattr_l(n,1024,TCA_DSMARK_VALUE,&tmp,1); - } - else { + addattr_l(n, 1024, TCA_DSMARK_VALUE, &tmp, 1); + } else { explain_class(); return -1; } @@ -134,33 +128,32 @@ struct rtattr *tb[TCA_DSMARK_MAX+1]; if (!opt) return 0; - memset(tb, 0, sizeof(tb)); parse_rtattr(tb, TCA_DSMARK_MAX, RTA_DATA(opt), RTA_PAYLOAD(opt)); if (tb[TCA_DSMARK_MASK]) { if (!RTA_PAYLOAD(tb[TCA_DSMARK_MASK])) - fprintf(stderr,"dsmark: empty mask\n"); - else fprintf(f,"mask 0x%02x ", + fprintf(stderr, "dsmark: empty mask\n"); + else fprintf(f, "mask 0x%02x ", rta_getattr_u8(tb[TCA_DSMARK_MASK])); } if (tb[TCA_DSMARK_VALUE]) { if (!RTA_PAYLOAD(tb[TCA_DSMARK_VALUE])) - fprintf(stderr,"dsmark: empty value\n"); - else fprintf(f,"value 0x%02x ", + fprintf(stderr, "dsmark: empty value\n"); + else fprintf(f, "value 0x%02x ", rta_getattr_u8(tb[TCA_DSMARK_VALUE])); } if (tb[TCA_DSMARK_INDICES]) { if (RTA_PAYLOAD(tb[TCA_DSMARK_INDICES]) < sizeof(__u16)) - fprintf(stderr,"dsmark: indices too short\n"); - else fprintf(f,"indices 0x%04x ", + fprintf(stderr, "dsmark: indices too short\n"); + else fprintf(f, "indices 0x%04x ", rta_getattr_u16(tb[TCA_DSMARK_INDICES])); } if (tb[TCA_DSMARK_DEFAULT_INDEX]) { if (RTA_PAYLOAD(tb[TCA_DSMARK_DEFAULT_INDEX]) < sizeof(__u16)) - fprintf(stderr,"dsmark: default_index too short\n"); - else fprintf(f,"default_index 0x%04x ", + fprintf(stderr, "dsmark: default_index too short\n"); + else fprintf(f, "default_index 0x%04x ", rta_getattr_u16(tb[TCA_DSMARK_DEFAULT_INDEX])); } - if (tb[TCA_DSMARK_SET_TC_INDEX]) fprintf(f,"set_tc_index "); + if (tb[TCA_DSMARK_SET_TC_INDEX]) fprintf(f, "set_tc_index "); return 0; } diff -Nru iproute2-4.3.0/tc/q_fifo.c iproute2-4.9.0/tc/q_fifo.c --- iproute2-4.3.0/tc/q_fifo.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_fifo.c 2016-12-12 23:07:42.000000000 +0000 @@ -30,9 +30,8 @@ static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - int ok=0; - struct tc_fifo_qopt opt; - memset(&opt, 0, sizeof(opt)); + int ok = 0; + struct tc_fifo_qopt opt = {}; while (argc > 0) { if (strcmp(*argv, "limit") == 0) { diff -Nru iproute2-4.3.0/tc/q_fq.c iproute2-4.9.0/tc/q_fq.c --- iproute2-4.3.0/tc/q_fq.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_fq.c 2016-12-12 23:07:42.000000000 +0000 @@ -218,6 +218,7 @@ unsigned int rate, quantum; unsigned int refill_delay; unsigned int orphan_mask; + SPRINT_BUF(b1); if (opt == NULL) @@ -311,6 +312,9 @@ fprintf(f, ", %llu throttled", st->throttled); + if (st->unthrottle_latency_ns) + fprintf(f, ", %u ns latency", st->unthrottle_latency_ns); + if (st->flows_plimit) fprintf(f, ", %llu flows_plimit", st->flows_plimit); diff -Nru iproute2-4.3.0/tc/q_fq_codel.c iproute2-4.9.0/tc/q_fq_codel.c --- iproute2-4.3.0/tc/q_fq_codel.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_fq_codel.c 2016-12-12 23:07:42.000000000 +0000 @@ -51,7 +51,7 @@ static void explain(void) { fprintf(stderr, "Usage: ... fq_codel [ limit PACKETS ] [ flows NUMBER ]\n"); - fprintf(stderr, " [ target TIME] [ interval TIME ]\n"); + fprintf(stderr, " [ target TIME ] [ interval TIME ]\n"); fprintf(stderr, " [ quantum BYTES ] [ [no]ecn ]\n"); fprintf(stderr, " [ ce_threshold TIME ]\n"); } @@ -59,12 +59,13 @@ static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - unsigned limit = 0; - unsigned flows = 0; - unsigned target = 0; - unsigned interval = 0; - unsigned quantum = 0; - unsigned ce_threshold = ~0U; + unsigned int limit = 0; + unsigned int flows = 0; + unsigned int target = 0; + unsigned int interval = 0; + unsigned int quantum = 0; + unsigned int ce_threshold = ~0U; + unsigned int memory = ~0U; int ecn = -1; struct rtattr *tail; @@ -99,6 +100,12 @@ fprintf(stderr, "Illegal \"ce_threshold\"\n"); return -1; } + } else if (strcmp(*argv, "memory_limit") == 0) { + NEXT_ARG(); + if (get_size(&memory, *argv)) { + fprintf(stderr, "Illegal \"memory_limit\"\n"); + return -1; + } } else if (strcmp(*argv, "interval") == 0) { NEXT_ARG(); if (get_time(&interval, *argv)) { @@ -137,6 +144,10 @@ if (ce_threshold != ~0U) addattr_l(n, 1024, TCA_FQ_CODEL_CE_THRESHOLD, &ce_threshold, sizeof(ce_threshold)); + if (memory != ~0U) + addattr_l(n, 1024, TCA_FQ_CODEL_MEMORY_LIMIT, + &memory, sizeof(memory)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; return 0; } @@ -144,13 +155,15 @@ static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_FQ_CODEL_MAX + 1]; - unsigned limit; - unsigned flows; - unsigned interval; - unsigned target; - unsigned ecn; - unsigned quantum; - unsigned ce_threshold; + unsigned int limit; + unsigned int flows; + unsigned int interval; + unsigned int target; + unsigned int ecn; + unsigned int quantum; + unsigned int ce_threshold; + unsigned int memory_limit; + SPRINT_BUF(b1); if (opt == NULL) @@ -188,6 +201,12 @@ interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]); fprintf(f, "interval %s ", sprint_time(interval, b1)); } + if (tb[TCA_FQ_CODEL_MEMORY_LIMIT] && + RTA_PAYLOAD(tb[TCA_FQ_CODEL_MEMORY_LIMIT]) >= sizeof(__u32)) { + memory_limit = rta_getattr_u32(tb[TCA_FQ_CODEL_MEMORY_LIMIT]); + + fprintf(f, "memory_limit %s ", sprint_size(memory_limit, b1)); + } if (tb[TCA_FQ_CODEL_ECN] && RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) { ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]); @@ -201,7 +220,8 @@ static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { - struct tc_fq_codel_xstats _st, *st; + struct tc_fq_codel_xstats _st = {}, *st; + SPRINT_BUF(b1); if (xstats == NULL) @@ -209,7 +229,6 @@ st = RTA_DATA(xstats); if (RTA_PAYLOAD(xstats) < sizeof(*st)) { - memset(&_st, 0, sizeof(_st)); memcpy(&_st, st, RTA_PAYLOAD(xstats)); st = &_st; } @@ -221,6 +240,10 @@ st->qdisc_stats.ecn_mark); if (st->qdisc_stats.ce_mark) fprintf(f, " ce_mark %u", st->qdisc_stats.ce_mark); + if (st->qdisc_stats.memory_usage) + fprintf(f, " memory_used %u", st->qdisc_stats.memory_usage); + if (st->qdisc_stats.drop_overmemory) + fprintf(f, " drop_overmemory %u", st->qdisc_stats.drop_overmemory); fprintf(f, "\n new_flows_len %u old_flows_len %u", st->qdisc_stats.new_flows_len, st->qdisc_stats.old_flows_len); diff -Nru iproute2-4.3.0/tc/q_gred.c iproute2-4.9.0/tc/q_gred.c --- iproute2-4.3.0/tc/q_gred.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_gred.c 2016-12-12 23:07:42.000000000 +0000 @@ -30,9 +30,9 @@ #if 0 -#define DPRINTF(format,args...) fprintf(stderr,format,##args) +#define DPRINTF(format, args...) fprintf(stderr, format, ##args) #else -#define DPRINTF(format,args...) +#define DPRINTF(format, args...) #endif static void explain(void) @@ -55,7 +55,7 @@ opt.def_DP = MAX_DPs; while (argc > 0) { - DPRINTF(stderr,"init_gred: invoked with %s\n",*argv); + DPRINTF(stderr, "init_gred: invoked with %s\n", *argv); if (strcmp(*argv, "vqs") == 0 || strcmp(*argv, "DPs") == 0) { NEXT_ARG(); @@ -63,14 +63,13 @@ fprintf(stderr, "Illegal \"vqs\"\n"); return -1; } else if (opt.DPs > MAX_DPs) { - fprintf(stderr, "GRED: only %u VQs are " - "currently supported\n", MAX_DPs); + fprintf(stderr, "GRED: only %u VQs are currently supported\n", + MAX_DPs); return -1; } } else if (strcmp(*argv, "default") == 0) { if (opt.DPs == 0) { - fprintf(stderr, "\"default\" must be defined " - "after \"vqs\"\n"); + fprintf(stderr, "\"default\" must be defined after \"vqs\"\n"); return -1; } NEXT_ARG(); @@ -78,8 +77,7 @@ fprintf(stderr, "Illegal \"default\"\n"); return -1; } else if (opt.def_DP >= opt.DPs) { - fprintf(stderr, "\"default\" must be less than " - "\"vqs\"\n"); + fprintf(stderr, "\"default\" must be less than \"vqs\"\n"); return -1; } } else if (strcmp(*argv, "grio") == 0) { @@ -102,12 +100,12 @@ } if (!opt.DPs || opt.def_DP == MAX_DPs) { - fprintf(stderr, "Illegal gred setup parameters \n"); + fprintf(stderr, "Illegal gred setup parameters\n"); return -1; } - DPRINTF("TC_GRED: sending DPs=%u def_DP=%u\n",opt.DPs,opt.def_DP); - n->nlmsg_flags|=NLM_F_CREATE; + DPRINTF("TC_GRED: sending DPs=%u def_DP=%u\n", opt.DPs, opt.def_DP); + n->nlmsg_flags |= NLM_F_CREATE; tail = NLMSG_TAIL(n); addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); addattr_l(n, 1024, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt)); @@ -121,12 +119,12 @@ */ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - int ok=0; + int ok = 0; struct tc_gred_qopt opt = { 0 }; - unsigned burst = 0; - unsigned avpkt = 0; + unsigned int burst = 0; + unsigned int avpkt = 0; double probability = 0.02; - unsigned rate = 0; + unsigned int rate = 0; int parm; __u8 sbuf[256]; struct rtattr *tail; @@ -169,8 +167,8 @@ fprintf(stderr, "Illegal \"vq\"\n"); return -1; } else if (opt.DP >= MAX_DPs) { - fprintf(stderr, "GRED: only %u VQs are " - "currently supported\n", MAX_DPs); + fprintf(stderr, "GRED: only %u VQs are currently supported\n", + MAX_DPs); return -1; } /* need a better error check */ ok++; @@ -197,7 +195,7 @@ ok++; } else if (strcmp(*argv, "prio") == 0) { NEXT_ARG(); - opt.prio=strtol(*argv, (char **)NULL, 10); + opt.prio = strtol(*argv, (char **)NULL, 10); /* some error check here */ ok++; } else if (strcmp(*argv, "bandwidth") == 0) { @@ -224,8 +222,7 @@ } if (opt.DP == MAX_DPs || !opt.limit || !opt.qth_min || !opt.qth_max || !avpkt) { - fprintf(stderr, "Required parameter (vq, limit, min, max, " - "avpkt) is missing\n"); + fprintf(stderr, "Required parameter (vq, limit, min, max, avpkt) is missing\n"); return -1; } if (!burst) { @@ -241,8 +238,8 @@ return -1; } if (parm >= 10) - fprintf(stderr, "GRED: WARNING. Burst %u seems to be too " - "large.\n", burst); + fprintf(stderr, "GRED: WARNING. Burst %u seems to be too large.\n", + burst); opt.Wlog = parm; if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { fprintf(stderr, "GRED: failed to calculate probability.\n"); @@ -251,8 +248,7 @@ opt.Plog = parm; if ((parm = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) { - fprintf(stderr, "GRED: failed to calculate idle damping " - "table.\n"); + fprintf(stderr, "GRED: failed to calculate idle damping table.\n"); return -1; } opt.Scell_log = parm; @@ -274,7 +270,8 @@ struct tc_gred_qopt *qopt; __u32 *max_p = NULL; __u32 *limit = NULL; - unsigned i; + unsigned int i; + SPRINT_BUF(b1); SPRINT_BUF(b2); SPRINT_BUF(b3); @@ -299,7 +296,7 @@ qopt = RTA_DATA(tb[TCA_GRED_PARMS]); if (RTA_PAYLOAD(tb[TCA_GRED_DPS]) < sizeof(*sopt) || RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) { - fprintf(f,"\n GRED received message smaller than expected\n"); + fprintf(f, "\n GRED received message smaller than expected\n"); return -1; } @@ -314,7 +311,7 @@ fprintf(f, "limit %s ", sprint_size(*limit, b1)); - for (i=0;iDP >= MAX_DPs) continue; fprintf(f, "\n vq %u prio %hhu limit %s min %s max %s ", qopt->DP, diff -Nru iproute2-4.3.0/tc/q_hfsc.c iproute2-4.9.0/tc/q_hfsc.c --- iproute2-4.3.0/tc/q_hfsc.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_hfsc.c 2016-12-12 23:07:42.000000000 +0000 @@ -73,9 +73,7 @@ static int hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - struct tc_hfsc_qopt qopt; - - memset(&qopt, 0, sizeof(qopt)); + struct tc_hfsc_qopt qopt = {}; while (argc > 0) { if (matches(*argv, "default") == 0) { @@ -144,17 +142,12 @@ static int hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n) { - struct tc_service_curve rsc, fsc, usc; - int rsc_ok, fsc_ok, usc_ok; + struct tc_service_curve rsc = {}, fsc = {}, usc = {}; + int rsc_ok = 0, fsc_ok = 0, usc_ok = 0; struct rtattr *tail; - memset(&rsc, 0, sizeof(rsc)); - memset(&fsc, 0, sizeof(fsc)); - memset(&usc, 0, sizeof(usc)); - rsc_ok = fsc_ok = usc_ok = 0; - while (argc > 0) { if (matches(*argv, "rt") == 0) { NEXT_ARG(); @@ -203,8 +196,7 @@ return -1; } if (usc_ok && !fsc_ok) { - fprintf(stderr, "HFSC: Upper-limit Service Curve without " - "Link-Share Service Curve\n"); + fprintf(stderr, "HFSC: Upper-limit Service Curve without Link-Share Service Curve\n"); explain_class(); return -1; } diff -Nru iproute2-4.3.0/tc/q_hhf.c iproute2-4.9.0/tc/q_hhf.c --- iproute2-4.3.0/tc/q_hhf.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_hhf.c 2016-12-12 23:07:42.000000000 +0000 @@ -28,13 +28,13 @@ static int hhf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - unsigned limit = 0; - unsigned quantum = 0; - unsigned hh_limit = 0; - unsigned reset_timeout = 0; - unsigned admit_bytes = 0; - unsigned evict_timeout = 0; - unsigned non_hh_weight = 0; + unsigned int limit = 0; + unsigned int quantum = 0; + unsigned int hh_limit = 0; + unsigned int reset_timeout = 0; + unsigned int admit_bytes = 0; + unsigned int evict_timeout = 0; + unsigned int non_hh_weight = 0; struct rtattr *tail; while (argc > 0) { @@ -120,13 +120,14 @@ static int hhf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_HHF_MAX + 1]; - unsigned limit; - unsigned quantum; - unsigned hh_limit; - unsigned reset_timeout; - unsigned admit_bytes; - unsigned evict_timeout; - unsigned non_hh_weight; + unsigned int limit; + unsigned int quantum; + unsigned int hh_limit; + unsigned int reset_timeout; + unsigned int admit_bytes; + unsigned int evict_timeout; + unsigned int non_hh_weight; + SPRINT_BUF(b1); if (opt == NULL) diff -Nru iproute2-4.3.0/tc/q_htb.c iproute2-4.9.0/tc/q_htb.c --- iproute2-4.3.0/tc/q_htb.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_htb.c 2016-12-12 23:07:42.000000000 +0000 @@ -49,7 +49,7 @@ " mtu max packet size we create rate map for {1600}\n" " prio priority of leaf; lower are served first {0}\n" " quantum how much bytes to serve from leaf at once {use r2q}\n" - "\nTC HTB version %d.%d\n",HTB_TC_VER>>16,HTB_TC_VER&0xffff + "\nTC HTB version %d.%d\n", HTB_TC_VER>>16, HTB_TC_VER&0xffff ); } @@ -63,12 +63,12 @@ static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { unsigned int direct_qlen = ~0U; - struct tc_htb_glob opt; + struct tc_htb_glob opt = { + .rate2quantum = 10, + .version = 3, + }; struct rtattr *tail; - unsigned i; char *p; - memset(&opt,0,sizeof(opt)); - opt.rate2quantum = 10; - opt.version = 3; + unsigned int i; char *p; while (argc > 0) { if (matches(*argv, "r2q") == 0) { @@ -83,8 +83,8 @@ } } else if (matches(*argv, "debug") == 0) { NEXT_ARG(); p = *argv; - for (i=0; i<16; i++,p++) { - if (*p<'0' || *p>'3') break; + for (i = 0; i < 16; i++, p++) { + if (*p < '0' || *p > '3') break; opt.debug |= (*p-'0')<<(2*i); } } else if (matches(*argv, "direct_qlen") == 0) { @@ -111,20 +111,18 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - int ok=0; - struct tc_htb_opt opt; - __u32 rtab[256],ctab[256]; - unsigned buffer=0,cbuffer=0; - int cell_log=-1,ccell_log = -1; - unsigned mtu; + int ok = 0; + struct tc_htb_opt opt = {}; + __u32 rtab[256], ctab[256]; + unsigned buffer = 0, cbuffer = 0; + int cell_log = -1, ccell_log = -1; + unsigned int mtu = 1600; /* eth packet len */ unsigned short mpu = 0; unsigned short overhead = 0; unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */ struct rtattr *tail; __u64 ceil64 = 0, rate64 = 0; - memset(&opt, 0, sizeof(opt)); mtu = 1600; /* eth packet len */ - while (argc > 0) { if (matches(*argv, "prio") == 0) { NEXT_ARG(); @@ -268,13 +266,13 @@ struct rtattr *tb[TCA_HTB_MAX + 1]; struct tc_htb_opt *hopt; struct tc_htb_glob *gopt; - double buffer,cbuffer; + double buffer, cbuffer; unsigned int linklayer; __u64 rate64, ceil64; + SPRINT_BUF(b1); SPRINT_BUF(b2); SPRINT_BUF(b3); - SPRINT_BUF(b4); if (opt == NULL) return 0; @@ -311,18 +309,16 @@ cbuffer = tc_calc_xmitsize(ceil64, hopt->cbuffer); linklayer = (hopt->rate.linklayer & TC_LINKLAYER_MASK); if (linklayer > TC_LINKLAYER_ETHERNET || show_details) - fprintf(f, "linklayer %s ", sprint_linklayer(linklayer, b4)); + fprintf(f, "linklayer %s ", sprint_linklayer(linklayer, b3)); if (show_details) { - fprintf(f, "burst %s/%u mpu %s overhead %s ", + fprintf(f, "burst %s/%u mpu %s ", sprint_size(buffer, b1), 1<rate.cell_log, - sprint_size(hopt->rate.mpu&0xFF, b2), - sprint_size((hopt->rate.mpu>>8)&0xFF, b3)); - fprintf(f, "cburst %s/%u mpu %s overhead %s ", + sprint_size(hopt->rate.mpu, b2)); + fprintf(f, "cburst %s/%u mpu %s ", sprint_size(cbuffer, b1), 1<ceil.cell_log, - sprint_size(hopt->ceil.mpu&0xFF, b2), - sprint_size((hopt->ceil.mpu>>8)&0xFF, b3)); + sprint_size(hopt->ceil.mpu, b2)); fprintf(f, "level %d ", (int)hopt->level); } else { fprintf(f, "burst %s ", sprint_size(buffer, b1)); @@ -330,16 +326,16 @@ } if (show_raw) fprintf(f, "buffer [%08x] cbuffer [%08x] ", - hopt->buffer,hopt->cbuffer); + hopt->buffer, hopt->cbuffer); } if (tb[TCA_HTB_INIT]) { gopt = RTA_DATA(tb[TCA_HTB_INIT]); if (RTA_PAYLOAD(tb[TCA_HTB_INIT]) < sizeof(*gopt)) return -1; fprintf(f, "r2q %d default %x direct_packets_stat %u", - gopt->rate2quantum,gopt->defcls,gopt->direct_pkts); + gopt->rate2quantum, gopt->defcls, gopt->direct_pkts); if (show_details) - fprintf(f," ver %d.%d",gopt->version >> 16,gopt->version & 0xffff); + fprintf(f, " ver %d.%d", gopt->version >> 16, gopt->version & 0xffff); } if (tb[TCA_HTB_DIRECT_QLEN] && RTA_PAYLOAD(tb[TCA_HTB_DIRECT_QLEN]) >= sizeof(__u32)) { @@ -353,6 +349,7 @@ static int htb_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_htb_xstats *st; + if (xstats == NULL) return 0; @@ -361,16 +358,16 @@ st = RTA_DATA(xstats); fprintf(f, " lended: %u borrowed: %u giants: %u\n", - st->lends,st->borrows,st->giants); - fprintf(f, " tokens: %d ctokens: %d\n", st->tokens,st->ctokens); + st->lends, st->borrows, st->giants); + fprintf(f, " tokens: %d ctokens: %d\n", st->tokens, st->ctokens); return 0; } struct qdisc_util htb_qdisc_util = { - .id = "htb", + .id = "htb", .parse_qopt = htb_parse_opt, .print_qopt = htb_print_opt, - .print_xstats = htb_print_xstats, + .print_xstats = htb_print_xstats, .parse_copt = htb_parse_class_opt, .print_copt = htb_print_opt, }; diff -Nru iproute2-4.3.0/tc/q_ingress.c iproute2-4.9.0/tc/q_ingress.c --- iproute2-4.3.0/tc/q_ingress.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_ingress.c 2016-12-12 23:07:42.000000000 +0000 @@ -1,5 +1,4 @@ /* - * * q_ingress.c INGRESS. * * This program is free software; you can redistribute it and/or @@ -8,20 +7,9 @@ * 2 of the License, or (at your option) any later version. * * Authors: J Hadi Salim - * - * This is here just in case it is needed - * useless right now; might be useful in the future - * */ #include -#include -#include -#include -#include -#include -#include -#include #include #include "utils.h" @@ -29,10 +17,11 @@ static void explain(void) { - fprintf(stderr, "Usage: ... ingress \n"); + fprintf(stderr, "Usage: ... ingress\n"); } -static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) { while (argc > 0) { if (strcmp(*argv, "handle") == 0) { @@ -45,11 +34,11 @@ } } - addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); return 0; } -static int ingress_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int ingress_print_opt(struct qdisc_util *qu, FILE *f, + struct rtattr *opt) { fprintf(f, "---------------- "); return 0; diff -Nru iproute2-4.3.0/tc/q_multiq.c iproute2-4.9.0/tc/q_multiq.c --- iproute2-4.3.0/tc/q_multiq.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_multiq.c 2016-12-12 23:07:42.000000000 +0000 @@ -77,7 +77,7 @@ } struct qdisc_util multiq_qdisc_util = { - .id = "multiq", + .id = "multiq", .parse_qopt = multiq_parse_opt, .print_qopt = multiq_print_opt, }; diff -Nru iproute2-4.3.0/tc/q_netem.c iproute2-4.9.0/tc/q_netem.c --- iproute2-4.3.0/tc/q_netem.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_netem.c 2016-12-12 23:07:42.000000000 +0000 @@ -30,10 +30,10 @@ static void explain(void) { fprintf(stderr, -"Usage: ... netem [ limit PACKETS ] \n" \ +"Usage: ... netem [ limit PACKETS ]\n" \ " [ delay TIME [ JITTER [CORRELATION]]]\n" \ " [ distribution {uniform|normal|pareto|paretonormal} ]\n" \ -" [ corrupt PERCENT [CORRELATION]] \n" \ +" [ corrupt PERCENT [CORRELATION]]\n" \ " [ duplicate PERCENT [CORRELATION]]\n" \ " [ loss random PERCENT [CORRELATION]]\n" \ " [ loss state P13 [P31 [P32 [P23 P14]]]\n" \ @@ -58,7 +58,7 @@ /* scaled value used to percent of maximum. */ static void set_percent(__u32 *percent, double per) { - *percent = (unsigned) rint(per * max_percent_value); + *percent = (unsigned int) rint(per * max_percent_value); } @@ -70,7 +70,7 @@ char *p; *val = strtod(str, &p) / 100.; - if (*p && strcmp(p, "%") ) + if (*p && strcmp(p, "%")) return -1; return 0; @@ -92,7 +92,7 @@ snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value); } -static char * sprint_percent(__u32 per, char *buf) +static char *sprint_percent(__u32 per, char *buf) { print_percent(buf, SPRINT_BSIZE-1, per); return buf; @@ -123,6 +123,7 @@ n = 0; while (getline(&line, &len, f) != -1) { char *p, *endp; + if (*line == '\n' || *line == '#') continue; @@ -154,9 +155,9 @@ (based on kernel PSCHED_CLOCK configuration */ static int get_ticks(__u32 *ticks, const char *str) { - unsigned t; + unsigned int t; - if(get_time(&t, str)) + if (get_time(&t, str)) return -1; if (tc_core_time2big(t)) { @@ -174,24 +175,18 @@ int dist_size = 0; struct rtattr *tail; struct tc_netem_qopt opt = { .limit = 1000 }; - struct tc_netem_corr cor; - struct tc_netem_reorder reorder; - struct tc_netem_corrupt corrupt; + struct tc_netem_corr cor = {}; + struct tc_netem_reorder reorder = {}; + struct tc_netem_corrupt corrupt = {}; struct tc_netem_gimodel gimodel; struct tc_netem_gemodel gemodel; - struct tc_netem_rate rate; + struct tc_netem_rate rate = {}; __s16 *dist_data = NULL; __u16 loss_type = NETEM_LOSS_UNSPEC; - int present[__TCA_NETEM_MAX]; + int present[__TCA_NETEM_MAX] = {}; __u64 rate64 = 0; - memset(&cor, 0, sizeof(cor)); - memset(&reorder, 0, sizeof(reorder)); - memset(&corrupt, 0, sizeof(corrupt)); - memset(&rate, 0, sizeof(rate)); - memset(present, 0, sizeof(present)); - - for( ; argc > 0; --argc, ++argv) { + for ( ; argc > 0; --argc, ++argv) { if (matches(*argv, "limit") == 0) { NEXT_ARG(); if (get_size(&opt.limit, *argv)) { @@ -236,7 +231,7 @@ if (!strcmp(*argv, "random")) { NEXT_ARG(); - random_loss_model: +random_loss_model: if (get_percent(&opt.loss, *argv)) { explain1("loss percent"); return -1; @@ -545,6 +540,7 @@ const struct tc_netem_rate *rate = NULL; int len = RTA_PAYLOAD(opt) - sizeof(qopt); __u64 rate64 = 0; + SPRINT_BUF(b1); if (opt == NULL) @@ -558,6 +554,7 @@ if (len > 0) { struct rtattr *tb[TCA_NETEM_MAX+1]; + parse_rtattr(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(qopt), len); @@ -684,7 +681,7 @@ } struct qdisc_util netem_qdisc_util = { - .id = "netem", + .id = "netem", .parse_qopt = netem_parse_opt, .print_qopt = netem_print_opt, }; diff -Nru iproute2-4.3.0/tc/q_pie.c iproute2-4.9.0/tc/q_pie.c --- iproute2-4.3.0/tc/q_pie.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_pie.c 2016-12-12 23:07:42.000000000 +0000 @@ -136,8 +136,9 @@ unsigned int target; unsigned int alpha; unsigned int beta; - unsigned ecn; - unsigned bytemode; + unsigned int ecn; + unsigned int bytemode; + SPRINT_BUF(b1); if (opt == NULL) diff -Nru iproute2-4.3.0/tc/q_prio.c iproute2-4.9.0/tc/q_prio.c --- iproute2-4.3.0/tc/q_prio.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_prio.c 2016-12-12 23:07:42.000000000 +0000 @@ -32,7 +32,7 @@ { int pmap_mode = 0; int idx = 0; - struct tc_prio_qopt opt={3,{ 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }}; + struct tc_prio_qopt opt = {3, { 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 } }; struct rtattr *nest; unsigned char mq = 0; @@ -57,7 +57,8 @@ explain(); return -1; } else { - unsigned band; + unsigned int band; + if (!pmap_mode) { fprintf(stderr, "What is \"%s\"?\n", *argv); explain(); @@ -104,10 +105,10 @@ if (parse_rtattr_nested_compat(tb, TCA_PRIO_MAX, opt, qopt, sizeof(*qopt))) - return -1; + return -1; fprintf(f, "bands %u priomap ", qopt->bands); - for (i=0; i<=TC_PRIO_MAX; i++) + for (i = 0; i <= TC_PRIO_MAX; i++) fprintf(f, " %d", qopt->priomap[i]); if (tb[TCA_PRIO_MQ]) @@ -118,7 +119,7 @@ } struct qdisc_util prio_qdisc_util = { - .id = "prio", + .id = "prio", .parse_qopt = prio_parse_opt, .print_qopt = prio_print_opt, }; diff -Nru iproute2-4.3.0/tc/q_qfq.c iproute2-4.9.0/tc/q_qfq.c --- iproute2-4.3.0/tc/q_qfq.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_qfq.c 2016-12-12 23:07:42.000000000 +0000 @@ -38,16 +38,11 @@ static int qfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - while (argc > 0) { - if (matches(*argv, "help") == 0) { - explain(); - return -1; - } else { + if (argc > 0) { + if (matches(*argv, "help") != 0) fprintf(stderr, "What is \"%s\"?\n", *argv); - explain(); - return -1; - } - argc--; argv++; + explain(); + return -1; } return 0; diff -Nru iproute2-4.3.0/tc/q_red.c iproute2-4.9.0/tc/q_red.c --- iproute2-4.3.0/tc/q_red.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_red.c 2016-12-12 23:07:42.000000000 +0000 @@ -35,18 +35,16 @@ static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - struct tc_red_qopt opt; - unsigned burst = 0; - unsigned avpkt = 0; + struct tc_red_qopt opt = {}; + unsigned int burst = 0; + unsigned int avpkt = 0; double probability = 0.02; - unsigned rate = 0; + unsigned int rate = 0; int parm; __u8 sbuf[256]; __u32 max_P; struct rtattr *tail; - memset(&opt, 0, sizeof(opt)); - while (argc > 0) { if (strcmp(*argv, "limit") == 0) { NEXT_ARG(); @@ -160,6 +158,7 @@ struct rtattr *tb[TCA_RED_MAX + 1]; struct tc_red_qopt *qopt; __u32 max_P = 0; + SPRINT_BUF(b1); SPRINT_BUF(b2); SPRINT_BUF(b3); diff -Nru iproute2-4.3.0/tc/q_rr.c iproute2-4.9.0/tc/q_rr.c --- iproute2-4.3.0/tc/q_rr.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_rr.c 2016-12-12 23:07:42.000000000 +0000 @@ -33,7 +33,7 @@ { int pmap_mode = 0; int idx = 0; - struct tc_prio_qopt opt={3,{ 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }}; + struct tc_prio_qopt opt = {3, { 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 } }; struct rtattr *nest; unsigned char mq = 0; @@ -58,7 +58,8 @@ } else if (strcmp(*argv, "multiqueue") == 0) { mq = 1; } else { - unsigned band; + unsigned int band; + if (!pmap_mode) { fprintf(stderr, "What is \"%s\"?\n", *argv); explain(); @@ -102,7 +103,7 @@ return -1; fprintf(f, "bands %u priomap ", qopt->bands); - for (i=0; i <= TC_PRIO_MAX; i++) + for (i = 0; i <= TC_PRIO_MAX; i++) fprintf(f, " %d", qopt->priomap[i]); if (tb[TCA_PRIO_MQ]) @@ -113,7 +114,7 @@ } struct qdisc_util rr_qdisc_util = { - .id = "rr", + .id = "rr", .parse_qopt = rr_parse_opt, .print_qopt = rr_print_opt, }; diff -Nru iproute2-4.3.0/tc/q_sfb.c iproute2-4.9.0/tc/q_sfb.c --- iproute2-4.3.0/tc/q_sfb.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_sfb.c 2016-12-12 23:07:42.000000000 +0000 @@ -51,17 +51,16 @@ static int sfb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - struct tc_sfb_qopt opt; + struct tc_sfb_qopt opt = { + .rehash_interval = 600*1000, + .warmup_time = 60*1000, + .penalty_rate = 10, + .penalty_burst = 20, + .increment = (SFB_MAX_PROB + 1000) / 2000, + .decrement = (SFB_MAX_PROB + 10000) / 20000, + }; struct rtattr *tail; - memset(&opt, 0, sizeof(opt)); - opt.rehash_interval = 600*1000; - opt.warmup_time = 60*1000; - opt.penalty_rate = 10; - opt.penalty_burst = 20; - opt.increment = (SFB_MAX_PROB + 1000) / 2000; - opt.decrement = (SFB_MAX_PROB + 10000) / 20000; - while (argc > 0) { if (strcmp(*argv, "rehash") == 0) { NEXT_ARG(); @@ -158,8 +157,7 @@ fprintf(f, "limit %d max %d target %d\n" - " increment %.5f decrement %.5f penalty rate %d burst %d " - "(%ums %ums)", + " increment %.5f decrement %.5f penalty rate %d burst %d (%ums %ums)", qopt->limit, qopt->max, qopt->bin_size, (double)qopt->increment / SFB_MAX_PROB, (double)qopt->decrement / SFB_MAX_PROB, diff -Nru iproute2-4.3.0/tc/q_sfq.c iproute2-4.9.0/tc/q_sfq.c --- iproute2-4.3.0/tc/q_sfq.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_sfq.c 2016-12-12 23:07:42.000000000 +0000 @@ -38,14 +38,12 @@ static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { int ok = 0, red = 0; - struct tc_sfq_qopt_v1 opt; + struct tc_sfq_qopt_v1 opt = {}; unsigned int burst = 0; int wlog; unsigned int avpkt = 1000; double probability = 0.02; - memset(&opt, 0, sizeof(opt)); - while (argc > 0) { if (strcmp(*argv, "quantum") == 0) { NEXT_ARG(); @@ -207,6 +205,7 @@ { struct tc_sfq_qopt *qopt; struct tc_sfq_qopt_v1 *qopt_ext = NULL; + SPRINT_BUF(b1); SPRINT_BUF(b2); SPRINT_BUF(b3); diff -Nru iproute2-4.3.0/tc/q_tbf.c iproute2-4.9.0/tc/q_tbf.c --- iproute2-4.3.0/tc/q_tbf.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/q_tbf.c 2016-12-12 23:07:42.000000000 +0000 @@ -38,19 +38,17 @@ static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - int ok=0; - struct tc_tbf_qopt opt; + int ok = 0; + struct tc_tbf_qopt opt = {}; __u32 rtab[256]; __u32 ptab[256]; - unsigned buffer=0, mtu=0, mpu=0, latency=0; - int Rcell_log=-1, Pcell_log = -1; - unsigned short overhead=0; + unsigned buffer = 0, mtu = 0, mpu = 0, latency = 0; + int Rcell_log = -1, Pcell_log = -1; + unsigned short overhead = 0; unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */ struct rtattr *tail; __u64 rate64 = 0, prate64 = 0; - memset(&opt, 0, sizeof(opt)); - while (argc > 0) { if (matches(*argv, "limit") == 0) { NEXT_ARG(); @@ -86,6 +84,7 @@ strcmp(*argv, "buffer") == 0 || strcmp(*argv, "maxburst") == 0) { const char *parm_name = *argv; + NEXT_ARG(); if (buffer) { fprintf(stderr, "tbf: duplicate \"buffer/burst/maxburst\" specification\n"); @@ -99,6 +98,7 @@ } else if (strcmp(*argv, "mtu") == 0 || strcmp(*argv, "minburst") == 0) { const char *parm_name = *argv; + NEXT_ARG(); if (mtu) { fprintf(stderr, "tbf: duplicate \"mtu/minburst\" specification\n"); @@ -167,12 +167,12 @@ argc--; argv++; } - int verdict = 0; + int verdict = 0; - /* Be nice to the user: try to emit all error messages in - * one go rather than reveal one more problem when a - * previous one has been fixed. - */ + /* Be nice to the user: try to emit all error messages in + * one go rather than reveal one more problem when a + * previous one has been fixed. + */ if (rate64 == 0) { fprintf(stderr, "tbf: the \"rate\" parameter is mandatory.\n"); verdict = -1; @@ -193,18 +193,20 @@ verdict = -1; } - if (verdict != 0) { - explain(); - return verdict; - } + if (verdict != 0) { + explain(); + return verdict; + } opt.rate.rate = (rate64 >= (1ULL << 32)) ? ~0U : rate64; opt.peakrate.rate = (prate64 >= (1ULL << 32)) ? ~0U : prate64; if (opt.limit == 0) { double lim = rate64*(double)latency/TIME_UNITS_PER_SEC + buffer; + if (prate64) { double lim2 = prate64*(double)latency/TIME_UNITS_PER_SEC + mtu; + if (lim2 < lim) lim = lim2; } @@ -254,6 +256,7 @@ double buffer, mtu; double latency; __u64 rate64 = 0, prate64 = 0; + SPRINT_BUF(b1); SPRINT_BUF(b2); SPRINT_BUF(b3); @@ -305,6 +308,7 @@ latency = TIME_UNITS_PER_SEC*(qopt->limit/(double)rate64) - tc_core_tick2time(qopt->buffer); if (prate64) { double lat2 = TIME_UNITS_PER_SEC*(qopt->limit/(double)prate64) - tc_core_tick2time(qopt->mtu); + if (lat2 > latency) latency = lat2; } diff -Nru iproute2-4.3.0/tc/tc_bpf.c iproute2-4.9.0/tc/tc_bpf.c --- iproute2-4.3.0/tc/tc_bpf.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_bpf.c 2016-12-12 23:07:42.000000000 +0000 @@ -20,18 +20,28 @@ #include #include #include -#include -#include -#include -#include -#include -#include +#include #ifdef HAVE_ELF #include #include #endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + #include "utils.h" #include "bpf_elf.h" @@ -40,9 +50,59 @@ #include "tc_util.h" #include "tc_bpf.h" -int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len, - char **bpf_string, bool *need_release, - const char separator) +#ifndef AF_ALG +#define AF_ALG 38 +#endif + +#ifndef EM_BPF +#define EM_BPF 247 +#endif + +#ifdef HAVE_ELF +static int bpf_obj_open(const char *path, enum bpf_prog_type type, + const char *sec, bool verbose); +#else +static int bpf_obj_open(const char *path, enum bpf_prog_type type, + const char *sec, bool verbose) +{ + fprintf(stderr, "No ELF library support compiled in.\n"); + errno = ENOSYS; + return -1; +} +#endif + +static inline __u64 bpf_ptr_to_u64(const void *ptr) +{ + return (__u64)(unsigned long)ptr; +} + +static int bpf(int cmd, union bpf_attr *attr, unsigned int size) +{ +#ifdef __NR_bpf + return syscall(__NR_bpf, cmd, attr, size); +#else + fprintf(stderr, "No bpf syscall, kernel headers too old?\n"); + errno = ENOSYS; + return -1; +#endif +} + +static int bpf_map_update(int fd, const void *key, const void *value, + uint64_t flags) +{ + union bpf_attr attr = {}; + + attr.map_fd = fd; + attr.key = bpf_ptr_to_u64(key); + attr.value = bpf_ptr_to_u64(value); + attr.flags = flags; + + return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); +} + +static int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len, + char **bpf_string, bool *need_release, + const char separator) { char sp; @@ -52,12 +112,10 @@ FILE *fp; tmp_len = sizeof("4096,") + BPF_MAXINSNS * op_len; - tmp_string = malloc(tmp_len); + tmp_string = calloc(1, tmp_len); if (tmp_string == NULL) return -ENOMEM; - memset(tmp_string, 0, tmp_len); - fp = fopen(arg, "r"); if (fp == NULL) { perror("Cannot fopen"); @@ -90,8 +148,8 @@ return 0; } -int bpf_parse_ops(int argc, char **argv, struct sock_filter *bpf_ops, - bool from_file) +static int bpf_ops_parse(int argc, char **argv, struct sock_filter *bpf_ops, + bool from_file) { char *bpf_string, *token, separator = ','; int ret = 0, i = 0; @@ -111,8 +169,7 @@ token = bpf_string; while ((token = strchr(token, separator)) && (++token)[0]) { if (i >= bpf_len) { - fprintf(stderr, "Real program length exceeds encoded " - "length parameter!\n"); + fprintf(stderr, "Real program length exceeds encoded length parameter!\n"); ret = -EINVAL; goto out; } @@ -129,13 +186,11 @@ } if (i != bpf_len) { - fprintf(stderr, "Parsed program length is less than encoded" - "length parameter!\n"); + fprintf(stderr, "Parsed program length is less than encoded length parameter!\n"); ret = -EINVAL; goto out; } ret = bpf_len; - out: if (need_release) free(bpf_string); @@ -161,6 +216,269 @@ ops[i].jf, ops[i].k); } +static void bpf_map_pin_report(const struct bpf_elf_map *pin, + const struct bpf_elf_map *obj) +{ + fprintf(stderr, "Map specification differs from pinned file!\n"); + + if (obj->type != pin->type) + fprintf(stderr, " - Type: %u (obj) != %u (pin)\n", + obj->type, pin->type); + if (obj->size_key != pin->size_key) + fprintf(stderr, " - Size key: %u (obj) != %u (pin)\n", + obj->size_key, pin->size_key); + if (obj->size_value != pin->size_value) + fprintf(stderr, " - Size value: %u (obj) != %u (pin)\n", + obj->size_value, pin->size_value); + if (obj->max_elem != pin->max_elem) + fprintf(stderr, " - Max elems: %u (obj) != %u (pin)\n", + obj->max_elem, pin->max_elem); + if (obj->flags != pin->flags) + fprintf(stderr, " - Flags: %#x (obj) != %#x (pin)\n", + obj->flags, pin->flags); + + fprintf(stderr, "\n"); +} + +static int bpf_map_selfcheck_pinned(int fd, const struct bpf_elf_map *map, + int length) +{ + char file[PATH_MAX], buff[4096]; + struct bpf_elf_map tmp = {}, zero = {}; + unsigned int val; + FILE *fp; + + snprintf(file, sizeof(file), "/proc/%d/fdinfo/%d", getpid(), fd); + + fp = fopen(file, "r"); + if (!fp) { + fprintf(stderr, "No procfs support?!\n"); + return -EIO; + } + + while (fgets(buff, sizeof(buff), fp)) { + if (sscanf(buff, "map_type:\t%u", &val) == 1) + tmp.type = val; + else if (sscanf(buff, "key_size:\t%u", &val) == 1) + tmp.size_key = val; + else if (sscanf(buff, "value_size:\t%u", &val) == 1) + tmp.size_value = val; + else if (sscanf(buff, "max_entries:\t%u", &val) == 1) + tmp.max_elem = val; + else if (sscanf(buff, "map_flags:\t%i", &val) == 1) + tmp.flags = val; + } + + fclose(fp); + + if (!memcmp(&tmp, map, length)) { + return 0; + } else { + /* If kernel doesn't have eBPF-related fdinfo, we cannot do much, + * so just accept it. We know we do have an eBPF fd and in this + * case, everything is 0. It is guaranteed that no such map exists + * since map type of 0 is unloadable BPF_MAP_TYPE_UNSPEC. + */ + if (!memcmp(&tmp, &zero, length)) + return 0; + + bpf_map_pin_report(&tmp, map); + return -EINVAL; + } +} + +static int bpf_mnt_fs(const char *target) +{ + bool bind_done = false; + + while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) { + if (errno != EINVAL || bind_done) { + fprintf(stderr, "mount --make-private %s failed: %s\n", + target, strerror(errno)); + return -1; + } + + if (mount(target, target, "none", MS_BIND, NULL)) { + fprintf(stderr, "mount --bind %s %s failed: %s\n", + target, target, strerror(errno)); + return -1; + } + + bind_done = true; + } + + if (mount("bpf", target, "bpf", 0, NULL)) { + fprintf(stderr, "mount -t bpf bpf %s failed: %s\n", + target, strerror(errno)); + return -1; + } + + return 0; +} + +static int bpf_valid_mntpt(const char *mnt, unsigned long magic) +{ + struct statfs st_fs; + + if (statfs(mnt, &st_fs) < 0) + return -ENOENT; + if ((unsigned long)st_fs.f_type != magic) + return -ENOENT; + + return 0; +} + +static const char *bpf_find_mntpt(const char *fstype, unsigned long magic, + char *mnt, int len, + const char * const *known_mnts) +{ + const char * const *ptr; + char type[100]; + FILE *fp; + + if (known_mnts) { + ptr = known_mnts; + while (*ptr) { + if (bpf_valid_mntpt(*ptr, magic) == 0) { + strncpy(mnt, *ptr, len - 1); + mnt[len - 1] = 0; + return mnt; + } + ptr++; + } + } + + fp = fopen("/proc/mounts", "r"); + if (fp == NULL || len != PATH_MAX) + return NULL; + + while (fscanf(fp, "%*s %" textify(PATH_MAX) "s %99s %*s %*d %*d\n", + mnt, type) == 2) { + if (strcmp(type, fstype) == 0) + break; + } + + fclose(fp); + if (strcmp(type, fstype) != 0) + return NULL; + + return mnt; +} + +int bpf_trace_pipe(void) +{ + char tracefs_mnt[PATH_MAX] = TRACE_DIR_MNT; + static const char * const tracefs_known_mnts[] = { + TRACE_DIR_MNT, + "/sys/kernel/debug/tracing", + "/tracing", + "/trace", + 0, + }; + char tpipe[PATH_MAX]; + const char *mnt; + int fd; + + mnt = bpf_find_mntpt("tracefs", TRACEFS_MAGIC, tracefs_mnt, + sizeof(tracefs_mnt), tracefs_known_mnts); + if (!mnt) { + fprintf(stderr, "tracefs not mounted?\n"); + return -1; + } + + snprintf(tpipe, sizeof(tpipe), "%s/trace_pipe", mnt); + + fd = open(tpipe, O_RDONLY); + if (fd < 0) + return -1; + + fprintf(stderr, "Running! Hang up with ^C!\n\n"); + while (1) { + static char buff[4096]; + ssize_t ret; + + ret = read(fd, buff, sizeof(buff) - 1); + if (ret > 0) { + write(2, buff, ret); + fflush(stderr); + } + } + + return 0; +} + +static const char *bpf_get_tc_dir(void) +{ + static bool bpf_mnt_cached; + static char bpf_tc_dir[PATH_MAX]; + static const char *mnt; + static const char * const bpf_known_mnts[] = { + BPF_DIR_MNT, + 0, + }; + char bpf_mnt[PATH_MAX] = BPF_DIR_MNT; + char bpf_glo_dir[PATH_MAX]; + int ret; + + if (bpf_mnt_cached) + goto done; + + mnt = bpf_find_mntpt("bpf", BPF_FS_MAGIC, bpf_mnt, sizeof(bpf_mnt), + bpf_known_mnts); + if (!mnt) { + mnt = getenv(BPF_ENV_MNT); + if (!mnt) + mnt = BPF_DIR_MNT; + ret = bpf_mnt_fs(mnt); + if (ret) { + mnt = NULL; + goto out; + } + } + + snprintf(bpf_tc_dir, sizeof(bpf_tc_dir), "%s/%s", mnt, BPF_DIR_TC); + ret = mkdir(bpf_tc_dir, S_IRWXU); + if (ret && errno != EEXIST) { + fprintf(stderr, "mkdir %s failed: %s\n", bpf_tc_dir, + strerror(errno)); + mnt = NULL; + goto out; + } + + snprintf(bpf_glo_dir, sizeof(bpf_glo_dir), "%s/%s", + bpf_tc_dir, BPF_DIR_GLOBALS); + ret = mkdir(bpf_glo_dir, S_IRWXU); + if (ret && errno != EEXIST) { + fprintf(stderr, "mkdir %s failed: %s\n", bpf_glo_dir, + strerror(errno)); + mnt = NULL; + goto out; + } + + mnt = bpf_tc_dir; +out: + bpf_mnt_cached = true; +done: + return mnt; +} + +static int bpf_obj_get(const char *pathname) +{ + union bpf_attr attr = {}; + char tmp[PATH_MAX]; + + if (strlen(pathname) > 2 && pathname[0] == 'm' && + pathname[1] == ':' && bpf_get_tc_dir()) { + snprintf(tmp, sizeof(tmp), "%s/%s", + bpf_get_tc_dir(), pathname + 2); + pathname = tmp; + } + + attr.pathname = bpf_ptr_to_u64(pathname); + + return bpf(BPF_OBJ_GET, &attr, sizeof(attr)); +} + const char *bpf_default_section(const enum bpf_prog_type type) { switch (type) { @@ -173,35 +491,267 @@ } } +enum bpf_mode { + CBPF_BYTECODE = 0, + CBPF_FILE, + EBPF_OBJECT, + EBPF_PINNED, + __BPF_MODE_MAX, +#define BPF_MODE_MAX __BPF_MODE_MAX +}; + +static int bpf_parse(int *ptr_argc, char ***ptr_argv, const bool *opt_tbl, + enum bpf_prog_type *type, enum bpf_mode *mode, + const char **ptr_object, const char **ptr_section, + const char **ptr_uds_name, struct sock_filter *opcodes) +{ + const char *file, *section, *uds_name; + bool verbose = false; + int ret, argc; + char **argv; + + argv = *ptr_argv; + argc = *ptr_argc; + + if (opt_tbl[CBPF_BYTECODE] && + (matches(*argv, "bytecode") == 0 || + strcmp(*argv, "bc") == 0)) { + *mode = CBPF_BYTECODE; + } else if (opt_tbl[CBPF_FILE] && + (matches(*argv, "bytecode-file") == 0 || + strcmp(*argv, "bcf") == 0)) { + *mode = CBPF_FILE; + } else if (opt_tbl[EBPF_OBJECT] && + (matches(*argv, "object-file") == 0 || + strcmp(*argv, "obj") == 0)) { + *mode = EBPF_OBJECT; + } else if (opt_tbl[EBPF_PINNED] && + (matches(*argv, "object-pinned") == 0 || + matches(*argv, "pinned") == 0 || + matches(*argv, "fd") == 0)) { + *mode = EBPF_PINNED; + } else { + fprintf(stderr, "What mode is \"%s\"?\n", *argv); + return -1; + } + + NEXT_ARG(); + file = section = uds_name = NULL; + if (*mode == EBPF_OBJECT || *mode == EBPF_PINNED) { + file = *argv; + NEXT_ARG_FWD(); + + if (*type == BPF_PROG_TYPE_UNSPEC) { + if (argc > 0 && matches(*argv, "type") == 0) { + NEXT_ARG(); + if (matches(*argv, "cls") == 0) { + *type = BPF_PROG_TYPE_SCHED_CLS; + } else if (matches(*argv, "act") == 0) { + *type = BPF_PROG_TYPE_SCHED_ACT; + } else { + fprintf(stderr, "What type is \"%s\"?\n", + *argv); + return -1; + } + NEXT_ARG_FWD(); + } else { + *type = BPF_PROG_TYPE_SCHED_CLS; + } + } + + section = bpf_default_section(*type); + if (argc > 0 && matches(*argv, "section") == 0) { + NEXT_ARG(); + section = *argv; + NEXT_ARG_FWD(); + } + + uds_name = getenv(BPF_ENV_UDS); + if (argc > 0 && !uds_name && + matches(*argv, "export") == 0) { + NEXT_ARG(); + uds_name = *argv; + NEXT_ARG_FWD(); + } + + if (argc > 0 && matches(*argv, "verbose") == 0) { + verbose = true; + NEXT_ARG_FWD(); + } + + PREV_ARG(); + } + + if (*mode == CBPF_BYTECODE || *mode == CBPF_FILE) + ret = bpf_ops_parse(argc, argv, opcodes, *mode == CBPF_FILE); + else if (*mode == EBPF_OBJECT) + ret = bpf_obj_open(file, *type, section, verbose); + else if (*mode == EBPF_PINNED) + ret = bpf_obj_get(file); + else + return -1; + + if (ptr_object) + *ptr_object = file; + if (ptr_section) + *ptr_section = section; + if (ptr_uds_name) + *ptr_uds_name = uds_name; + + *ptr_argc = argc; + *ptr_argv = argv; + + return ret; +} + +int bpf_parse_common(int *ptr_argc, char ***ptr_argv, const int *nla_tbl, + enum bpf_prog_type type, const char **ptr_object, + const char **ptr_uds_name, struct nlmsghdr *n) +{ + struct sock_filter opcodes[BPF_MAXINSNS]; + const bool opt_tbl[BPF_MODE_MAX] = { + [CBPF_BYTECODE] = true, + [CBPF_FILE] = true, + [EBPF_OBJECT] = true, + [EBPF_PINNED] = true, + }; + char annotation[256]; + const char *section; + enum bpf_mode mode; + int ret; + + ret = bpf_parse(ptr_argc, ptr_argv, opt_tbl, &type, &mode, + ptr_object, §ion, ptr_uds_name, opcodes); + if (ret < 0) + return ret; + + if (mode == CBPF_BYTECODE || mode == CBPF_FILE) { + addattr16(n, MAX_MSG, nla_tbl[BPF_NLA_OPS_LEN], ret); + addattr_l(n, MAX_MSG, nla_tbl[BPF_NLA_OPS], opcodes, + ret * sizeof(struct sock_filter)); + } + + if (mode == EBPF_OBJECT || mode == EBPF_PINNED) { + snprintf(annotation, sizeof(annotation), "%s:[%s]", + basename(*ptr_object), mode == EBPF_PINNED ? + "*fsobj" : section); + + addattr32(n, MAX_MSG, nla_tbl[BPF_NLA_FD], ret); + addattrstrz(n, MAX_MSG, nla_tbl[BPF_NLA_NAME], annotation); + } + + return 0; +} + +int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv) +{ + enum bpf_prog_type type = BPF_PROG_TYPE_UNSPEC; + const bool opt_tbl[BPF_MODE_MAX] = { + [CBPF_BYTECODE] = false, + [CBPF_FILE] = false, + [EBPF_OBJECT] = true, + [EBPF_PINNED] = true, + }; + const struct bpf_elf_map test = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .size_key = sizeof(int), + .size_value = sizeof(int), + }; + int ret, prog_fd, map_fd; + const char *section; + enum bpf_mode mode; + uint32_t map_key; + + prog_fd = bpf_parse(&argc, &argv, opt_tbl, &type, &mode, + NULL, §ion, NULL, NULL); + if (prog_fd < 0) + return prog_fd; + if (key) { + map_key = *key; + } else { + ret = sscanf(section, "%*i/%i", &map_key); + if (ret != 1) { + fprintf(stderr, "Couldn\'t infer map key from section name! Please provide \'key\' argument!\n"); + ret = -EINVAL; + goto out_prog; + } + } + + map_fd = bpf_obj_get(map_path); + if (map_fd < 0) { + fprintf(stderr, "Couldn\'t retrieve pinned map \'%s\': %s\n", + map_path, strerror(errno)); + ret = map_fd; + goto out_prog; + } + + ret = bpf_map_selfcheck_pinned(map_fd, &test, + offsetof(struct bpf_elf_map, max_elem)); + if (ret < 0) { + fprintf(stderr, "Map \'%s\' self-check failed!\n", map_path); + goto out_map; + } + + ret = bpf_map_update(map_fd, &map_key, &prog_fd, BPF_ANY); + if (ret < 0) + fprintf(stderr, "Map update failed: %s\n", strerror(errno)); +out_map: + close(map_fd); +out_prog: + close(prog_fd); + return ret; +} + #ifdef HAVE_ELF -struct bpf_elf_sec_data { - GElf_Shdr sec_hdr; - char *sec_name; - Elf_Data *sec_data; +struct bpf_elf_prog { + enum bpf_prog_type type; + const struct bpf_insn *insns; + size_t size; + const char *license; }; -struct bpf_map_data { - int *fds; - const char *obj; - struct bpf_elf_st *st; - struct bpf_elf_map *ent; +struct bpf_hash_entry { + unsigned int pinning; + const char *subpath; + struct bpf_hash_entry *next; }; -/* If we provide a small buffer with log level enabled, the kernel - * could fail program load as no buffer space is available for the - * log and thus verifier fails. In case something doesn't pass the - * verifier we still want to hand something descriptive to the user. - */ -static char bpf_log_buf[65536]; -static bool bpf_verbose; +struct bpf_elf_ctx { + Elf *elf_fd; + GElf_Ehdr elf_hdr; + Elf_Data *sym_tab; + Elf_Data *str_tab; + int obj_fd; + int map_fds[ELF_MAX_MAPS]; + struct bpf_elf_map maps[ELF_MAX_MAPS]; + int sym_num; + int map_num; + bool *sec_done; + int sec_maps; + char license[ELF_MAX_LICENSE_LEN]; + enum bpf_prog_type type; + bool verbose; + struct bpf_elf_st stat; + struct bpf_hash_entry *ht[256]; + char *log; + size_t log_size; +}; -static struct bpf_elf_st bpf_st; +struct bpf_elf_sec_data { + GElf_Shdr sec_hdr; + Elf_Data *sec_data; + const char *sec_name; +}; -static int map_fds[ELF_MAX_MAPS]; -static struct bpf_elf_map map_ent[ELF_MAX_MAPS]; +struct bpf_map_data { + int *fds; + const char *obj; + struct bpf_elf_st *st; + struct bpf_elf_map *ent; +}; -static void bpf_dump_error(const char *format, ...) __check_format_string(1, 2); -static void bpf_dump_error(const char *format, ...) +static __check_format_string(2, 3) void +bpf_dump_error(struct bpf_elf_ctx *ctx, const char *format, ...) { va_list vl; @@ -209,193 +759,507 @@ vfprintf(stderr, format, vl); va_end(vl); - if (bpf_log_buf[0]) { - fprintf(stderr, "%s\n", bpf_log_buf); - memset(bpf_log_buf, 0, sizeof(bpf_log_buf)); + if (ctx->log && ctx->log[0]) { + if (ctx->verbose) { + fprintf(stderr, "%s\n", ctx->log); + } else { + unsigned int off = 0, len = strlen(ctx->log); + + if (len > BPF_MAX_LOG) { + off = len - BPF_MAX_LOG; + fprintf(stderr, "Skipped %u bytes, use \'verb\' option for the full verbose log.\n[...]\n", + off); + } + fprintf(stderr, "%s\n", ctx->log + off); + } + + memset(ctx->log, 0, ctx->log_size); } } -static void bpf_save_finfo(int file_fd) +static int bpf_log_realloc(struct bpf_elf_ctx *ctx) { - struct stat st; - int ret; + size_t log_size = ctx->log_size; + void *ptr; - memset(&bpf_st, 0, sizeof(bpf_st)); + if (!ctx->log) { + log_size = 65536; + } else { + log_size <<= 1; + if (log_size > (UINT_MAX >> 8)) + return -EINVAL; + } - ret = fstat(file_fd, &st); - if (ret < 0) { - fprintf(stderr, "Stat of elf file failed: %s\n", - strerror(errno)); - return; + ptr = realloc(ctx->log, log_size); + if (!ptr) + return -ENOMEM; + + ctx->log = ptr; + ctx->log_size = log_size; + + return 0; +} + +static int bpf_map_create(enum bpf_map_type type, uint32_t size_key, + uint32_t size_value, uint32_t max_elem, + uint32_t flags) +{ + union bpf_attr attr = {}; + + attr.map_type = type; + attr.key_size = size_key; + attr.value_size = size_value; + attr.max_entries = max_elem; + attr.map_flags = flags; + + return bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); +} + +static int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns, + size_t size_insns, const char *license, char *log, + size_t size_log) +{ + union bpf_attr attr = {}; + + attr.prog_type = type; + attr.insns = bpf_ptr_to_u64(insns); + attr.insn_cnt = size_insns / sizeof(struct bpf_insn); + attr.license = bpf_ptr_to_u64(license); + + if (size_log > 0) { + attr.log_buf = bpf_ptr_to_u64(log); + attr.log_size = size_log; + attr.log_level = 1; } - bpf_st.st_dev = st.st_dev; - bpf_st.st_ino = st.st_ino; + return bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); } -static void bpf_clear_finfo(void) +static int bpf_obj_pin(int fd, const char *pathname) { - memset(&bpf_st, 0, sizeof(bpf_st)); + union bpf_attr attr = {}; + + attr.pathname = bpf_ptr_to_u64(pathname); + attr.bpf_fd = fd; + + return bpf(BPF_OBJ_PIN, &attr, sizeof(attr)); } -static bool bpf_may_skip_map_creation(int file_fd) +static int bpf_obj_hash(const char *object, uint8_t *out, size_t len) { - struct stat st; - int ret; + struct sockaddr_alg alg = { + .salg_family = AF_ALG, + .salg_type = "hash", + .salg_name = "sha1", + }; + int ret, cfd, ofd, ffd; + struct stat stbuff; + ssize_t size; - ret = fstat(file_fd, &st); + if (!object || len != 20) + return -EINVAL; + + cfd = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (cfd < 0) { + fprintf(stderr, "Cannot get AF_ALG socket: %s\n", + strerror(errno)); + return cfd; + } + + ret = bind(cfd, (struct sockaddr *)&alg, sizeof(alg)); if (ret < 0) { - fprintf(stderr, "Stat of elf file failed: %s\n", + fprintf(stderr, "Error binding socket: %s\n", strerror(errno)); + goto out_cfd; + } + + ofd = accept(cfd, NULL, 0); + if (ofd < 0) { + fprintf(stderr, "Error accepting socket: %s\n", + strerror(errno)); + ret = ofd; + goto out_cfd; + } + + ffd = open(object, O_RDONLY); + if (ffd < 0) { + fprintf(stderr, "Error opening object %s: %s\n", + object, strerror(errno)); + ret = ffd; + goto out_ofd; + } + + ret = fstat(ffd, &stbuff); + if (ret < 0) { + fprintf(stderr, "Error doing fstat: %s\n", strerror(errno)); + goto out_ffd; + } + + size = sendfile(ofd, ffd, NULL, stbuff.st_size); + if (size != stbuff.st_size) { + fprintf(stderr, "Error from sendfile (%zd vs %zu bytes): %s\n", + size, stbuff.st_size, strerror(errno)); + ret = -1; + goto out_ffd; + } + + size = read(ofd, out, len); + if (size != len) { + fprintf(stderr, "Error from read (%zd vs %zu bytes): %s\n", + size, len, strerror(errno)); + ret = -1; + } else { + ret = 0; + } +out_ffd: + close(ffd); +out_ofd: + close(ofd); +out_cfd: + close(cfd); + return ret; +} + +static const char *bpf_get_obj_uid(const char *pathname) +{ + static bool bpf_uid_cached; + static char bpf_uid[64]; + uint8_t tmp[20]; + int ret; + + if (bpf_uid_cached) + goto done; + + ret = bpf_obj_hash(pathname, tmp, sizeof(tmp)); + if (ret) { + fprintf(stderr, "Object hashing failed!\n"); + return NULL; + } + + hexstring_n2a(tmp, sizeof(tmp), bpf_uid, sizeof(bpf_uid)); + bpf_uid_cached = true; +done: + return bpf_uid; +} + +static int bpf_init_env(const char *pathname) +{ + struct rlimit limit = { + .rlim_cur = RLIM_INFINITY, + .rlim_max = RLIM_INFINITY, + }; + + /* Don't bother in case we fail! */ + setrlimit(RLIMIT_MEMLOCK, &limit); + + if (!bpf_get_tc_dir()) { + fprintf(stderr, "Continuing without mounted eBPF fs. Too old kernel?\n"); + return 0; + } + + if (!bpf_get_obj_uid(pathname)) + return -1; + + return 0; +} + +static const char *bpf_custom_pinning(const struct bpf_elf_ctx *ctx, + uint32_t pinning) +{ + struct bpf_hash_entry *entry; + + entry = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)]; + while (entry && entry->pinning != pinning) + entry = entry->next; + + return entry ? entry->subpath : NULL; +} + +static bool bpf_no_pinning(const struct bpf_elf_ctx *ctx, + uint32_t pinning) +{ + switch (pinning) { + case PIN_OBJECT_NS: + case PIN_GLOBAL_NS: return false; + case PIN_NONE: + return true; + default: + return !bpf_custom_pinning(ctx, pinning); + } +} + +static void bpf_make_pathname(char *pathname, size_t len, const char *name, + const struct bpf_elf_ctx *ctx, uint32_t pinning) +{ + switch (pinning) { + case PIN_OBJECT_NS: + snprintf(pathname, len, "%s/%s/%s", bpf_get_tc_dir(), + bpf_get_obj_uid(NULL), name); + break; + case PIN_GLOBAL_NS: + snprintf(pathname, len, "%s/%s/%s", bpf_get_tc_dir(), + BPF_DIR_GLOBALS, name); + break; + default: + snprintf(pathname, len, "%s/../%s/%s", bpf_get_tc_dir(), + bpf_custom_pinning(ctx, pinning), name); + break; + } +} + +static int bpf_probe_pinned(const char *name, const struct bpf_elf_ctx *ctx, + uint32_t pinning) +{ + char pathname[PATH_MAX]; + + if (bpf_no_pinning(ctx, pinning) || !bpf_get_tc_dir()) + return 0; + + bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning); + return bpf_obj_get(pathname); +} + +static int bpf_make_obj_path(void) +{ + char tmp[PATH_MAX]; + int ret; + + snprintf(tmp, sizeof(tmp), "%s/%s", bpf_get_tc_dir(), + bpf_get_obj_uid(NULL)); + + ret = mkdir(tmp, S_IRWXU); + if (ret && errno != EEXIST) { + fprintf(stderr, "mkdir %s failed: %s\n", tmp, strerror(errno)); + return ret; + } + + return 0; +} + +static int bpf_make_custom_path(const char *todo) +{ + char tmp[PATH_MAX], rem[PATH_MAX], *sub; + int ret; + + snprintf(tmp, sizeof(tmp), "%s/../", bpf_get_tc_dir()); + snprintf(rem, sizeof(rem), "%s/", todo); + sub = strtok(rem, "/"); + + while (sub) { + if (strlen(tmp) + strlen(sub) + 2 > PATH_MAX) + return -EINVAL; + + strcat(tmp, sub); + strcat(tmp, "/"); + + ret = mkdir(tmp, S_IRWXU); + if (ret && errno != EEXIST) { + fprintf(stderr, "mkdir %s failed: %s\n", tmp, + strerror(errno)); + return ret; + } + + sub = strtok(NULL, "/"); } - return (bpf_st.st_dev == st.st_dev) && - (bpf_st.st_ino == st.st_ino); + return 0; } -static int bpf_create_map(enum bpf_map_type type, unsigned int size_key, - unsigned int size_value, unsigned int max_elem) +static int bpf_place_pinned(int fd, const char *name, + const struct bpf_elf_ctx *ctx, uint32_t pinning) { - union bpf_attr attr = { - .map_type = type, - .key_size = size_key, - .value_size = size_value, - .max_entries = max_elem, - }; + char pathname[PATH_MAX]; + const char *tmp; + int ret = 0; + + if (bpf_no_pinning(ctx, pinning) || !bpf_get_tc_dir()) + return 0; + + if (pinning == PIN_OBJECT_NS) + ret = bpf_make_obj_path(); + else if ((tmp = bpf_custom_pinning(ctx, pinning))) + ret = bpf_make_custom_path(tmp); + if (ret < 0) + return ret; - return bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); + bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning); + return bpf_obj_pin(fd, pathname); } -static int bpf_update_map(int fd, const void *key, const void *value, - uint64_t flags) -{ - union bpf_attr attr = { - .map_fd = fd, - .key = bpf_ptr_to_u64(key), - .value = bpf_ptr_to_u64(value), - .flags = flags, - }; +static void bpf_prog_report(int fd, const char *section, + const struct bpf_elf_prog *prog, + struct bpf_elf_ctx *ctx) +{ + unsigned int insns = prog->size / sizeof(struct bpf_insn); + + fprintf(stderr, "\nProg section \'%s\' %s%s (%d)!\n", section, + fd < 0 ? "rejected: " : "loaded", + fd < 0 ? strerror(errno) : "", + fd < 0 ? errno : fd); + + fprintf(stderr, " - Type: %u\n", prog->type); + fprintf(stderr, " - Instructions: %u (%u over limit)\n", + insns, insns > BPF_MAXINSNS ? insns - BPF_MAXINSNS : 0); + fprintf(stderr, " - License: %s\n\n", prog->license); + + bpf_dump_error(ctx, "Verifier analysis:\n\n"); +} + +static int bpf_prog_attach(const char *section, + const struct bpf_elf_prog *prog, + struct bpf_elf_ctx *ctx) +{ + int tries = 0, fd; +retry: + errno = 0; + fd = bpf_prog_load(prog->type, prog->insns, prog->size, + prog->license, ctx->log, ctx->log_size); + if (fd < 0 || ctx->verbose) { + /* The verifier log is pretty chatty, sometimes so chatty + * on larger programs, that we could fail to dump everything + * into our buffer. Still, try to give a debuggable error + * log for the user, so enlarge it and re-fail. + */ + if (fd < 0 && (errno == ENOSPC || !ctx->log_size)) { + if (tries++ < 6 && !bpf_log_realloc(ctx)) + goto retry; + + fprintf(stderr, "Log buffer too small to dump verifier log %zu bytes (%d tries)!\n", + ctx->log_size, tries); + return fd; + } - return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); + bpf_prog_report(fd, section, prog, ctx); + } + + return fd; } -static int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns, - unsigned int len, const char *license) +static void bpf_map_report(int fd, const char *name, + const struct bpf_elf_map *map, + struct bpf_elf_ctx *ctx) { - union bpf_attr attr = { - .prog_type = type, - .insns = bpf_ptr_to_u64(insns), - .insn_cnt = len / sizeof(struct bpf_insn), - .license = bpf_ptr_to_u64(license), - .log_buf = bpf_ptr_to_u64(bpf_log_buf), - .log_size = sizeof(bpf_log_buf), - .log_level = 1, - }; + fprintf(stderr, "Map object \'%s\' %s%s (%d)!\n", name, + fd < 0 ? "rejected: " : "loaded", + fd < 0 ? strerror(errno) : "", + fd < 0 ? errno : fd); - return bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); + fprintf(stderr, " - Type: %u\n", map->type); + fprintf(stderr, " - Identifier: %u\n", map->id); + fprintf(stderr, " - Pinning: %u\n", map->pinning); + fprintf(stderr, " - Size key: %u\n", map->size_key); + fprintf(stderr, " - Size value: %u\n", map->size_value); + fprintf(stderr, " - Max elems: %u\n", map->max_elem); + fprintf(stderr, " - Flags: %#x\n\n", map->flags); } -static int bpf_prog_attach(enum bpf_prog_type type, const char *sec, - const struct bpf_insn *insns, unsigned int size, - const char *license) +static int bpf_map_attach(const char *name, const struct bpf_elf_map *map, + struct bpf_elf_ctx *ctx) { - int prog_fd = bpf_prog_load(type, insns, size, license); + int fd, ret; - if (prog_fd < 0 || bpf_verbose) { - bpf_dump_error("%s (section \'%s\'): %s\n", prog_fd < 0 ? - "BPF program rejected" : - "BPF program verification", - sec, strerror(errno)); + fd = bpf_probe_pinned(name, ctx, map->pinning); + if (fd > 0) { + ret = bpf_map_selfcheck_pinned(fd, map, + offsetof(struct bpf_elf_map, + id)); + if (ret < 0) { + close(fd); + fprintf(stderr, "Map \'%s\' self-check failed!\n", + name); + return ret; + } + if (ctx->verbose) + fprintf(stderr, "Map \'%s\' loaded as pinned!\n", + name); + return fd; + } + + errno = 0; + fd = bpf_map_create(map->type, map->size_key, map->size_value, + map->max_elem, map->flags); + if (fd < 0 || ctx->verbose) { + bpf_map_report(fd, name, map, ctx); + if (fd < 0) + return fd; + } + + ret = bpf_place_pinned(fd, name, ctx, map->pinning); + if (ret < 0 && errno != EEXIST) { + fprintf(stderr, "Could not pin %s map: %s\n", name, + strerror(errno)); + close(fd); + return ret; } - return prog_fd; + return fd; } -static int bpf_map_attach(enum bpf_map_type type, unsigned int size_key, - unsigned int size_value, unsigned int max_elem) +static const char *bpf_str_tab_name(const struct bpf_elf_ctx *ctx, + const GElf_Sym *sym) { - int map_fd = bpf_create_map(type, size_key, size_value, max_elem); - - if (map_fd < 0) - bpf_dump_error("BPF map rejected: %s\n", strerror(errno)); - - return map_fd; + return ctx->str_tab->d_buf + sym->st_name; } -static void bpf_maps_init(void) +static const char *bpf_map_fetch_name(struct bpf_elf_ctx *ctx, int which) { + GElf_Sym sym; int i; - memset(map_ent, 0, sizeof(map_ent)); - for (i = 0; i < ARRAY_SIZE(map_fds); i++) - map_fds[i] = -1; -} + for (i = 0; i < ctx->sym_num; i++) { + if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym) + continue; -static int bpf_maps_count(void) -{ - int i, count = 0; + if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL || + GELF_ST_TYPE(sym.st_info) != STT_NOTYPE || + sym.st_shndx != ctx->sec_maps || + sym.st_value / sizeof(struct bpf_elf_map) != which) + continue; - for (i = 0; i < ARRAY_SIZE(map_fds); i++) { - if (map_fds[i] < 0) - break; - count++; + return bpf_str_tab_name(ctx, &sym); } - return count; -} - -static void bpf_maps_destroy(void) -{ - int i; - - memset(map_ent, 0, sizeof(map_ent)); - for (i = 0; i < ARRAY_SIZE(map_fds); i++) { - if (map_fds[i] >= 0) - close(map_fds[i]); - } + return NULL; } -static int bpf_maps_attach(struct bpf_elf_map *maps, unsigned int num_maps) +static int bpf_maps_attach_all(struct bpf_elf_ctx *ctx) { - int i, ret; + const char *map_name; + int i, fd; - for (i = 0; (i < num_maps) && (num_maps <= ARRAY_SIZE(map_fds)); i++) { - struct bpf_elf_map *map = &maps[i]; + for (i = 0; i < ctx->map_num; i++) { + map_name = bpf_map_fetch_name(ctx, i); + if (!map_name) + return -EIO; - ret = bpf_map_attach(map->type, map->size_key, - map->size_value, map->max_elem); - if (ret < 0) - goto err_unwind; + fd = bpf_map_attach(map_name, &ctx->maps[i], ctx); + if (fd < 0) + return fd; - map_fds[i] = ret; + ctx->map_fds[i] = fd; } return 0; - -err_unwind: - bpf_maps_destroy(); - return ret; } -static int bpf_fill_section_data(Elf *elf_fd, GElf_Ehdr *elf_hdr, int sec_index, - struct bpf_elf_sec_data *sec_data) +static int bpf_fill_section_data(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) { + Elf_Data *sec_edata; GElf_Shdr sec_hdr; Elf_Scn *sec_fd; - Elf_Data *sec_edata; char *sec_name; - memset(sec_data, 0, sizeof(*sec_data)); + memset(data, 0, sizeof(*data)); - sec_fd = elf_getscn(elf_fd, sec_index); + sec_fd = elf_getscn(ctx->elf_fd, section); if (!sec_fd) return -EINVAL; - if (gelf_getshdr(sec_fd, &sec_hdr) != &sec_hdr) return -EIO; - sec_name = elf_strptr(elf_fd, elf_hdr->e_shstrndx, + sec_name = elf_strptr(ctx->elf_fd, ctx->elf_hdr.e_shstrndx, sec_hdr.sh_name); if (!sec_name || !sec_hdr.sh_size) return -ENOENT; @@ -404,16 +1268,142 @@ if (!sec_edata || elf_getdata(sec_fd, sec_edata)) return -EIO; - memcpy(&sec_data->sec_hdr, &sec_hdr, sizeof(sec_hdr)); - sec_data->sec_name = sec_name; - sec_data->sec_data = sec_edata; + memcpy(&data->sec_hdr, &sec_hdr, sizeof(sec_hdr)); + + data->sec_name = sec_name; + data->sec_data = sec_edata; + return 0; +} + +static int bpf_fetch_maps(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + if (data->sec_data->d_size % sizeof(struct bpf_elf_map) != 0) + return -EINVAL; + + ctx->map_num = data->sec_data->d_size / sizeof(struct bpf_elf_map); + ctx->sec_maps = section; + ctx->sec_done[section] = true; + + if (ctx->map_num > ARRAY_SIZE(ctx->map_fds)) { + fprintf(stderr, "Too many BPF maps in ELF section!\n"); + return -ENOMEM; + } + + memcpy(ctx->maps, data->sec_data->d_buf, data->sec_data->d_size); + return 0; +} + +static int bpf_fetch_license(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + if (data->sec_data->d_size > sizeof(ctx->license)) + return -ENOMEM; + + memcpy(ctx->license, data->sec_data->d_buf, data->sec_data->d_size); + ctx->sec_done[section] = true; + return 0; +} + +static int bpf_fetch_symtab(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + ctx->sym_tab = data->sec_data; + ctx->sym_num = data->sec_hdr.sh_size / data->sec_hdr.sh_entsize; + ctx->sec_done[section] = true; + return 0; +} +static int bpf_fetch_strtab(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + ctx->str_tab = data->sec_data; + ctx->sec_done[section] = true; return 0; } -static int bpf_apply_relo_data(struct bpf_elf_sec_data *data_relo, - struct bpf_elf_sec_data *data_insn, - Elf_Data *sym_tab) +static bool bpf_has_map_data(const struct bpf_elf_ctx *ctx) +{ + return ctx->sym_tab && ctx->str_tab && ctx->sec_maps; +} + +static int bpf_fetch_ancillary(struct bpf_elf_ctx *ctx) +{ + struct bpf_elf_sec_data data; + int i, ret = -1; + + for (i = 1; i < ctx->elf_hdr.e_shnum; i++) { + ret = bpf_fill_section_data(ctx, i, &data); + if (ret < 0) + continue; + + if (data.sec_hdr.sh_type == SHT_PROGBITS && + !strcmp(data.sec_name, ELF_SECTION_MAPS)) + ret = bpf_fetch_maps(ctx, i, &data); + else if (data.sec_hdr.sh_type == SHT_PROGBITS && + !strcmp(data.sec_name, ELF_SECTION_LICENSE)) + ret = bpf_fetch_license(ctx, i, &data); + else if (data.sec_hdr.sh_type == SHT_SYMTAB && + !strcmp(data.sec_name, ".symtab")) + ret = bpf_fetch_symtab(ctx, i, &data); + else if (data.sec_hdr.sh_type == SHT_STRTAB && + !strcmp(data.sec_name, ".strtab")) + ret = bpf_fetch_strtab(ctx, i, &data); + if (ret < 0) { + fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n", + i); + break; + } + } + + if (bpf_has_map_data(ctx)) { + ret = bpf_maps_attach_all(ctx); + if (ret < 0) { + fprintf(stderr, "Error loading maps into kernel!\n"); + return ret; + } + } + + return ret; +} + +static int bpf_fetch_prog(struct bpf_elf_ctx *ctx, const char *section) +{ + struct bpf_elf_sec_data data; + struct bpf_elf_prog prog; + int ret, i, fd = -1; + + for (i = 1; i < ctx->elf_hdr.e_shnum; i++) { + if (ctx->sec_done[i]) + continue; + + ret = bpf_fill_section_data(ctx, i, &data); + if (ret < 0 || + !(data.sec_hdr.sh_type == SHT_PROGBITS && + data.sec_hdr.sh_flags & SHF_EXECINSTR && + !strcmp(data.sec_name, section))) + continue; + + memset(&prog, 0, sizeof(prog)); + prog.type = ctx->type; + prog.insns = data.sec_data->d_buf; + prog.size = data.sec_data->d_size; + prog.license = ctx->license; + + fd = bpf_prog_attach(section, &prog, ctx); + if (fd < 0) + break; + + ctx->sec_done[i] = true; + break; + } + + return fd; +} + +static int bpf_apply_relo_data(struct bpf_elf_ctx *ctx, + struct bpf_elf_sec_data *data_relo, + struct bpf_elf_sec_data *data_insn) { Elf_Data *idata = data_insn->sec_data; GElf_Shdr *rhdr = &data_relo->sec_hdr; @@ -422,7 +1412,7 @@ unsigned int num_insns = idata->d_size / sizeof(*insns); for (relo_ent = 0; relo_ent < relo_num; relo_ent++) { - unsigned int ioff, fnum; + unsigned int ioff, rmap; GElf_Rel relo; GElf_Sym sym; @@ -430,307 +1420,453 @@ return -EIO; ioff = relo.r_offset / sizeof(struct bpf_insn); - if (ioff >= num_insns) - return -EINVAL; - if (insns[ioff].code != (BPF_LD | BPF_IMM | BPF_DW)) + if (ioff >= num_insns || + insns[ioff].code != (BPF_LD | BPF_IMM | BPF_DW)) { + fprintf(stderr, "ELF contains relo data for non ld64 instruction at offset %u! Compiler bug?!\n", + ioff); + if (ioff < num_insns && + insns[ioff].code == (BPF_JMP | BPF_CALL)) + fprintf(stderr, " - Try to annotate functions with always_inline attribute!\n"); return -EINVAL; + } - if (gelf_getsym(sym_tab, GELF_R_SYM(relo.r_info), &sym) != &sym) + if (gelf_getsym(ctx->sym_tab, GELF_R_SYM(relo.r_info), &sym) != &sym) return -EIO; + if (sym.st_shndx != ctx->sec_maps) { + fprintf(stderr, "ELF contains non-map related relo data in entry %u pointing to section %u! Compiler bug?!\n", + relo_ent, sym.st_shndx); + return -EIO; + } - fnum = sym.st_value / sizeof(struct bpf_elf_map); - if (fnum >= ARRAY_SIZE(map_fds)) + rmap = sym.st_value / sizeof(struct bpf_elf_map); + if (rmap >= ARRAY_SIZE(ctx->map_fds)) return -EINVAL; - if (map_fds[fnum] < 0) + if (!ctx->map_fds[rmap]) return -EINVAL; + if (ctx->verbose) + fprintf(stderr, "Map \'%s\' (%d) injected into prog section \'%s\' at offset %u!\n", + bpf_str_tab_name(ctx, &sym), ctx->map_fds[rmap], + data_insn->sec_name, ioff); + insns[ioff].src_reg = BPF_PSEUDO_MAP_FD; - insns[ioff].imm = map_fds[fnum]; + insns[ioff].imm = ctx->map_fds[rmap]; } return 0; } -static int bpf_fetch_ancillary(int file_fd, Elf *elf_fd, GElf_Ehdr *elf_hdr, - bool *sec_done, char *license, unsigned int lic_len, - Elf_Data **sym_tab) +static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section, + bool *lderr) { - int sec_index, ret = -1; + struct bpf_elf_sec_data data_relo, data_insn; + struct bpf_elf_prog prog; + int ret, idx, i, fd = -1; + + for (i = 1; i < ctx->elf_hdr.e_shnum; i++) { + ret = bpf_fill_section_data(ctx, i, &data_relo); + if (ret < 0 || data_relo.sec_hdr.sh_type != SHT_REL) + continue; - for (sec_index = 1; sec_index < elf_hdr->e_shnum; sec_index++) { - struct bpf_elf_sec_data data_anc; + idx = data_relo.sec_hdr.sh_info; + ret = bpf_fill_section_data(ctx, idx, &data_insn); + if (ret < 0 || + !(data_insn.sec_hdr.sh_type == SHT_PROGBITS && + data_insn.sec_hdr.sh_flags & SHF_EXECINSTR && + !strcmp(data_insn.sec_name, section))) + continue; - ret = bpf_fill_section_data(elf_fd, elf_hdr, sec_index, - &data_anc); + ret = bpf_apply_relo_data(ctx, &data_relo, &data_insn); if (ret < 0) continue; - /* Extract and load eBPF map fds. */ - if (!strcmp(data_anc.sec_name, ELF_SECTION_MAPS) && - !bpf_may_skip_map_creation(file_fd)) { - struct bpf_elf_map *maps; - unsigned int maps_num; - - if (data_anc.sec_data->d_size % sizeof(*maps) != 0) - return -EINVAL; - - maps = data_anc.sec_data->d_buf; - maps_num = data_anc.sec_data->d_size / sizeof(*maps); - memcpy(map_ent, maps, data_anc.sec_data->d_size); - - ret = bpf_maps_attach(maps, maps_num); - if (ret < 0) - return ret; - - sec_done[sec_index] = true; - } - /* Extract eBPF license. */ - else if (!strcmp(data_anc.sec_name, ELF_SECTION_LICENSE)) { - if (data_anc.sec_data->d_size > lic_len) - return -ENOMEM; - - sec_done[sec_index] = true; - memcpy(license, data_anc.sec_data->d_buf, - data_anc.sec_data->d_size); - } - /* Extract symbol table for relocations (map fd fixups). */ - else if (data_anc.sec_hdr.sh_type == SHT_SYMTAB) { - sec_done[sec_index] = true; - *sym_tab = data_anc.sec_data; + memset(&prog, 0, sizeof(prog)); + prog.type = ctx->type; + prog.insns = data_insn.sec_data->d_buf; + prog.size = data_insn.sec_data->d_size; + prog.license = ctx->license; + + fd = bpf_prog_attach(section, &prog, ctx); + if (fd < 0) { + *lderr = true; + break; } + + ctx->sec_done[i] = true; + ctx->sec_done[idx] = true; + break; } + return fd; +} + +static int bpf_fetch_prog_sec(struct bpf_elf_ctx *ctx, const char *section) +{ + bool lderr = false; + int ret = -1; + + if (bpf_has_map_data(ctx)) + ret = bpf_fetch_prog_relo(ctx, section, &lderr); + if (ret < 0 && !lderr) + ret = bpf_fetch_prog(ctx, section); + return ret; } -static int bpf_fetch_prog_relo(Elf *elf_fd, GElf_Ehdr *elf_hdr, bool *sec_done, - enum bpf_prog_type type, const char *sec, - const char *license, Elf_Data *sym_tab) -{ - int sec_index, prog_fd = -1; - - for (sec_index = 1; sec_index < elf_hdr->e_shnum; sec_index++) { - struct bpf_elf_sec_data data_relo, data_insn; - int ins_index, ret; - - /* Attach eBPF programs with relocation data (maps). */ - ret = bpf_fill_section_data(elf_fd, elf_hdr, sec_index, - &data_relo); - if (ret < 0 || data_relo.sec_hdr.sh_type != SHT_REL) - continue; +static int bpf_find_map_by_id(struct bpf_elf_ctx *ctx, uint32_t id) +{ + int i; - ins_index = data_relo.sec_hdr.sh_info; + for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) + if (ctx->map_fds[i] && ctx->maps[i].id == id && + ctx->maps[i].type == BPF_MAP_TYPE_PROG_ARRAY) + return i; + return -1; +} - ret = bpf_fill_section_data(elf_fd, elf_hdr, ins_index, - &data_insn); - if (ret < 0) - continue; - if (strcmp(data_insn.sec_name, sec)) +static int bpf_fill_prog_arrays(struct bpf_elf_ctx *ctx) +{ + struct bpf_elf_sec_data data; + uint32_t map_id, key_id; + int fd, i, ret, idx; + + for (i = 1; i < ctx->elf_hdr.e_shnum; i++) { + if (ctx->sec_done[i]) continue; - ret = bpf_apply_relo_data(&data_relo, &data_insn, sym_tab); + ret = bpf_fill_section_data(ctx, i, &data); if (ret < 0) continue; - prog_fd = bpf_prog_attach(type, sec, data_insn.sec_data->d_buf, - data_insn.sec_data->d_size, license); - if (prog_fd < 0) + ret = sscanf(data.sec_name, "%i/%i", &map_id, &key_id); + if (ret != 2) continue; - sec_done[sec_index] = true; - sec_done[ins_index] = true; - break; + idx = bpf_find_map_by_id(ctx, map_id); + if (idx < 0) + continue; + + fd = bpf_fetch_prog_sec(ctx, data.sec_name); + if (fd < 0) + return -EIO; + + ret = bpf_map_update(ctx->map_fds[idx], &key_id, + &fd, BPF_ANY); + if (ret < 0) { + if (errno == E2BIG) + fprintf(stderr, "Tail call key %u for map %u out of bounds?\n", + key_id, map_id); + return -errno; + } + + ctx->sec_done[i] = true; } - return prog_fd; + return 0; } -static int bpf_fetch_prog(Elf *elf_fd, GElf_Ehdr *elf_hdr, bool *sec_done, - enum bpf_prog_type type, const char *sec, - const char *license) +static void bpf_save_finfo(struct bpf_elf_ctx *ctx) { - int sec_index, prog_fd = -1; + struct stat st; + int ret; - for (sec_index = 1; sec_index < elf_hdr->e_shnum; sec_index++) { - struct bpf_elf_sec_data data_insn; - int ret; + memset(&ctx->stat, 0, sizeof(ctx->stat)); - /* Attach eBPF programs without relocation data. */ - if (sec_done[sec_index]) - continue; + ret = fstat(ctx->obj_fd, &st); + if (ret < 0) { + fprintf(stderr, "Stat of elf file failed: %s\n", + strerror(errno)); + return; + } - ret = bpf_fill_section_data(elf_fd, elf_hdr, sec_index, - &data_insn); - if (ret < 0) - continue; - if (strcmp(data_insn.sec_name, sec)) - continue; + ctx->stat.st_dev = st.st_dev; + ctx->stat.st_ino = st.st_ino; +} + +static int bpf_read_pin_mapping(FILE *fp, uint32_t *id, char *path) +{ + char buff[PATH_MAX]; + + while (fgets(buff, sizeof(buff), fp)) { + char *ptr = buff; - prog_fd = bpf_prog_attach(type, sec, data_insn.sec_data->d_buf, - data_insn.sec_data->d_size, license); - if (prog_fd < 0) + while (*ptr == ' ' || *ptr == '\t') + ptr++; + + if (*ptr == '#' || *ptr == '\n' || *ptr == 0) continue; - sec_done[sec_index] = true; - break; + if (sscanf(ptr, "%i %s\n", id, path) != 2 && + sscanf(ptr, "%i %s #", id, path) != 2) { + strcpy(path, ptr); + return -1; + } + + return 1; } - return prog_fd; + return 0; } -static int bpf_fetch_prog_sec(Elf *elf_fd, GElf_Ehdr *elf_hdr, bool *sec_done, - enum bpf_prog_type type, const char *sec, - const char *license, Elf_Data *sym_tab) +static bool bpf_pinning_reserved(uint32_t pinning) { - int ret = -1; - - if (sym_tab) - ret = bpf_fetch_prog_relo(elf_fd, elf_hdr, sec_done, type, - sec, license, sym_tab); - if (ret < 0) - ret = bpf_fetch_prog(elf_fd, elf_hdr, sec_done, type, sec, - license); - return ret; + switch (pinning) { + case PIN_NONE: + case PIN_OBJECT_NS: + case PIN_GLOBAL_NS: + return true; + default: + return false; + } } -static int bpf_fill_prog_arrays(Elf *elf_fd, GElf_Ehdr *elf_hdr, bool *sec_done, - enum bpf_prog_type type, const char *license, - Elf_Data *sym_tab) +static void bpf_hash_init(struct bpf_elf_ctx *ctx, const char *db_file) { - int sec_index; + struct bpf_hash_entry *entry; + char subpath[PATH_MAX] = {}; + uint32_t pinning; + FILE *fp; + int ret; + + fp = fopen(db_file, "r"); + if (!fp) + return; - for (sec_index = 1; sec_index < elf_hdr->e_shnum; sec_index++) { - struct bpf_elf_sec_data data_insn; - int ret, map_id, key_id, prog_fd; + while ((ret = bpf_read_pin_mapping(fp, &pinning, subpath))) { + if (ret == -1) { + fprintf(stderr, "Database %s is corrupted at: %s\n", + db_file, subpath); + fclose(fp); + return; + } - if (sec_done[sec_index]) + if (bpf_pinning_reserved(pinning)) { + fprintf(stderr, "Database %s, id %u is reserved - ignoring!\n", + db_file, pinning); continue; + } - ret = bpf_fill_section_data(elf_fd, elf_hdr, sec_index, - &data_insn); - if (ret < 0) + entry = malloc(sizeof(*entry)); + if (!entry) { + fprintf(stderr, "No memory left for db entry!\n"); continue; + } - ret = sscanf(data_insn.sec_name, "%i/%i", &map_id, &key_id); - if (ret != 2) + entry->pinning = pinning; + entry->subpath = strdup(subpath); + if (!entry->subpath) { + fprintf(stderr, "No memory left for db entry!\n"); + free(entry); continue; + } - if (map_id >= ARRAY_SIZE(map_fds) || map_fds[map_id] < 0) - return -ENOENT; - if (map_ent[map_id].type != BPF_MAP_TYPE_PROG_ARRAY || - map_ent[map_id].max_elem <= key_id) - return -EINVAL; + entry->next = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)]; + ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)] = entry; + } - prog_fd = bpf_fetch_prog_sec(elf_fd, elf_hdr, sec_done, - type, data_insn.sec_name, - license, sym_tab); - if (prog_fd < 0) - return -EIO; + fclose(fp); +} - ret = bpf_update_map(map_fds[map_id], &key_id, &prog_fd, - BPF_ANY); - if (ret < 0) - return -ENOENT; +static void bpf_hash_destroy(struct bpf_elf_ctx *ctx) +{ + struct bpf_hash_entry *entry; + int i; + + for (i = 0; i < ARRAY_SIZE(ctx->ht); i++) { + while ((entry = ctx->ht[i]) != NULL) { + ctx->ht[i] = entry->next; + free((char *)entry->subpath); + free(entry); + } + } +} + +static int bpf_elf_check_ehdr(const struct bpf_elf_ctx *ctx) +{ + if (ctx->elf_hdr.e_type != ET_REL || + (ctx->elf_hdr.e_machine != EM_NONE && + ctx->elf_hdr.e_machine != EM_BPF) || + ctx->elf_hdr.e_version != EV_CURRENT) { + fprintf(stderr, "ELF format error, ELF file not for eBPF?\n"); + return -EINVAL; + } - sec_done[sec_index] = true; + switch (ctx->elf_hdr.e_ident[EI_DATA]) { + default: + fprintf(stderr, "ELF format error, wrong endianness info?\n"); + return -EINVAL; + case ELFDATA2LSB: + if (htons(1) == 1) { + fprintf(stderr, + "We are big endian, eBPF object is little endian!\n"); + return -EIO; + } + break; + case ELFDATA2MSB: + if (htons(1) != 1) { + fprintf(stderr, + "We are little endian, eBPF object is big endian!\n"); + return -EIO; + } + break; } return 0; } -int bpf_open_object(const char *path, enum bpf_prog_type type, - const char *sec, bool verbose) +static int bpf_elf_ctx_init(struct bpf_elf_ctx *ctx, const char *pathname, + enum bpf_prog_type type, bool verbose) { - char license[ELF_MAX_LICENSE_LEN]; - int file_fd, prog_fd = -1, ret; - Elf_Data *sym_tab = NULL; - GElf_Ehdr elf_hdr; - bool *sec_done; - Elf *elf_fd; + int ret = -EINVAL; - if (elf_version(EV_CURRENT) == EV_NONE) - return -EINVAL; + if (elf_version(EV_CURRENT) == EV_NONE || + bpf_init_env(pathname)) + return ret; - file_fd = open(path, O_RDONLY, 0); - if (file_fd < 0) - return -errno; + memset(ctx, 0, sizeof(*ctx)); + ctx->verbose = verbose; + ctx->type = type; - elf_fd = elf_begin(file_fd, ELF_C_READ, NULL); - if (!elf_fd) { + ctx->obj_fd = open(pathname, O_RDONLY); + if (ctx->obj_fd < 0) + return ctx->obj_fd; + + ctx->elf_fd = elf_begin(ctx->obj_fd, ELF_C_READ, NULL); + if (!ctx->elf_fd) { ret = -EINVAL; - goto out; + goto out_fd; } - if (gelf_getehdr(elf_fd, &elf_hdr) != &elf_hdr) { + if (elf_kind(ctx->elf_fd) != ELF_K_ELF) { + ret = -EINVAL; + goto out_fd; + } + + if (gelf_getehdr(ctx->elf_fd, &ctx->elf_hdr) != + &ctx->elf_hdr) { ret = -EIO; goto out_elf; } - sec_done = calloc(elf_hdr.e_shnum, sizeof(*sec_done)); - if (!sec_done) { + ret = bpf_elf_check_ehdr(ctx); + if (ret < 0) + goto out_elf; + + ctx->sec_done = calloc(ctx->elf_hdr.e_shnum, + sizeof(*(ctx->sec_done))); + if (!ctx->sec_done) { ret = -ENOMEM; goto out_elf; } - memset(license, 0, sizeof(license)); - bpf_verbose = verbose; + if (ctx->verbose && bpf_log_realloc(ctx)) { + ret = -ENOMEM; + goto out_free; + } - if (!bpf_may_skip_map_creation(file_fd)) - bpf_maps_init(); + bpf_save_finfo(ctx); + bpf_hash_init(ctx, CONFDIR "/bpf_pinning"); - ret = bpf_fetch_ancillary(file_fd, elf_fd, &elf_hdr, sec_done, - license, sizeof(license), &sym_tab); - if (ret < 0) - goto out_maps; + return 0; +out_free: + free(ctx->sec_done); +out_elf: + elf_end(ctx->elf_fd); +out_fd: + close(ctx->obj_fd); + return ret; +} - prog_fd = bpf_fetch_prog_sec(elf_fd, &elf_hdr, sec_done, type, - sec, license, sym_tab); - if (prog_fd < 0) - goto out_maps; +static int bpf_maps_count(struct bpf_elf_ctx *ctx) +{ + int i, count = 0; - if (!bpf_may_skip_map_creation(file_fd)) { - ret = bpf_fill_prog_arrays(elf_fd, &elf_hdr, sec_done, - type, license, sym_tab); - if (ret < 0) - goto out_prog; + for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) { + if (!ctx->map_fds[i]) + break; + count++; } - bpf_save_finfo(file_fd); + return count; +} - free(sec_done); +static void bpf_maps_teardown(struct bpf_elf_ctx *ctx) +{ + int i; - elf_end(elf_fd); - close(file_fd); + for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) { + if (ctx->map_fds[i]) + close(ctx->map_fds[i]); + } +} - return prog_fd; +static void bpf_elf_ctx_destroy(struct bpf_elf_ctx *ctx, bool failure) +{ + if (failure) + bpf_maps_teardown(ctx); -out_prog: - close(prog_fd); -out_maps: - bpf_maps_destroy(); - free(sec_done); -out_elf: - elf_end(elf_fd); + bpf_hash_destroy(ctx); + + free(ctx->sec_done); + free(ctx->log); + + elf_end(ctx->elf_fd); + close(ctx->obj_fd); +} + +static struct bpf_elf_ctx __ctx; + +static int bpf_obj_open(const char *pathname, enum bpf_prog_type type, + const char *section, bool verbose) +{ + struct bpf_elf_ctx *ctx = &__ctx; + int fd = 0, ret; + + ret = bpf_elf_ctx_init(ctx, pathname, type, verbose); + if (ret < 0) { + fprintf(stderr, "Cannot initialize ELF context!\n"); + return ret; + } + + ret = bpf_fetch_ancillary(ctx); + if (ret < 0) { + fprintf(stderr, "Error fetching ELF ancillary data!\n"); + goto out; + } + + fd = bpf_fetch_prog_sec(ctx, section); + if (fd < 0) { + fprintf(stderr, "Error fetching program/map!\n"); + ret = fd; + goto out; + } + + ret = bpf_fill_prog_arrays(ctx); + if (ret < 0) + fprintf(stderr, "Error filling program arrays!\n"); out: - close(file_fd); - bpf_clear_finfo(); - return prog_fd; + bpf_elf_ctx_destroy(ctx, ret < 0); + if (ret < 0) { + if (fd) + close(fd); + return ret; + } + + return fd; } static int bpf_map_set_send(int fd, struct sockaddr_un *addr, unsigned int addr_len, const struct bpf_map_data *aux, unsigned int entries) { - struct bpf_map_set_msg msg; + struct bpf_map_set_msg msg = { + .aux.uds_ver = BPF_SCM_AUX_VER, + .aux.num_ent = entries, + }; int *cmsg_buf, min_fd; char *amsg_buf; int i; - memset(&msg, 0, sizeof(msg)); - - msg.aux.uds_ver = BPF_SCM_AUX_VER; - msg.aux.num_ent = entries; - strncpy(msg.aux.obj_name, aux->obj, sizeof(msg.aux.obj_name)); memcpy(&msg.aux.obj_st, aux->st, sizeof(msg.aux.obj_st)); @@ -803,8 +1939,14 @@ int bpf_send_map_fds(const char *path, const char *obj) { - struct sockaddr_un addr; - struct bpf_map_data bpf_aux; + struct bpf_elf_ctx *ctx = &__ctx; + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + struct bpf_map_data bpf_aux = { + .fds = ctx->map_fds, + .ent = ctx->maps, + .st = &ctx->stat, + .obj = obj, + }; int fd, ret; fd = socket(AF_UNIX, SOCK_DGRAM, 0); @@ -814,8 +1956,6 @@ return -1; } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path, sizeof(addr.sun_path)); ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); @@ -825,20 +1965,13 @@ return -1; } - memset(&bpf_aux, 0, sizeof(bpf_aux)); - - bpf_aux.fds = map_fds; - bpf_aux.ent = map_ent; - - bpf_aux.obj = obj; - bpf_aux.st = &bpf_st; - ret = bpf_map_set_send(fd, &addr, sizeof(addr), &bpf_aux, - bpf_maps_count()); + bpf_maps_count(ctx)); if (ret < 0) fprintf(stderr, "Cannot send fds to %s: %s\n", path, strerror(errno)); + bpf_maps_teardown(ctx); close(fd); return ret; } @@ -846,7 +1979,7 @@ int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux, unsigned int entries) { - struct sockaddr_un addr; + struct sockaddr_un addr = { .sun_family = AF_UNIX }; int fd, ret; fd = socket(AF_UNIX, SOCK_DGRAM, 0); @@ -856,8 +1989,6 @@ return -1; } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path, sizeof(addr.sun_path)); ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); diff -Nru iproute2-4.3.0/tc/tc_bpf.h iproute2-4.9.0/tc/tc_bpf.h --- iproute2-4.3.0/tc/tc_bpf.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_bpf.h 2016-12-12 23:07:42.000000000 +0000 @@ -13,61 +13,60 @@ #ifndef _TC_BPF_H_ #define _TC_BPF_H_ 1 -#include #include -#include #include -#include -#include -#include -#include +#include #include "utils.h" #include "bpf_scm.h" +enum { + BPF_NLA_OPS_LEN = 0, + BPF_NLA_OPS, + BPF_NLA_FD, + BPF_NLA_NAME, + __BPF_NLA_MAX, +}; + +#define BPF_NLA_MAX __BPF_NLA_MAX + #define BPF_ENV_UDS "TC_BPF_UDS" +#define BPF_ENV_MNT "TC_BPF_MNT" -int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len, - char **bpf_string, bool *need_release, - const char separator); -int bpf_parse_ops(int argc, char **argv, struct sock_filter *bpf_ops, - bool from_file); -void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len); +#ifndef BPF_MAX_LOG +# define BPF_MAX_LOG 4096 +#endif + +#ifndef BPF_FS_MAGIC +# define BPF_FS_MAGIC 0xcafe4a11 +#endif +#define BPF_DIR_MNT "/sys/fs/bpf" + +#define BPF_DIR_TC "tc" +#define BPF_DIR_GLOBALS "globals" + +#ifndef TRACEFS_MAGIC +# define TRACEFS_MAGIC 0x74726163 +#endif + +#define TRACE_DIR_MNT "/sys/kernel/tracing" + +int bpf_trace_pipe(void); const char *bpf_default_section(const enum bpf_prog_type type); -#ifdef HAVE_ELF -int bpf_open_object(const char *path, enum bpf_prog_type type, - const char *sec, bool verbose); +int bpf_parse_common(int *ptr_argc, char ***ptr_argv, const int *nla_tbl, + enum bpf_prog_type type, const char **ptr_object, + const char **ptr_uds_name, struct nlmsghdr *n); +int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv); +void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len); + +#ifdef HAVE_ELF int bpf_send_map_fds(const char *path, const char *obj); int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux, unsigned int entries); - -static inline __u64 bpf_ptr_to_u64(const void *ptr) -{ - return (__u64) (unsigned long) ptr; -} - -static inline int bpf(int cmd, union bpf_attr *attr, unsigned int size) -{ -#ifdef __NR_bpf - return syscall(__NR_bpf, cmd, attr, size); -#else - fprintf(stderr, "No bpf syscall, kernel headers too old?\n"); - errno = ENOSYS; - return -1; -#endif -} #else -static inline int bpf_open_object(const char *path, enum bpf_prog_type type, - const char *sec, bool verbose) -{ - fprintf(stderr, "No ELF library support compiled in.\n"); - errno = ENOSYS; - return -1; -} - static inline int bpf_send_map_fds(const char *path, const char *obj) { return 0; diff -Nru iproute2-4.3.0/tc/tc.c iproute2-4.9.0/tc/tc.c --- iproute2-4.3.0/tc/tc.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc.c 2016-12-12 23:07:42.000000000 +0000 @@ -31,33 +31,33 @@ #include "tc_common.h" #include "namespace.h" -int show_stats = 0; -int show_details = 0; -int show_raw = 0; -int show_pretty = 0; -int show_graph = 0; +int show_stats; +int show_details; +int show_raw; +int show_pretty; +int show_graph; int timestamp; -int batch_mode = 0; -int resolve_hosts = 0; -int use_iec = 0; -int force = 0; -bool use_names = false; +int batch_mode; +int resolve_hosts; +int use_iec; +int force; +bool use_names; static char *conf_file; struct rtnl_handle rth; -static void *BODY = NULL; /* cached handle dlopen(NULL) */ -static struct qdisc_util * qdisc_list; -static struct filter_util * filter_list; +static void *BODY; /* cached handle dlopen(NULL) */ +static struct qdisc_util *qdisc_list; +static struct filter_util *filter_list; static int print_noqopt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { if (opt && RTA_PAYLOAD(opt)) fprintf(f, "[Unknown qdisc, optlen=%u] ", - (unsigned) RTA_PAYLOAD(opt)); + (unsigned int) RTA_PAYLOAD(opt)); return 0; } @@ -74,7 +74,7 @@ { if (opt && RTA_PAYLOAD(opt)) fprintf(f, "fh %08x [Unknown filter, optlen=%u] ", - fhandle, (unsigned) RTA_PAYLOAD(opt)); + fhandle, (unsigned int) RTA_PAYLOAD(opt)); else if (fhandle) fprintf(f, "fh %08x ", fhandle); return 0; @@ -90,6 +90,7 @@ } if (fhandle) { struct tcmsg *t = NLMSG_DATA(n); + if (get_u32(&handle, fhandle, 16)) { fprintf(stderr, "Unparsable filter ID \"%s\"\n", fhandle); return -1; @@ -132,11 +133,9 @@ return q; noexist: - q = malloc(sizeof(*q)); + q = calloc(1, sizeof(*q)); if (q) { - - memset(q, 0, sizeof(*q)); - q->id = strcpy(malloc(strlen(str)+1), str); + q->id = strdup(str); q->parse_qopt = parse_noqopt; q->print_qopt = print_noqopt; goto reg; @@ -176,9 +175,8 @@ filter_list = q; return q; noexist: - q = malloc(sizeof(*q)); + q = calloc(1, sizeof(*q)); if (q) { - memset(q, 0, sizeof(*q)); strncpy(q->id, str, 15); q->parse_fopt = parse_nofopt; q->print_fopt = print_nofopt; @@ -191,9 +189,8 @@ { fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n" " tc [-force] -batch filename\n" - "where OBJECT := { qdisc | class | filter | action | monitor | exec }\n" - " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -p[retty] | -b[atch] [filename] | " - "-n[etns] name |\n" + "where OBJECT := { qdisc | class | filter | action | monitor | exec }\n" + " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -p[retty] | -b[atch] [filename] | -n[etns] name |\n" " -nm | -nam[es] | { -cf | -conf } path }\n"); } diff -Nru iproute2-4.3.0/tc/tc_cbq.c iproute2-4.9.0/tc/tc_cbq.c --- iproute2-4.3.0/tc/tc_cbq.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_cbq.c 2016-12-12 23:07:42.000000000 +0000 @@ -24,8 +24,8 @@ #include "tc_core.h" #include "tc_cbq.h" -unsigned tc_cbq_calc_maxidle(unsigned bndw, unsigned rate, unsigned avpkt, - int ewma_log, unsigned maxburst) +unsigned int tc_cbq_calc_maxidle(unsigned int bndw, unsigned int rate, unsigned int avpkt, + int ewma_log, unsigned int maxburst) { double maxidle; double g = 1.0 - 1.0/(1< maxidle) maxidle = vxmt; @@ -41,8 +42,8 @@ return tc_core_time2tick(maxidle*(1< help\n"); - return; } -static int tc_class_modify(int cmd, unsigned flags, int argc, char **argv) +static int tc_class_modify(int cmd, unsigned int flags, int argc, char **argv) { struct { - struct nlmsghdr n; - struct tcmsg t; - char buf[4096]; - } req; + struct nlmsghdr n; + struct tcmsg t; + char buf[4096]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .t.tcm_family = AF_UNSPEC, + }; struct qdisc_util *q = NULL; - struct tc_estimator est; - char d[16]; - char k[16]; - - memset(&req, 0, sizeof(req)); - memset(&est, 0, sizeof(est)); - memset(d, 0, sizeof(d)); - memset(k, 0, sizeof(k)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.t.tcm_family = AF_UNSPEC; + struct tc_estimator est = {}; + char d[16] = {}; + char k[16] = {}; while (argc > 0) { if (strcmp(*argv, "dev") == 0) { @@ -85,6 +79,7 @@ strncpy(d, *argv, sizeof(d)-1); } else if (strcmp(*argv, "classid") == 0) { __u32 handle; + NEXT_ARG(); if (req.t.tcm_handle) duparg("classid", *argv); @@ -102,6 +97,7 @@ req.t.tcm_parent = TC_H_ROOT; } else if (strcmp(*argv, "parent") == 0) { __u32 handle; + NEXT_ARG(); if (req.t.tcm_parent) duparg("parent", *argv); @@ -166,9 +162,8 @@ static void graph_node_add(__u32 parent_id, __u32 id, void *data, int len) { - struct graph_node *node = malloc(sizeof(struct graph_node)); + struct graph_node *node = calloc(1, sizeof(struct graph_node)); - memset(node, 0, sizeof(*node)); node->id = id; node->parent_id = parent_id; @@ -223,7 +218,7 @@ { struct hlist_node *n, *tmp_cls; char cls_id_str[256] = {}; - struct rtattr *tb[TCA_MAX + 1] = {}; + struct rtattr *tb[TCA_MAX + 1]; struct qdisc_util *q; char str[100] = {}; @@ -305,10 +300,10 @@ int print_class(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct tcmsg *t = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr *tb[TCA_MAX + 1] = {}; + struct rtattr *tb[TCA_MAX + 1]; struct qdisc_util *q; char abuf[256]; @@ -393,14 +388,10 @@ static int tc_class_list(int argc, char **argv) { - struct tcmsg t; - char d[16]; + struct tcmsg t = { .tcm_family = AF_UNSPEC }; + char d[16] = {}; char buf[1024] = {0}; - memset(&t, 0, sizeof(t)); - t.tcm_family = AF_UNSPEC; - memset(d, 0, sizeof(d)); - filter_qdisc = 0; filter_classid = 0; @@ -430,6 +421,7 @@ t.tcm_parent = TC_H_ROOT; } else if (strcmp(*argv, "parent") == 0) { __u32 handle; + if (t.tcm_parent) duparg("parent", *argv); NEXT_ARG(); diff -Nru iproute2-4.3.0/tc/tc_core.c iproute2-4.9.0/tc/tc_core.c --- iproute2-4.3.0/tc/tc_core.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_core.c 2016-12-12 23:07:42.000000000 +0000 @@ -27,7 +27,7 @@ static double tick_in_usec = 1; static double clock_factor = 1; -int tc_core_time2big(unsigned time) +int tc_core_time2big(unsigned int time) { __u64 t = time; @@ -36,32 +36,32 @@ } -unsigned tc_core_time2tick(unsigned time) +unsigned int tc_core_time2tick(unsigned int time) { return time*tick_in_usec; } -unsigned tc_core_tick2time(unsigned tick) +unsigned int tc_core_tick2time(unsigned int tick) { return tick/tick_in_usec; } -unsigned tc_core_time2ktime(unsigned time) +unsigned int tc_core_time2ktime(unsigned int time) { return time * clock_factor; } -unsigned tc_core_ktime2time(unsigned ktime) +unsigned int tc_core_ktime2time(unsigned int ktime) { return ktime / clock_factor; } -unsigned tc_calc_xmittime(__u64 rate, unsigned size) +unsigned int tc_calc_xmittime(__u64 rate, unsigned int size) { return tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/(double)rate)); } -unsigned tc_calc_xmitsize(__u64 rate, unsigned ticks) +unsigned int tc_calc_xmitsize(__u64 rate, unsigned int ticks) { return ((double)rate*tc_core_tick2time(ticks))/TIME_UNITS_PER_SEC; } @@ -76,9 +76,10 @@ * (as the table will always be aligned for 48 bytes). * --Hawk, d.7/11-2004. */ -static unsigned tc_align_to_atm(unsigned size) +static unsigned int tc_align_to_atm(unsigned int size) { int linksize, cells; + cells = size / ATM_CELL_PAYLOAD; if ((size % ATM_CELL_PAYLOAD) > 0) cells++; @@ -87,7 +88,7 @@ return linksize; } -static unsigned tc_adjust_size(unsigned sz, unsigned mpu, enum link_layer linklayer) +static unsigned int tc_adjust_size(unsigned int sz, unsigned int mpu, enum link_layer linklayer) { if (sz < mpu) sz = mpu; @@ -97,7 +98,7 @@ return tc_align_to_atm(sz); case LINKLAYER_ETHERNET: default: - // No size adjustments on Ethernet + /* No size adjustments on Ethernet */ return sz; } } @@ -122,13 +123,13 @@ */ int tc_calc_rtable(struct tc_ratespec *r, __u32 *rtab, - int cell_log, unsigned mtu, + int cell_log, unsigned int mtu, enum link_layer linklayer) { int i; - unsigned sz; - unsigned bps = r->rate; - unsigned mpu = r->mpu; + unsigned int sz; + unsigned int bps = r->rate; + unsigned int mpu = r->mpu; if (mtu == 0) mtu = 2047; @@ -139,13 +140,13 @@ cell_log++; } - for (i=0; i<256; i++) { + for (i = 0; i < 256; i++) { sz = tc_adjust_size((i + 1) << cell_log, mpu, linklayer); rtab[i] = tc_calc_xmittime(bps, sz); } - r->cell_align=-1; // Due to the sz calc - r->cell_log=cell_log; + r->cell_align = -1; + r->cell_log = cell_log; r->linklayer = (linklayer & TC_LINKLAYER_MASK); return cell_log; } @@ -193,7 +194,7 @@ (*stab)[i] = sz >> s->size_log; } - s->cell_align = -1; // Due to the sz calc + s->cell_align = -1; /* Due to the sz calc */ return 0; } diff -Nru iproute2-4.3.0/tc/tc_estimator.c iproute2-4.9.0/tc/tc_estimator.c --- iproute2-4.3.0/tc/tc_estimator.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_estimator.c 2016-12-12 23:07:42.000000000 +0000 @@ -23,22 +23,23 @@ #include "tc_core.h" -int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est) +int tc_setup_estimator(unsigned int A, unsigned int time_const, struct tc_estimator *est) { - for (est->interval=0; est->interval<=5; est->interval++) { + for (est->interval = 0; est->interval <= 5; est->interval++) { if (A <= (1<interval)*(TIME_UNITS_PER_SEC/4)) break; } if (est->interval > 5) return -1; est->interval -= 2; - for (est->ewma_log=1; est->ewma_log<32; est->ewma_log++) { + for (est->ewma_log = 1; est->ewma_log < 32; est->ewma_log++) { double w = 1.0 - 1.0/(1<ewma_log); + if (A/(-log(w)) > time_const) break; } est->ewma_log--; - if (est->ewma_log==0 || est->ewma_log >= 31) + if (est->ewma_log == 0 || est->ewma_log >= 31) return -1; return 0; } diff -Nru iproute2-4.3.0/tc/tc_exec.c iproute2-4.9.0/tc/tc_exec.c --- iproute2-4.3.0/tc/tc_exec.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_exec.c 2016-12-12 23:07:42.000000000 +0000 @@ -19,7 +19,7 @@ #include "tc_common.h" static struct exec_util *exec_list; -static void *BODY = NULL; +static void *BODY; static void usage(void) { @@ -32,8 +32,8 @@ static int parse_noeopt(struct exec_util *eu, int argc, char **argv) { if (argc) { - fprintf(stderr, "Unknown exec \"%s\", hence option \"%s\" " - "is unparsable\n", eu->id, *argv); + fprintf(stderr, "Unknown exec \"%s\", hence option \"%s\" is unparsable\n", + eu->id, *argv); return -1; } @@ -71,9 +71,8 @@ return eu; noexist: - eu = malloc(sizeof(*eu)); + eu = calloc(1, sizeof(*eu)); if (eu) { - memset(eu, 0, sizeof(*eu)); strncpy(eu->id, name, sizeof(eu->id) - 1); eu->parse_eopt = parse_noeopt; goto reg; @@ -85,7 +84,7 @@ int do_exec(int argc, char **argv) { struct exec_util *eu; - char kind[16]; + char kind[16] = {}; if (argc < 1) { fprintf(stderr, "No command given, try \"tc exec help\".\n"); @@ -97,7 +96,6 @@ return 0; } - memset(kind, 0, sizeof(kind)); strncpy(kind, *argv, sizeof(kind) - 1); eu = get_exec_kind(kind); diff -Nru iproute2-4.3.0/tc/tc_filter.c iproute2-4.9.0/tc/tc_filter.c --- iproute2-4.3.0/tc/tc_filter.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_filter.c 2016-12-12 23:07:42.000000000 +0000 @@ -26,51 +26,43 @@ #include "tc_util.h" #include "tc_common.h" -static void usage(void); - static void usage(void) { - fprintf(stderr, "Usage: tc filter [ add | del | change | replace | show ] dev STRING\n"); - fprintf(stderr, " [ pref PRIO ] protocol PROTO\n"); - fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); - fprintf(stderr, " [ root | classid CLASSID ] [ handle FILTERID ]\n"); - fprintf(stderr, " [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " tc filter show [ dev STRING ] [ root | parent CLASSID ]\n"); - fprintf(stderr, "Where:\n"); - fprintf(stderr, "FILTER_TYPE := { rsvp | u32 | bpf | fw | route | etc. }\n"); - fprintf(stderr, "FILTERID := ... format depends on classifier, see there\n"); - fprintf(stderr, "OPTIONS := ... try tc filter add help\n"); - return; + fprintf(stderr, + "Usage: tc filter [ add | del | change | replace | show ] dev STRING\n" + "Usage: tc filter get dev STRING parent CLASSID protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n" + " [ pref PRIO ] protocol PROTO\n" + " [ estimator INTERVAL TIME_CONSTANT ]\n" + " [ root | ingress | egress | parent CLASSID ]\n" + " [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n" + "\n" + " tc filter show [ dev STRING ] [ root | ingress | egress | parent CLASSID ]\n" + "Where:\n" + "FILTER_TYPE := { rsvp | u32 | bpf | fw | route | etc. }\n" + "FILTERID := ... format depends on classifier, see there\n" + "OPTIONS := ... try tc filter add help\n"); } - -static int tc_filter_modify(int cmd, unsigned flags, int argc, char **argv) +static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv) { struct { - struct nlmsghdr n; - struct tcmsg t; - char buf[MAX_MSG]; - } req; + struct nlmsghdr n; + struct tcmsg t; + char buf[MAX_MSG]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .t.tcm_family = AF_UNSPEC, + }; struct filter_util *q = NULL; __u32 prio = 0; __u32 protocol = 0; int protocol_set = 0; char *fhandle = NULL; - char d[16]; - char k[16]; - struct tc_estimator est; - - memset(&req, 0, sizeof(req)); - memset(&est, 0, sizeof(est)); - memset(d, 0, sizeof(d)); - memset(k, 0, sizeof(k)); - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.t.tcm_family = AF_UNSPEC; + char d[16] = {}; + char k[16] = {}; + struct tc_estimator est = {}; if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE) protocol = htons(ETH_P_ALL); @@ -83,12 +75,30 @@ strncpy(d, *argv, sizeof(d)-1); } else if (strcmp(*argv, "root") == 0) { if (req.t.tcm_parent) { - fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + fprintf(stderr, + "Error: \"root\" is duplicate parent ID\n"); return -1; } req.t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "ingress") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, + "Error: \"ingress\" is duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, + TC_H_MIN_INGRESS); + } else if (strcmp(*argv, "egress") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, + "Error: \"egress\" is duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, + TC_H_MIN_EGRESS); } else if (strcmp(*argv, "parent") == 0) { __u32 handle; + NEXT_ARG(); if (req.t.tcm_parent) duparg("parent", *argv); @@ -109,6 +119,7 @@ invarg("invalid priority value", *argv); } else if (matches(*argv, "protocol") == 0) { __u16 id; + NEXT_ARG(); if (protocol_set) duparg("protocol", *argv); @@ -143,14 +154,16 @@ return 1; } else { if (fhandle) { - fprintf(stderr, "Must specify filter type when using " - "\"handle\"\n"); + fprintf(stderr, + "Must specify filter type when using \"handle\"\n"); return -1; } if (argc) { if (matches(*argv, "help") == 0) usage(); - fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", *argv); + fprintf(stderr, + "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", + *argv); return -1; } } @@ -161,7 +174,8 @@ if (d[0]) { ll_init_map(&rth); - if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) { + req.t.tcm_ifindex = ll_name_to_index(d); + if (req.t.tcm_ifindex == 0) { fprintf(stderr, "Cannot find device \"%s\"\n", d); return 1; } @@ -179,21 +193,21 @@ static int filter_ifindex; static __u32 filter_prio; static __u32 filter_protocol; -__u16 f_proto = 0; +__u16 f_proto; -int print_filter(const struct sockaddr_nl *who, - struct nlmsghdr *n, - void *arg) +int print_filter(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct tcmsg *t = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[TCA_MAX+1]; + struct rtattr *tb[TCA_MAX+1]; struct filter_util *q; char abuf[256]; - if (n->nlmsg_type != RTM_NEWTFILTER && n->nlmsg_type != RTM_DELTFILTER) { - fprintf(stderr, "Not a filter\n"); + if (n->nlmsg_type != RTM_NEWTFILTER && + n->nlmsg_type != RTM_GETTFILTER && + n->nlmsg_type != RTM_DELTFILTER) { + fprintf(stderr, "Not a filter(cmd %d)\n", n->nlmsg_type); return 0; } len -= NLMSG_LENGTH(sizeof(*t)); @@ -202,7 +216,6 @@ return -1; } - memset(tb, 0, sizeof(tb)); parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); if (tb[TCA_KIND] == NULL) { @@ -213,6 +226,16 @@ if (n->nlmsg_type == RTM_DELTFILTER) fprintf(fp, "deleted "); + if (n->nlmsg_type == RTM_NEWTFILTER && + (n->nlmsg_flags & NLM_F_CREATE) && + !(n->nlmsg_flags & NLM_F_EXCL)) + fprintf(fp, "replaced "); + + if (n->nlmsg_type == RTM_NEWTFILTER && + (n->nlmsg_flags & NLM_F_CREATE) && + (n->nlmsg_flags & NLM_F_EXCL)) + fprintf(fp, "added "); + fprintf(fp, "filter "); if (!filter_ifindex || filter_ifindex != t->tcm_ifindex) fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); @@ -220,14 +243,20 @@ if (!filter_parent || filter_parent != t->tcm_parent) { if (t->tcm_parent == TC_H_ROOT) fprintf(fp, "root "); + else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS)) + fprintf(fp, "ingress "); + else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS)) + fprintf(fp, "egress "); else { print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); fprintf(fp, "parent %s ", abuf); } } + if (t->tcm_info) { f_proto = TC_H_MIN(t->tcm_info); __u32 prio = TC_H_MAJ(t->tcm_info)>>16; + if (!filter_protocol || filter_protocol != f_proto) { if (f_proto) { SPRINT_BUF(b1); @@ -259,19 +288,181 @@ return 0; } +static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct tcmsg t; + char buf[MAX_MSG]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), + /* NLM_F_ECHO is for backward compatibility. old kernels never + * respond without it and newer kernels will ignore it. + * In old kernels there is a side effect: + * In addition to a response to the GET you will receive an + * event (if you do tc mon). + */ + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ECHO | flags, + .n.nlmsg_type = cmd, + .t.tcm_parent = TC_H_UNSPEC, + .t.tcm_family = AF_UNSPEC, + }; + struct filter_util *q = NULL; + __u32 prio = 0; + __u32 protocol = 0; + int protocol_set = 0; + __u32 parent_handle = 0; + char *fhandle = NULL; + char d[16] = {}; + char k[16] = {}; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "root") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, + "Error: \"root\" is duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "ingress") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, + "Error: \"ingress\" is duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, + TC_H_MIN_INGRESS); + } else if (strcmp(*argv, "egress") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, + "Error: \"egress\" is duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, + TC_H_MIN_EGRESS); + } else if (strcmp(*argv, "parent") == 0) { + + NEXT_ARG(); + if (req.t.tcm_parent) + duparg("parent", *argv); + if (get_tc_classid(&parent_handle, *argv)) + invarg("Invalid parent ID", *argv); + req.t.tcm_parent = parent_handle; + } else if (strcmp(*argv, "handle") == 0) { + NEXT_ARG(); + if (fhandle) + duparg("handle", *argv); + fhandle = *argv; + } else if (matches(*argv, "preference") == 0 || + matches(*argv, "priority") == 0) { + NEXT_ARG(); + if (prio) + duparg("priority", *argv); + if (get_u32(&prio, *argv, 0) || prio > 0xFFFF) + invarg("invalid priority value", *argv); + } else if (matches(*argv, "protocol") == 0) { + __u16 id; + + NEXT_ARG(); + if (protocol_set) + duparg("protocol", *argv); + if (ll_proto_a2n(&id, *argv)) + invarg("invalid protocol", *argv); + protocol = id; + protocol_set = 1; + } else if (matches(*argv, "help") == 0) { + usage(); + return 0; + } else { + strncpy(k, *argv, sizeof(k)-1); + + q = get_filter_kind(k); + argc--; argv++; + break; + } + + argc--; argv++; + } + + if (!protocol_set) { + fprintf(stderr, "Must specify filter protocol\n"); + return -1; + } + + if (!prio) { + fprintf(stderr, "Must specify filter priority\n"); + return -1; + } + + req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); + + if (req.t.tcm_parent == TC_H_UNSPEC) { + fprintf(stderr, "Must specify filter parent\n"); + return -1; + } + + if (k[0]) + addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); + else { + fprintf(stderr, "Must specify filter type\n"); + return -1; + } + + if (q->parse_fopt(q, fhandle, argc, argv, &req.n)) + return 1; + + + if (!fhandle) { + fprintf(stderr, "Must specify filter \"handle\"\n"); + return -1; + } + + if (argc) { + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, + "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", + *argv); + return -1; + } + + if (d[0]) { + ll_init_map(&rth); + + req.t.tcm_ifindex = ll_name_to_index(d); + if (req.t.tcm_ifindex == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + filter_ifindex = req.t.tcm_ifindex; + } else { + fprintf(stderr, "Must specify netdevice \"dev\"\n"); + return -1; + } + + if (rtnl_talk(&rth, &req.n, &req.n, MAX_MSG) < 0) { + fprintf(stderr, "We have an error talking to the kernel\n"); + return 2; + } + + print_filter(NULL, &req.n, (void *)stdout); + + return 0; +} static int tc_filter_list(int argc, char **argv) { - struct tcmsg t; - char d[16]; + struct tcmsg t = { .tcm_family = AF_UNSPEC }; + char d[16] = {}; __u32 prio = 0; __u32 protocol = 0; char *fhandle = NULL; - memset(&t, 0, sizeof(t)); - t.tcm_family = AF_UNSPEC; - memset(d, 0, sizeof(d)); - while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); @@ -280,12 +471,32 @@ strncpy(d, *argv, sizeof(d)-1); } else if (strcmp(*argv, "root") == 0) { if (t.tcm_parent) { - fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + fprintf(stderr, + "Error: \"root\" is duplicate parent ID\n"); return -1; } filter_parent = t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "ingress") == 0) { + if (t.tcm_parent) { + fprintf(stderr, + "Error: \"ingress\" is duplicate parent ID\n"); + return -1; + } + filter_parent = TC_H_MAKE(TC_H_CLSACT, + TC_H_MIN_INGRESS); + t.tcm_parent = filter_parent; + } else if (strcmp(*argv, "egress") == 0) { + if (t.tcm_parent) { + fprintf(stderr, + "Error: \"egress\" is duplicate parent ID\n"); + return -1; + } + filter_parent = TC_H_MAKE(TC_H_CLSACT, + TC_H_MIN_EGRESS); + t.tcm_parent = filter_parent; } else if (strcmp(*argv, "parent") == 0) { __u32 handle; + NEXT_ARG(); if (t.tcm_parent) duparg("parent", *argv); @@ -307,6 +518,7 @@ filter_prio = prio; } else if (matches(*argv, "protocol") == 0) { __u16 res; + NEXT_ARG(); if (protocol) duparg("protocol", *argv); @@ -317,7 +529,9 @@ } else if (matches(*argv, "help") == 0) { usage(); } else { - fprintf(stderr, " What is \"%s\"? Try \"tc filter help\"\n", *argv); + fprintf(stderr, + " What is \"%s\"? Try \"tc filter help\"\n", + *argv); return -1; } @@ -329,7 +543,8 @@ ll_init_map(&rth); if (d[0]) { - if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { + t.tcm_ifindex = ll_name_to_index(d); + if (t.tcm_ifindex == 0) { fprintf(stderr, "Cannot find device \"%s\"\n", d); return 1; } @@ -354,24 +569,25 @@ if (argc < 1) return tc_filter_list(0, NULL); if (matches(*argv, "add") == 0) - return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); + return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, + argc-1, argv+1); if (matches(*argv, "change") == 0) return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1); if (matches(*argv, "replace") == 0) - return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1, argv+1); + return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1, + argv+1); if (matches(*argv, "delete") == 0) return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1); -#if 0 if (matches(*argv, "get") == 0) return tc_filter_get(RTM_GETTFILTER, 0, argc-1, argv+1); -#endif if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 || matches(*argv, "lst") == 0) return tc_filter_list(argc-1, argv+1); if (matches(*argv, "help") == 0) { usage(); return 0; - } - fprintf(stderr, "Command \"%s\" is unknown, try \"tc filter help\".\n", *argv); + } + fprintf(stderr, "Command \"%s\" is unknown, try \"tc filter help\".\n", + *argv); return -1; } diff -Nru iproute2-4.3.0/tc/tc_monitor.c iproute2-4.9.0/tc/tc_monitor.c --- iproute2-4.3.0/tc/tc_monitor.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_monitor.c 2016-12-12 23:07:42.000000000 +0000 @@ -39,7 +39,7 @@ struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; if (timestamp) print_timestamp(fp); @@ -73,7 +73,7 @@ { struct rtnl_handle rth; char *file = NULL; - unsigned groups = nl_mgrp(RTNLGRP_TC); + unsigned int groups = nl_mgrp(RTNLGRP_TC); while (argc > 0) { if (matches(*argv, "file") == 0) { @@ -91,13 +91,17 @@ } if (file) { - FILE *fp; - fp = fopen(file, "r"); + FILE *fp = fopen(file, "r"); + int ret; + if (fp == NULL) { perror("Cannot fopen"); exit(-1); } - return rtnl_from_file(fp, accept_tcmsg, (void*)stdout); + + ret = rtnl_from_file(fp, accept_tcmsg, stdout); + fclose(fp); + return ret; } if (rtnl_open(&rth, groups) < 0) @@ -105,7 +109,7 @@ ll_init_map(&rth); - if (rtnl_listen(&rth, accept_tcmsg, (void*)stdout) < 0) { + if (rtnl_listen(&rth, accept_tcmsg, (void *)stdout) < 0) { rtnl_close(&rth); exit(2); } diff -Nru iproute2-4.3.0/tc/tc_qdisc.c iproute2-4.9.0/tc/tc_qdisc.c --- iproute2-4.3.0/tc/tc_qdisc.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_qdisc.c 2016-12-12 23:07:42.000000000 +0000 @@ -26,17 +26,15 @@ #include "tc_util.h" #include "tc_common.h" -static int usage(void); - static int usage(void) { fprintf(stderr, "Usage: tc qdisc [ add | del | replace | change | show ] dev STRING\n"); - fprintf(stderr, " [ handle QHANDLE ] [ root | ingress | parent CLASSID ]\n"); + fprintf(stderr, " [ handle QHANDLE ] [ root | ingress | clsact | parent CLASSID ]\n"); fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); fprintf(stderr, " [ stab [ help | STAB_OPTIONS] ]\n"); fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"); fprintf(stderr, "\n"); - fprintf(stderr, " tc qdisc show [ dev STRING ] [ingress]\n"); + fprintf(stderr, " tc qdisc show [ dev STRING ] [ ingress | clsact ]\n"); fprintf(stderr, "Where:\n"); fprintf(stderr, "QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n"); fprintf(stderr, "OPTIONS := ... try tc qdisc add help\n"); @@ -44,32 +42,26 @@ return -1; } -static int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv) +static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv) { struct qdisc_util *q = NULL; - struct tc_estimator est; + struct tc_estimator est = {}; struct { struct tc_sizespec szopts; __u16 *data; - } stab; - char d[16]; - char k[16]; + } stab = {}; + char d[16] = {}; + char k[16] = {}; struct { - struct nlmsghdr n; - struct tcmsg t; - char buf[TCA_BUF_MAX]; - } req; - - memset(&req, 0, sizeof(req)); - memset(&stab, 0, sizeof(stab)); - memset(&est, 0, sizeof(est)); - memset(&d, 0, sizeof(d)); - memset(&k, 0, sizeof(k)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.t.tcm_family = AF_UNSPEC; + struct nlmsghdr n; + struct tcmsg t; + char buf[TCA_BUF_MAX]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .t.tcm_family = AF_UNSPEC, + }; while (argc > 0) { if (strcmp(*argv, "dev") == 0) { @@ -79,6 +71,7 @@ strncpy(d, *argv, sizeof(d)-1); } else if (strcmp(*argv, "handle") == 0) { __u32 handle; + if (req.t.tcm_handle) duparg("handle", *argv); NEXT_ARG(); @@ -91,22 +84,31 @@ return -1; } req.t.tcm_parent = TC_H_ROOT; -#ifdef TC_H_INGRESS + } else if (strcmp(*argv, "clsact") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, "Error: \"clsact\" is a duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_CLSACT; + strncpy(k, "clsact", sizeof(k) - 1); + q = get_qdisc_kind(k); + req.t.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0); + NEXT_ARG_FWD(); + break; } else if (strcmp(*argv, "ingress") == 0) { if (req.t.tcm_parent) { fprintf(stderr, "Error: \"ingress\" is a duplicate parent ID\n"); return -1; } req.t.tcm_parent = TC_H_INGRESS; - strncpy(k, "ingress", sizeof(k)-1); + strncpy(k, "ingress", sizeof(k) - 1); q = get_qdisc_kind(k); - req.t.tcm_handle = 0xffff0000; - - argc--; argv++; + req.t.tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0); + NEXT_ARG_FWD(); break; -#endif } else if (strcmp(*argv, "parent") == 0) { __u32 handle; + NEXT_ARG(); if (req.t.tcm_parent) duparg("parent", *argv); @@ -180,7 +182,8 @@ ll_init_map(&rth); - if ((idx = ll_name_to_index(d)) == 0) { + idx = ll_name_to_index(d); + if (idx == 0) { fprintf(stderr, "Cannot find device \"%s\"\n", d); return 1; } @@ -196,13 +199,12 @@ static int filter_ifindex; int print_qdisc(const struct sockaddr_nl *who, - struct nlmsghdr *n, - void *arg) + struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct tcmsg *t = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[TCA_MAX+1]; + struct rtattr *tb[TCA_MAX+1]; struct qdisc_util *q; char abuf[256]; @@ -219,7 +221,6 @@ if (filter_ifindex && filter_ifindex != t->tcm_ifindex) return 0; - memset(tb, 0, sizeof(tb)); parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); if (tb[TCA_KIND] == NULL) { @@ -230,21 +231,29 @@ if (n->nlmsg_type == RTM_DELQDISC) fprintf(fp, "deleted "); - fprintf(fp, "qdisc %s %x: ", rta_getattr_str(tb[TCA_KIND]), t->tcm_handle>>16); + if (show_raw) + fprintf(fp, "qdisc %s %x:[%08x] ", + rta_getattr_str(tb[TCA_KIND]), + t->tcm_handle >> 16, t->tcm_handle); + else + fprintf(fp, "qdisc %s %x: ", rta_getattr_str(tb[TCA_KIND]), + t->tcm_handle >> 16); + if (filter_ifindex == 0) fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); + if (t->tcm_parent == TC_H_ROOT) fprintf(fp, "root "); else if (t->tcm_parent) { print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); fprintf(fp, "parent %s ", abuf); } - if (t->tcm_info != 1) { + + if (t->tcm_info != 1) fprintf(fp, "refcnt %d ", t->tcm_info); - } - /* pfifo_fast is generic enough to warrant the hardcoding --JHS */ - if (0 == strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND]))) + /* pfifo_fast is generic enough to warrant the hardcoding --JHS */ + if (strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND])) == 0) q = get_qdisc_kind("prio"); else q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); @@ -256,10 +265,12 @@ fprintf(fp, "[cannot parse qdisc parameters]"); } fprintf(fp, "\n"); + if (show_details && tb[TCA_STAB]) { print_size_table(fp, " ", tb[TCA_STAB]); fprintf(fp, "\n"); } + if (show_stats) { struct rtattr *xstats = NULL; @@ -277,28 +288,22 @@ return 0; } - static int tc_qdisc_list(int argc, char **argv) { - struct tcmsg t; - char d[16]; - - memset(&t, 0, sizeof(t)); - t.tcm_family = AF_UNSPEC; - memset(&d, 0, sizeof(d)); + struct tcmsg t = { .tcm_family = AF_UNSPEC }; + char d[16] = {}; while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); strncpy(d, *argv, sizeof(d)-1); -#ifdef TC_H_INGRESS - } else if (strcmp(*argv, "ingress") == 0) { - if (t.tcm_parent) { - fprintf(stderr, "Duplicate parent ID\n"); - usage(); - } - t.tcm_parent = TC_H_INGRESS; -#endif + } else if (strcmp(*argv, "ingress") == 0 || + strcmp(*argv, "clsact") == 0) { + if (t.tcm_parent) { + fprintf(stderr, "Duplicate parent ID\n"); + usage(); + } + t.tcm_parent = TC_H_INGRESS; } else if (matches(*argv, "help") == 0) { usage(); } else { @@ -312,7 +317,8 @@ ll_init_map(&rth); if (d[0]) { - if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { + t.tcm_ifindex = ll_name_to_index(d); + if (t.tcm_ifindex == 0) { fprintf(stderr, "Cannot find device \"%s\"\n", d); return 1; } @@ -356,7 +362,7 @@ if (matches(*argv, "help") == 0) { usage(); return 0; - } + } fprintf(stderr, "Command \"%s\" is unknown, try \"tc qdisc help\".\n", *argv); return -1; } diff -Nru iproute2-4.3.0/tc/tc_red.c iproute2-4.9.0/tc/tc_red.c --- iproute2-4.3.0/tc/tc_red.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_red.c 2016-12-12 23:07:42.000000000 +0000 @@ -27,7 +27,7 @@ /* Plog = log(prob/(qmax - qmin)) */ -int tc_red_eval_P(unsigned qmin, unsigned qmax, double prob) +int tc_red_eval_P(unsigned int qmin, unsigned int qmax, double prob) { int i = qmax - qmin; @@ -36,12 +36,12 @@ prob /= i; - for (i=0; i<32; i++) { + for (i = 0; i < 32; i++) { if (prob > 1.0) break; prob *= 2; } - if (i>=32) + if (i >= 32) return -1; return i; } @@ -50,18 +50,18 @@ burst + 1 - qmin/avpkt < (1-(1-W)^burst)/W */ -int tc_red_eval_ewma(unsigned qmin, unsigned burst, unsigned avpkt) +int tc_red_eval_ewma(unsigned int qmin, unsigned int burst, unsigned int avpkt) { int wlog = 1; double W = 0.5; double a = (double)burst + 1 - (double)qmin/avpkt; if (a < 1.0) { - fprintf(stderr, "tc_red_eval_ewma() burst %u is too small ?" - " Try burst %u\n", burst, 1 + qmin/avpkt); + fprintf(stderr, "tc_red_eval_ewma() burst %u is too small ? Try burst %u\n", + burst, 1 + qmin/avpkt); return -1; } - for (wlog=1; wlog<32; wlog++, W /= 2) { + for (wlog = 1; wlog < 32; wlog++, W /= 2) { if (a <= (1 - pow(1-W, burst))/W) return wlog; } @@ -72,7 +72,7 @@ Stab[t>>Scell_log] = -log(1-W) * t/xmit_time */ -int tc_red_eval_idle_damping(int Wlog, unsigned avpkt, unsigned bps, __u8 *sbuf) +int tc_red_eval_idle_damping(int Wlog, unsigned int avpkt, unsigned int bps, __u8 *sbuf) { double xmit_time = tc_calc_xmittime(bps, avpkt); double lW = -log(1.0 - 1.0/(1< 31) sbuf[i] = 31; diff -Nru iproute2-4.3.0/tc/tc_stab.c iproute2-4.9.0/tc/tc_stab.c --- iproute2-4.3.0/tc/tc_stab.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_stab.c 2016-12-12 23:07:42.000000000 +0000 @@ -31,7 +31,7 @@ static void stab_help(void) { fprintf(stderr, - "Usage: ... stab [ mtu BYTES ] [ tsize SLOTS ] [ mpu BYTES ] \n" + "Usage: ... stab [ mtu BYTES ] [ tsize SLOTS ] [ mpu BYTES ]\n" " [ overhead BYTES ] [ linklayer TYPE ] ...\n" " mtu : max packet size we create rate map for {2047}\n" " tsize : how many slots should size table have {512}\n" @@ -40,7 +40,6 @@ " linklayer : adapting to a linklayer e.g. atm\n" "Example: ... stab overhead 20 linklayer atm\n"); - return; } int check_size_table_opts(struct tc_sizespec *s) @@ -53,9 +52,7 @@ { char **argv = *argvp; int argc = *argcp; - struct tc_sizespec s; - - memset(&s, 0, sizeof(s)); + struct tc_sizespec s = {}; NEXT_ARG(); if (matches(*argv, "help") == 0) { @@ -110,12 +107,14 @@ void print_size_table(FILE *fp, const char *prefix, struct rtattr *rta) { struct rtattr *tb[TCA_STAB_MAX + 1]; + SPRINT_BUF(b1); parse_rtattr_nested(tb, TCA_STAB_MAX, rta); if (tb[TCA_STAB_BASE]) { struct tc_sizespec s = {0}; + memcpy(&s, RTA_DATA(tb[TCA_STAB_BASE]), MIN(RTA_PAYLOAD(tb[TCA_STAB_BASE]), sizeof(s))); @@ -135,8 +134,9 @@ #if 0 if (tb[TCA_STAB_DATA]) { - unsigned i, j, dlen; + unsigned int i, j, dlen; __u16 *data = RTA_DATA(tb[TCA_STAB_DATA]); + dlen = RTA_PAYLOAD(tb[TCA_STAB_DATA]) / sizeof(__u16); fprintf(fp, "\n%sstab data:", prefix); diff -Nru iproute2-4.3.0/tc/tc_util.c iproute2-4.9.0/tc/tc_util.c --- iproute2-4.3.0/tc/tc_util.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_util.c 2016-12-12 23:07:42.000000000 +0000 @@ -32,7 +32,7 @@ #define LIBDIR "/usr/lib" #endif -static struct db_names *cls_names = NULL; +static struct db_names *cls_names; #define NAMES_DB "/etc/iproute2/tc_cls" @@ -82,10 +82,10 @@ if (strcmp(str, "none") == 0) goto ok; maj = strtoul(str, &p, 16); - if (p == str) + if (p == str || maj >= (1 << 16)) return -1; maj <<= 16; - if (*p != ':' && *p!=0) + if (*p != ':' && *p != 0) return -1; ok: *h = maj; @@ -192,7 +192,7 @@ }; -int get_rate(unsigned *rate, const char *str) +int get_rate(unsigned int *rate, const char *str) { char *p; double bps = strtod(str, &p); @@ -266,13 +266,13 @@ snprintf(buf, len, "%.0f%s%sbit", (double)rate, units[i], str); } -char * sprint_rate(__u64 rate, char *buf) +char *sprint_rate(__u64 rate, char *buf) { print_rate(buf, SPRINT_BSIZE-1, rate); return buf; } -int get_time(unsigned *time, const char *str) +int get_time(unsigned int *time, const char *str) { double t; char *p; @@ -282,13 +282,13 @@ return -1; if (*p) { - if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 || - strcasecmp(p, "secs")==0) + if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec") == 0 || + strcasecmp(p, "secs") == 0) t *= TIME_UNITS_PER_SEC; - else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 || + else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec") == 0 || strcasecmp(p, "msecs") == 0) t *= TIME_UNITS_PER_SEC/1000; - else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 || + else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec") == 0 || strcasecmp(p, "usecs") == 0) t *= TIME_UNITS_PER_SEC/1000000; else @@ -312,18 +312,18 @@ snprintf(buf, len, "%uus", time); } -char * sprint_time(__u32 time, char *buf) +char *sprint_time(__u32 time, char *buf) { print_time(buf, SPRINT_BSIZE-1, time); return buf; } -char * sprint_ticks(__u32 ticks, char *buf) +char *sprint_ticks(__u32 ticks, char *buf) { return sprint_time(tc_core_tick2time(ticks), buf); } -int get_size(unsigned *size, const char *str) +int get_size(unsigned int *size, const char *str) { double sz; char *p; @@ -333,13 +333,13 @@ return -1; if (*p) { - if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k")==0) + if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k") == 0) sz *= 1024; - else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g")==0) + else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g") == 0) sz *= 1024*1024*1024; else if (strcasecmp(p, "gbit") == 0) sz *= 1024*1024*1024/8; - else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m")==0) + else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m") == 0) sz *= 1024*1024; else if (strcasecmp(p, "mbit") == 0) sz *= 1024*1024/8; @@ -353,9 +353,9 @@ return 0; } -int get_size_and_cell(unsigned *size, int *cell_log, char *str) +int get_size_and_cell(unsigned int *size, int *cell_log, char *str) { - char * slash = strchr(str, '/'); + char *slash = strchr(str, '/'); if (slash) *slash = 0; @@ -371,7 +371,7 @@ return -1; *slash = '/'; - for (i=0; i<32; i++) { + for (i = 0; i < 32; i++) { if ((1<>16); } -char * sprint_qdisc_handle(__u32 h, char *buf) +char *sprint_qdisc_handle(__u32 h, char *buf) { print_qdisc_handle(buf, SPRINT_BSIZE-1, h); return buf; } -char * action_n2a(int action, char *buf, int len) +const char *action_n2a(int action) { + static char buf[64]; + switch (action) { - case -1: + case TC_ACT_UNSPEC: return "continue"; - break; case TC_ACT_OK: return "pass"; - break; case TC_ACT_SHOT: return "drop"; - break; case TC_ACT_RECLASSIFY: return "reclassify"; case TC_ACT_PIPE: @@ -430,37 +429,53 @@ case TC_ACT_STOLEN: return "stolen"; default: - snprintf(buf, len, "%d", action); + snprintf(buf, 64, "%d", action); + buf[63] = '\0'; return buf; } } -int action_a2n(char *arg, int *result) -{ - int res; - - if (matches(arg, "continue") == 0) - res = -1; - else if (matches(arg, "drop") == 0) - res = TC_ACT_SHOT; - else if (matches(arg, "shot") == 0) - res = TC_ACT_SHOT; - else if (matches(arg, "pass") == 0) - res = TC_ACT_OK; - else if (strcmp(arg, "ok") == 0) - res = TC_ACT_OK; - else if (matches(arg, "reclassify") == 0) - res = TC_ACT_RECLASSIFY; - else { - char dummy; - if (sscanf(arg, "%d%c", &res, &dummy) != 1) - return -1; +/* Convert action branch name into numeric format. + * + * Parameters: + * @arg - string to parse + * @result - pointer to output variable + * @allow_num - whether @arg may be in numeric format already + * + * In error case, returns -1 and does not touch @result. Otherwise returns 0. + */ +int action_a2n(char *arg, int *result, bool allow_num) +{ + int n; + char dummy; + struct { + const char *a; + int n; + } a2n[] = { + {"continue", TC_ACT_UNSPEC}, + {"drop", TC_ACT_SHOT}, + {"shot", TC_ACT_SHOT}, + {"pass", TC_ACT_OK}, + {"ok", TC_ACT_OK}, + {"reclassify", TC_ACT_RECLASSIFY}, + {"pipe", TC_ACT_PIPE}, + { NULL }, + }, *iter; + + for (iter = a2n; iter->a; iter++) { + if (matches(arg, iter->a) != 0) + continue; + *result = iter->n; + return 0; } - *result = res; + if (!allow_num || sscanf(arg, "%d%c", &n, &dummy) != 1) + return -1; + + *result = n; return 0; } -int get_linklayer(unsigned *val, const char *arg) +int get_linklayer(unsigned int *val, const char *arg) { int res; @@ -477,7 +492,7 @@ return 0; } -void print_linklayer(char *buf, int len, unsigned linklayer) +void print_linklayer(char *buf, int len, unsigned int linklayer) { switch (linklayer) { case LINKLAYER_UNSPEC: @@ -495,21 +510,22 @@ } } -char *sprint_linklayer(unsigned linklayer, char *buf) +char *sprint_linklayer(unsigned int linklayer, char *buf) { print_linklayer(buf, SPRINT_BSIZE-1, linklayer); return buf; } -void print_tm(FILE * f, const struct tcf_t *tm) +void print_tm(FILE *f, const struct tcf_t *tm) { int hz = get_user_hz(); + if (tm->install != 0) - fprintf(f, " installed %u sec", (unsigned)(tm->install/hz)); + fprintf(f, " installed %u sec", (unsigned int)(tm->install/hz)); if (tm->lastuse != 0) - fprintf(f, " used %u sec", (unsigned)(tm->lastuse/hz)); + fprintf(f, " used %u sec", (unsigned int)(tm->lastuse/hz)); if (tm->expires != 0) - fprintf(f, " expires %u sec", (unsigned)(tm->expires/hz)); + fprintf(f, " expires %u sec", (unsigned int)(tm->expires/hz)); } void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats) @@ -521,6 +537,7 @@ if (tbs[TCA_STATS_BASIC]) { struct gnet_stats_basic bs = {0}; + memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs))); fprintf(fp, "%sSent %llu bytes %u pkt", prefix, (unsigned long long) bs.bytes, bs.packets); @@ -528,6 +545,7 @@ if (tbs[TCA_STATS_QUEUE]) { struct gnet_stats_queue q = {0}; + memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); fprintf(fp, " (dropped %u, overlimits %u requeues %u) ", q.drops, q.overlimits, q.requeues); @@ -552,6 +570,7 @@ if (tbs[TCA_STATS_QUEUE]) { struct gnet_stats_queue q = {0}; + memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); if (!tbs[TCA_STATS_RATE_EST]) fprintf(fp, "\n%s", prefix); @@ -575,10 +594,9 @@ } /* backward compatibility */ if (tb[TCA_STATS]) { - struct tc_stats st; + struct tc_stats st = {}; /* handle case where kernel returns more/less than we know about */ - memset(&st, 0, sizeof(st)); memcpy(&st, RTA_DATA(tb[TCA_STATS]), MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st))); fprintf(fp, "%sSent %llu bytes %u pkts (dropped %u, overlimits %u) ", diff -Nru iproute2-4.3.0/tc/tc_util.h iproute2-4.9.0/tc/tc_util.h --- iproute2-4.3.0/tc/tc_util.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tc/tc_util.h 2016-12-12 23:07:42.000000000 +0000 @@ -22,89 +22,96 @@ struct qdisc_util { struct qdisc_util *next; const char *id; - int (*parse_qopt)(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n); - int (*print_qopt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); - int (*print_xstats)(struct qdisc_util *qu, FILE *f, struct rtattr *xstats); - - int (*parse_copt)(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n); - int (*print_copt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); + int (*parse_qopt)(struct qdisc_util *qu, int argc, + char **argv, struct nlmsghdr *n); + int (*print_qopt)(struct qdisc_util *qu, + FILE *f, struct rtattr *opt); + int (*print_xstats)(struct qdisc_util *qu, + FILE *f, struct rtattr *xstats); + + int (*parse_copt)(struct qdisc_util *qu, int argc, + char **argv, struct nlmsghdr *n); + int (*print_copt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); }; extern __u16 f_proto; struct filter_util { struct filter_util *next; - char id[16]; - int (*parse_fopt)(struct filter_util *qu, char *fhandle, int argc, - char **argv, struct nlmsghdr *n); - int (*print_fopt)(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle); + char id[16]; + int (*parse_fopt)(struct filter_util *qu, char *fhandle, + int argc, char **argv, struct nlmsghdr *n); + int (*print_fopt)(struct filter_util *qu, + FILE *f, struct rtattr *opt, __u32 fhandle); }; struct action_util { - struct action_util *next; - char id[16]; - int (*parse_aopt)(struct action_util *a, int *argc, char ***argv, - int code, struct nlmsghdr *n); - int (*print_aopt)(struct action_util *au, FILE *f, struct rtattr *opt); - int (*print_xstats)(struct action_util *au, FILE *f, struct rtattr *xstats); + struct action_util *next; + char id[16]; + int (*parse_aopt)(struct action_util *a, int *argc, + char ***argv, int code, struct nlmsghdr *n); + int (*print_aopt)(struct action_util *au, FILE *f, struct rtattr *opt); + int (*print_xstats)(struct action_util *au, + FILE *f, struct rtattr *xstats); }; struct exec_util { - struct exec_util *next; - char id[16]; - int (*parse_eopt)(struct exec_util *eu, int argc, char **argv); + struct exec_util *next; + char id[16]; + int (*parse_eopt)(struct exec_util *eu, int argc, char **argv); }; -extern const char *get_tc_lib(void); +const char *get_tc_lib(void); -extern struct qdisc_util *get_qdisc_kind(const char *str); -extern struct filter_util *get_filter_kind(const char *str); +struct qdisc_util *get_qdisc_kind(const char *str); +struct filter_util *get_filter_kind(const char *str); -extern int get_qdisc_handle(__u32 *h, const char *str); -extern int get_rate(unsigned *rate, const char *str); -extern int get_rate64(__u64 *rate, const char *str); -extern int get_size(unsigned *size, const char *str); -extern int get_size_and_cell(unsigned *size, int *cell_log, char *str); -extern int get_time(unsigned *time, const char *str); -extern int get_linklayer(unsigned *val, const char *arg); - -extern void print_rate(char *buf, int len, __u64 rate); -extern void print_size(char *buf, int len, __u32 size); -extern void print_qdisc_handle(char *buf, int len, __u32 h); -extern void print_time(char *buf, int len, __u32 time); -extern void print_linklayer(char *buf, int len, unsigned linklayer); - -extern char * sprint_rate(__u64 rate, char *buf); -extern char * sprint_size(__u32 size, char *buf); -extern char * sprint_qdisc_handle(__u32 h, char *buf); -extern char * sprint_tc_classid(__u32 h, char *buf); -extern char * sprint_time(__u32 time, char *buf); -extern char * sprint_ticks(__u32 ticks, char *buf); -extern char * sprint_linklayer(unsigned linklayer, char *buf); - -extern void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats); -extern void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats); - -extern int get_tc_classid(__u32 *h, const char *str); -extern int print_tc_classid(char *buf, int len, __u32 h); -extern char * sprint_tc_classid(__u32 h, char *buf); - -extern int tc_print_police(FILE *f, struct rtattr *tb); -extern int parse_police(int *, char ***, int, struct nlmsghdr *); - -extern char *action_n2a(int action, char *buf, int len); -extern int action_a2n(char *arg, int *result); -extern int act_parse_police(struct action_util *a,int *, char ***, int, struct nlmsghdr *); -extern int print_police(struct action_util *a, FILE *f, - struct rtattr *tb); -extern int police_print_xstats(struct action_util *a,FILE *f, - struct rtattr *tb); -extern int tc_print_action(FILE *f, const struct rtattr *tb); -extern int tc_print_ipt(FILE *f, const struct rtattr *tb); -extern int parse_action(int *, char ***, int, struct nlmsghdr *); -extern void print_tm(FILE *f, const struct tcf_t *tm); -extern int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt); +int get_qdisc_handle(__u32 *h, const char *str); +int get_rate(unsigned int *rate, const char *str); +int get_rate64(__u64 *rate, const char *str); +int get_size(unsigned int *size, const char *str); +int get_size_and_cell(unsigned int *size, int *cell_log, char *str); +int get_time(unsigned int *time, const char *str); +int get_linklayer(unsigned int *val, const char *arg); + +void print_rate(char *buf, int len, __u64 rate); +void print_size(char *buf, int len, __u32 size); +void print_qdisc_handle(char *buf, int len, __u32 h); +void print_time(char *buf, int len, __u32 time); +void print_linklayer(char *buf, int len, unsigned int linklayer); + +char *sprint_rate(__u64 rate, char *buf); +char *sprint_size(__u32 size, char *buf); +char *sprint_qdisc_handle(__u32 h, char *buf); +char *sprint_tc_classid(__u32 h, char *buf); +char *sprint_time(__u32 time, char *buf); +char *sprint_ticks(__u32 ticks, char *buf); +char *sprint_linklayer(unsigned int linklayer, char *buf); + +void print_tcstats_attr(FILE *fp, struct rtattr *tb[], + char *prefix, struct rtattr **xstats); +void print_tcstats2_attr(FILE *fp, struct rtattr *rta, + char *prefix, struct rtattr **xstats); + +int get_tc_classid(__u32 *h, const char *str); +int print_tc_classid(char *buf, int len, __u32 h); +char *sprint_tc_classid(__u32 h, char *buf); + +int tc_print_police(FILE *f, struct rtattr *tb); +int parse_police(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n); + +const char *action_n2a(int action); +int action_a2n(char *arg, int *result, bool allow_num); +int act_parse_police(struct action_util *a, int *argc_p, + char ***argv_p, int tca_id, struct nlmsghdr *n); +int print_police(struct action_util *a, FILE *f, struct rtattr *tb); +int police_print_xstats(struct action_util *a, FILE *f, struct rtattr *tb); +int tc_print_action(FILE *f, const struct rtattr *tb); +int tc_print_ipt(FILE *f, const struct rtattr *tb); +int parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n); +void print_tm(FILE *f, const struct tcf_t *tm); +int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt); -extern int cls_names_init(char *path); -extern void cls_names_uninit(void); +int cls_names_init(char *path); +void cls_names_uninit(void); #endif diff -Nru iproute2-4.3.0/testsuite/tests/ip/netns/set_nsid_batch.t iproute2-4.9.0/testsuite/tests/ip/netns/set_nsid_batch.t --- iproute2-4.3.0/testsuite/tests/ip/netns/set_nsid_batch.t 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/testsuite/tests/ip/netns/set_nsid_batch.t 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,18 @@ +#!/bin/sh + +source lib/generic.sh + +ts_log "[Testing netns nsid in batch mode]" + +NS=testnsid +NSID=99 +BATCHFILE=`mktemp` + +echo "netns add $NS" >> $BATCHFILE +echo "netns set $NS $NSID" >> $BATCHFILE +echo "netns list-id" >> $BATCHFILE +ts_ip "$0" "Add ns, set nsid and list in batch mode" -b $BATCHFILE +test_on "nsid $NSID \(iproute2 netns name: $NS\)" +rm -f $BATCHFILE + +ts_ip "$0" "Delete netns $NS" netns del $NS diff -Nru iproute2-4.3.0/testsuite/tests/ip/netns/set_nsid.t iproute2-4.9.0/testsuite/tests/ip/netns/set_nsid.t --- iproute2-4.3.0/testsuite/tests/ip/netns/set_nsid.t 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/testsuite/tests/ip/netns/set_nsid.t 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,22 @@ +#!/bin/sh + +source lib/generic.sh + +ts_log "[Testing netns nsid]" + +NS=testnsid +NSID=99 + +ts_ip "$0" "Add new netns $NS" netns add $NS +ts_ip "$0" "Set $NS nsid to $NSID" netns set $NS $NSID + +ts_ip "$0" "List netns" netns list +test_on "$NS \(id: $NSID\)" + +ts_ip "$0" "List netns without explicit list or show" netns +test_on "$NS \(id: $NSID\)" + +ts_ip "$0" "List nsid" netns list-id +test_on "$NSID \(iproute2 netns name: $NS\)" + +ts_ip "$0" "Delete netns $NS" netns del $NS diff -Nru iproute2-4.3.0/testsuite/tests/ip/tunnel/add_tunnel.t iproute2-4.9.0/testsuite/tests/ip/tunnel/add_tunnel.t --- iproute2-4.3.0/testsuite/tests/ip/tunnel/add_tunnel.t 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/testsuite/tests/ip/tunnel/add_tunnel.t 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,14 @@ +#!/bin/sh + +source lib/generic.sh + +TUNNEL_NAME="tunnel_test_ip" + +ts_log "[Testing add/del tunnels]" + +ts_ip "$0" "Add GRE tunnel over IPv4" tunnel add name $TUNNEL_NAME mode gre local 1.1.1.1 remote 2.2.2.2 +ts_ip "$0" "Del GRE tunnel over IPv4" tunnel del $TUNNEL_NAME + +ts_ip "$0" "Add GRE tunnel over IPv6" tunnel add name $TUNNEL_NAME mode ip6gre local dead:beef::1 remote dead:beef::2 +ts_ip "$0" "Del GRE tunnel over IPv6" tunnel del $TUNNEL_NAME + diff -Nru iproute2-4.3.0/testsuite/tests/tc/pedit.t iproute2-4.9.0/testsuite/tests/tc/pedit.t --- iproute2-4.3.0/testsuite/tests/tc/pedit.t 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/testsuite/tests/tc/pedit.t 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,217 @@ +#!/bin/sh + +source lib/generic.sh + +DEV="$(rand_dev)" +ts_ip "$0" "Add $DEV dummy interface" link add dev $DEV type dummy +ts_ip "$0" "Enable $DEV" link set $DEV up +ts_tc "pedit" "Add ingress qdisc" qdisc add dev $DEV ingress + + +do_pedit() { + ts_tc "pedit" "Drop ingress qdisc" \ + qdisc del dev $DEV ingress + ts_tc "pedit" "Add ingress qdisc" \ + qdisc add dev $DEV ingress + ts_tc "pedit" "Add pedit action $*" \ + filter add dev $DEV parent ffff: \ + u32 match u32 0 0 \ + action pedit munge $@ + ts_tc "pedit" "Show ingress filters" \ + filter show dev $DEV parent ffff: +} + +do_pedit offset 12 u32 set 0x12345678 +test_on "key #0 at 12: val 12345678 mask 00000000" +do_pedit offset 12 u16 set 0x1234 +test_on "key #0 at 12: val 12340000 mask 0000ffff" +do_pedit offset 14 u16 set 0x1234 +test_on "key #0 at 12: val 00001234 mask ffff0000" +do_pedit offset 12 u8 set 0x23 +test_on "key #0 at 12: val 23000000 mask 00ffffff" +do_pedit offset 13 u8 set 0x23 +test_on "key #0 at 12: val 00230000 mask ff00ffff" +do_pedit offset 14 u8 set 0x23 +test_on "key #0 at 12: val 00002300 mask ffff00ff" +do_pedit offset 15 u8 set 0x23 +test_on "key #0 at 12: val 00000023 mask ffffff00" + +do_pedit offset 13 u8 invert +test_on "key #0 at 12: val 00ff0000 mask ffffffff" +do_pedit offset 13 u8 clear +test_on "key #0 at 12: val 00000000 mask ff00ffff" +do_pedit offset 13 u8 preserve +test_on "key #0 at 12: val 00000000 mask ffffffff" + +# the following set of tests has been auto-generated by running this little +# shell script: +# +# do_it() { +# echo "do_pedit $@" +# tc qd del dev veth0 ingress >/dev/null 2>&1 +# tc qd add dev veth0 ingress >/dev/null 2>&1 +# tc filter add dev veth0 parent ffff: u32 \ +# match u32 0 0 \ +# action pedit munge $@ >/dev/null 2>&1 +# tc filter show dev veth0 parent ffff: | \ +# sed -n 's/^[\t ]*\(key #0.*\)/test_on "\1"/p' +# } +# +# do_it_all() { # (field, val1 [, val2, ...]) +# local field=$1 +# shift +# for val in $@; do +# do_it ip $field set $val +# done +# for i in preserve invert clear; do +# do_it ip $field $i +# done +# } +# +# do_it_all ihl 0x04 0x40 +# do_it_all src 1.2.3.4 +# do_it_all dst 1.2.3.4 +# do_it_all tos 0x1 0x10 +# do_it_all protocol 0x23 +# do_it_all nofrag 0x23 0xf4 +# do_it_all firstfrag 0x03 0xfa +# do_it_all ce 0x23 0x04 0xf3 +# do_it_all df 0x23 0x04 0xf3 +# do_it_all mf 0x23 0x04 0xf3 +# do_it_all dport 0x1234 +# do_it_all sport 0x1234 +# do_it_all icmp_type 0x23 +# do_it_all icmp_code 0x23 + +do_pedit ip ihl set 0x04 +test_on "key #0 at 0: val 04000000 mask f0ffffff" +do_pedit ip ihl set 0x40 +test_on "key #0 at 0: val 00000000 mask f0ffffff" +do_pedit ip ihl preserve +test_on "key #0 at 0: val 00000000 mask ffffffff" +do_pedit ip ihl invert +test_on "key #0 at 0: val 0f000000 mask ffffffff" +do_pedit ip ihl clear +test_on "key #0 at 0: val 00000000 mask f0ffffff" +do_pedit ip src set 1.2.3.4 +test_on "key #0 at 12: val 01020304 mask 00000000" +do_pedit ip src preserve +test_on "key #0 at 12: val 00000000 mask ffffffff" +do_pedit ip src invert +test_on "key #0 at 12: val ffffffff mask ffffffff" +do_pedit ip src clear +test_on "key #0 at 12: val 00000000 mask 00000000" +do_pedit ip dst set 1.2.3.4 +test_on "key #0 at 16: val 01020304 mask 00000000" +do_pedit ip dst preserve +test_on "key #0 at 16: val 00000000 mask ffffffff" +do_pedit ip dst invert +test_on "key #0 at 16: val ffffffff mask ffffffff" +do_pedit ip dst clear +test_on "key #0 at 16: val 00000000 mask 00000000" +do_pedit ip tos set 0x1 +test_on "key #0 at 0: val 00010000 mask ff00ffff" +do_pedit ip tos set 0x10 +test_on "key #0 at 0: val 00100000 mask ff00ffff" +do_pedit ip tos preserve +test_on "key #0 at 0: val 00000000 mask ffffffff" +do_pedit ip tos invert +test_on "key #0 at 0: val 00ff0000 mask ffffffff" +do_pedit ip tos clear +test_on "key #0 at 0: val 00000000 mask ff00ffff" +do_pedit ip protocol set 0x23 +test_on "key #0 at 8: val 00230000 mask ff00ffff" +do_pedit ip protocol preserve +test_on "key #0 at 8: val 00000000 mask ffffffff" +do_pedit ip protocol invert +test_on "key #0 at 8: val 00ff0000 mask ffffffff" +do_pedit ip protocol clear +test_on "key #0 at 8: val 00000000 mask ff00ffff" +do_pedit ip nofrag set 0x23 +test_on "key #0 at 4: val 00002300 mask ffffc0ff" +do_pedit ip nofrag set 0xf4 +test_on "key #0 at 4: val 00003400 mask ffffc0ff" +do_pedit ip nofrag preserve +test_on "key #0 at 4: val 00000000 mask ffffffff" +do_pedit ip nofrag invert +test_on "key #0 at 4: val 00003f00 mask ffffffff" +do_pedit ip nofrag clear +test_on "key #0 at 4: val 00000000 mask ffffc0ff" +do_pedit ip firstfrag set 0x03 +test_on "key #0 at 4: val 00000300 mask ffffe0ff" +do_pedit ip firstfrag set 0xfa +test_on "key #0 at 4: val 00001a00 mask ffffe0ff" +do_pedit ip firstfrag preserve +test_on "key #0 at 4: val 00000000 mask ffffffff" +do_pedit ip firstfrag invert +test_on "key #0 at 4: val 00001f00 mask ffffffff" +do_pedit ip firstfrag clear +test_on "key #0 at 4: val 00000000 mask ffffe0ff" +do_pedit ip ce set 0x23 +test_on "key #0 at 4: val 00000000 mask ffff7fff" +do_pedit ip ce set 0x04 +test_on "key #0 at 4: val 00000000 mask ffff7fff" +do_pedit ip ce set 0xf3 +test_on "key #0 at 4: val 00008000 mask ffff7fff" +do_pedit ip ce preserve +test_on "key #0 at 4: val 00000000 mask ffffffff" +do_pedit ip ce invert +test_on "key #0 at 4: val 00008000 mask ffffffff" +do_pedit ip ce clear +test_on "key #0 at 4: val 00000000 mask ffff7fff" +do_pedit ip df set 0x23 +test_on "key #0 at 4: val 00000000 mask ffffbfff" +do_pedit ip df set 0x04 +test_on "key #0 at 4: val 00000000 mask ffffbfff" +do_pedit ip df set 0xf3 +test_on "key #0 at 4: val 00004000 mask ffffbfff" +do_pedit ip df preserve +test_on "key #0 at 4: val 00000000 mask ffffffff" +do_pedit ip df invert +test_on "key #0 at 4: val 00004000 mask ffffffff" +do_pedit ip df clear +test_on "key #0 at 4: val 00000000 mask ffffbfff" +do_pedit ip mf set 0x23 +test_on "key #0 at 4: val 00002000 mask ffffdfff" +do_pedit ip mf set 0x04 +test_on "key #0 at 4: val 00000000 mask ffffdfff" +do_pedit ip mf set 0xf3 +test_on "key #0 at 4: val 00002000 mask ffffdfff" +do_pedit ip mf preserve +test_on "key #0 at 4: val 00000000 mask ffffffff" +do_pedit ip mf invert +test_on "key #0 at 4: val 00002000 mask ffffffff" +do_pedit ip mf clear +test_on "key #0 at 4: val 00000000 mask ffffdfff" +do_pedit ip dport set 0x1234 +test_on "key #0 at 20: val 00001234 mask ffff0000" +do_pedit ip dport preserve +test_on "key #0 at 20: val 00000000 mask ffffffff" +do_pedit ip dport invert +test_on "key #0 at 20: val 0000ffff mask ffffffff" +do_pedit ip dport clear +test_on "key #0 at 20: val 00000000 mask ffff0000" +do_pedit ip sport set 0x1234 +test_on "key #0 at 20: val 12340000 mask 0000ffff" +do_pedit ip sport preserve +test_on "key #0 at 20: val 00000000 mask ffffffff" +do_pedit ip sport invert +test_on "key #0 at 20: val ffff0000 mask ffffffff" +do_pedit ip sport clear +test_on "key #0 at 20: val 00000000 mask 0000ffff" +do_pedit ip icmp_type set 0x23 +test_on "key #0 at 20: val 23000000 mask 00ffffff" +do_pedit ip icmp_type preserve +test_on "key #0 at 20: val 00000000 mask ffffffff" +do_pedit ip icmp_type invert +test_on "key #0 at 20: val ff000000 mask ffffffff" +do_pedit ip icmp_type clear +test_on "key #0 at 20: val 00000000 mask 00ffffff" +do_pedit ip icmp_code set 0x23 +test_on "key #0 at 20: val 23000000 mask 00ffffff" +do_pedit ip icmp_code preserve +test_on "key #0 at 20: val 00000000 mask ffffffff" +do_pedit ip icmp_code invert +test_on "key #0 at 20: val ff000000 mask ffffffff" +do_pedit ip icmp_code clear +test_on "key #0 at 20: val 00000000 mask 00ffffff" diff -Nru iproute2-4.3.0/tipc/bearer.c iproute2-4.9.0/tipc/bearer.c --- iproute2-4.3.0/tipc/bearer.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tipc/bearer.c 2016-12-12 23:07:42.000000000 +0000 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -26,16 +27,25 @@ #include "msg.h" #include "bearer.h" +#define UDP_PROP_IP 1 +#define UDP_PROP_PORT 2 + +struct cb_data { + int attr; + int prop; + struct nlmsghdr *nlh; +}; + static void _print_bearer_opts(void) { fprintf(stderr, - "\nOPTIONS\n" + "OPTIONS\n" " priority - Bearer link priority\n" " tolerance - Bearer link tolerance\n" " window - Bearer link window\n"); } -static void _print_bearer_media(void) +void print_bearer_media(void) { fprintf(stderr, "\nMEDIA\n" @@ -44,43 +54,30 @@ " eth - Ethernet\n"); } -static void cmd_bearer_enable_l2_help(struct cmdl *cmdl) +static void cmd_bearer_enable_l2_help(struct cmdl *cmdl, char *media) { fprintf(stderr, - "Usage: %s bearer enable media MEDIA device DEVICE [OPTIONS]\n" + "Usage: %s bearer enable media %s device DEVICE [OPTIONS]\n" "\nOPTIONS\n" " domain DOMAIN - Discovery domain\n" " priority PRIORITY - Bearer priority\n", - cmdl->argv[0]); + cmdl->argv[0], media); } -static void cmd_bearer_enable_udp_help(struct cmdl *cmdl) +static void cmd_bearer_enable_udp_help(struct cmdl *cmdl, char *media) { fprintf(stderr, - "Usage: %s bearer enable media udp name NAME localip IP [OPTIONS]\n" - "\nOPTIONS\n" + "Usage: %s bearer enable [OPTIONS] media %s name NAME localip IP [UDP OPTIONS]\n\n", + cmdl->argv[0], media); + fprintf(stderr, + "OPTIONS\n" " domain DOMAIN - Discovery domain\n" - " priority PRIORITY - Bearer priority\n" + " priority PRIORITY - Bearer priority\n\n"); + fprintf(stderr, + "UDP OPTIONS\n" " localport PORT - Local UDP port (default 6118)\n" " remoteip IP - Remote IP address\n" - " remoteport IP - Remote UDP port (default 6118)\n", - cmdl->argv[0]); -} - -static int enable_l2_bearer(struct nlmsghdr *nlh, struct opt *opts, - struct cmdl *cmdl) -{ - struct opt *opt; - char id[TIPC_MAX_BEARER_NAME]; - - if (!(opt = get_opt(opts, "device"))) { - fprintf(stderr, "error: missing bearer device\n"); - return -EINVAL; - } - snprintf(id, sizeof(id), "eth:%s", opt->val); - mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, id); - - return 0; + " remoteport PORT - Remote UDP port (default 6118)\n"); } static int get_netid_cb(const struct nlmsghdr *nlh, void *data) @@ -123,8 +120,8 @@ return 0; } -static int enable_udp_bearer(struct nlmsghdr *nlh, struct opt *opts, - struct cmdl *cmdl) +static int nl_add_udp_enable_opts(struct nlmsghdr *nlh, struct opt *opts, + struct cmdl *cmdl) { int err; struct opt *opt; @@ -134,7 +131,6 @@ char *remport = "6118"; char *locip = NULL; char *remip = NULL; - char name[TIPC_MAX_BEARER_NAME]; struct addrinfo *loc = NULL; struct addrinfo *rem = NULL; struct addrinfo hints = { @@ -142,22 +138,9 @@ .ai_socktype = SOCK_DGRAM }; - if (help_flag) { - cmd_bearer_enable_udp_help(cmdl); - /* TODO find a better error code? */ - return -EINVAL; - } - - if (!(opt = get_opt(opts, "name"))) { - fprintf(stderr, "error, udp bearer name missing\n"); - cmd_bearer_enable_udp_help(cmdl); - return -EINVAL; - } - snprintf(name, sizeof(name), "udp:%s", opt->val); - if (!(opt = get_opt(opts, "localip"))) { fprintf(stderr, "error, udp bearer localip missing\n"); - cmd_bearer_enable_udp_help(cmdl); + cmd_bearer_enable_udp_help(cmdl, "udp"); return -EINVAL; } locip = opt->val; @@ -197,8 +180,6 @@ return -EINVAL; } - mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, name); - nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER_UDP_OPTS); mnl_attr_put(nlh, TIPC_NLA_UDP_LOCAL, loc->ai_addrlen, loc->ai_addr); mnl_attr_put(nlh, TIPC_NLA_UDP_REMOTE, rem->ai_addrlen, rem->ai_addr); @@ -210,6 +191,186 @@ return 0; } +static int nl_add_bearer_name(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, struct opt *opts, + const struct tipc_sup_media *sup_media) +{ + char bname[TIPC_MAX_BEARER_NAME]; + int err; + + if ((err = cmd_get_unique_bearer_name(cmd, cmdl, opts, bname, sup_media))) + return err; + + mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, bname); + return 0; +} + +int cmd_get_unique_bearer_name(const struct cmd *cmd, struct cmdl *cmdl, + struct opt *opts, char *bname, + const struct tipc_sup_media *sup_media) +{ + char *media; + char *identifier; + struct opt *opt; + const struct tipc_sup_media *entry; + + + if (!(opt = get_opt(opts, "media"))) { + if (help_flag) + (cmd->help)(cmdl); + else + fprintf(stderr, "error, missing bearer media\n"); + return -EINVAL; + } + media = opt->val; + + for (entry = sup_media; entry->media; entry++) { + if (strcmp(entry->media, media)) + continue; + + if (!(opt = get_opt(opts, entry->identifier))) { + if (help_flag) + (entry->help)(cmdl, media); + else + fprintf(stderr, "error, missing bearer %s\n", + entry->identifier); + return -EINVAL; + } + + identifier = opt->val; + snprintf(bname, TIPC_MAX_BEARER_NAME, "%s:%s", media, identifier); + + return 0; + } + + fprintf(stderr, "error, invalid media type %s\n", media); + + return -EINVAL; +} + +static void cmd_bearer_add_udp_help(struct cmdl *cmdl, char *media) +{ + fprintf(stderr, "Usage: %s bearer add media %s name NAME remoteip REMOTEIP\n\n", + cmdl->argv[0], media); +} + +static void cmd_bearer_add_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s bearer add media udp name NAME remoteip REMOTEIP\n", + cmdl->argv[0]); +} + +static int udp_bearer_add(struct nlmsghdr *nlh, struct opt *opts, + struct cmdl *cmdl) +{ + int err; + struct opt *opt; + struct nlattr *opts_nest; + char *remport = "6118"; + + opts_nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER_UDP_OPTS); + + if ((opt = get_opt(opts, "remoteport"))) + remport = opt->val; + + if ((opt = get_opt(opts, "remoteip"))) { + char *ip = opt->val; + struct addrinfo *addr = NULL; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM + }; + + if ((err = getaddrinfo(ip, remport, &hints, &addr))) { + fprintf(stderr, "UDP address error: %s\n", + gai_strerror(err)); + freeaddrinfo(addr); + return err; + } + + mnl_attr_put(nlh, TIPC_NLA_UDP_REMOTE, addr->ai_addrlen, + addr->ai_addr); + freeaddrinfo(addr); + } else { + fprintf(stderr, "error, missing remoteip\n"); + return -EINVAL; + } + mnl_attr_nest_end(nlh, opts_nest); + + return 0; +} + +static int cmd_bearer_add_media(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int err; + char *media; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct opt *opt; + struct nlattr *attrs; + struct opt opts[] = { + { "remoteip", OPT_KEYVAL, NULL }, + { "remoteport", OPT_KEYVAL, NULL }, + { "name", OPT_KEYVAL, NULL }, + { "media", OPT_KEYVAL, NULL }, + { NULL } + }; + const struct tipc_sup_media sup_media[] = { + { "udp", "name", cmd_bearer_add_udp_help}, + { NULL, }, + }; + + /* Rewind optind to include media in the option list */ + cmdl->optind--; + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if (!(opt = get_opt(opts, "media"))) { + fprintf(stderr, "error, missing media value\n"); + return -EINVAL; + } + media = opt->val; + + if (strcmp(media, "udp") != 0) { + fprintf(stderr, "error, no \"%s\" media specific options available\n", + media); + return -EINVAL; + } + if (!(opt = get_opt(opts, "name"))) { + fprintf(stderr, "error, missing media name\n"); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_BEARER_ADD))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + attrs = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER); + err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media); + if (err) + return err; + + err = udp_bearer_add(nlh, opts, cmdl); + if (err) + return err; + + mnl_attr_nest_end(nlh, attrs); + + return msg_doit(nlh, NULL, NULL); +} + +static int cmd_bearer_add(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "media", cmd_bearer_add_media, cmd_bearer_add_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + static void cmd_bearer_enable_help(struct cmdl *cmdl) { fprintf(stderr, @@ -218,7 +379,7 @@ " domain DOMAIN - Discovery domain\n" " priority PRIORITY - Bearer priority\n", cmdl->argv[0]); - _print_bearer_media(); + print_bearer_media(); } static int cmd_bearer_enable(struct nlmsghdr *nlh, const struct cmd *cmd, @@ -228,19 +389,24 @@ struct opt *opt; struct nlattr *nest; char buf[MNL_SOCKET_BUFFER_SIZE]; - char *media; struct opt opts[] = { - { "device", NULL }, - { "domain", NULL }, - { "localip", NULL }, - { "localport", NULL }, - { "media", NULL }, - { "name", NULL }, - { "priority", NULL }, - { "remoteip", NULL }, - { "remoteport", NULL }, + { "device", OPT_KEYVAL, NULL }, + { "domain", OPT_KEYVAL, NULL }, + { "localip", OPT_KEYVAL, NULL }, + { "localport", OPT_KEYVAL, NULL }, + { "media", OPT_KEYVAL, NULL }, + { "name", OPT_KEYVAL, NULL }, + { "priority", OPT_KEYVAL, NULL }, + { "remoteip", OPT_KEYVAL, NULL }, + { "remoteport", OPT_KEYVAL, NULL }, { NULL } }; + struct tipc_sup_media sup_media[] = { + { "udp", "name", cmd_bearer_enable_udp_help}, + { "eth", "device", cmd_bearer_enable_l2_help }, + { "ib", "device", cmd_bearer_enable_l2_help }, + { NULL, }, + }; if (parse_opts(opts, cmdl) < 0) { if (help_flag) @@ -248,15 +414,6 @@ return -EINVAL; } - if (!(opt = get_opt(opts, "media"))) { - if (help_flag) - (cmd->help)(cmdl); - else - fprintf(stderr, "error, missing bearer media\n"); - return -EINVAL; - } - media = opt->val; - if (!(nlh = msg_init(buf, TIPC_NL_BEARER_ENABLE))) { fprintf(stderr, "error: message initialisation failed\n"); return -1; @@ -274,95 +431,58 @@ mnl_attr_nest_end(nlh, props); } - if (strcmp(media, "udp") == 0) { - if (help_flag) { - cmd_bearer_enable_udp_help(cmdl); - return -EINVAL; - } - if ((err = enable_udp_bearer(nlh, opts, cmdl))) - return err; - } else if ((strcmp(media, "eth") == 0) || (strcmp(media, "udp") == 0)) { - if (help_flag) { - cmd_bearer_enable_l2_help(cmdl); - return -EINVAL; - } - if ((err = enable_l2_bearer(nlh, opts, cmdl))) + err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media); + if (err) + return err; + + opt = get_opt(opts, "media"); + if (strcmp(opt->val, "udp") == 0) { + err = nl_add_udp_enable_opts(nlh, opts, cmdl); + if (err) return err; - } else { - fprintf(stderr, "error, invalid media type \"%s\"\n", media); - return -EINVAL; } - mnl_attr_nest_end(nlh, nest); return msg_doit(nlh, NULL, NULL); } -static int add_l2_bearer(struct nlmsghdr *nlh, struct opt *opts) +static void cmd_bearer_disable_l2_help(struct cmdl *cmdl, char *media) { - struct opt *opt; - char id[TIPC_MAX_BEARER_NAME]; - - if (!(opt = get_opt(opts, "device"))) { - fprintf(stderr, "error: missing bearer device\n"); - return -EINVAL; - } - snprintf(id, sizeof(id), "eth:%s", opt->val); - - mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, id); - - return 0; -} - -static int add_udp_bearer(struct nlmsghdr *nlh, struct opt *opts) -{ - struct opt *opt; - char id[TIPC_MAX_BEARER_NAME]; - - if (!(opt = get_opt(opts, "name"))) { - fprintf(stderr, "error: missing bearer name\n"); - return -EINVAL; - } - snprintf(id, sizeof(id), "udp:%s", opt->val); - - mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, id); - - return 0; -} - -static void cmd_bearer_disable_l2_help(struct cmdl *cmdl) -{ - fprintf(stderr, "Usage: %s bearer disable media udp device DEVICE\n", - cmdl->argv[0]); + fprintf(stderr, "Usage: %s bearer disable media %s device DEVICE\n", + cmdl->argv[0], media); } -static void cmd_bearer_disable_udp_help(struct cmdl *cmdl) +static void cmd_bearer_disable_udp_help(struct cmdl *cmdl, char *media) { - fprintf(stderr, "Usage: %s bearer disable media udp name NAME\n", - cmdl->argv[0]); + fprintf(stderr, "Usage: %s bearer disable media %s name NAME\n", + cmdl->argv[0], media); } static void cmd_bearer_disable_help(struct cmdl *cmdl) { fprintf(stderr, "Usage: %s bearer disable media MEDIA ARGS...\n", cmdl->argv[0]); - _print_bearer_media(); + print_bearer_media(); } static int cmd_bearer_disable(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, void *data) { int err; - char *media; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlattr *nest; - struct opt *opt; struct opt opts[] = { - { "device", NULL }, - { "name", NULL }, - { "media", NULL }, + { "device", OPT_KEYVAL, NULL }, + { "name", OPT_KEYVAL, NULL }, + { "media", OPT_KEYVAL, NULL }, { NULL } }; + struct tipc_sup_media sup_media[] = { + { "udp", "name", cmd_bearer_disable_udp_help}, + { "eth", "device", cmd_bearer_disable_l2_help }, + { "ib", "device", cmd_bearer_disable_l2_help }, + { NULL, }, + }; if (parse_opts(opts, cmdl) < 0) { if (help_flag) @@ -370,40 +490,15 @@ return -EINVAL; } - if (!(opt = get_opt(opts, "media"))) { - if (help_flag) - (cmd->help)(cmdl); - else - fprintf(stderr, "error, missing bearer media\n"); - return -EINVAL; - } - media = opt->val; - if (!(nlh = msg_init(buf, TIPC_NL_BEARER_DISABLE))) { fprintf(stderr, "error, message initialisation failed\n"); return -1; } nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER); - - if (strcmp(media, "udp") == 0) { - if (help_flag) { - cmd_bearer_disable_udp_help(cmdl); - return -EINVAL; - } - if ((err = add_udp_bearer(nlh, opts))) - return err; - } else if ((strcmp(media, "eth") == 0) || (strcmp(media, "udp") == 0)) { - if (help_flag) { - cmd_bearer_disable_l2_help(cmdl); - return -EINVAL; - } - if ((err = add_l2_bearer(nlh, opts))) - return err; - } else { - fprintf(stderr, "error, invalid media type \"%s\"\n", media); - return -EINVAL; - } + err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media); + if (err) + return err; mnl_attr_nest_end(nlh, nest); return msg_doit(nlh, NULL, NULL); @@ -415,13 +510,13 @@ fprintf(stderr, "Usage: %s bearer set OPTION media MEDIA ARGS...\n", cmdl->argv[0]); _print_bearer_opts(); - _print_bearer_media(); + print_bearer_media(); } -static void cmd_bearer_set_udp_help(struct cmdl *cmdl) +static void cmd_bearer_set_udp_help(struct cmdl *cmdl, char *media) { - fprintf(stderr, "Usage: %s bearer set OPTION media udp name NAME\n\n", - cmdl->argv[0]); + fprintf(stderr, "Usage: %s bearer set OPTION media %s name NAME\n\n", + cmdl->argv[0], media); _print_bearer_opts(); } @@ -434,22 +529,26 @@ } static int cmd_bearer_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd, - struct cmdl *cmdl, void *data) + struct cmdl *cmdl, void *data) { int err; int val; int prop; - char *media; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlattr *props; struct nlattr *attrs; - struct opt *opt; struct opt opts[] = { - { "device", NULL }, - { "media", NULL }, - { "name", NULL }, + { "device", OPT_KEYVAL, NULL }, + { "media", OPT_KEYVAL, NULL }, + { "name", OPT_KEYVAL, NULL }, { NULL } }; + struct tipc_sup_media sup_media[] = { + { "udp", "name", cmd_bearer_set_udp_help}, + { "eth", "device", cmd_bearer_set_l2_help }, + { "ib", "device", cmd_bearer_set_l2_help }, + { NULL, }, + }; if (strcmp(cmd->cmd, "priority") == 0) prop = TIPC_NLA_PROP_PRIO; @@ -460,11 +559,6 @@ else return -EINVAL; - if (help_flag) { - (cmd->help)(cmdl); - return -EINVAL; - } - if (cmdl->optind >= cmdl->argc) { fprintf(stderr, "error, missing value\n"); return -EINVAL; @@ -484,30 +578,10 @@ mnl_attr_put_u32(nlh, prop, val); mnl_attr_nest_end(nlh, props); - if (!(opt = get_opt(opts, "media"))) { - fprintf(stderr, "error, missing media\n"); - return -EINVAL; - } - media = opt->val; + err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media); + if (err) + return err; - if (strcmp(media, "udp") == 0) { - if (help_flag) { - cmd_bearer_set_udp_help(cmdl); - return -EINVAL; - } - if ((err = add_udp_bearer(nlh, opts))) - return err; - } else if ((strcmp(media, "eth") == 0) || (strcmp(media, "udp") == 0)) { - if (help_flag) { - cmd_bearer_set_l2_help(cmdl, media); - return -EINVAL; - } - if ((err = add_l2_bearer(nlh, opts))) - return err; - } else { - fprintf(stderr, "error, invalid media type \"%s\"\n", media); - return -EINVAL; - } mnl_attr_nest_end(nlh, attrs); return msg_doit(nlh, NULL, NULL); @@ -528,27 +602,142 @@ static void cmd_bearer_get_help(struct cmdl *cmdl) { - fprintf(stderr, "Usage: %s bearer get OPTION media MEDIA ARGS...\n", + fprintf(stderr, "Usage: %s bearer get [OPTION] media MEDIA ARGS...\n", cmdl->argv[0]); _print_bearer_opts(); - _print_bearer_media(); + print_bearer_media(); } -static void cmd_bearer_get_udp_help(struct cmdl *cmdl) +static void cmd_bearer_get_udp_help(struct cmdl *cmdl, char *media) { - fprintf(stderr, "Usage: %s bearer get OPTION media udp name NAME\n\n", - cmdl->argv[0]); + fprintf(stderr, "Usage: %s bearer get [OPTION] media %s name NAME [UDP OPTIONS]\n\n", + cmdl->argv[0], media); + fprintf(stderr, + "UDP OPTIONS\n" + " remoteip - Remote ip address\n" + " remoteport - Remote port\n" + " localip - Local ip address\n" + " localport - Local port\n\n"); _print_bearer_opts(); } static void cmd_bearer_get_l2_help(struct cmdl *cmdl, char *media) { fprintf(stderr, - "Usage: %s bearer get [OPTION]... media %s device DEVICE\n", + "Usage: %s bearer get OPTION media %s device DEVICE\n", cmdl->argv[0], media); _print_bearer_opts(); } + +static int bearer_dump_udp_cb(const struct nlmsghdr *nlh, void *data) +{ + struct sockaddr_storage *addr; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_UDP_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + + if (!info[TIPC_NLA_UDP_REMOTE]) + return MNL_CB_ERROR; + + addr = mnl_attr_get_payload(info[TIPC_NLA_UDP_REMOTE]); + + if (addr->ss_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *) addr; + + printf("%s\n", inet_ntoa(ipv4->sin_addr)); + } else if (addr->ss_family == AF_INET6) { + char straddr[INET6_ADDRSTRLEN]; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) addr; + + if (!inet_ntop(AF_INET6, &ipv6->sin6_addr, straddr, + sizeof(straddr))) { + fprintf(stderr, "error, parsing IPv6 addr\n"); + return MNL_CB_ERROR; + } + printf("%s\n", straddr); + + } else { + return MNL_CB_ERROR; + } + + return MNL_CB_OK; +} + +static int bearer_get_udp_cb(const struct nlmsghdr *nlh, void *data) +{ + struct cb_data *cb_data = (struct cb_data *) data; + struct sockaddr_storage *addr; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1] = {}; + struct nlattr *opts[TIPC_NLA_UDP_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_BEARER]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_BEARER], parse_attrs, attrs); + if (!attrs[TIPC_NLA_BEARER_UDP_OPTS]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(attrs[TIPC_NLA_BEARER_UDP_OPTS], parse_attrs, opts); + if (!opts[TIPC_NLA_UDP_LOCAL]) + return MNL_CB_ERROR; + + if ((cb_data->attr == TIPC_NLA_UDP_REMOTE) && + (cb_data->prop == UDP_PROP_IP) && + opts[TIPC_NLA_UDP_MULTI_REMOTEIP]) { + struct genlmsghdr *genl = mnl_nlmsg_get_payload(cb_data->nlh); + + genl->cmd = TIPC_NL_UDP_GET_REMOTEIP; + return msg_dumpit(cb_data->nlh, bearer_dump_udp_cb, NULL); + } + + addr = mnl_attr_get_payload(opts[cb_data->attr]); + + if (addr->ss_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *) addr; + + switch (cb_data->prop) { + case UDP_PROP_IP: + printf("%s\n", inet_ntoa(ipv4->sin_addr)); + break; + case UDP_PROP_PORT: + printf("%u\n", ntohs(ipv4->sin_port)); + break; + default: + return MNL_CB_ERROR; + } + + } else if (addr->ss_family == AF_INET6) { + char straddr[INET6_ADDRSTRLEN]; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) addr; + + switch (cb_data->prop) { + case UDP_PROP_IP: + if (!inet_ntop(AF_INET6, &ipv6->sin6_addr, straddr, + sizeof(straddr))) { + fprintf(stderr, "error, parsing IPv6 addr\n"); + return MNL_CB_ERROR; + } + printf("%s\n", straddr); + break; + case UDP_PROP_PORT: + printf("%u\n", ntohs(ipv6->sin6_port)); + break; + default: + return MNL_CB_ERROR; + } + + } else { + return MNL_CB_ERROR; + } + + return MNL_CB_OK; +} + static int bearer_get_cb(const struct nlmsghdr *nlh, void *data) { int *prop = data; @@ -574,21 +763,110 @@ return MNL_CB_OK; } +static int cmd_bearer_get_media(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int err; + char *media; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct opt *opt; + struct cb_data cb_data = {0}; + struct nlattr *attrs; + struct opt opts[] = { + { "localip", OPT_KEY, NULL }, + { "localport", OPT_KEY, NULL }, + { "remoteip", OPT_KEY, NULL }, + { "remoteport", OPT_KEY, NULL }, + { "name", OPT_KEYVAL, NULL }, + { "media", OPT_KEYVAL, NULL }, + { NULL } + }; + struct tipc_sup_media sup_media[] = { + { "udp", "name", cmd_bearer_get_udp_help}, + { NULL, }, + }; + + /* Rewind optind to include media in the option list */ + cmdl->optind--; + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if (!(opt = get_opt(opts, "media"))) { + fprintf(stderr, "error, missing media value\n"); + return -EINVAL; + } + media = opt->val; + + if (help_flag) { + cmd_bearer_get_udp_help(cmdl, media); + return -EINVAL; + } + if (strcmp(media, "udp") != 0) { + fprintf(stderr, "error, no \"%s\" media specific options\n", media); + return -EINVAL; + } + if (!(opt = get_opt(opts, "name"))) { + fprintf(stderr, "error, missing media name\n"); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_BEARER_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + attrs = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER); + err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media); + if (err) + return err; + mnl_attr_nest_end(nlh, attrs); + cb_data.nlh = nlh; + + if (has_opt(opts, "localip")) { + cb_data.attr = TIPC_NLA_UDP_LOCAL; + cb_data.prop = UDP_PROP_IP; + return msg_doit(nlh, bearer_get_udp_cb, &cb_data); + } else if (has_opt(opts, "localport")) { + cb_data.attr = TIPC_NLA_UDP_LOCAL; + cb_data.prop = UDP_PROP_PORT; + return msg_doit(nlh, bearer_get_udp_cb, &cb_data); + } else if (has_opt(opts, "remoteip")) { + cb_data.attr = TIPC_NLA_UDP_REMOTE; + cb_data.prop = UDP_PROP_IP; + return msg_doit(nlh, bearer_get_udp_cb, &cb_data); + } else if (has_opt(opts, "remoteport")) { + cb_data.attr = TIPC_NLA_UDP_REMOTE; + cb_data.prop = UDP_PROP_PORT; + return msg_doit(nlh, bearer_get_udp_cb, &cb_data); + } + fprintf(stderr, "error, missing UDP option\n"); + return -EINVAL; +} + static int cmd_bearer_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, void *data) { int err; int prop; - char *media; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlattr *attrs; - struct opt *opt; struct opt opts[] = { - { "device", NULL }, - { "media", NULL }, - { "name", NULL }, + { "device", OPT_KEYVAL, NULL }, + { "media", OPT_KEYVAL, NULL }, + { "name", OPT_KEYVAL, NULL }, { NULL } }; + struct tipc_sup_media sup_media[] = { + { "udp", "name", cmd_bearer_get_udp_help}, + { "eth", "device", cmd_bearer_get_l2_help }, + { "ib", "device", cmd_bearer_get_l2_help }, + { NULL, }, + }; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } if (strcmp(cmd->cmd, "priority") == 0) prop = TIPC_NLA_PROP_PRIO; @@ -599,11 +877,6 @@ else return -EINVAL; - if (help_flag) { - (cmd->help)(cmdl); - return -EINVAL; - } - if (parse_opts(opts, cmdl) < 0) return -EINVAL; @@ -612,31 +885,10 @@ return -1; } - if (!(opt = get_opt(opts, "media"))) { - fprintf(stderr, "error, missing media\n"); - return -EINVAL; - } - media = opt->val; - attrs = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER); - if (strcmp(media, "udp") == 0) { - if (help_flag) { - cmd_bearer_get_udp_help(cmdl); - return -EINVAL; - } - if ((err = add_udp_bearer(nlh, opts))) - return err; - } else if ((strcmp(media, "eth") == 0) || (strcmp(media, "udp") == 0)) { - if (help_flag) { - cmd_bearer_get_l2_help(cmdl, media); - return -EINVAL; - } - if ((err = add_l2_bearer(nlh, opts))) - return err; - } else { - fprintf(stderr, "error, invalid media type \"%s\"\n", media); - return -EINVAL; - } + err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media); + if (err) + return err; mnl_attr_nest_end(nlh, attrs); return msg_doit(nlh, bearer_get_cb, &prop); @@ -649,6 +901,7 @@ { "priority", cmd_bearer_get_prop, cmd_bearer_get_help }, { "tolerance", cmd_bearer_get_prop, cmd_bearer_get_help }, { "window", cmd_bearer_get_prop, cmd_bearer_get_help }, + { "media", cmd_bearer_get_media, cmd_bearer_get_help }, { NULL } }; @@ -702,6 +955,7 @@ "Usage: %s bearer COMMAND [ARGS] ...\n" "\n" "COMMANDS\n" + " add - Add data to existing bearer\n" " enable - Enable a bearer\n" " disable - Disable a bearer\n" " set - Set various bearer properties\n" @@ -713,6 +967,7 @@ void *data) { const struct cmd cmds[] = { + { "add", cmd_bearer_add, cmd_bearer_add_help }, { "disable", cmd_bearer_disable, cmd_bearer_disable_help }, { "enable", cmd_bearer_enable, cmd_bearer_enable_help }, { "get", cmd_bearer_get, cmd_bearer_get_help }, diff -Nru iproute2-4.3.0/tipc/bearer.h iproute2-4.9.0/tipc/bearer.h --- iproute2-4.3.0/tipc/bearer.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tipc/bearer.h 2016-12-12 23:07:42.000000000 +0000 @@ -19,4 +19,8 @@ int cmd_bearer(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, void *data); void cmd_bearer_help(struct cmdl *cmdl); +void print_bearer_media(void); +int cmd_get_unique_bearer_name(const struct cmd *cmd, struct cmdl *cmdl, + struct opt *opts, char *bname, + const struct tipc_sup_media *sup_media); #endif diff -Nru iproute2-4.3.0/tipc/cmdl.c iproute2-4.9.0/tipc/cmdl.c --- iproute2-4.3.0/tipc/cmdl.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tipc/cmdl.c 2016-12-12 23:07:42.000000000 +0000 @@ -62,6 +62,11 @@ return NULL; } +bool has_opt(struct opt *opts, char *key) +{ + return get_opt(opts, key) ? true : false; +} + char *shift_cmdl(struct cmdl *cmdl) { int next; @@ -80,7 +85,7 @@ int i; int cnt = 0; - for (i = cmdl->optind; i < cmdl->argc; i += 2) { + for (i = cmdl->optind; i < cmdl->argc; i++) { struct opt *o; o = find_opt(opts, cmdl->argv[i]); @@ -89,9 +94,13 @@ cmdl->argv[i]); return -EINVAL; } + if (o->flag & OPT_KEYVAL) { + cmdl->optind++; + i++; + } cnt++; - o->val = cmdl->argv[i + 1]; - cmdl->optind += 2; + o->val = cmdl->argv[i]; + cmdl->optind++; } return cnt; diff -Nru iproute2-4.3.0/tipc/cmdl.h iproute2-4.9.0/tipc/cmdl.h --- iproute2-4.3.0/tipc/cmdl.h 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tipc/cmdl.h 2016-12-12 23:07:42.000000000 +0000 @@ -16,12 +16,23 @@ extern int help_flag; +enum { + OPT_KEY = (1 << 0), + OPT_KEYVAL = (1 << 1), +}; + struct cmdl { int optind; int argc; char **argv; }; +struct tipc_sup_media { + char *media; + char *identifier; + void (*help)(struct cmdl *cmdl, char *media); +}; + struct cmd { const char *cmd; int (*func)(struct nlmsghdr *nlh, const struct cmd *cmd, @@ -31,10 +42,12 @@ struct opt { const char *key; + uint16_t flag; char *val; }; struct opt *get_opt(struct opt *opts, char *key); +bool has_opt(struct opt *opts, char *key); int parse_opts(struct opt *opts, struct cmdl *cmdl); char *shift_cmdl(struct cmdl *cmdl); diff -Nru iproute2-4.3.0/tipc/link.c iproute2-4.9.0/tipc/link.c --- iproute2-4.3.0/tipc/link.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tipc/link.c 2016-12-12 23:07:42.000000000 +0000 @@ -22,6 +22,7 @@ #include "cmdl.h" #include "msg.h" #include "link.h" +#include "bearer.h" static int link_list_cb(const struct nlmsghdr *nlh, void *data) { @@ -57,7 +58,8 @@ return -EINVAL; } - if (!(nlh = msg_init(buf, TIPC_NL_LINK_GET))) { + nlh = msg_init(buf, TIPC_NL_LINK_GET); + if (!nlh) { fprintf(stderr, "error, message initialisation failed\n"); return -1; } @@ -90,7 +92,6 @@ return MNL_CB_OK; } - static int cmd_link_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, void *data) { @@ -98,7 +99,7 @@ char buf[MNL_SOCKET_BUFFER_SIZE]; struct opt *opt; struct opt opts[] = { - { "link", NULL }, + { "link", OPT_KEYVAL, NULL }, { NULL } }; @@ -119,12 +120,14 @@ if (parse_opts(opts, cmdl) < 0) return -EINVAL; - if (!(nlh = msg_init(buf, TIPC_NL_LINK_GET))) { + nlh = msg_init(buf, TIPC_NL_LINK_GET); + if (!nlh) { fprintf(stderr, "error, message initialisation failed\n"); return -1; } - if (!(opt = get_opt(opts, "link"))) { + opt = get_opt(opts, "link"); + if (!opt) { fprintf(stderr, "error, missing link\n"); return -EINVAL; } @@ -169,7 +172,7 @@ struct opt *opt; struct nlattr *nest; struct opt opts[] = { - { "link", NULL }, + { "link", OPT_KEYVAL, NULL }, { NULL } }; @@ -183,12 +186,14 @@ return -EINVAL; } - if (!(nlh = msg_init(buf, TIPC_NL_LINK_RESET_STATS))) { + nlh = msg_init(buf, TIPC_NL_LINK_RESET_STATS); + if (!nlh) { fprintf(stderr, "error, message initialisation failed\n"); return -1; } - if (!(opt = get_opt(opts, "link"))) { + opt = get_opt(opts, "link"); + if (!opt) { fprintf(stderr, "error, missing link\n"); return -EINVAL; } @@ -245,8 +250,7 @@ mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_CNT]), mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_TOT]) / proft); - printf(" 0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% " - "-16384:%u%% -32768:%u%% -66000:%u%%\n", + printf(" 0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% -16384:%u%% -32768:%u%% -66000:%u%%\n", perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P0]), proft), perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P1]), proft), perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P2]), proft), @@ -365,7 +369,7 @@ char buf[MNL_SOCKET_BUFFER_SIZE]; struct opt *opt; struct opt opts[] = { - { "link", NULL }, + { "link", OPT_KEYVAL, NULL }, { NULL } }; @@ -374,7 +378,8 @@ return -EINVAL; } - if (!(nlh = msg_init(buf, TIPC_NL_LINK_GET))) { + nlh = msg_init(buf, TIPC_NL_LINK_GET); + if (!nlh) { fprintf(stderr, "error, message initialisation failed\n"); return -1; } @@ -382,7 +387,8 @@ if (parse_opts(opts, cmdl) < 0) return -EINVAL; - if ((opt = get_opt(opts, "link"))) + opt = get_opt(opts, "link"); + if (opt) link = opt->val; return msg_dumpit(nlh, link_stat_show_cb, link); @@ -429,7 +435,7 @@ struct nlattr *attrs; struct opt *opt; struct opt opts[] = { - { "link", NULL }, + { "link", OPT_KEYVAL, NULL }, { NULL } }; @@ -456,13 +462,15 @@ if (parse_opts(opts, cmdl) < 0) return -EINVAL; - if (!(nlh = msg_init(buf, TIPC_NL_LINK_SET))) { + nlh = msg_init(buf, TIPC_NL_LINK_SET); + if (!nlh) { fprintf(stderr, "error, message initialisation failed\n"); return -1; } attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK); - if (!(opt = get_opt(opts, "link"))) { + opt = get_opt(opts, "link"); + if (!opt) { fprintf(stderr, "error, missing link\n"); return -EINVAL; } @@ -475,8 +483,6 @@ mnl_attr_nest_end(nlh, attrs); return msg_doit(nlh, link_get_cb, &prop); - - return 0; } static int cmd_link_set(struct nlmsghdr *nlh, const struct cmd *cmd, @@ -492,6 +498,414 @@ return run_cmd(nlh, cmd, cmds, cmdl, NULL); } +static int cmd_link_mon_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int size; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *attrs; + + if (cmdl->argc != cmdl->optind + 1) { + fprintf(stderr, "error, missing value\n"); + return -EINVAL; + } + size = atoi(shift_cmdl(cmdl)); + + nlh = msg_init(buf, TIPC_NL_MON_SET); + if (!nlh) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + attrs = mnl_attr_nest_start(nlh, TIPC_NLA_MON); + + mnl_attr_put_u32(nlh, TIPC_NLA_MON_ACTIVATION_THRESHOLD, size); + + mnl_attr_nest_end(nlh, attrs); + + return msg_doit(nlh, NULL, NULL); +} + +static int link_mon_summary_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_MON_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_MON]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_MON], parse_attrs, attrs); + + printf("\nbearer %s\n", + mnl_attr_get_str(attrs[TIPC_NLA_MON_BEARER_NAME])); + + printf(" table_generation %u\n", + mnl_attr_get_u32(attrs[TIPC_NLA_MON_LISTGEN])); + printf(" cluster_size %u\n", + mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEERCNT])); + printf(" algorithm %s\n", + attrs[TIPC_NLA_MON_ACTIVE] ? "overlapping-ring" : "full-mesh"); + + return MNL_CB_OK; +} + +static int cmd_link_mon_summary(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (help_flag) { + fprintf(stderr, "Usage: %s monitor summary\n", cmdl->argv[0]); + return -EINVAL; + } + + nlh = msg_init(buf, TIPC_NL_MON_GET); + if (!nlh) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_dumpit(nlh, link_mon_summary_cb, NULL); +} + +#define STATUS_WIDTH 7 +#define MAX_NODE_WIDTH 14 /* 255.4095.4095 */ +#define MAX_DOM_GEN_WIDTH 11 /* 65535 */ +#define DIRECTLY_MON_WIDTH 10 + +#define APPL_NODE_STATUS_WIDTH 5 + +static int map_get(uint64_t up_map, int i) +{ + return (up_map & (1 << i)) >> i; +} + +/* print the applied members, since we know the the members + * are listed in ascending order, we print only the state + */ +static void link_mon_print_applied(uint16_t applied, uint64_t up_map) +{ + int i; + char state; + + for (i = 0; i < applied; i++) { + /* print the delimiter for every -n- entry */ + if (i && !(i % APPL_NODE_STATUS_WIDTH)) + printf(","); + + state = map_get(up_map, i) ? 'U' : 'D'; + printf("%c", state); + } +} + +/* print the non applied members, since we dont know + * the members, we print them along with the state + */ +static void link_mon_print_non_applied(uint16_t applied, uint16_t member_cnt, + uint64_t up_map, uint32_t *members) +{ + int i; + char state; + + printf(" ["); + for (i = applied; i < member_cnt; i++) { + char addr_str[16]; + + /* print the delimiter for every entry */ + if (i != applied) + printf(","); + + sprintf(addr_str, "%u.%u.%u:", tipc_zone(members[i]), + tipc_cluster(members[i]), tipc_node(members[i])); + state = map_get(up_map, i) ? 'U' : 'D'; + printf("%s%c", addr_str, state); + } + printf("]"); +} + +static void link_mon_print_peer_state(const uint32_t addr, const char *status, + const char *monitored, + const uint32_t dom_gen) +{ + char addr_str[16]; + + sprintf(addr_str, "%u.%u.%u", tipc_zone(addr), tipc_cluster(addr), + tipc_node(addr)); + + printf("%-*s", MAX_NODE_WIDTH, addr_str); + printf("%-*s", STATUS_WIDTH, status); + printf("%-*s", DIRECTLY_MON_WIDTH, monitored); + printf("%-*u", MAX_DOM_GEN_WIDTH, dom_gen); +} + +static int link_mon_peer_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *attrs[TIPC_NLA_MON_PEER_MAX + 1] = {}; + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + uint16_t member_cnt; + uint32_t applied; + uint32_t dom_gen; + uint64_t up_map; + char status[16]; + char monitored[16]; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_MON_PEER]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_MON_PEER], parse_attrs, attrs); + + (attrs[TIPC_NLA_MON_PEER_LOCAL] || attrs[TIPC_NLA_MON_PEER_HEAD]) ? + strcpy(monitored, "direct") : + strcpy(monitored, "indirect"); + + attrs[TIPC_NLA_MON_PEER_UP] ? + strcpy(status, "up") : + strcpy(status, "down"); + + dom_gen = attrs[TIPC_NLA_MON_PEER_DOMGEN] ? + mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEER_DOMGEN]) : 0; + + link_mon_print_peer_state(mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEER_ADDR]), + status, monitored, dom_gen); + + applied = mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEER_APPLIED]); + + if (!applied) + goto exit; + + up_map = mnl_attr_get_u64(attrs[TIPC_NLA_MON_PEER_UPMAP]); + + member_cnt = mnl_attr_get_payload_len(attrs[TIPC_NLA_MON_PEER_MEMBERS]); + + /* each tipc address occupies 4 bytes of payload, hence compensate it */ + member_cnt /= sizeof(uint32_t); + + link_mon_print_applied(applied, up_map); + + link_mon_print_non_applied(applied, member_cnt, up_map, + mnl_attr_get_payload(attrs[TIPC_NLA_MON_PEER_MEMBERS])); + +exit: + printf("\n"); + + return MNL_CB_OK; +} + +static int link_mon_peer_list(uint32_t mon_ref) +{ + struct nlmsghdr *nlh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *nest; + + nlh = msg_init(buf, TIPC_NL_MON_PEER_GET); + if (!nlh) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + nest = mnl_attr_nest_start(nlh, TIPC_NLA_MON); + mnl_attr_put_u32(nlh, TIPC_NLA_MON_REF, mon_ref); + mnl_attr_nest_end(nlh, nest); + + return msg_dumpit(nlh, link_mon_peer_list_cb, NULL); +} + +static int link_mon_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_MON_MAX + 1] = {}; + char *req_bearer = data; + const char *bname; + const char title[] = + "node status monitored generation applied_node_status [non_applied_node:status]"; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_MON]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_MON], parse_attrs, attrs); + + bname = mnl_attr_get_str(attrs[TIPC_NLA_MON_BEARER_NAME]); + + if (*req_bearer && (strcmp(req_bearer, bname) != 0)) + return MNL_CB_OK; + + printf("\nbearer %s\n", bname); + printf("%s\n", title); + + if (mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEERCNT])) + link_mon_peer_list(mnl_attr_get_u32(attrs[TIPC_NLA_MON_REF])); + + return MNL_CB_OK; +} + +static void cmd_link_mon_list_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s monitor list [ media MEDIA ARGS...]\n\n", + cmdl->argv[0]); + print_bearer_media(); +} + +static void cmd_link_mon_list_l2_help(struct cmdl *cmdl, char *media) +{ + fprintf(stderr, + "Usage: %s monitor list media %s device DEVICE [OPTIONS]\n", + cmdl->argv[0], media); +} + +static void cmd_link_mon_list_udp_help(struct cmdl *cmdl, char *media) +{ + fprintf(stderr, + "Usage: %s monitor list media udp name NAME\n\n", + cmdl->argv[0]); +} + +static int cmd_link_mon_list(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + char bname[TIPC_MAX_BEARER_NAME] = {0}; + struct opt opts[] = { + { "media", OPT_KEYVAL, NULL }, + { "device", OPT_KEYVAL, NULL }, + { "name", OPT_KEYVAL, NULL }, + { NULL } + }; + struct tipc_sup_media sup_media[] = { + { "udp", "name", cmd_link_mon_list_udp_help}, + { "eth", "device", cmd_link_mon_list_l2_help }, + { "ib", "device", cmd_link_mon_list_l2_help }, + { NULL, }, + }; + + int err; + + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if (get_opt(opts, "media")) { + err = cmd_get_unique_bearer_name(cmd, cmdl, opts, bname, + sup_media); + if (err) + return err; + } + + if (help_flag) { + cmd->help(cmdl); + return -EINVAL; + } + + nlh = msg_init(buf, TIPC_NL_MON_GET); + if (!nlh) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_dumpit(nlh, link_mon_list_cb, bname); +} + +static void cmd_link_mon_set_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s monitor set PPROPERTY\n\n" + "PROPERTIES\n" + " threshold SIZE - Set monitor activation threshold\n", + cmdl->argv[0]); +} + +static int cmd_link_mon_set(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "threshold", cmd_link_mon_set_prop, NULL }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +static void cmd_link_mon_get_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s monitor get PPROPERTY\n\n" + "PROPERTIES\n" + " threshold - Get monitor activation threshold\n", + cmdl->argv[0]); +} + +static int link_mon_get_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_MON_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_MON]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_MON], parse_attrs, attrs); + if (!attrs[TIPC_NLA_MON_ACTIVATION_THRESHOLD]) + return MNL_CB_ERROR; + + printf("%u\n", + mnl_attr_get_u32(attrs[TIPC_NLA_MON_ACTIVATION_THRESHOLD])); + + return MNL_CB_OK; +} + +static int cmd_link_mon_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + + nlh = msg_init(buf, TIPC_NL_MON_GET); + if (!nlh) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_doit(nlh, link_mon_get_cb, NULL); +} + +static int cmd_link_mon_get(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "threshold", cmd_link_mon_get_prop, NULL}, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +static void cmd_link_mon_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s montior COMMAND [ARGS] ...\n\n" + "COMMANDS\n" + " set - Set monitor properties\n" + " get - Get monitor properties\n" + " list - List all cluster members\n" + " summary - Show local node monitor summary\n", + cmdl->argv[0]); +} + +static int cmd_link_mon(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data) +{ + const struct cmd cmds[] = { + { "set", cmd_link_mon_set, cmd_link_mon_set_help }, + { "get", cmd_link_mon_get, cmd_link_mon_get_help }, + { "list", cmd_link_mon_list, cmd_link_mon_list_help }, + { "summary", cmd_link_mon_summary, NULL }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + void cmd_link_help(struct cmdl *cmdl) { fprintf(stderr, @@ -501,7 +915,8 @@ " list - List links\n" " get - Get various link properties\n" " set - Set various link properties\n" - " statistics - Show or reset statistics\n", + " statistics - Show or reset statistics\n" + " monitor - Show or set link supervision\n", cmdl->argv[0]); } @@ -513,6 +928,7 @@ { "list", cmd_link_list, NULL }, { "set", cmd_link_set, cmd_link_set_help }, { "statistics", cmd_link_stat, cmd_link_stat_help }, + { "monitor", cmd_link_mon, cmd_link_mon_help }, { NULL } }; diff -Nru iproute2-4.3.0/tipc/Makefile iproute2-4.9.0/tipc/Makefile --- iproute2-4.3.0/tipc/Makefile 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tipc/Makefile 2016-12-12 23:07:42.000000000 +0000 @@ -6,7 +6,7 @@ media.o misc.o \ msg.o nametable.o \ node.o socket.o \ - tipc.o + peer.o tipc.o include ../Config @@ -19,6 +19,7 @@ all: $(TARGETS) $(LIBS) tipc: $(TIPCOBJ) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ install: all install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR) diff -Nru iproute2-4.3.0/tipc/media.c iproute2-4.9.0/tipc/media.c --- iproute2-4.3.0/tipc/media.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tipc/media.c 2016-12-12 23:07:42.000000000 +0000 @@ -93,7 +93,7 @@ struct nlattr *nest; struct opt *opt; struct opt opts[] = { - { "media", NULL }, + { "media", OPT_KEYVAL, NULL }, { NULL } }; @@ -173,7 +173,7 @@ struct nlattr *attrs; struct opt *opt; struct opt opts[] = { - { "media", NULL }, + { "media", OPT_KEYVAL, NULL }, { NULL } }; diff -Nru iproute2-4.3.0/tipc/node.c iproute2-4.9.0/tipc/node.c --- iproute2-4.3.0/tipc/node.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tipc/node.c 2016-12-12 23:07:42.000000000 +0000 @@ -245,7 +245,7 @@ void cmd_node_help(struct cmdl *cmdl) { fprintf(stderr, - "Usage: %s media COMMAND [ARGS] ...\n\n" + "Usage: %s node COMMAND [ARGS] ...\n\n" "COMMANDS\n" " list - List remote nodes\n" " get - Get local node parameters\n" diff -Nru iproute2-4.3.0/tipc/peer.c iproute2-4.9.0/tipc/peer.c --- iproute2-4.3.0/tipc/peer.c 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/tipc/peer.c 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * peer.c TIPC peer functionality. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Richard Alpe + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cmdl.h" +#include "msg.h" +#include "misc.h" +#include "peer.h" + +static int cmd_peer_rm_addr(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char *str; + uint32_t addr; + struct nlattr *nest; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if ((cmdl->argc != cmdl->optind + 1) || help_flag) { + fprintf(stderr, "Usage: %s peer remove address ADDRESS\n", + cmdl->argv[0]); + return -EINVAL; + } + + str = shift_cmdl(cmdl); + addr = str2addr(str); + if (!addr) + return -1; + + if (!(nlh = msg_init(buf, TIPC_NL_PEER_REMOVE))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET); + mnl_attr_put_u32(nlh, TIPC_NLA_NET_ADDR, addr); + mnl_attr_nest_end(nlh, nest); + + return msg_doit(nlh, NULL, NULL); +} + +static void cmd_peer_rm_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s peer remove address ADDRESS\n", + cmdl->argv[0]); +} + +static int cmd_peer_rm(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "address", cmd_peer_rm_addr, cmd_peer_rm_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +void cmd_peer_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s peer COMMAND [ARGS] ...\n\n" + "COMMANDS\n" + " remove - Remove an offline peer node\n", + cmdl->argv[0]); +} + +int cmd_peer(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data) +{ + const struct cmd cmds[] = { + { "remove", cmd_peer_rm, cmd_peer_rm_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} diff -Nru iproute2-4.3.0/tipc/peer.h iproute2-4.9.0/tipc/peer.h --- iproute2-4.3.0/tipc/peer.h 1970-01-01 00:00:00.000000000 +0000 +++ iproute2-4.9.0/tipc/peer.h 2016-12-12 23:07:42.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * peer.h TIPC peer functionality. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Richard Alpe + */ + +#ifndef _TIPC_PEER_H +#define _TIPC_PEER_H + +extern int help_flag; + +int cmd_peer(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data); +void cmd_peer_help(struct cmdl *cmdl); + +#endif diff -Nru iproute2-4.3.0/tipc/tipc.c iproute2-4.9.0/tipc/tipc.c --- iproute2-4.3.0/tipc/tipc.c 2015-11-04 00:34:46.000000000 +0000 +++ iproute2-4.9.0/tipc/tipc.c 2016-12-12 23:07:42.000000000 +0000 @@ -20,6 +20,7 @@ #include "socket.h" #include "media.h" #include "node.h" +#include "peer.h" #include "cmdl.h" int help_flag; @@ -39,6 +40,7 @@ " media - Show or modify media\n" " nametable - Show nametable\n" " node - Show or modify node related parameters\n" + " peer - Peer related operations\n" " socket - Show sockets\n", cmdl->argv[0]); } @@ -59,6 +61,7 @@ { "media", cmd_media, cmd_media_help}, { "nametable", cmd_nametable, cmd_nametable_help}, { "node", cmd_node, cmd_node_help}, + { "peer", cmd_peer, cmd_peer_help}, { "socket", cmd_socket, cmd_socket_help}, { NULL } };