#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 """ Tests for the nlctrl genetlink family (family info and policy dumps). """ from lib.py import ksft_run, ksft_exit from lib.py import ksft_eq, ksft_ge, ksft_true, ksft_in, ksft_not_in from lib.py import NetdevFamily, EthtoolFamily, NlctrlFamily def getfamily_do(ctrl) -> None: """Query a single family by name and validate its ops.""" fam = ctrl.getfamily({'family-name': 'netdev'}) ksft_eq(fam['family-name'], 'netdev') ksft_true(fam['family-id'] > 0) # The format of ops is quite odd, [{$idx: {"id"...}}, {$idx: {"id"...}}] # Discard the indices and re-key by command id. ops_by_id = {v['id']: v for op in fam['ops'] for v in op.values()} ksft_eq(len(ops_by_id), len(fam['ops'])) # All ops should have a policy (either do or dump has one) for op in ops_by_id.values(): ksft_in('cmd-cap-haspol', op['flags'], comment=f"op {op['id']} missing haspol") # dev-get (id 1) should support both do and dump ksft_in('cmd-cap-do', ops_by_id[1]['flags']) ksft_in('cmd-cap-dump', ops_by_id[1]['flags']) # qstats-get (id 12) is dump-only ksft_not_in('cmd-cap-do', ops_by_id[12]['flags']) ksft_in('cmd-cap-dump', ops_by_id[12]['flags']) # napi-set (id 14) is do-only and requires admin ksft_in('cmd-cap-do', ops_by_id[14]['flags']) ksft_not_in('cmd-cap-dump', ops_by_id[14]['flags']) ksft_in('admin-perm', ops_by_id[14]['flags']) # Notification-only commands (dev-add/del/change-ntf etc.) must # not appear in the ops list since they have no do/dump handlers. for ntf_id in [2, 3, 4, 6, 7, 8]: ksft_not_in(ntf_id, ops_by_id, comment=f"ntf-only cmd {ntf_id} should not be in ops") def getfamily_dump(ctrl) -> None: """Dump all families and verify expected entries.""" families = ctrl.getfamily({}, dump=True) ksft_ge(len(families), 2) names = [f['family-name'] for f in families] ksft_in('nlctrl', names, comment="nlctrl not found in family dump") ksft_in('netdev', names, comment="netdev not found in family dump") def getpolicy_dump(_ctrl) -> None: """Dump policies for ops using get_policy() and validate results. Test with netdev (split ops) where do and dump can have different policies, and with ethtool (full ops) where they always share one. """ # -- netdev (split ops) -- ndev = NetdevFamily() # dev-get: do has a real policy with ifindex, dump has no policy # (only the reject-all policy with maxattr=0) pol = ndev.get_policy('dev-get', 'do') ksft_in('ifindex', pol, comment="dev-get do policy should have ifindex") ksft_eq(pol['ifindex'].type, 'u32') pol_dump = ndev.get_policy('dev-get', 'dump') ksft_eq(len(pol_dump), 0, comment="dev-get should not accept any attrs") # napi-get: both do and dump have real policies pol_do = ndev.get_policy('napi-get', 'do') ksft_ge(len(pol_do), 1) pol_dump = ndev.get_policy('napi-get', 'dump') ksft_ge(len(pol_dump), 1) # -- ethtool (full ops) -- et = EthtoolFamily() # strset-get (has both do and dump, full ops share policy) pol_do = et.get_policy('strset-get', 'do') ksft_ge(len(pol_do), 1, comment="strset-get should have a do policy") pol_dump = et.get_policy('strset-get', 'dump') ksft_ge(len(pol_dump), 1, comment="strset-get should have a dump policy") # Same policy means same attribute names ksft_eq(set(pol_do.keys()), set(pol_dump.keys())) # linkinfo-set is do-only (SET command), no dump pol_do = et.get_policy('linkinfo-set', 'do') ksft_ge(len(pol_do), 1, comment="linkinfo-set should have a do policy") pol_dump = et.get_policy('linkinfo-set', 'dump') ksft_eq(pol_dump, None, comment="linkinfo-set should not have a dump policy") def getpolicy_by_op(_ctrl) -> None: """Query policy for specific ops, check attr names are resolved.""" ndev = NetdevFamily() # dev-get do policy should have named attributes from the spec pol = ndev.get_policy('dev-get', 'do') ksft_ge(len(pol), 1) # All attr names should be resolved (no 'attr-N' fallbacks) for name in pol: ksft_true(not name.startswith('attr-'), comment=f"unresolved attr name: {name}") def main() -> None: """ Ksft boiler plate main """ ctrl = NlctrlFamily() ksft_run([getfamily_do, getfamily_dump, getpolicy_dump, getpolicy_by_op], args=(ctrl, )) ksft_exit() if __name__ == "__main__": main()