Commit dee1efb3 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'tools-ynl-fill-in-some-gaps-of-ethtool-spec'

Stanislav Fomichev says:

====================
tools: ynl: fill in some gaps of ethtool spec

I was trying to fill in the spec while exploring ethtool API for some
related work. I don't think I'll have the patience to fill in the rest,
so decided to share whatever I currently have.

Patches 1-2 add the be16 + spec.
Patches 3-4 implement an ethtool-like python tool to test the spec.

Patches 3-4 are there because it felt more fun do the tool instead
of writing the actual tests; feel free to drop it; sharing mostly
to show that the spec is not a complete nonsense.

The spec is not 100% complete, see patch 2 for what's missing.
I was hoping to finish the stats-get message, but I'm too dump
to implement bitmask marshaling (multi-attr).
====================

Link: https://lore.kernel.org/r/20230329221655.708489-1-sdf@google.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 709d0b88 f3d07b02
......@@ -6,6 +6,12 @@ protocol: genetlink-legacy
doc: Partial family for Ethtool Netlink.
definitions:
-
name: udp-tunnel-type
type: enum
entries: [ vxlan, geneve, vxlan-gpe ]
attribute-sets:
-
name: header
......@@ -38,6 +44,7 @@ attribute-sets:
-
name: bit
type: nest
multi-attr: true
nested-attributes: bitset-bit
-
name: bitset
......@@ -53,6 +60,22 @@ attribute-sets:
type: nest
nested-attributes: bitset-bits
-
name: u64-array
attributes:
-
name: u64
type: nest
multi-attr: true
nested-attributes: u64
-
name: s32-array
attributes:
-
name: s32
type: nest
multi-attr: true
nested-attributes: s32
-
name: string
attributes:
......@@ -234,118 +257,1351 @@ attribute-sets:
name: stats
type: nest
nested-attributes: mm-stat
operations:
enum-model: directional
list:
-
name: strset-get
doc: Get string set from the kernel.
attribute-set: strset
do: &strset-get-op
request:
name: linkinfo
attributes:
- header
- stringsets
- counts-only
reply:
-
name: header
type: nest
nested-attributes: header
-
name: port
type: u8
-
name: phyaddr
type: u8
-
name: tp-mdix
type: u8
-
name: tp-mdix-ctrl
type: u8
-
name: transceiver
type: u8
-
name: linkmodes
attributes:
- header
- stringsets
dump: *strset-get-op
# TODO: fill in the requests in between
-
name: privflags-get
doc: Get device private flags.
attribute-set: privflags
do: &privflag-get-op
request:
value: 13
name: header
type: nest
nested-attributes: header
-
name: autoneg
type: u8
-
name: ours
type: nest
nested-attributes: bitset
-
name: peer
type: nest
nested-attributes: bitset
-
name: speed
type: u32
-
name: duplex
type: u8
-
name: master-slave-cfg
type: u8
-
name: master-slave-state
type: u8
-
name: master-slave-lanes
type: u32
-
name: rate-matching
type: u8
-
name: linkstate
attributes:
- header
reply:
value: 14
-
name: header
type: nest
nested-attributes: header
-
name: link
type: u8
-
name: sqi
type: u32
-
name: sqi-max
type: u32
-
name: ext-state
type: u8
-
name: ext-substate
type: u8
-
name: down-cnt
type: u32
-
name: debug
attributes:
- header
- flags
dump: *privflag-get-op
-
name: privflags-set
doc: Set device private flags.
attribute-set: privflags
do:
request:
name: header
type: nest
nested-attributes: header
-
name: msgmask
type: nest
nested-attributes: bitset
-
name: wol
attributes:
- header
- flags
-
name: privflags-ntf
doc: Notification for change in device private flags.
notify: privflags-get
name: header
type: nest
nested-attributes: header
-
name: rings-get
doc: Get ring params.
attribute-set: rings
do: &ring-get-op
request:
name: modes
type: nest
nested-attributes: bitset
-
name: sopass
type: binary
-
name: features
attributes:
- header
reply:
-
name: header
type: nest
nested-attributes: header
-
name: hw
type: nest
nested-attributes: bitset
-
name: wanted
type: nest
nested-attributes: bitset
-
name: active
type: nest
nested-attributes: bitset
-
name: nochange
type: nest
nested-attributes: bitset
-
name: channels
attributes:
- header
- rx-max
- rx-mini-max
- rx-jumbo-max
- tx-max
- rx
- rx-mini
- rx-jumbo
- tx
- rx-buf-len
- tcp-data-split
- cqe-size
- tx-push
- rx-push
- tx-push-buf-len
- tx-push-buf-len-max
dump: *ring-get-op
-
name: rings-set
doc: Set ring params.
attribute-set: rings
name: header
type: nest
nested-attributes: header
-
name: rx-max
type: u32
-
name: tx-max
type: u32
-
name: other-max
type: u32
-
name: combined-max
type: u32
-
name: rx-count
type: u32
-
name: tx-count
type: u32
-
name: other-count
type: u32
-
name: combined-count
type: u32
do:
request:
-
name: coalesce
attributes:
- header
- rx
- rx-mini
- rx-jumbo
- tx
- rx-buf-len
- tcp-data-split
- cqe-size
- tx-push
- rx-push
-
name: rings-ntf
doc: Notification for change in ring params.
notify: rings-get
# TODO: fill in the requests in between
name: header
type: nest
nested-attributes: header
-
name: rx-usecs
type: u32
-
name: rx-max-frames
type: u32
-
name: rx-usecs-irq
type: u32
-
name: rx-max-frames-irq
type: u32
-
name: tx-usecs
type: u32
-
name: tx-max-frames
type: u32
-
name: tx-usecs-irq
type: u32
-
name: tx-max-frames-irq
type: u32
-
name: stats-block-usecs
type: u32
-
name: use-adaptive-rx
type: u8
-
name: use-adaptive-tx
type: u8
-
name: pkt-rate-low
type: u32
-
name: rx-usecs-low
type: u32
-
name: rx-max-frames-low
type: u32
-
name: tx-usecs-low
type: u32
-
name: tx-max-frames-low
type: u32
-
name: pkt-rate-high
type: u32
-
name: rx-usecs-high
type: u32
-
name: rx-max-frames-high
type: u32
-
name: tx-usecs-high
type: u32
-
name: tx-max-frames-high
type: u32
-
name: rate-sample-interval
type: u32
-
name: use-cqe-mode-tx
type: u8
-
name: use-cqe-mode-rx
type: u8
-
name: tx-aggr-max-bytes
type: u32
-
name: tx-aggr-max-frames
type: u32
-
name: tx-aggr-time-usecs
type: u32
-
name: pause-stat
attributes:
-
name: pad
type: u32
-
name: tx-frames
type: u64
-
name: rx-frames
type: u64
-
name: pause
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: autoneg
type: u8
-
name: rx
type: u8
-
name: tx
type: u8
-
name: stats
type: nest
nested-attributes: pause-stat
-
name: stats-src
type: u32
-
name: eee
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: modes-ours
type: nest
nested-attributes: bitset
-
name: modes-peer
type: nest
nested-attributes: bitset
-
name: active
type: u8
-
name: enabled
type: u8
-
name: tx-lpi-enabled
type: u8
-
name: tx-lpi-timer
type: u32
-
name: tsinfo
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: timestamping
type: nest
nested-attributes: bitset
-
name: tx-types
type: nest
nested-attributes: bitset
-
name: rx-filters
type: nest
nested-attributes: bitset
-
name: phc-index
type: u32
-
name: cable-test-nft-nest-result
attributes:
-
name: pair
type: u8
-
name: code
type: u8
-
name: cable-test-nft-nest-fault-length
attributes:
-
name: pair
type: u8
-
name: cm
type: u32
-
name: cable-test-nft-nest
attributes:
-
name: result
type: nest
nested-attributes: cable-test-nft-nest-result
-
name: fault-length
type: nest
nested-attributes: cable-test-nft-nest-fault-length
-
name: cable-test
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: status
type: u8
-
name: nest
type: nest
nested-attributes: cable-test-nft-nest
-
name: cable-test-tdr-cfg
attributes:
-
name: first
type: u32
-
name: last
type: u32
-
name: step
type: u32
-
name: pari
type: u8
-
name: cable-test-tdr
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: cfg
type: nest
nested-attributes: cable-test-tdr-cfg
-
name: tunnel-info-udp-entry
attributes:
-
name: port
type: u16
byte-order: big-endian
-
name: type
type: u32
enum: udp-tunnel-type
-
name: tunnel-info-udp-table
attributes:
-
name: size
type: u32
-
name: types
type: nest
nested-attributes: bitset
-
name: udp-ports
type: nest
nested-attributes: tunnel-info-udp-entry
-
name: tunnel-info
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: udp-ports
type: nest
nested-attributes: tunnel-info-udp-table
-
name: fec-stat
attributes:
-
name: pad
type: u8
-
name: corrected
type: nest
nested-attributes: u64-array
-
name: uncorr
type: nest
nested-attributes: u64-array
-
name: corr-bits
type: nest
nested-attributes: u64-array
-
name: fec
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: modes
type: nest
nested-attributes: bitset
-
name: auto
type: u8
-
name: active
type: u32
-
name: stats
type: nest
nested-attributes: fec-stat
-
name: module-eeprom
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: offset
type: u32
-
name: length
type: u32
-
name: page
type: u8
-
name: bank
type: u8
-
name: i2c-address
type: u8
-
name: data
type: binary
-
name: stats-grp
attributes:
-
name: pad
type: u32
-
name: id
type: u32
-
name: ss-id
type: u32
-
name: stat
type: nest
nested-attributes: u64
-
name: hist-rx
type: nest
nested-attributes: u64
-
name: hist-tx
type: nest
nested-attributes: u64
-
name: hist-bkt-low
type: u32
-
name: hist-bkt-hi
type: u32
-
name: hist-bkt-val
type: u64
-
name: stats
attributes:
-
name: pad
type: u32
-
name: header
type: nest
nested-attributes: header
-
name: groups
type: nest
nested-attributes: bitset
-
name: grp
type: nest
nested-attributes: stats-grp
-
name: src
type: u32
-
name: phc-vclocks
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: num
type: u32
-
name: index
type: nest
nested-attributes: s32-array
-
name: module
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: power-mode-policy
type: u8
-
name: power-mode
type: u8
-
name: pse
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: admin-state
type: u32
-
name: admin-control
type: u32
-
name: pw-d-status
type: u32
-
name: rss
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: context
type: u32
-
name: hfunc
type: u32
-
name: indir
type: binary
-
name: hkey
type: binary
-
name: plca
attributes:
-
name: header
type: nest
nested-attributes: header
-
name: version
type: u16
-
name: enabled
type: u8
-
name: status
type: u8
-
name: node-cnt
type: u32
-
name: node-id
type: u32
-
name: to-tmr
type: u32
-
name: burst-cnt
type: u32
-
name: burst-tmr
type: u32
operations:
enum-model: directional
list:
-
name: strset-get
doc: Get string set from the kernel.
attribute-set: strset
do: &strset-get-op
request:
attributes:
- header
- stringsets
- counts-only
reply:
attributes:
- header
- stringsets
dump: *strset-get-op
-
name: linkinfo-get
doc: Get link info.
attribute-set: linkinfo
do: &linkinfo-get-op
request:
attributes:
- header
reply:
attributes: &linkinfo
- header
- port
- phyaddr
- tp-mdix
- tp-mdix-ctrl
- transceiver
dump: *linkinfo-get-op
-
name: linkinfo-set
doc: Set link info.
attribute-set: linkinfo
do:
request:
attributes: *linkinfo
-
name: linkinfo-ntf
doc: Notification for change in link info.
notify: linkinfo-get
-
name: linkmodes-get
doc: Get link modes.
attribute-set: linkmodes
do: &linkmodes-get-op
request:
attributes:
- header
reply:
attributes: &linkmodes
- header
- autoneg
- ours
- peer
- speed
- duplex
- master-slave-cfg
- master-slave-state
- master-slave-lanes
- rate-matching
dump: *linkmodes-get-op
-
name: linkmodes-set
doc: Set link modes.
attribute-set: linkmodes
do:
request:
attributes: *linkmodes
-
name: linkmodes-ntf
doc: Notification for change in link modes.
notify: linkmodes-get
-
name: linkstate-get
doc: Get link state.
attribute-set: linkstate
do: &linkstate-get-op
request:
attributes:
- header
reply:
attributes:
- header
- link
- sqi
- sqi-max
- ext-state
- ext-substate
- down-cnt
dump: *linkstate-get-op
-
name: debug-get
doc: Get debug message mask.
attribute-set: debug
do: &debug-get-op
request:
attributes:
- header
reply:
attributes: &debug
- header
- msgmask
dump: *debug-get-op
-
name: debug-set
doc: Set debug message mask.
attribute-set: debug
do:
request:
attributes: *debug
-
name: debug-ntf
doc: Notification for change in debug message mask.
notify: debug-get
-
name: wol-get
doc: Get WOL params.
attribute-set: wol
do: &wol-get-op
request:
attributes:
- header
reply:
attributes: &wol
- header
- modes
- sopass
dump: *wol-get-op
-
name: wol-set
doc: Set WOL params.
attribute-set: wol
do:
request:
attributes: *wol
-
name: wol-ntf
doc: Notification for change in WOL params.
notify: wol-get
-
name: features-get
doc: Get features.
attribute-set: features
do: &feature-get-op
request:
attributes:
- header
reply:
attributes: &feature
- header
# User-changeable features.
- hw
# User-requested features.
- wanted
# Currently active features.
- active
# Unchangeable features.
- nochange
dump: *feature-get-op
-
name: features-set
doc: Set features.
attribute-set: features
do: &feature-set-op
request:
attributes: *feature
reply:
attributes: *feature
-
name: features-ntf
doc: Notification for change in features.
notify: features-get
-
name: privflags-get
doc: Get device private flags.
attribute-set: privflags
do: &privflag-get-op
request:
attributes:
- header
reply:
attributes: &privflag
- header
- flags
dump: *privflag-get-op
-
name: privflags-set
doc: Set device private flags.
attribute-set: privflags
do:
request:
attributes: *privflag
-
name: privflags-ntf
doc: Notification for change in device private flags.
notify: privflags-get
-
name: rings-get
doc: Get ring params.
attribute-set: rings
do: &ring-get-op
request:
attributes:
- header
reply:
attributes: &ring
- header
- rx-max
- rx-mini-max
- rx-jumbo-max
- tx-max
- rx
- rx-mini
- rx-jumbo
- tx
- rx-buf-len
- tcp-data-split
- cqe-size
- tx-push
- rx-push
- tx-push-buf-len
- tx-push-buf-len-max
dump: *ring-get-op
-
name: rings-set
doc: Set ring params.
attribute-set: rings
do:
request:
attributes: *ring
-
name: rings-ntf
doc: Notification for change in ring params.
notify: rings-get
-
name: channels-get
doc: Get channel params.
attribute-set: channels
do: &channel-get-op
request:
attributes:
- header
reply:
attributes: &channel
- header
- rx-max
- tx-max
- other-max
- combined-max
- rx-count
- tx-count
- other-count
- combined-count
dump: *channel-get-op
-
name: channels-set
doc: Set channel params.
attribute-set: channels
do:
request:
attributes: *channel
-
name: channels-ntf
doc: Notification for change in channel params.
notify: channels-get
-
name: coalesce-get
doc: Get coalesce params.
attribute-set: coalesce
do: &coalesce-get-op
request:
attributes:
- header
reply:
attributes: &coalesce
- header
- rx-usecs
- rx-max-frames
- rx-usecs-irq
- rx-max-frames-irq
- tx-usecs
- tx-max-frames
- tx-usecs-irq
- tx-max-frames-irq
- stats-block-usecs
- use-adaptive-rx
- use-adaptive-tx
- pkt-rate-low
- rx-usecs-low
- rx-max-frames-low
- tx-usecs-low
- tx-max-frames-low
- pkt-rate-high
- rx-usecs-high
- rx-max-frames-high
- tx-usecs-high
- tx-max-frames-high
- rate-sample-interval
- use-cqe-mode-tx
- use-cqe-mode-rx
- tx-aggr-max-bytes
- tx-aggr-max-frames
- tx-aggr-time-usecs
dump: *coalesce-get-op
-
name: coalesce-set
doc: Set coalesce params.
attribute-set: coalesce
do:
request:
attributes: *coalesce
-
name: coalesce-ntf
doc: Notification for change in coalesce params.
notify: coalesce-get
-
name: pause-get
doc: Get pause params.
attribute-set: pause
do: &pause-get-op
request:
attributes:
- header
reply:
attributes: &pause
- header
- autoneg
- rx
- tx
- stats
- stats-src
dump: *pause-get-op
-
name: pause-set
doc: Set pause params.
attribute-set: pause
do:
request:
attributes: *pause
-
name: pause-ntf
doc: Notification for change in pause params.
notify: pause-get
-
name: eee-get
doc: Get eee params.
attribute-set: eee
do: &eee-get-op
request:
attributes:
- header
reply:
attributes: &eee
- header
- modes-ours
- modes-peer
- active
- enabled
- tx-lpi-enabled
- tx-lpi-timer
dump: *eee-get-op
-
name: eee-set
doc: Set eee params.
attribute-set: eee
do:
request:
attributes: *eee
-
name: eee-ntf
doc: Notification for change in eee params.
notify: eee-get
-
name: tsinfo-get
doc: Get tsinfo params.
attribute-set: tsinfo
do: &tsinfo-get-op
request:
attributes:
- header
reply:
attributes:
- header
- timestamping
- tx-types
- rx-filters
- phc-index
dump: *tsinfo-get-op
-
name: cable-test-act
doc: Cable test.
attribute-set: cable-test
do:
request:
attributes:
- header
reply:
attributes:
- header
- cable-test-nft-nest
-
name: cable-test-tdr-act
doc: Cable test TDR.
attribute-set: cable-test-tdr
do:
request:
attributes:
- header
reply:
attributes:
- header
- cable-test-tdr-cfg
-
name: tunnel-info-get
doc: Get tsinfo params.
attribute-set: tunnel-info
do: &tunnel-info-get-op
request:
attributes:
- header
reply:
attributes:
- header
- udp-ports
dump: *tunnel-info-get-op
-
name: fec-get
doc: Get FEC params.
attribute-set: fec
do: &fec-get-op
request:
attributes:
- header
reply:
attributes: &fec
- header
- modes
- auto
- active
- stats
dump: *fec-get-op
-
name: fec-set
doc: Set FEC params.
attribute-set: fec
do:
request:
attributes: *fec
-
name: fec-ntf
doc: Notification for change in FEC params.
notify: fec-get
-
name: module-eeprom-get
doc: Get module EEPROM params.
attribute-set: module-eeprom
do: &module-eeprom-get-op
request:
attributes:
- header
reply:
attributes:
- header
- offset
- length
- page
- bank
- i2c-address
- data
dump: *module-eeprom-get-op
-
name: stats-get
doc: Get statistics.
attribute-set: stats
do: &stats-get-op
request:
attributes:
- header
- groups
reply:
attributes:
- header
- groups
- grp
- src
dump: *stats-get-op
-
name: phc-vclocks-get
doc: Get PHC VCLOCKs.
attribute-set: phc-vclocks
do: &phc-vclocks-get-op
request:
attributes:
- header
reply:
attributes:
- header
- num
dump: *phc-vclocks-get-op
-
name: module-get
doc: Get module params.
attribute-set: module
do: &module-get-op
request:
attributes:
- header
reply:
attributes: &module
- header
- power-mode-policy
- power-mode
dump: *module-get-op
-
name: module-set
doc: Set module params.
attribute-set: module
do:
request:
attributes: *module
-
name: module-ntf
doc: Notification for change in module params.
notify: module-get
-
name: pse-get
doc: Get Power Sourcing Equipment params.
attribute-set: pse
do: &pse-get-op
request:
attributes:
- header
reply:
attributes: &pse
- header
- admin-state
- admin-control
- pw-d-status
dump: *pse-get-op
-
name: pse-set
doc: Set Power Sourcing Equipment params.
attribute-set: pse
do:
request:
attributes: *pse
-
name: rss-get
doc: Get RSS params.
attribute-set: rss
do: &rss-get-op
request:
attributes:
- header
reply:
attributes:
- header
- context
- hfunc
- indir
- hkey
dump: *rss-get-op
-
name: plca-get
doc: Get PLCA params.
attribute-set: plca
do: &plca-get-op
request:
attributes:
- header
reply:
attributes: &plca
- header
- version
- enabled
- status
- node-cnt
- node-id
- to-tmr
- burst-cnt
- burst-tmr
dump: *plca-get-op
-
name: plca-set
doc: Set PLCA params.
attribute-set: plca
do:
request:
attributes: *plca
-
name: plca-get-status
doc: Get PLCA status params.
attribute-set: plca
do: &plca-get-status-op
request:
attributes:
- header
reply:
attributes: *plca
dump: *plca-get-status-op
-
name: plca-ntf
doc: Notification for change in PLCA params.
notify: plca-get
-
name: mm-get
doc: Get MAC Merge configuration and state
......@@ -354,11 +1610,9 @@ operations:
do: &mm-get-op
request:
value: 42
attributes:
- header
reply:
value: 42
attributes:
- header
- pmac-enabled
......
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
import argparse
import json
import pprint
import sys
import re
from lib import YnlFamily
def args_to_req(ynl, op_name, args, req):
"""
Verify and convert command-line arguments to the ynl-compatible request.
"""
valid_attrs = ynl.operation_do_attributes(op_name)
valid_attrs.remove('header') # not user-provided
if len(args) == 0:
print(f'no attributes, expected: {valid_attrs}')
sys.exit(1)
i = 0
while i < len(args):
attr = args[i]
if i + 1 >= len(args):
print(f'expected value for \'{attr}\'')
sys.exit(1)
if attr not in valid_attrs:
print(f'invalid attribute \'{attr}\', expected: {valid_attrs}')
sys.exit(1)
val = args[i+1]
i += 2
req[attr] = val
def print_field(reply, *desc):
"""
Pretty-print a set of fields from the reply. desc specifies the
fields and the optional type (bool/yn).
"""
if len(desc) == 0:
return print_field(reply, *zip(reply.keys(), reply.keys()))
for spec in desc:
try:
field, name, tp = spec
except:
field, name = spec
tp = 'int'
value = reply.get(field, None)
if tp == 'yn':
value = 'yes' if value else 'no'
elif tp == 'bool' or isinstance(value, bool):
value = 'on' if value else 'off'
else:
value = 'n/a' if value is None else value
print(f'{name}: {value}')
def print_speed(name, value):
"""
Print out the speed-like strings from the value dict.
"""
speed_re = re.compile(r'[0-9]+base[^/]+/.+')
speed = [ k for k, v in value.items() if v and speed_re.match(k) ]
print(f'{name}: {" ".join(speed)}')
def doit(ynl, args, op_name):
"""
Prepare request header, parse arguments and doit.
"""
req = {
'header': {
'dev-name': args.device,
},
}
args_to_req(ynl, op_name, args.args, req)
ynl.do(op_name, req)
def dumpit(ynl, args, op_name, extra = {}):
"""
Prepare request header, parse arguments and dumpit (filtering out the
devices we're not interested in).
"""
reply = ynl.dump(op_name, { 'header': {} } | extra)
if not reply:
return {}
for msg in reply:
if msg['header']['dev-name'] == args.device:
if args.json:
pprint.PrettyPrinter().pprint(msg)
sys.exit(0)
msg.pop('header', None)
return msg
print(f"Not supported for device {args.device}")
sys.exit(1)
def bits_to_dict(attr):
"""
Convert ynl-formatted bitmask to a dict of bit=value.
"""
ret = {}
if 'bits' not in attr:
return dict()
if 'bit' not in attr['bits']:
return dict()
for bit in attr['bits']['bit']:
if bit['name'] == '':
continue
name = bit['name']
value = bit.get('value', False)
ret[name] = value
return ret
def main():
parser = argparse.ArgumentParser(description='ethtool wannabe')
parser.add_argument('--json', action=argparse.BooleanOptionalAction)
parser.add_argument('--show-priv-flags', action=argparse.BooleanOptionalAction)
parser.add_argument('--set-priv-flags', action=argparse.BooleanOptionalAction)
parser.add_argument('--show-eee', action=argparse.BooleanOptionalAction)
parser.add_argument('--set-eee', action=argparse.BooleanOptionalAction)
parser.add_argument('-a', '--show-pause', action=argparse.BooleanOptionalAction)
parser.add_argument('-A', '--set-pause', action=argparse.BooleanOptionalAction)
parser.add_argument('-c', '--show-coalesce', action=argparse.BooleanOptionalAction)
parser.add_argument('-C', '--set-coalesce', action=argparse.BooleanOptionalAction)
parser.add_argument('-g', '--show-ring', action=argparse.BooleanOptionalAction)
parser.add_argument('-G', '--set-ring', action=argparse.BooleanOptionalAction)
parser.add_argument('-k', '--show-features', action=argparse.BooleanOptionalAction)
parser.add_argument('-K', '--set-features', action=argparse.BooleanOptionalAction)
parser.add_argument('-l', '--show-channels', action=argparse.BooleanOptionalAction)
parser.add_argument('-L', '--set-channels', action=argparse.BooleanOptionalAction)
parser.add_argument('-T', '--show-time-stamping', action=argparse.BooleanOptionalAction)
parser.add_argument('-S', '--statistics', action=argparse.BooleanOptionalAction)
# TODO: --show-tunnels tunnel-info-get
# TODO: --show-module module-get
# TODO: --get-plca-cfg plca-get
# TODO: --get-plca-status plca-get-status
# TODO: --show-mm mm-get
# TODO: --show-fec fec-get
# TODO: --dump-module-eerpom module-eeprom-get
# TODO: pse-get
# TODO: rss-get
parser.add_argument('device', metavar='device', type=str)
parser.add_argument('args', metavar='args', type=str, nargs='*')
global args
args = parser.parse_args()
spec = '/usr/local/google/home/sdf/src/linux/Documentation/netlink/specs/ethtool.yaml'
schema = '/usr/local/google/home/sdf/src/linux/Documentation/netlink/genetlink-legacy.yaml'
ynl = YnlFamily(spec, schema)
if args.set_priv_flags:
# TODO: parse the bitmask
print("not implemented")
return
if args.set_eee:
return doit(ynl, args, 'eee-set')
if args.set_pause:
return doit(ynl, args, 'pause-set')
if args.set_coalesce:
return doit(ynl, args, 'coalesce-set')
if args.set_features:
# TODO: parse the bitmask
print("not implemented")
return
if args.set_channels:
return doit(ynl, args, 'channels-set')
if args.set_ring:
return doit(ynl, args, 'rings-set')
if args.show_priv_flags:
flags = bits_to_dict(dumpit(ynl, args, 'privflags-get')['flags'])
print_field(flags)
return
if args.show_eee:
eee = dumpit(ynl, args, 'eee-get')
ours = bits_to_dict(eee['modes-ours'])
peer = bits_to_dict(eee['modes-peer'])
if 'enabled' in eee:
status = 'enabled' if eee['enabled'] else 'disabled'
if 'active' in eee and eee['active']:
status = status + ' - active'
else:
status = status + ' - inactive'
else:
status = 'not supported'
print(f'EEE status: {status}')
print_field(eee, ('tx-lpi-timer', 'Tx LPI'))
print_speed('Advertised EEE link modes', ours)
print_speed('Link partner advertised EEE link modes', peer)
return
if args.show_pause:
print_field(dumpit(ynl, args, 'pause-get'),
('autoneg', 'Autonegotiate', 'bool'),
('rx', 'RX', 'bool'),
('tx', 'TX', 'bool'))
return
if args.show_coalesce:
print_field(dumpit(ynl, args, 'coalesce-get'))
return
if args.show_features:
reply = dumpit(ynl, args, 'features-get')
available = bits_to_dict(reply['hw'])
requested = bits_to_dict(reply['wanted']).keys()
active = bits_to_dict(reply['active']).keys()
never_changed = bits_to_dict(reply['nochange']).keys()
for f in sorted(available):
value = "off"
if f in active:
value = "on"
fixed = ""
if f not in available or f in never_changed:
fixed = " [fixed]"
req = ""
if f in requested:
if f in active:
req = " [requested on]"
else:
req = " [requested off]"
print(f'{f}: {value}{fixed}{req}')
return
if args.show_channels:
reply = dumpit(ynl, args, 'channels-get')
print(f'Channel parameters for {args.device}:')
print(f'Pre-set maximums:')
print_field(reply,
('rx-max', 'RX'),
('tx-max', 'TX'),
('other-max', 'Other'),
('combined-max', 'Combined'))
print(f'Current hardware settings:')
print_field(reply,
('rx-count', 'RX'),
('tx-count', 'TX'),
('other-count', 'Other'),
('combined-count', 'Combined'))
return
if args.show_ring:
reply = dumpit(ynl, args, 'channels-get')
print(f'Ring parameters for {args.device}:')
print(f'Pre-set maximums:')
print_field(reply,
('rx-max', 'RX'),
('rx-mini-max', 'RX Mini'),
('rx-jumbo-max', 'RX Jumbo'),
('tx-max', 'TX'))
print(f'Current hardware settings:')
print_field(reply,
('rx', 'RX'),
('rx-mini', 'RX Mini'),
('rx-jumbo', 'RX Jumbo'),
('tx', 'TX'))
print_field(reply,
('rx-buf-len', 'RX Buf Len'),
('cqe-size', 'CQE Size'),
('tx-push', 'TX Push', 'bool'))
return
if args.statistics:
print(f'NIC statistics:')
# TODO: pass id?
strset = dumpit(ynl, args, 'strset-get')
pprint.PrettyPrinter().pprint(strset)
req = {
'groups': {
'size': 1,
'bits': {
'bit':
# TODO: support passing the bitmask
#[
#{ 'name': 'eth-phy', 'value': True },
{ 'name': 'eth-mac', 'value': True },
#{ 'name': 'eth-ctrl', 'value': True },
#{ 'name': 'rmon', 'value': True },
#],
},
},
}
rsp = dumpit(ynl, args, 'stats-get', req)
pprint.PrettyPrinter().pprint(rsp)
return
if args.show_time_stamping:
tsinfo = dumpit(ynl, args, 'tsinfo-get')
print(f'Time stamping parameters for {args.device}:')
print('Capabilities:')
[print(f'\t{v}') for v in bits_to_dict(tsinfo['timestamping'])]
print(f'PTP Hardware Clock: {tsinfo["phc-index"]}')
print('Hardware Transmit Timestamp Modes:')
[print(f'\t{v}') for v in bits_to_dict(tsinfo['tx-types'])]
print('Hardware Receive Filter Modes:')
[print(f'\t{v}') for v in bits_to_dict(tsinfo['rx-filters'])]
return
print(f'Settings for {args.device}:')
linkmodes = dumpit(ynl, args, 'linkmodes-get')
ours = bits_to_dict(linkmodes['ours'])
supported_ports = ('TP', 'AUI', 'BNC', 'MII', 'FIBRE', 'Backplane')
ports = [ p for p in supported_ports if ours.get(p, False)]
print(f'Supported ports: [ {" ".join(ports)} ]')
print_speed('Supported link modes', ours)
print_field(ours, ('Pause', 'Supported pause frame use', 'yn'))
print_field(ours, ('Autoneg', 'Supports auto-negotiation', 'yn'))
supported_fec = ('None', 'PS', 'BASER', 'LLRS')
fec = [ p for p in supported_fec if ours.get(p, False)]
fec_str = " ".join(fec)
if len(fec) == 0:
fec_str = "Not reported"
print(f'Supported FEC modes: {fec_str}')
speed = 'Unknown!'
if linkmodes['speed'] > 0 and linkmodes['speed'] < 0xffffffff:
speed = f'{linkmodes["speed"]}Mb/s'
print(f'Speed: {speed}')
duplex_modes = {
0: 'Half',
1: 'Full',
}
duplex = duplex_modes.get(linkmodes["duplex"], None)
if not duplex:
duplex = f'Unknown! ({linkmodes["duplex"]})'
print(f'Duplex: {duplex}')
autoneg = "off"
if linkmodes.get("autoneg", 0) != 0:
autoneg = "on"
print(f'Auto-negotiation: {autoneg}')
ports = {
0: 'Twisted Pair',
1: 'AUI',
2: 'MII',
3: 'FIBRE',
4: 'BNC',
5: 'Directly Attached Copper',
0xef: 'None',
}
linkinfo = dumpit(ynl, args, 'linkinfo-get')
print(f'Port: {ports.get(linkinfo["port"], "Other")}')
print_field(linkinfo, ('phyaddr', 'PHYAD'))
transceiver = {
0: 'Internal',
1: 'External',
}
print(f'Transceiver: {transceiver.get(linkinfo["transceiver"], "Unknown")}')
mdix_ctrl = {
1: 'off',
2: 'on',
}
mdix = mdix_ctrl.get(linkinfo['tp-mdix-ctrl'], None)
if mdix:
mdix = mdix + ' (forced)'
else:
mdix = mdix_ctrl.get(linkinfo['tp-mdix'], 'Unknown (auto)')
print(f'MDI-X: {mdix}')
debug = dumpit(ynl, args, 'debug-get')
msgmask = bits_to_dict(debug.get("msgmask", [])).keys()
print(f'Current message level: {" ".join(msgmask)}')
linkstate = dumpit(ynl, args, 'linkstate-get')
detected_states = {
0: 'no',
1: 'yes',
}
# TODO: wol-get
detected = detected_states.get(linkstate['link'], 'unknown')
print(f'Link detected: {detected}')
if __name__ == '__main__':
main()
......@@ -163,6 +163,7 @@ class SpecAttr(SpecElement):
self.is_multi = yaml.get('multi-attr', False)
self.struct_name = yaml.get('struct')
self.sub_type = yaml.get('sub-type')
self.byte_order = yaml.get('byte-order')
class SpecAttrSet(SpecElement):
......@@ -443,6 +444,15 @@ class SpecFamily(SpecElement):
self.msgs[op.name] = op
def find_operation(self, name):
"""
For a given operation name, find and return operation spec.
"""
for op in self.yaml['operations']['list']:
if name == op['name']:
return op
return None
def resolve(self):
self.resolve_up(super())
......
......@@ -67,6 +67,14 @@ class Netlink:
NLMSGERR_ATTR_MISS_NEST = 6
class NlError(Exception):
def __init__(self, nl_msg):
self.nl_msg = nl_msg
def __str__(self):
return f"Netlink error: {os.strerror(-self.nl_msg.error)}\n{self.nl_msg}"
class NlAttr:
type_formats = { 'u8' : ('B', 1), 's8' : ('b', 1),
'u16': ('H', 2), 's16': ('h', 2),
......@@ -80,17 +88,25 @@ class NlAttr:
self.full_len = (self.payload_len + 3) & ~3
self.raw = raw[offset + 4:offset + self.payload_len]
def format_byte_order(byte_order):
if byte_order:
return ">" if byte_order == "big-endian" else "<"
return ""
def as_u8(self):
return struct.unpack("B", self.raw)[0]
def as_u16(self):
return struct.unpack("H", self.raw)[0]
def as_u16(self, byte_order=None):
endian = NlAttr.format_byte_order(byte_order)
return struct.unpack(f"{endian}H", self.raw)[0]
def as_u32(self):
return struct.unpack("I", self.raw)[0]
def as_u32(self, byte_order=None):
endian = NlAttr.format_byte_order(byte_order)
return struct.unpack(f"{endian}I", self.raw)[0]
def as_u64(self):
return struct.unpack("Q", self.raw)[0]
def as_u64(self, byte_order=None):
endian = NlAttr.format_byte_order(byte_order)
return struct.unpack(f"{endian}Q", self.raw)[0]
def as_strz(self):
return self.raw.decode('ascii')[:-1]
......@@ -365,11 +381,14 @@ class YnlFamily(SpecFamily):
elif attr["type"] == 'u8':
attr_payload = struct.pack("B", int(value))
elif attr["type"] == 'u16':
attr_payload = struct.pack("H", int(value))
endian = NlAttr.format_byte_order(attr.byte_order)
attr_payload = struct.pack(f"{endian}H", int(value))
elif attr["type"] == 'u32':
attr_payload = struct.pack("I", int(value))
endian = NlAttr.format_byte_order(attr.byte_order)
attr_payload = struct.pack(f"{endian}I", int(value))
elif attr["type"] == 'u64':
attr_payload = struct.pack("Q", int(value))
endian = NlAttr.format_byte_order(attr.byte_order)
attr_payload = struct.pack(f"{endian}Q", int(value))
elif attr["type"] == 'string':
attr_payload = str(value).encode('ascii') + b'\x00'
elif attr["type"] == 'binary':
......@@ -415,11 +434,11 @@ class YnlFamily(SpecFamily):
elif attr_spec['type'] == 'u8':
decoded = attr.as_u8()
elif attr_spec['type'] == 'u16':
decoded = attr.as_u16()
decoded = attr.as_u16(attr_spec.byte_order)
elif attr_spec['type'] == 'u32':
decoded = attr.as_u32()
decoded = attr.as_u32(attr_spec.byte_order)
elif attr_spec['type'] == 'u64':
decoded = attr.as_u64()
decoded = attr.as_u64(attr_spec.byte_order)
elif attr_spec["type"] == 'string':
decoded = attr.as_strz()
elif attr_spec["type"] == 'binary':
......@@ -508,6 +527,17 @@ class YnlFamily(SpecFamily):
self.handle_ntf(nl_msg, gm)
def operation_do_attributes(self, name):
"""
For a given operation name, find and return a supported
set of attributes (as a dict).
"""
op = self.find_operation(name)
if not op:
return None
return op['do']['request']['attributes'].copy()
def _op(self, method, vals, dump=False):
op = self.ops[method]
......@@ -540,9 +570,7 @@ class YnlFamily(SpecFamily):
self._decode_extack(msg, op.attr_set, nl_msg.extack)
if nl_msg.error:
print("Netlink error:", os.strerror(-nl_msg.error))
print(nl_msg)
return
raise NlError(nl_msg)
if nl_msg.done:
if nl_msg.extack:
print("Netlink warning:")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment