1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4""" 5Tests for the nlctrl genetlink family (family info and policy dumps). 6""" 7 8from lib.py import ksft_run, ksft_exit 9from lib.py import ksft_eq, ksft_ge, ksft_true, ksft_in, ksft_not_in 10from lib.py import NetdevFamily, EthtoolFamily, NlctrlFamily 11 12 13def getfamily_do(ctrl) -> None: 14 """Query a single family by name and validate its ops.""" 15 fam = ctrl.getfamily({'family-name': 'netdev'}) 16 ksft_eq(fam['family-name'], 'netdev') 17 ksft_true(fam['family-id'] > 0) 18 19 # The format of ops is quite odd, [{$idx: {"id"...}}, {$idx: {"id"...}}] 20 # Discard the indices and re-key by command id. 21 ops_by_id = {v['id']: v for op in fam['ops'] for v in op.values()} 22 ksft_eq(len(ops_by_id), len(fam['ops'])) 23 24 # All ops should have a policy (either do or dump has one) 25 for op in ops_by_id.values(): 26 ksft_in('cmd-cap-haspol', op['flags'], 27 comment=f"op {op['id']} missing haspol") 28 29 # dev-get (id 1) should support both do and dump 30 ksft_in('cmd-cap-do', ops_by_id[1]['flags']) 31 ksft_in('cmd-cap-dump', ops_by_id[1]['flags']) 32 33 # qstats-get (id 12) is dump-only 34 ksft_not_in('cmd-cap-do', ops_by_id[12]['flags']) 35 ksft_in('cmd-cap-dump', ops_by_id[12]['flags']) 36 37 # napi-set (id 14) is do-only and requires admin 38 ksft_in('cmd-cap-do', ops_by_id[14]['flags']) 39 ksft_not_in('cmd-cap-dump', ops_by_id[14]['flags']) 40 ksft_in('admin-perm', ops_by_id[14]['flags']) 41 42 # Notification-only commands (dev-add/del/change-ntf etc.) must 43 # not appear in the ops list since they have no do/dump handlers. 44 for ntf_id in [2, 3, 4, 6, 7, 8]: 45 ksft_not_in(ntf_id, ops_by_id, 46 comment=f"ntf-only cmd {ntf_id} should not be in ops") 47 48 49def getfamily_dump(ctrl) -> None: 50 """Dump all families and verify expected entries.""" 51 families = ctrl.getfamily({}, dump=True) 52 ksft_ge(len(families), 2) 53 54 names = [f['family-name'] for f in families] 55 ksft_in('nlctrl', names, comment="nlctrl not found in family dump") 56 ksft_in('netdev', names, comment="netdev not found in family dump") 57 58 59def getpolicy_dump(_ctrl) -> None: 60 """Dump policies for ops using get_policy() and validate results. 61 62 Test with netdev (split ops) where do and dump can have different 63 policies, and with ethtool (full ops) where they always share one. 64 """ 65 # -- netdev (split ops) -- 66 ndev = NetdevFamily() 67 68 # dev-get: do has a real policy with ifindex, dump has no policy 69 # (only the reject-all policy with maxattr=0) 70 pol = ndev.get_policy('dev-get', 'do') 71 ksft_in('ifindex', pol, comment="dev-get do policy should have ifindex") 72 ksft_eq(pol['ifindex'].type, 'u32') 73 74 pol_dump = ndev.get_policy('dev-get', 'dump') 75 ksft_eq(len(pol_dump), 0, comment="dev-get should not accept any attrs") 76 77 # napi-get: both do and dump have real policies 78 pol_do = ndev.get_policy('napi-get', 'do') 79 ksft_ge(len(pol_do), 1) 80 81 pol_dump = ndev.get_policy('napi-get', 'dump') 82 ksft_ge(len(pol_dump), 1) 83 84 # -- ethtool (full ops) -- 85 et = EthtoolFamily() 86 87 # strset-get (has both do and dump, full ops share policy) 88 pol_do = et.get_policy('strset-get', 'do') 89 ksft_ge(len(pol_do), 1, comment="strset-get should have a do policy") 90 91 pol_dump = et.get_policy('strset-get', 'dump') 92 ksft_ge(len(pol_dump), 1, comment="strset-get should have a dump policy") 93 94 # Same policy means same attribute names 95 ksft_eq(set(pol_do.keys()), set(pol_dump.keys())) 96 97 # linkinfo-set is do-only (SET command), no dump 98 pol_do = et.get_policy('linkinfo-set', 'do') 99 ksft_ge(len(pol_do), 1, comment="linkinfo-set should have a do policy") 100 101 pol_dump = et.get_policy('linkinfo-set', 'dump') 102 ksft_eq(pol_dump, None, 103 comment="linkinfo-set should not have a dump policy") 104 105 106def getpolicy_by_op(_ctrl) -> None: 107 """Query policy for specific ops, check attr names are resolved.""" 108 ndev = NetdevFamily() 109 110 # dev-get do policy should have named attributes from the spec 111 pol = ndev.get_policy('dev-get', 'do') 112 ksft_ge(len(pol), 1) 113 # All attr names should be resolved (no 'attr-N' fallbacks) 114 for name in pol: 115 ksft_true(not name.startswith('attr-'), 116 comment=f"unresolved attr name: {name}") 117 118 119def main() -> None: 120 """ Ksft boiler plate main """ 121 ctrl = NlctrlFamily() 122 ksft_run([getfamily_do, 123 getfamily_dump, 124 getpolicy_dump, 125 getpolicy_by_op], 126 args=(ctrl, )) 127 ksft_exit() 128 129 130if __name__ == "__main__": 131 main() 132