xref: /freebsd/tests/atf_python/sys/netlink/attrs.py (revision 04a036601e10237ae00655e515aeb78762eb5d1a)
1fee65b7eSAlexander V. Chernikovimport socket
2fee65b7eSAlexander V. Chernikovimport struct
3fee65b7eSAlexander V. Chernikovfrom enum import Enum
4fee65b7eSAlexander V. Chernikov
5388420e6SAlexander V. Chernikovfrom atf_python.sys.netlink.utils import align4
6388420e6SAlexander V. Chernikovfrom atf_python.sys.netlink.utils import enum_or_int
7fee65b7eSAlexander V. Chernikov
8fee65b7eSAlexander V. Chernikov
9fee65b7eSAlexander V. Chernikovclass NlAttr(object):
103e5d0784SAlexander V. Chernikov    HDR_LEN = 4  # sizeof(struct nlattr)
113e5d0784SAlexander V. Chernikov
12fee65b7eSAlexander V. Chernikov    def __init__(self, nla_type, data):
13fee65b7eSAlexander V. Chernikov        if isinstance(nla_type, Enum):
14fee65b7eSAlexander V. Chernikov            self._nla_type = nla_type.value
15fee65b7eSAlexander V. Chernikov            self._enum = nla_type
16fee65b7eSAlexander V. Chernikov        else:
17fee65b7eSAlexander V. Chernikov            self._nla_type = nla_type
18fee65b7eSAlexander V. Chernikov            self._enum = None
19fee65b7eSAlexander V. Chernikov        self.nla_list = []
20fee65b7eSAlexander V. Chernikov        self._data = data
21fee65b7eSAlexander V. Chernikov
22fee65b7eSAlexander V. Chernikov    @property
23fee65b7eSAlexander V. Chernikov    def nla_type(self):
24*04a03660SAlexander V. Chernikov        return self._nla_type & 0x3FFF
25fee65b7eSAlexander V. Chernikov
26fee65b7eSAlexander V. Chernikov    @property
27fee65b7eSAlexander V. Chernikov    def nla_len(self):
28fee65b7eSAlexander V. Chernikov        return len(self._data) + 4
29fee65b7eSAlexander V. Chernikov
30fee65b7eSAlexander V. Chernikov    def add_nla(self, nla):
31fee65b7eSAlexander V. Chernikov        self.nla_list.append(nla)
32fee65b7eSAlexander V. Chernikov
33fee65b7eSAlexander V. Chernikov    def print_attr(self, prepend=""):
34fee65b7eSAlexander V. Chernikov        if self._enum is not None:
35fee65b7eSAlexander V. Chernikov            type_str = self._enum.name
36fee65b7eSAlexander V. Chernikov        else:
37fee65b7eSAlexander V. Chernikov            type_str = "nla#{}".format(self.nla_type)
38fee65b7eSAlexander V. Chernikov        print(
39fee65b7eSAlexander V. Chernikov            "{}len={} type={}({}){}".format(
40fee65b7eSAlexander V. Chernikov                prepend, self.nla_len, type_str, self.nla_type, self._print_attr_value()
41fee65b7eSAlexander V. Chernikov            )
42fee65b7eSAlexander V. Chernikov        )
43fee65b7eSAlexander V. Chernikov
44fee65b7eSAlexander V. Chernikov    @staticmethod
45fee65b7eSAlexander V. Chernikov    def _validate(data):
46fee65b7eSAlexander V. Chernikov        if len(data) < 4:
47fee65b7eSAlexander V. Chernikov            raise ValueError("attribute too short")
48fee65b7eSAlexander V. Chernikov        nla_len, nla_type = struct.unpack("@HH", data[:4])
49fee65b7eSAlexander V. Chernikov        if nla_len > len(data):
50fee65b7eSAlexander V. Chernikov            raise ValueError("attribute length too big")
51fee65b7eSAlexander V. Chernikov        if nla_len < 4:
52fee65b7eSAlexander V. Chernikov            raise ValueError("attribute length too short")
53fee65b7eSAlexander V. Chernikov
54fee65b7eSAlexander V. Chernikov    @classmethod
55fee65b7eSAlexander V. Chernikov    def _parse(cls, data):
56fee65b7eSAlexander V. Chernikov        nla_len, nla_type = struct.unpack("@HH", data[:4])
57fee65b7eSAlexander V. Chernikov        return cls(nla_type, data[4:])
58fee65b7eSAlexander V. Chernikov
59fee65b7eSAlexander V. Chernikov    @classmethod
60fee65b7eSAlexander V. Chernikov    def from_bytes(cls, data, attr_type_enum=None):
61fee65b7eSAlexander V. Chernikov        cls._validate(data)
62fee65b7eSAlexander V. Chernikov        attr = cls._parse(data)
63fee65b7eSAlexander V. Chernikov        attr._enum = attr_type_enum
64fee65b7eSAlexander V. Chernikov        return attr
65fee65b7eSAlexander V. Chernikov
66fee65b7eSAlexander V. Chernikov    def _to_bytes(self, data: bytes):
67fee65b7eSAlexander V. Chernikov        ret = data
68fee65b7eSAlexander V. Chernikov        if align4(len(ret)) != len(ret):
69fee65b7eSAlexander V. Chernikov            ret = data + bytes(align4(len(ret)) - len(ret))
70fee65b7eSAlexander V. Chernikov        return struct.pack("@HH", len(data) + 4, self._nla_type) + ret
71fee65b7eSAlexander V. Chernikov
72fee65b7eSAlexander V. Chernikov    def __bytes__(self):
73fee65b7eSAlexander V. Chernikov        return self._to_bytes(self._data)
74fee65b7eSAlexander V. Chernikov
75fee65b7eSAlexander V. Chernikov    def _print_attr_value(self):
76fee65b7eSAlexander V. Chernikov        return " " + " ".join(["x{:02X}".format(b) for b in self._data])
77fee65b7eSAlexander V. Chernikov
78fee65b7eSAlexander V. Chernikov
79fee65b7eSAlexander V. Chernikovclass NlAttrNested(NlAttr):
80fee65b7eSAlexander V. Chernikov    def __init__(self, nla_type, val):
81fee65b7eSAlexander V. Chernikov        super().__init__(nla_type, b"")
82fee65b7eSAlexander V. Chernikov        self.nla_list = val
83fee65b7eSAlexander V. Chernikov
84fee65b7eSAlexander V. Chernikov    @property
85fee65b7eSAlexander V. Chernikov    def nla_len(self):
86fee65b7eSAlexander V. Chernikov        return align4(len(b"".join([bytes(nla) for nla in self.nla_list]))) + 4
87fee65b7eSAlexander V. Chernikov
88fee65b7eSAlexander V. Chernikov    def print_attr(self, prepend=""):
89fee65b7eSAlexander V. Chernikov        if self._enum is not None:
90fee65b7eSAlexander V. Chernikov            type_str = self._enum.name
91fee65b7eSAlexander V. Chernikov        else:
92fee65b7eSAlexander V. Chernikov            type_str = "nla#{}".format(self.nla_type)
93fee65b7eSAlexander V. Chernikov        print(
94fee65b7eSAlexander V. Chernikov            "{}len={} type={}({}) {{".format(
95fee65b7eSAlexander V. Chernikov                prepend, self.nla_len, type_str, self.nla_type
96fee65b7eSAlexander V. Chernikov            )
97fee65b7eSAlexander V. Chernikov        )
98fee65b7eSAlexander V. Chernikov        for nla in self.nla_list:
99fee65b7eSAlexander V. Chernikov            nla.print_attr(prepend + "  ")
100fee65b7eSAlexander V. Chernikov        print("{}}}".format(prepend))
101fee65b7eSAlexander V. Chernikov
102fee65b7eSAlexander V. Chernikov    def __bytes__(self):
103fee65b7eSAlexander V. Chernikov        return self._to_bytes(b"".join([bytes(nla) for nla in self.nla_list]))
104fee65b7eSAlexander V. Chernikov
105fee65b7eSAlexander V. Chernikov
106fee65b7eSAlexander V. Chernikovclass NlAttrU32(NlAttr):
107fee65b7eSAlexander V. Chernikov    def __init__(self, nla_type, val):
108fee65b7eSAlexander V. Chernikov        self.u32 = enum_or_int(val)
109fee65b7eSAlexander V. Chernikov        super().__init__(nla_type, b"")
110fee65b7eSAlexander V. Chernikov
111fee65b7eSAlexander V. Chernikov    @property
112fee65b7eSAlexander V. Chernikov    def nla_len(self):
113fee65b7eSAlexander V. Chernikov        return 8
114fee65b7eSAlexander V. Chernikov
115fee65b7eSAlexander V. Chernikov    def _print_attr_value(self):
116fee65b7eSAlexander V. Chernikov        return " val={}".format(self.u32)
117fee65b7eSAlexander V. Chernikov
118fee65b7eSAlexander V. Chernikov    @staticmethod
119fee65b7eSAlexander V. Chernikov    def _validate(data):
120fee65b7eSAlexander V. Chernikov        assert len(data) == 8
121fee65b7eSAlexander V. Chernikov        nla_len, nla_type = struct.unpack("@HH", data[:4])
122fee65b7eSAlexander V. Chernikov        assert nla_len == 8
123fee65b7eSAlexander V. Chernikov
124fee65b7eSAlexander V. Chernikov    @classmethod
125fee65b7eSAlexander V. Chernikov    def _parse(cls, data):
126fee65b7eSAlexander V. Chernikov        nla_len, nla_type, val = struct.unpack("@HHI", data)
127fee65b7eSAlexander V. Chernikov        return cls(nla_type, val)
128fee65b7eSAlexander V. Chernikov
129fee65b7eSAlexander V. Chernikov    def __bytes__(self):
130fee65b7eSAlexander V. Chernikov        return self._to_bytes(struct.pack("@I", self.u32))
131fee65b7eSAlexander V. Chernikov
132fee65b7eSAlexander V. Chernikov
133fee65b7eSAlexander V. Chernikovclass NlAttrU16(NlAttr):
134fee65b7eSAlexander V. Chernikov    def __init__(self, nla_type, val):
135fee65b7eSAlexander V. Chernikov        self.u16 = enum_or_int(val)
136fee65b7eSAlexander V. Chernikov        super().__init__(nla_type, b"")
137fee65b7eSAlexander V. Chernikov
138fee65b7eSAlexander V. Chernikov    @property
139fee65b7eSAlexander V. Chernikov    def nla_len(self):
140fee65b7eSAlexander V. Chernikov        return 6
141fee65b7eSAlexander V. Chernikov
142fee65b7eSAlexander V. Chernikov    def _print_attr_value(self):
143fee65b7eSAlexander V. Chernikov        return " val={}".format(self.u16)
144fee65b7eSAlexander V. Chernikov
145fee65b7eSAlexander V. Chernikov    @staticmethod
146fee65b7eSAlexander V. Chernikov    def _validate(data):
147fee65b7eSAlexander V. Chernikov        assert len(data) == 6
148fee65b7eSAlexander V. Chernikov        nla_len, nla_type = struct.unpack("@HH", data[:4])
149fee65b7eSAlexander V. Chernikov        assert nla_len == 6
150fee65b7eSAlexander V. Chernikov
151fee65b7eSAlexander V. Chernikov    @classmethod
152fee65b7eSAlexander V. Chernikov    def _parse(cls, data):
153fee65b7eSAlexander V. Chernikov        nla_len, nla_type, val = struct.unpack("@HHH", data)
154fee65b7eSAlexander V. Chernikov        return cls(nla_type, val)
155fee65b7eSAlexander V. Chernikov
156fee65b7eSAlexander V. Chernikov    def __bytes__(self):
157fee65b7eSAlexander V. Chernikov        return self._to_bytes(struct.pack("@H", self.u16))
158fee65b7eSAlexander V. Chernikov
159fee65b7eSAlexander V. Chernikov
160fee65b7eSAlexander V. Chernikovclass NlAttrU8(NlAttr):
161fee65b7eSAlexander V. Chernikov    def __init__(self, nla_type, val):
162fee65b7eSAlexander V. Chernikov        self.u8 = enum_or_int(val)
163fee65b7eSAlexander V. Chernikov        super().__init__(nla_type, b"")
164fee65b7eSAlexander V. Chernikov
165fee65b7eSAlexander V. Chernikov    @property
166fee65b7eSAlexander V. Chernikov    def nla_len(self):
167fee65b7eSAlexander V. Chernikov        return 5
168fee65b7eSAlexander V. Chernikov
169fee65b7eSAlexander V. Chernikov    def _print_attr_value(self):
170fee65b7eSAlexander V. Chernikov        return " val={}".format(self.u8)
171fee65b7eSAlexander V. Chernikov
172fee65b7eSAlexander V. Chernikov    @staticmethod
173fee65b7eSAlexander V. Chernikov    def _validate(data):
174fee65b7eSAlexander V. Chernikov        assert len(data) == 5
175fee65b7eSAlexander V. Chernikov        nla_len, nla_type = struct.unpack("@HH", data[:4])
176fee65b7eSAlexander V. Chernikov        assert nla_len == 5
177fee65b7eSAlexander V. Chernikov
178fee65b7eSAlexander V. Chernikov    @classmethod
179fee65b7eSAlexander V. Chernikov    def _parse(cls, data):
180fee65b7eSAlexander V. Chernikov        nla_len, nla_type, val = struct.unpack("@HHB", data)
181fee65b7eSAlexander V. Chernikov        return cls(nla_type, val)
182fee65b7eSAlexander V. Chernikov
183fee65b7eSAlexander V. Chernikov    def __bytes__(self):
184fee65b7eSAlexander V. Chernikov        return self._to_bytes(struct.pack("@B", self.u8))
185fee65b7eSAlexander V. Chernikov
186fee65b7eSAlexander V. Chernikov
187fee65b7eSAlexander V. Chernikovclass NlAttrIp(NlAttr):
188fee65b7eSAlexander V. Chernikov    def __init__(self, nla_type, addr: str):
189fee65b7eSAlexander V. Chernikov        super().__init__(nla_type, b"")
190fee65b7eSAlexander V. Chernikov        self.addr = addr
191fee65b7eSAlexander V. Chernikov        if ":" in self.addr:
192fee65b7eSAlexander V. Chernikov            self.family = socket.AF_INET6
193fee65b7eSAlexander V. Chernikov        else:
194fee65b7eSAlexander V. Chernikov            self.family = socket.AF_INET
195fee65b7eSAlexander V. Chernikov
196fee65b7eSAlexander V. Chernikov    @staticmethod
197fee65b7eSAlexander V. Chernikov    def _validate(data):
198fee65b7eSAlexander V. Chernikov        nla_len, nla_type = struct.unpack("@HH", data[:4])
199fee65b7eSAlexander V. Chernikov        data_len = nla_len - 4
200fee65b7eSAlexander V. Chernikov        if data_len != 4 and data_len != 16:
201fee65b7eSAlexander V. Chernikov            raise ValueError(
202fee65b7eSAlexander V. Chernikov                "Error validating attr {}: nla_len is not valid".format(  # noqa: E501
203fee65b7eSAlexander V. Chernikov                    nla_type
204fee65b7eSAlexander V. Chernikov                )
205fee65b7eSAlexander V. Chernikov            )
206fee65b7eSAlexander V. Chernikov
207fee65b7eSAlexander V. Chernikov    @property
208fee65b7eSAlexander V. Chernikov    def nla_len(self):
209fee65b7eSAlexander V. Chernikov        if self.family == socket.AF_INET6:
210fee65b7eSAlexander V. Chernikov            return 20
211fee65b7eSAlexander V. Chernikov        else:
212fee65b7eSAlexander V. Chernikov            return 8
213fee65b7eSAlexander V. Chernikov        return align4(len(self._data)) + 4
214fee65b7eSAlexander V. Chernikov
215fee65b7eSAlexander V. Chernikov    @classmethod
216fee65b7eSAlexander V. Chernikov    def _parse(cls, data):
217fee65b7eSAlexander V. Chernikov        nla_len, nla_type = struct.unpack("@HH", data[:4])
218fee65b7eSAlexander V. Chernikov        data_len = len(data) - 4
219fee65b7eSAlexander V. Chernikov        if data_len == 4:
220fee65b7eSAlexander V. Chernikov            addr = socket.inet_ntop(socket.AF_INET, data[4:8])
221fee65b7eSAlexander V. Chernikov        else:
222fee65b7eSAlexander V. Chernikov            addr = socket.inet_ntop(socket.AF_INET6, data[4:20])
223fee65b7eSAlexander V. Chernikov        return cls(nla_type, addr)
224fee65b7eSAlexander V. Chernikov
225fee65b7eSAlexander V. Chernikov    def __bytes__(self):
226fee65b7eSAlexander V. Chernikov        return self._to_bytes(socket.inet_pton(self.family, self.addr))
227fee65b7eSAlexander V. Chernikov
228fee65b7eSAlexander V. Chernikov    def _print_attr_value(self):
229fee65b7eSAlexander V. Chernikov        return " addr={}".format(self.addr)
230fee65b7eSAlexander V. Chernikov
231fee65b7eSAlexander V. Chernikov
232fee65b7eSAlexander V. Chernikovclass NlAttrStr(NlAttr):
233fee65b7eSAlexander V. Chernikov    def __init__(self, nla_type, text):
234fee65b7eSAlexander V. Chernikov        super().__init__(nla_type, b"")
235fee65b7eSAlexander V. Chernikov        self.text = text
236fee65b7eSAlexander V. Chernikov
237fee65b7eSAlexander V. Chernikov    @staticmethod
238fee65b7eSAlexander V. Chernikov    def _validate(data):
239fee65b7eSAlexander V. Chernikov        NlAttr._validate(data)
240fee65b7eSAlexander V. Chernikov        try:
241fee65b7eSAlexander V. Chernikov            data[4:].decode("utf-8")
242fee65b7eSAlexander V. Chernikov        except Exception as e:
243fee65b7eSAlexander V. Chernikov            raise ValueError("wrong utf-8 string: {}".format(e))
244fee65b7eSAlexander V. Chernikov
245fee65b7eSAlexander V. Chernikov    @property
246fee65b7eSAlexander V. Chernikov    def nla_len(self):
247fee65b7eSAlexander V. Chernikov        return len(self.text) + 5
248fee65b7eSAlexander V. Chernikov
249fee65b7eSAlexander V. Chernikov    @classmethod
250fee65b7eSAlexander V. Chernikov    def _parse(cls, data):
251fee65b7eSAlexander V. Chernikov        text = data[4:-1].decode("utf-8")
252fee65b7eSAlexander V. Chernikov        nla_len, nla_type = struct.unpack("@HH", data[:4])
253fee65b7eSAlexander V. Chernikov        return cls(nla_type, text)
254fee65b7eSAlexander V. Chernikov
255fee65b7eSAlexander V. Chernikov    def __bytes__(self):
256fee65b7eSAlexander V. Chernikov        return self._to_bytes(bytes(self.text, encoding="utf-8") + bytes(1))
257fee65b7eSAlexander V. Chernikov
258fee65b7eSAlexander V. Chernikov    def _print_attr_value(self):
259fee65b7eSAlexander V. Chernikov        return ' val="{}"'.format(self.text)
260fee65b7eSAlexander V. Chernikov
261fee65b7eSAlexander V. Chernikov
262fee65b7eSAlexander V. Chernikovclass NlAttrStrn(NlAttr):
263fee65b7eSAlexander V. Chernikov    def __init__(self, nla_type, text):
264fee65b7eSAlexander V. Chernikov        super().__init__(nla_type, b"")
265fee65b7eSAlexander V. Chernikov        self.text = text
266fee65b7eSAlexander V. Chernikov
267fee65b7eSAlexander V. Chernikov    @staticmethod
268fee65b7eSAlexander V. Chernikov    def _validate(data):
269fee65b7eSAlexander V. Chernikov        NlAttr._validate(data)
270fee65b7eSAlexander V. Chernikov        try:
271fee65b7eSAlexander V. Chernikov            data[4:].decode("utf-8")
272fee65b7eSAlexander V. Chernikov        except Exception as e:
273fee65b7eSAlexander V. Chernikov            raise ValueError("wrong utf-8 string: {}".format(e))
274fee65b7eSAlexander V. Chernikov
275fee65b7eSAlexander V. Chernikov    @property
276fee65b7eSAlexander V. Chernikov    def nla_len(self):
277fee65b7eSAlexander V. Chernikov        return len(self.text) + 4
278fee65b7eSAlexander V. Chernikov
279fee65b7eSAlexander V. Chernikov    @classmethod
280fee65b7eSAlexander V. Chernikov    def _parse(cls, data):
281fee65b7eSAlexander V. Chernikov        text = data[4:].decode("utf-8")
282fee65b7eSAlexander V. Chernikov        nla_len, nla_type = struct.unpack("@HH", data[:4])
283fee65b7eSAlexander V. Chernikov        return cls(nla_type, text)
284fee65b7eSAlexander V. Chernikov
285fee65b7eSAlexander V. Chernikov    def __bytes__(self):
286fee65b7eSAlexander V. Chernikov        return self._to_bytes(bytes(self.text, encoding="utf-8"))
287fee65b7eSAlexander V. Chernikov
288fee65b7eSAlexander V. Chernikov    def _print_attr_value(self):
289fee65b7eSAlexander V. Chernikov        return ' val="{}"'.format(self.text)
290