xref: /freebsd-src/tests/atf_python/sys/netlink/netlink.py (revision 54b955f4df5e76b5679ba7f3eb6bb2d5fc62923d)
1fee65b7eSAlexander V. Chernikov#!/usr/local/bin/python3
2fee65b7eSAlexander V. Chernikovimport os
3fee65b7eSAlexander V. Chernikovimport socket
4fee65b7eSAlexander V. Chernikovimport sys
5fee65b7eSAlexander V. Chernikovfrom ctypes import c_int
6fee65b7eSAlexander V. Chernikovfrom ctypes import c_ubyte
7fee65b7eSAlexander V. Chernikovfrom ctypes import c_uint
8fee65b7eSAlexander V. Chernikovfrom ctypes import c_ushort
9fee65b7eSAlexander V. Chernikovfrom ctypes import sizeof
10fee65b7eSAlexander V. Chernikovfrom ctypes import Structure
11fee65b7eSAlexander V. Chernikovfrom enum import auto
12fee65b7eSAlexander V. Chernikovfrom enum import Enum
13fee65b7eSAlexander V. Chernikov
14fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.attrs import NlAttr
15fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.attrs import NlAttrStr
16fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.attrs import NlAttrU32
17fc2538cbSAlexander V. Chernikovfrom atf_python.sys.netlink.base_headers import GenlMsgHdr
18fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.base_headers import NlmBaseFlags
19fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.base_headers import Nlmsghdr
20fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.base_headers import NlMsgType
21fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.message import BaseNetlinkMessage
22fc2538cbSAlexander V. Chernikovfrom atf_python.sys.netlink.message import NlMsgCategory
23fc2538cbSAlexander V. Chernikovfrom atf_python.sys.netlink.message import NlMsgProps
24fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.message import StdNetlinkMessage
25fc2538cbSAlexander V. Chernikovfrom atf_python.sys.netlink.netlink_generic import GenlCtrlAttrType
263e5d0784SAlexander V. Chernikovfrom atf_python.sys.netlink.netlink_generic import GenlCtrlMsgType
27fc2538cbSAlexander V. Chernikovfrom atf_python.sys.netlink.netlink_generic import handler_classes as genl_classes
28fc2538cbSAlexander V. Chernikovfrom atf_python.sys.netlink.netlink_route import handler_classes as rt_classes
29fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.utils import align4
30fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.utils import AttrDescr
31fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.utils import build_propmap
32fc2538cbSAlexander V. Chernikovfrom atf_python.sys.netlink.utils import enum_or_int
33fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.utils import get_bitmask_map
34fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.utils import NlConst
35fee65b7eSAlexander V. Chernikovfrom atf_python.sys.netlink.utils import prepare_attrs_map
36fee65b7eSAlexander V. Chernikov
37fee65b7eSAlexander V. Chernikov
38fee65b7eSAlexander V. Chernikovclass SockaddrNl(Structure):
39fee65b7eSAlexander V. Chernikov    _fields_ = [
40fee65b7eSAlexander V. Chernikov        ("nl_len", c_ubyte),
41fee65b7eSAlexander V. Chernikov        ("nl_family", c_ubyte),
42fee65b7eSAlexander V. Chernikov        ("nl_pad", c_ushort),
43fee65b7eSAlexander V. Chernikov        ("nl_pid", c_uint),
44fee65b7eSAlexander V. Chernikov        ("nl_groups", c_uint),
45fee65b7eSAlexander V. Chernikov    ]
46fee65b7eSAlexander V. Chernikov
47fee65b7eSAlexander V. Chernikov
48fee65b7eSAlexander V. Chernikovclass Nlmsgdone(Structure):
49fee65b7eSAlexander V. Chernikov    _fields_ = [
50fee65b7eSAlexander V. Chernikov        ("error", c_int),
51fee65b7eSAlexander V. Chernikov    ]
52fee65b7eSAlexander V. Chernikov
53fee65b7eSAlexander V. Chernikov
54fee65b7eSAlexander V. Chernikovclass Nlmsgerr(Structure):
55fee65b7eSAlexander V. Chernikov    _fields_ = [
56fee65b7eSAlexander V. Chernikov        ("error", c_int),
57fee65b7eSAlexander V. Chernikov        ("msg", Nlmsghdr),
58fee65b7eSAlexander V. Chernikov    ]
59fee65b7eSAlexander V. Chernikov
60fee65b7eSAlexander V. Chernikov
61fee65b7eSAlexander V. Chernikovclass NlErrattrType(Enum):
62fee65b7eSAlexander V. Chernikov    NLMSGERR_ATTR_UNUSED = 0
63fee65b7eSAlexander V. Chernikov    NLMSGERR_ATTR_MSG = auto()
64fee65b7eSAlexander V. Chernikov    NLMSGERR_ATTR_OFFS = auto()
65fee65b7eSAlexander V. Chernikov    NLMSGERR_ATTR_COOKIE = auto()
66fee65b7eSAlexander V. Chernikov    NLMSGERR_ATTR_POLICY = auto()
67fee65b7eSAlexander V. Chernikov
68fee65b7eSAlexander V. Chernikov
69fee65b7eSAlexander V. Chernikovclass AddressFamilyLinux(Enum):
70fee65b7eSAlexander V. Chernikov    AF_INET = socket.AF_INET
71fee65b7eSAlexander V. Chernikov    AF_INET6 = socket.AF_INET6
72fee65b7eSAlexander V. Chernikov    AF_NETLINK = 16
73fee65b7eSAlexander V. Chernikov
74fee65b7eSAlexander V. Chernikov
75fee65b7eSAlexander V. Chernikovclass AddressFamilyBsd(Enum):
76fee65b7eSAlexander V. Chernikov    AF_INET = socket.AF_INET
77fee65b7eSAlexander V. Chernikov    AF_INET6 = socket.AF_INET6
78fee65b7eSAlexander V. Chernikov    AF_NETLINK = 38
79fee65b7eSAlexander V. Chernikov
80fee65b7eSAlexander V. Chernikov
81fee65b7eSAlexander V. Chernikovclass NlHelper:
82fee65b7eSAlexander V. Chernikov    def __init__(self):
83fee65b7eSAlexander V. Chernikov        self._pmap = {}
84fee65b7eSAlexander V. Chernikov        self._af_cls = self.get_af_cls()
85fee65b7eSAlexander V. Chernikov        self._seq_counter = 1
86fee65b7eSAlexander V. Chernikov        self.pid = os.getpid()
87fee65b7eSAlexander V. Chernikov
88fee65b7eSAlexander V. Chernikov    def get_seq(self):
89fee65b7eSAlexander V. Chernikov        ret = self._seq_counter
90fee65b7eSAlexander V. Chernikov        self._seq_counter += 1
91fee65b7eSAlexander V. Chernikov        return ret
92fee65b7eSAlexander V. Chernikov
93fee65b7eSAlexander V. Chernikov    def get_af_cls(self):
94fee65b7eSAlexander V. Chernikov        if sys.platform.startswith("freebsd"):
95fee65b7eSAlexander V. Chernikov            cls = AddressFamilyBsd
96fee65b7eSAlexander V. Chernikov        else:
97fee65b7eSAlexander V. Chernikov            cls = AddressFamilyLinux
98fee65b7eSAlexander V. Chernikov        return cls
99fee65b7eSAlexander V. Chernikov
100fee65b7eSAlexander V. Chernikov    def get_propmap(self, cls):
101fee65b7eSAlexander V. Chernikov        if cls not in self._pmap:
102fee65b7eSAlexander V. Chernikov            self._pmap[cls] = build_propmap(cls)
103fee65b7eSAlexander V. Chernikov        return self._pmap[cls]
104fee65b7eSAlexander V. Chernikov
105fee65b7eSAlexander V. Chernikov    def get_name_propmap(self, cls):
106fee65b7eSAlexander V. Chernikov        ret = {}
107fee65b7eSAlexander V. Chernikov        for prop in dir(cls):
108fee65b7eSAlexander V. Chernikov            if not prop.startswith("_"):
109fee65b7eSAlexander V. Chernikov                ret[prop] = getattr(cls, prop).value
110fee65b7eSAlexander V. Chernikov        return ret
111fee65b7eSAlexander V. Chernikov
112fee65b7eSAlexander V. Chernikov    def get_attr_byval(self, cls, attr_val):
113fee65b7eSAlexander V. Chernikov        propmap = self.get_propmap(cls)
114fee65b7eSAlexander V. Chernikov        return propmap.get(attr_val)
115fee65b7eSAlexander V. Chernikov
116fee65b7eSAlexander V. Chernikov    def get_af_name(self, family):
117fee65b7eSAlexander V. Chernikov        v = self.get_attr_byval(self._af_cls, family)
118fee65b7eSAlexander V. Chernikov        if v is not None:
119fee65b7eSAlexander V. Chernikov            return v
120fee65b7eSAlexander V. Chernikov        return "af#{}".format(family)
121fee65b7eSAlexander V. Chernikov
122fee65b7eSAlexander V. Chernikov    def get_af_value(self, family_str: str) -> int:
123fee65b7eSAlexander V. Chernikov        propmap = self.get_name_propmap(self._af_cls)
124fee65b7eSAlexander V. Chernikov        return propmap.get(family_str)
125fee65b7eSAlexander V. Chernikov
126fee65b7eSAlexander V. Chernikov    def get_bitmask_str(self, cls, val):
127fee65b7eSAlexander V. Chernikov        bmap = get_bitmask_map(self.get_propmap(cls), val)
128fee65b7eSAlexander V. Chernikov        return ",".join([v for k, v in bmap.items()])
129fee65b7eSAlexander V. Chernikov
130fee65b7eSAlexander V. Chernikov    @staticmethod
131fee65b7eSAlexander V. Chernikov    def get_bitmask_str_uncached(cls, val):
132fee65b7eSAlexander V. Chernikov        pmap = NlHelper.build_propmap(cls)
133fee65b7eSAlexander V. Chernikov        bmap = NlHelper.get_bitmask_map(pmap, val)
134fee65b7eSAlexander V. Chernikov        return ",".join([v for k, v in bmap.items()])
135fee65b7eSAlexander V. Chernikov
136fee65b7eSAlexander V. Chernikov
137fee65b7eSAlexander V. Chernikovnldone_attrs = prepare_attrs_map([])
138fee65b7eSAlexander V. Chernikov
139fee65b7eSAlexander V. Chernikovnlerr_attrs = prepare_attrs_map(
140fee65b7eSAlexander V. Chernikov    [
141fee65b7eSAlexander V. Chernikov        AttrDescr(NlErrattrType.NLMSGERR_ATTR_MSG, NlAttrStr),
142fee65b7eSAlexander V. Chernikov        AttrDescr(NlErrattrType.NLMSGERR_ATTR_OFFS, NlAttrU32),
143fee65b7eSAlexander V. Chernikov        AttrDescr(NlErrattrType.NLMSGERR_ATTR_COOKIE, NlAttr),
144fee65b7eSAlexander V. Chernikov    ]
145fee65b7eSAlexander V. Chernikov)
146fee65b7eSAlexander V. Chernikov
147fee65b7eSAlexander V. Chernikov
148fee65b7eSAlexander V. Chernikovclass NetlinkDoneMessage(StdNetlinkMessage):
149fc2538cbSAlexander V. Chernikov    messages = [NlMsgProps(NlMsgType.NLMSG_DONE, NlMsgCategory.ACK)]
150fee65b7eSAlexander V. Chernikov    nl_attrs_map = nldone_attrs
151fee65b7eSAlexander V. Chernikov
152fee65b7eSAlexander V. Chernikov    @property
153fee65b7eSAlexander V. Chernikov    def error_code(self):
154fee65b7eSAlexander V. Chernikov        return self.base_hdr.error
155fee65b7eSAlexander V. Chernikov
156fee65b7eSAlexander V. Chernikov    def parse_base_header(self, data):
157fee65b7eSAlexander V. Chernikov        if len(data) < sizeof(Nlmsgdone):
158fee65b7eSAlexander V. Chernikov            raise ValueError("length less than nlmsgdone header")
159fee65b7eSAlexander V. Chernikov        done_hdr = Nlmsgdone.from_buffer_copy(data)
160fee65b7eSAlexander V. Chernikov        sz = sizeof(Nlmsgdone)
161fee65b7eSAlexander V. Chernikov        return (done_hdr, sz)
162fee65b7eSAlexander V. Chernikov
163fee65b7eSAlexander V. Chernikov    def print_base_header(self, hdr, prepend=""):
164fee65b7eSAlexander V. Chernikov        print("{}error={}".format(prepend, hdr.error))
165fee65b7eSAlexander V. Chernikov
166fee65b7eSAlexander V. Chernikov
167fee65b7eSAlexander V. Chernikovclass NetlinkErrorMessage(StdNetlinkMessage):
168fc2538cbSAlexander V. Chernikov    messages = [NlMsgProps(NlMsgType.NLMSG_ERROR, NlMsgCategory.ACK)]
169fee65b7eSAlexander V. Chernikov    nl_attrs_map = nlerr_attrs
170fee65b7eSAlexander V. Chernikov
171fee65b7eSAlexander V. Chernikov    @property
172fee65b7eSAlexander V. Chernikov    def error_code(self):
173fee65b7eSAlexander V. Chernikov        return self.base_hdr.error
174fee65b7eSAlexander V. Chernikov
175fee65b7eSAlexander V. Chernikov    @property
176fee65b7eSAlexander V. Chernikov    def error_str(self):
177fee65b7eSAlexander V. Chernikov        nla = self.get_nla(NlErrattrType.NLMSGERR_ATTR_MSG)
178fee65b7eSAlexander V. Chernikov        if nla:
179fee65b7eSAlexander V. Chernikov            return nla.text
180fee65b7eSAlexander V. Chernikov        return None
181fee65b7eSAlexander V. Chernikov
182fee65b7eSAlexander V. Chernikov    @property
183fee65b7eSAlexander V. Chernikov    def error_offset(self):
184fee65b7eSAlexander V. Chernikov        nla = self.get_nla(NlErrattrType.NLMSGERR_ATTR_OFFS)
185fee65b7eSAlexander V. Chernikov        if nla:
186fee65b7eSAlexander V. Chernikov            return nla.u32
187fee65b7eSAlexander V. Chernikov        return None
188fee65b7eSAlexander V. Chernikov
189fee65b7eSAlexander V. Chernikov    @property
190fee65b7eSAlexander V. Chernikov    def cookie(self):
191fee65b7eSAlexander V. Chernikov        return self.get_nla(NlErrattrType.NLMSGERR_ATTR_COOKIE)
192fee65b7eSAlexander V. Chernikov
193fee65b7eSAlexander V. Chernikov    def parse_base_header(self, data):
194fee65b7eSAlexander V. Chernikov        if len(data) < sizeof(Nlmsgerr):
195fee65b7eSAlexander V. Chernikov            raise ValueError("length less than nlmsgerr header")
196fee65b7eSAlexander V. Chernikov        err_hdr = Nlmsgerr.from_buffer_copy(data)
197fee65b7eSAlexander V. Chernikov        sz = sizeof(Nlmsgerr)
198fee65b7eSAlexander V. Chernikov        if (self.nl_hdr.nlmsg_flags & 0x100) == 0:
199fee65b7eSAlexander V. Chernikov            sz += align4(err_hdr.msg.nlmsg_len - sizeof(Nlmsghdr))
200fee65b7eSAlexander V. Chernikov        return (err_hdr, sz)
201fee65b7eSAlexander V. Chernikov
202fee65b7eSAlexander V. Chernikov    def print_base_header(self, errhdr, prepend=""):
203fee65b7eSAlexander V. Chernikov        print("{}error={}, ".format(prepend, errhdr.error), end="")
204fc2538cbSAlexander V. Chernikov        hdr = errhdr.msg
205fc2538cbSAlexander V. Chernikov        print(
206fc2538cbSAlexander V. Chernikov            "{}len={}, type={}, flags={}(0x{:X}), seq={}, pid={}".format(
207fc2538cbSAlexander V. Chernikov                prepend,
208fc2538cbSAlexander V. Chernikov                hdr.nlmsg_len,
209fc2538cbSAlexander V. Chernikov                "msg#{}".format(hdr.nlmsg_type),
210fc2538cbSAlexander V. Chernikov                self.helper.get_bitmask_str(NlmBaseFlags, hdr.nlmsg_flags),
211fc2538cbSAlexander V. Chernikov                hdr.nlmsg_flags,
212fc2538cbSAlexander V. Chernikov                hdr.nlmsg_seq,
213fc2538cbSAlexander V. Chernikov                hdr.nlmsg_pid,
214fc2538cbSAlexander V. Chernikov            )
215fc2538cbSAlexander V. Chernikov        )
216fc2538cbSAlexander V. Chernikov
217fc2538cbSAlexander V. Chernikov
218fc2538cbSAlexander V. Chernikovcore_classes = {
219fc2538cbSAlexander V. Chernikov    "netlink_core": [
220fc2538cbSAlexander V. Chernikov        NetlinkDoneMessage,
221fc2538cbSAlexander V. Chernikov        NetlinkErrorMessage,
222fc2538cbSAlexander V. Chernikov    ],
223fc2538cbSAlexander V. Chernikov}
224fee65b7eSAlexander V. Chernikov
225fee65b7eSAlexander V. Chernikov
226fee65b7eSAlexander V. Chernikovclass Nlsock:
227fc2538cbSAlexander V. Chernikov    HANDLER_CLASSES = [core_classes, rt_classes, genl_classes]
228fc2538cbSAlexander V. Chernikov
229fee65b7eSAlexander V. Chernikov    def __init__(self, family, helper):
230fee65b7eSAlexander V. Chernikov        self.helper = helper
231fee65b7eSAlexander V. Chernikov        self.sock_fd = self._setup_netlink(family)
232fc2538cbSAlexander V. Chernikov        self._sock_family = family
233fee65b7eSAlexander V. Chernikov        self._data = bytes()
234fee65b7eSAlexander V. Chernikov        self.msgmap = self.build_msgmap()
235fc2538cbSAlexander V. Chernikov        self._family_map = {
236fc2538cbSAlexander V. Chernikov            NlConst.GENL_ID_CTRL: "nlctrl",
237fc2538cbSAlexander V. Chernikov        }
238fee65b7eSAlexander V. Chernikov
239fee65b7eSAlexander V. Chernikov    def build_msgmap(self):
240fc2538cbSAlexander V. Chernikov        handler_classes = {}
241fc2538cbSAlexander V. Chernikov        for d in self.HANDLER_CLASSES:
242fc2538cbSAlexander V. Chernikov            handler_classes.update(d)
243fee65b7eSAlexander V. Chernikov        xmap = {}
244fc2538cbSAlexander V. Chernikov        # 'family_name': [class.messages[MsgProps.msg],  ]
245fc2538cbSAlexander V. Chernikov        for family_id, family_classes in handler_classes.items():
246fc2538cbSAlexander V. Chernikov            xmap[family_id] = {}
247fc2538cbSAlexander V. Chernikov            for cls in family_classes:
248fc2538cbSAlexander V. Chernikov                for msg_props in cls.messages:
249fc2538cbSAlexander V. Chernikov                    xmap[family_id][enum_or_int(msg_props.msg)] = cls
250fee65b7eSAlexander V. Chernikov        return xmap
251fee65b7eSAlexander V. Chernikov
252fee65b7eSAlexander V. Chernikov    def _setup_netlink(self, netlink_family) -> int:
253fee65b7eSAlexander V. Chernikov        family = self.helper.get_af_value("AF_NETLINK")
254fee65b7eSAlexander V. Chernikov        s = socket.socket(family, socket.SOCK_RAW, netlink_family)
255fee65b7eSAlexander V. Chernikov        s.setsockopt(270, 10, 1)  # NETLINK_CAP_ACK
256fee65b7eSAlexander V. Chernikov        s.setsockopt(270, 11, 1)  # NETLINK_EXT_ACK
257fee65b7eSAlexander V. Chernikov        return s
258fee65b7eSAlexander V. Chernikov
259fee65b7eSAlexander V. Chernikov    def set_groups(self, mask: int):
260fee65b7eSAlexander V. Chernikov        self.sock_fd.setsockopt(socket.SOL_SOCKET, 1, mask)
261fee65b7eSAlexander V. Chernikov        # snl = SockaddrNl(nl_len = sizeof(SockaddrNl), nl_family=38,
262fee65b7eSAlexander V. Chernikov        #                  nl_pid=self.pid, nl_groups=mask)
263fee65b7eSAlexander V. Chernikov        # xbuffer = create_string_buffer(sizeof(SockaddrNl))
264fee65b7eSAlexander V. Chernikov        # memmove(xbuffer, addressof(snl), sizeof(SockaddrNl))
265fee65b7eSAlexander V. Chernikov        # k = struct.pack("@BBHII", 12, 38, 0, self.pid, mask)
266fee65b7eSAlexander V. Chernikov        # self.sock_fd.bind(k)
267fee65b7eSAlexander V. Chernikov
268*54b955f4SAlexander V. Chernikov    def join_group(self, group_id: int):
269*54b955f4SAlexander V. Chernikov        self.sock_fd.setsockopt(270, 1, group_id)
270*54b955f4SAlexander V. Chernikov
271fc2538cbSAlexander V. Chernikov    def write_message(self, msg, verbose=True):
272fc2538cbSAlexander V. Chernikov        if verbose:
273fee65b7eSAlexander V. Chernikov            print("vvvvvvvv OUT vvvvvvvv")
274fee65b7eSAlexander V. Chernikov            msg.print_message()
275fee65b7eSAlexander V. Chernikov        msg_bytes = bytes(msg)
276fee65b7eSAlexander V. Chernikov        try:
277fee65b7eSAlexander V. Chernikov            ret = os.write(self.sock_fd.fileno(), msg_bytes)
278fee65b7eSAlexander V. Chernikov            assert ret == len(msg_bytes)
279fee65b7eSAlexander V. Chernikov        except Exception as e:
280fee65b7eSAlexander V. Chernikov            print("write({}) -> {}".format(len(msg_bytes), e))
281fee65b7eSAlexander V. Chernikov
282fee65b7eSAlexander V. Chernikov    def parse_message(self, data: bytes):
283fee65b7eSAlexander V. Chernikov        if len(data) < sizeof(Nlmsghdr):
284fee65b7eSAlexander V. Chernikov            raise Exception("Short read from nl: {} bytes".format(len(data)))
285fee65b7eSAlexander V. Chernikov        hdr = Nlmsghdr.from_buffer_copy(data)
286fc2538cbSAlexander V. Chernikov        if hdr.nlmsg_type < 16:
287fc2538cbSAlexander V. Chernikov            family_name = "netlink_core"
288fee65b7eSAlexander V. Chernikov            nlmsg_type = hdr.nlmsg_type
289fc2538cbSAlexander V. Chernikov        elif self._sock_family == NlConst.NETLINK_ROUTE:
290fc2538cbSAlexander V. Chernikov            family_name = "netlink_route"
291fc2538cbSAlexander V. Chernikov            nlmsg_type = hdr.nlmsg_type
292fc2538cbSAlexander V. Chernikov        else:
293fc2538cbSAlexander V. Chernikov            # Genetlink
294fc2538cbSAlexander V. Chernikov            if len(data) < sizeof(Nlmsghdr) + sizeof(GenlMsgHdr):
295fc2538cbSAlexander V. Chernikov                raise Exception("Short read from genl: {} bytes".format(len(data)))
296fc2538cbSAlexander V. Chernikov            family_name = self._family_map.get(hdr.nlmsg_type, "")
297fc2538cbSAlexander V. Chernikov            ghdr = GenlMsgHdr.from_buffer_copy(data[sizeof(Nlmsghdr):])
298fc2538cbSAlexander V. Chernikov            nlmsg_type = ghdr.cmd
299fc2538cbSAlexander V. Chernikov        cls = self.msgmap.get(family_name, {}).get(nlmsg_type)
300fee65b7eSAlexander V. Chernikov        if not cls:
301fee65b7eSAlexander V. Chernikov            cls = BaseNetlinkMessage
302fee65b7eSAlexander V. Chernikov        return cls.from_bytes(self.helper, data)
303fee65b7eSAlexander V. Chernikov
304fc2538cbSAlexander V. Chernikov    def get_genl_family_id(self, family_name):
305fc2538cbSAlexander V. Chernikov        hdr = Nlmsghdr(
306fc2538cbSAlexander V. Chernikov            nlmsg_type=NlConst.GENL_ID_CTRL,
307fc2538cbSAlexander V. Chernikov            nlmsg_flags=NlmBaseFlags.NLM_F_REQUEST.value,
308fc2538cbSAlexander V. Chernikov            nlmsg_seq=self.helper.get_seq(),
309fc2538cbSAlexander V. Chernikov        )
310fc2538cbSAlexander V. Chernikov        ghdr = GenlMsgHdr(cmd=GenlCtrlMsgType.CTRL_CMD_GETFAMILY.value)
311fc2538cbSAlexander V. Chernikov        nla = NlAttrStr(GenlCtrlAttrType.CTRL_ATTR_FAMILY_NAME, family_name)
312fc2538cbSAlexander V. Chernikov        hdr.nlmsg_len = sizeof(Nlmsghdr) + sizeof(GenlMsgHdr) + len(bytes(nla))
313fc2538cbSAlexander V. Chernikov
314fc2538cbSAlexander V. Chernikov        msg_bytes = bytes(hdr) + bytes(ghdr) + bytes(nla)
315fc2538cbSAlexander V. Chernikov        self.write_data(msg_bytes)
316fc2538cbSAlexander V. Chernikov        while True:
317fc2538cbSAlexander V. Chernikov            rx_msg = self.read_message()
318fc2538cbSAlexander V. Chernikov            if hdr.nlmsg_seq == rx_msg.nl_hdr.nlmsg_seq:
319fc2538cbSAlexander V. Chernikov                if rx_msg.is_type(NlMsgType.NLMSG_ERROR):
320fc2538cbSAlexander V. Chernikov                    if rx_msg.error_code != 0:
321fc2538cbSAlexander V. Chernikov                        raise ValueError("unable to get family {}".format(family_name))
322fc2538cbSAlexander V. Chernikov                else:
323fc2538cbSAlexander V. Chernikov                    family_id = rx_msg.get_nla(GenlCtrlAttrType.CTRL_ATTR_FAMILY_ID).u16
324fc2538cbSAlexander V. Chernikov                    self._family_map[family_id] = family_name
325fc2538cbSAlexander V. Chernikov                    return family_id
326fc2538cbSAlexander V. Chernikov        raise ValueError("unable to get family {}".format(family_name))
327fc2538cbSAlexander V. Chernikov
328fee65b7eSAlexander V. Chernikov    def write_data(self, data: bytes):
329fee65b7eSAlexander V. Chernikov        self.sock_fd.send(data)
330fee65b7eSAlexander V. Chernikov
331fee65b7eSAlexander V. Chernikov    def read_data(self):
332fee65b7eSAlexander V. Chernikov        while True:
333fee65b7eSAlexander V. Chernikov            data = self.sock_fd.recv(65535)
334fee65b7eSAlexander V. Chernikov            self._data += data
335fee65b7eSAlexander V. Chernikov            if len(self._data) >= sizeof(Nlmsghdr):
336fee65b7eSAlexander V. Chernikov                break
337fee65b7eSAlexander V. Chernikov
338fee65b7eSAlexander V. Chernikov    def read_message(self) -> bytes:
339fee65b7eSAlexander V. Chernikov        if len(self._data) < sizeof(Nlmsghdr):
340fee65b7eSAlexander V. Chernikov            self.read_data()
341fee65b7eSAlexander V. Chernikov        hdr = Nlmsghdr.from_buffer_copy(self._data)
342fee65b7eSAlexander V. Chernikov        while hdr.nlmsg_len > len(self._data):
343fee65b7eSAlexander V. Chernikov            self.read_data()
344fee65b7eSAlexander V. Chernikov        raw_msg = self._data[: hdr.nlmsg_len]
345fee65b7eSAlexander V. Chernikov        self._data = self._data[hdr.nlmsg_len:]
346fee65b7eSAlexander V. Chernikov        return self.parse_message(raw_msg)
347f3065e76SAlexander V. Chernikov
348f3065e76SAlexander V. Chernikov    def get_reply(self, tx_msg):
349f3065e76SAlexander V. Chernikov        self.write_message(tx_msg)
350f3065e76SAlexander V. Chernikov        while True:
351f3065e76SAlexander V. Chernikov            rx_msg = self.read_message()
352f3065e76SAlexander V. Chernikov            if tx_msg.nl_hdr.nlmsg_seq == rx_msg.nl_hdr.nlmsg_seq:
353f3065e76SAlexander V. Chernikov                return rx_msg
354fee65b7eSAlexander V. Chernikov
355fee65b7eSAlexander V. Chernikov
356fee65b7eSAlexander V. Chernikovclass NetlinkMultipartIterator(object):
357fee65b7eSAlexander V. Chernikov    def __init__(self, obj, seq_number: int, msg_type):
358fee65b7eSAlexander V. Chernikov        self._obj = obj
359fee65b7eSAlexander V. Chernikov        self._seq = seq_number
360fee65b7eSAlexander V. Chernikov        self._msg_type = msg_type
361fee65b7eSAlexander V. Chernikov
362fee65b7eSAlexander V. Chernikov    def __iter__(self):
363fee65b7eSAlexander V. Chernikov        return self
364fee65b7eSAlexander V. Chernikov
365fee65b7eSAlexander V. Chernikov    def __next__(self):
366fee65b7eSAlexander V. Chernikov        msg = self._obj.read_message()
367fee65b7eSAlexander V. Chernikov        if self._seq != msg.nl_hdr.nlmsg_seq:
368fee65b7eSAlexander V. Chernikov            raise ValueError("bad sequence number")
369fee65b7eSAlexander V. Chernikov        if msg.is_type(NlMsgType.NLMSG_ERROR):
370fee65b7eSAlexander V. Chernikov            raise ValueError(
371fee65b7eSAlexander V. Chernikov                "error while handling multipart msg: {}".format(msg.error_code)
372fee65b7eSAlexander V. Chernikov            )
373fee65b7eSAlexander V. Chernikov        elif msg.is_type(NlMsgType.NLMSG_DONE):
374fee65b7eSAlexander V. Chernikov            if msg.error_code == 0:
375fee65b7eSAlexander V. Chernikov                raise StopIteration
376fee65b7eSAlexander V. Chernikov            raise ValueError(
377fee65b7eSAlexander V. Chernikov                "error listing some parts of the multipart msg: {}".format(
378fee65b7eSAlexander V. Chernikov                    msg.error_code
379fee65b7eSAlexander V. Chernikov                )
380fee65b7eSAlexander V. Chernikov            )
381fee65b7eSAlexander V. Chernikov        elif not msg.is_type(self._msg_type):
382fee65b7eSAlexander V. Chernikov            raise ValueError("bad message type: {}".format(msg))
383fee65b7eSAlexander V. Chernikov        return msg
384fee65b7eSAlexander V. Chernikov
385fee65b7eSAlexander V. Chernikov
386fee65b7eSAlexander V. Chernikovclass NetlinkTestTemplate(object):
387fee65b7eSAlexander V. Chernikov    REQUIRED_MODULES = ["netlink"]
388fee65b7eSAlexander V. Chernikov
389fee65b7eSAlexander V. Chernikov    def setup_netlink(self, netlink_family: NlConst):
390fee65b7eSAlexander V. Chernikov        self.helper = NlHelper()
391fee65b7eSAlexander V. Chernikov        self.nlsock = Nlsock(netlink_family, self.helper)
392fee65b7eSAlexander V. Chernikov
393fee65b7eSAlexander V. Chernikov    def write_message(self, msg, silent=False):
394fee65b7eSAlexander V. Chernikov        if not silent:
395fee65b7eSAlexander V. Chernikov            print("")
396fee65b7eSAlexander V. Chernikov            print("============= >> TX MESSAGE =============")
397fee65b7eSAlexander V. Chernikov            msg.print_message()
398fee65b7eSAlexander V. Chernikov            msg.print_as_bytes(bytes(msg), "-- DATA --")
399fee65b7eSAlexander V. Chernikov        self.nlsock.write_data(bytes(msg))
400fee65b7eSAlexander V. Chernikov
401fee65b7eSAlexander V. Chernikov    def read_message(self, silent=False):
402fee65b7eSAlexander V. Chernikov        msg = self.nlsock.read_message()
403fee65b7eSAlexander V. Chernikov        if not silent:
404fee65b7eSAlexander V. Chernikov            print("")
405fee65b7eSAlexander V. Chernikov            print("============= << RX MESSAGE =============")
406fee65b7eSAlexander V. Chernikov            msg.print_message()
407fee65b7eSAlexander V. Chernikov        return msg
408fee65b7eSAlexander V. Chernikov
409fee65b7eSAlexander V. Chernikov    def get_reply(self, tx_msg):
410fee65b7eSAlexander V. Chernikov        self.write_message(tx_msg)
411fee65b7eSAlexander V. Chernikov        while True:
412fee65b7eSAlexander V. Chernikov            rx_msg = self.read_message()
413fee65b7eSAlexander V. Chernikov            if tx_msg.nl_hdr.nlmsg_seq == rx_msg.nl_hdr.nlmsg_seq:
414fee65b7eSAlexander V. Chernikov                return rx_msg
415fee65b7eSAlexander V. Chernikov
416fee65b7eSAlexander V. Chernikov    def read_msg_list(self, seq, msg_type):
417fee65b7eSAlexander V. Chernikov        return list(NetlinkMultipartIterator(self, seq, msg_type))
418