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