xref: /freebsd/tests/atf_python/sys/netpfil/ipfw/ioctl.py (revision 84b41342da1ff3b9cf4eb785752201575c5e4391)
19f44a47fSAlexander V. Chernikov#!/usr/bin/env python3
29f44a47fSAlexander V. Chernikovimport os
39f44a47fSAlexander V. Chernikovimport socket
49f44a47fSAlexander V. Chernikovimport struct
59f44a47fSAlexander V. Chernikovimport subprocess
69f44a47fSAlexander V. Chernikovimport sys
79f44a47fSAlexander V. Chernikovfrom ctypes import c_byte
89f44a47fSAlexander V. Chernikovfrom ctypes import c_char
99f44a47fSAlexander V. Chernikovfrom ctypes import c_int
109f44a47fSAlexander V. Chernikovfrom ctypes import c_long
119f44a47fSAlexander V. Chernikovfrom ctypes import c_uint32
129f44a47fSAlexander V. Chernikovfrom ctypes import c_uint8
139f44a47fSAlexander V. Chernikovfrom ctypes import c_ulong
149f44a47fSAlexander V. Chernikovfrom ctypes import c_ushort
159f44a47fSAlexander V. Chernikovfrom ctypes import sizeof
169f44a47fSAlexander V. Chernikovfrom ctypes import Structure
179f44a47fSAlexander V. Chernikovfrom enum import Enum
189f44a47fSAlexander V. Chernikovfrom typing import Any
199f44a47fSAlexander V. Chernikovfrom typing import Dict
209f44a47fSAlexander V. Chernikovfrom typing import List
219f44a47fSAlexander V. Chernikovfrom typing import NamedTuple
229f44a47fSAlexander V. Chernikovfrom typing import Optional
239f44a47fSAlexander V. Chernikovfrom typing import Union
249f44a47fSAlexander V. Chernikov
259f44a47fSAlexander V. Chernikovimport pytest
269f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.insns import BaseInsn
279f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.insns import insn_attrs
289f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.ioctl_headers import IpFwTableLookupType
299f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.ioctl_headers import IpFwTlvType
309f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.ioctl_headers import Op3CmdType
319f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import align8
329f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import AttrDescr
339f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import enum_from_int
349f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import prepare_attrs_map
359f44a47fSAlexander V. Chernikov
369f44a47fSAlexander V. Chernikov
379f44a47fSAlexander V. Chernikovclass IpFw3OpHeader(Structure):
389f44a47fSAlexander V. Chernikov    _fields_ = [
399f44a47fSAlexander V. Chernikov        ("opcode", c_ushort),
409f44a47fSAlexander V. Chernikov        ("version", c_ushort),
419f44a47fSAlexander V. Chernikov        ("reserved1", c_ushort),
429f44a47fSAlexander V. Chernikov        ("reserved2", c_ushort),
439f44a47fSAlexander V. Chernikov    ]
449f44a47fSAlexander V. Chernikov
459f44a47fSAlexander V. Chernikov
469f44a47fSAlexander V. Chernikovclass IpFwObjTlv(Structure):
479f44a47fSAlexander V. Chernikov    _fields_ = [
489f44a47fSAlexander V. Chernikov        ("n_type", c_ushort),
499f44a47fSAlexander V. Chernikov        ("flags", c_ushort),
509f44a47fSAlexander V. Chernikov        ("length", c_uint32),
519f44a47fSAlexander V. Chernikov    ]
529f44a47fSAlexander V. Chernikov
539f44a47fSAlexander V. Chernikov
549f44a47fSAlexander V. Chernikovclass BaseTlv(object):
559f44a47fSAlexander V. Chernikov    obj_enum_class = IpFwTlvType
569f44a47fSAlexander V. Chernikov
579f44a47fSAlexander V. Chernikov    def __init__(self, obj_type):
589f44a47fSAlexander V. Chernikov        if isinstance(obj_type, Enum):
599f44a47fSAlexander V. Chernikov            self.obj_type = obj_type.value
609f44a47fSAlexander V. Chernikov            self._enum = obj_type
619f44a47fSAlexander V. Chernikov        else:
629f44a47fSAlexander V. Chernikov            self.obj_type = obj_type
639f44a47fSAlexander V. Chernikov            self._enum = enum_from_int(self.obj_enum_class, obj_type)
649f44a47fSAlexander V. Chernikov        self.obj_list = []
659f44a47fSAlexander V. Chernikov
669f44a47fSAlexander V. Chernikov    def add_obj(self, obj):
679f44a47fSAlexander V. Chernikov        self.obj_list.append(obj)
689f44a47fSAlexander V. Chernikov
699f44a47fSAlexander V. Chernikov    @property
709f44a47fSAlexander V. Chernikov    def len(self):
719f44a47fSAlexander V. Chernikov        return len(bytes(self))
729f44a47fSAlexander V. Chernikov
739f44a47fSAlexander V. Chernikov    @property
749f44a47fSAlexander V. Chernikov    def obj_name(self):
759f44a47fSAlexander V. Chernikov        if self._enum is not None:
769f44a47fSAlexander V. Chernikov            return self._enum.name
779f44a47fSAlexander V. Chernikov        else:
789f44a47fSAlexander V. Chernikov            return "tlv#{}".format(self.obj_type)
799f44a47fSAlexander V. Chernikov
809f44a47fSAlexander V. Chernikov    def print_hdr(self, prepend=""):
819f44a47fSAlexander V. Chernikov        print(
829f44a47fSAlexander V. Chernikov            "{}len={} type={}({}){}".format(
839f44a47fSAlexander V. Chernikov                prepend, self.len, self.obj_name, self.obj_type, self._print_obj_value()
849f44a47fSAlexander V. Chernikov            )
859f44a47fSAlexander V. Chernikov        )
869f44a47fSAlexander V. Chernikov
879f44a47fSAlexander V. Chernikov    def print_obj(self, prepend=""):
889f44a47fSAlexander V. Chernikov        self.print_hdr(prepend)
899f44a47fSAlexander V. Chernikov        prepend = "  " + prepend
909f44a47fSAlexander V. Chernikov        for obj in self.obj_list:
919f44a47fSAlexander V. Chernikov            obj.print_obj(prepend)
929f44a47fSAlexander V. Chernikov
93*84b41342SAlexander V. Chernikov    def print_obj_hex(self, prepend=""):
94*84b41342SAlexander V. Chernikov        print(prepend)
95*84b41342SAlexander V. Chernikov        print()
96*84b41342SAlexander V. Chernikov        print(" ".join(["x{:02X}".format(b) for b in bytes(self)]))
97*84b41342SAlexander V. Chernikov
989f44a47fSAlexander V. Chernikov    @classmethod
999f44a47fSAlexander V. Chernikov    def _validate(cls, data):
1009f44a47fSAlexander V. Chernikov        if len(data) < sizeof(IpFwObjTlv):
1019f44a47fSAlexander V. Chernikov            raise ValueError("TLV too short")
1029f44a47fSAlexander V. Chernikov        hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
1039f44a47fSAlexander V. Chernikov        if len(data) != hdr.length:
1049f44a47fSAlexander V. Chernikov            raise ValueError("wrong TLV size")
1059f44a47fSAlexander V. Chernikov
1069f44a47fSAlexander V. Chernikov    @classmethod
1079f44a47fSAlexander V. Chernikov    def _parse(cls, data, attr_map):
1089f44a47fSAlexander V. Chernikov        hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
1099f44a47fSAlexander V. Chernikov        return cls(hdr.n_type)
1109f44a47fSAlexander V. Chernikov
1119f44a47fSAlexander V. Chernikov    @classmethod
1129f44a47fSAlexander V. Chernikov    def from_bytes(cls, data, attr_map=None):
1139f44a47fSAlexander V. Chernikov        cls._validate(data)
1149f44a47fSAlexander V. Chernikov        obj = cls._parse(data, attr_map)
1159f44a47fSAlexander V. Chernikov        return obj
1169f44a47fSAlexander V. Chernikov
1179f44a47fSAlexander V. Chernikov    def __bytes__(self):
1189f44a47fSAlexander V. Chernikov        raise NotImplementedError()
1199f44a47fSAlexander V. Chernikov
1209f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
1219f44a47fSAlexander V. Chernikov        return " " + " ".join(
1229f44a47fSAlexander V. Chernikov            ["x{:02X}".format(b) for b in self._data[sizeof(IpFwObjTlv) :]]
1239f44a47fSAlexander V. Chernikov        )
1249f44a47fSAlexander V. Chernikov
1259f44a47fSAlexander V. Chernikov    def as_hexdump(self):
1269f44a47fSAlexander V. Chernikov        return " ".join(["x{:02X}".format(b) for b in bytes(self)])
1279f44a47fSAlexander V. Chernikov
1289f44a47fSAlexander V. Chernikov
1299f44a47fSAlexander V. Chernikovclass UnknownTlv(BaseTlv):
1309f44a47fSAlexander V. Chernikov    def __init__(self, obj_type, data):
1319f44a47fSAlexander V. Chernikov        super().__init__(obj_type)
1329f44a47fSAlexander V. Chernikov        self._data = data
1339f44a47fSAlexander V. Chernikov
1349f44a47fSAlexander V. Chernikov    @classmethod
1359f44a47fSAlexander V. Chernikov    def _validate(cls, data):
1369f44a47fSAlexander V. Chernikov        if len(data) < sizeof(IpFwObjNTlv):
1379f44a47fSAlexander V. Chernikov            raise ValueError("TLV size is too short")
1389f44a47fSAlexander V. Chernikov        hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
1399f44a47fSAlexander V. Chernikov        if len(data) != hdr.length:
1409f44a47fSAlexander V. Chernikov            raise ValueError("wrong TLV size")
1419f44a47fSAlexander V. Chernikov
1429f44a47fSAlexander V. Chernikov    @classmethod
1439f44a47fSAlexander V. Chernikov    def _parse(cls, data, attr_map):
1449f44a47fSAlexander V. Chernikov        hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
1459f44a47fSAlexander V. Chernikov        self = cls(hdr.n_type, data)
1469f44a47fSAlexander V. Chernikov        return self
1479f44a47fSAlexander V. Chernikov
1489f44a47fSAlexander V. Chernikov    def __bytes__(self):
1499f44a47fSAlexander V. Chernikov        return self._data
1509f44a47fSAlexander V. Chernikov
1519f44a47fSAlexander V. Chernikov
1529f44a47fSAlexander V. Chernikovclass Tlv(BaseTlv):
1539f44a47fSAlexander V. Chernikov    @staticmethod
1549f44a47fSAlexander V. Chernikov    def parse_tlvs(data, attr_map):
1559f44a47fSAlexander V. Chernikov        # print("PARSING " + " ".join(["x{:02X}".format(b) for b in data]))
1569f44a47fSAlexander V. Chernikov        off = 0
1579f44a47fSAlexander V. Chernikov        ret = []
1589f44a47fSAlexander V. Chernikov        while off + sizeof(IpFwObjTlv) <= len(data):
1599f44a47fSAlexander V. Chernikov            hdr = IpFwObjTlv.from_buffer_copy(data[off : off + sizeof(IpFwObjTlv)])
1609f44a47fSAlexander V. Chernikov            if off + hdr.length > len(data):
1619f44a47fSAlexander V. Chernikov                raise ValueError("TLV size do not match")
1629f44a47fSAlexander V. Chernikov            obj_data = data[off : off + hdr.length]
1639f44a47fSAlexander V. Chernikov            obj_descr = attr_map.get(hdr.n_type, None)
1649f44a47fSAlexander V. Chernikov            if obj_descr is None:
1659f44a47fSAlexander V. Chernikov                # raise ValueError("unknown child TLV {}".format(hdr.n_type))
1669f44a47fSAlexander V. Chernikov                cls = UnknownTlv
1679f44a47fSAlexander V. Chernikov                child_map = {}
1689f44a47fSAlexander V. Chernikov            else:
1699f44a47fSAlexander V. Chernikov                cls = obj_descr["ad"].cls
1709f44a47fSAlexander V. Chernikov                child_map = obj_descr.get("child", {})
1719f44a47fSAlexander V. Chernikov            # print("FOUND OBJECT type {}".format(cls))
1729f44a47fSAlexander V. Chernikov            # print()
1739f44a47fSAlexander V. Chernikov            obj = cls.from_bytes(obj_data, child_map)
1749f44a47fSAlexander V. Chernikov            ret.append(obj)
1759f44a47fSAlexander V. Chernikov            off += hdr.length
1769f44a47fSAlexander V. Chernikov        return ret
1779f44a47fSAlexander V. Chernikov
1789f44a47fSAlexander V. Chernikov
1799f44a47fSAlexander V. Chernikovclass IpFwObjNTlv(Structure):
1809f44a47fSAlexander V. Chernikov    _fields_ = [
1819f44a47fSAlexander V. Chernikov        ("head", IpFwObjTlv),
1829f44a47fSAlexander V. Chernikov        ("idx", c_ushort),
1839f44a47fSAlexander V. Chernikov        ("n_set", c_uint8),
1849f44a47fSAlexander V. Chernikov        ("n_type", c_uint8),
1859f44a47fSAlexander V. Chernikov        ("spare", c_uint32),
1869f44a47fSAlexander V. Chernikov        ("name", c_char * 64),
1879f44a47fSAlexander V. Chernikov    ]
1889f44a47fSAlexander V. Chernikov
1899f44a47fSAlexander V. Chernikov
1909f44a47fSAlexander V. Chernikovclass NTlv(Tlv):
1919f44a47fSAlexander V. Chernikov    def __init__(self, obj_type, idx=0, n_set=0, n_type=0, name=None):
1929f44a47fSAlexander V. Chernikov        super().__init__(obj_type)
1939f44a47fSAlexander V. Chernikov        self.n_idx = idx
1949f44a47fSAlexander V. Chernikov        self.n_set = n_set
1959f44a47fSAlexander V. Chernikov        self.n_type = n_type
1969f44a47fSAlexander V. Chernikov        self.n_name = name
1979f44a47fSAlexander V. Chernikov
1989f44a47fSAlexander V. Chernikov    @classmethod
1999f44a47fSAlexander V. Chernikov    def _validate(cls, data):
2009f44a47fSAlexander V. Chernikov        if len(data) != sizeof(IpFwObjNTlv):
2019f44a47fSAlexander V. Chernikov            raise ValueError("TLV size is not correct")
2029f44a47fSAlexander V. Chernikov        hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
2039f44a47fSAlexander V. Chernikov        if len(data) != hdr.length:
2049f44a47fSAlexander V. Chernikov            raise ValueError("wrong TLV size")
2059f44a47fSAlexander V. Chernikov
2069f44a47fSAlexander V. Chernikov    @classmethod
2079f44a47fSAlexander V. Chernikov    def _parse(cls, data, attr_map):
2089f44a47fSAlexander V. Chernikov        hdr = IpFwObjNTlv.from_buffer_copy(data[: sizeof(IpFwObjNTlv)])
2099f44a47fSAlexander V. Chernikov        name = hdr.name.decode("utf-8")
2109f44a47fSAlexander V. Chernikov        self = cls(hdr.head.n_type, hdr.idx, hdr.n_set, hdr.n_type, name)
2119f44a47fSAlexander V. Chernikov        return self
2129f44a47fSAlexander V. Chernikov
2139f44a47fSAlexander V. Chernikov    def __bytes__(self):
2149f44a47fSAlexander V. Chernikov        name_bytes = self.n_name.encode("utf-8")
2159f44a47fSAlexander V. Chernikov        if len(name_bytes) < 64:
2169f44a47fSAlexander V. Chernikov            name_bytes += b"\0" * (64 - len(name_bytes))
2179f44a47fSAlexander V. Chernikov        hdr = IpFwObjNTlv(
2189f44a47fSAlexander V. Chernikov            head=IpFwObjTlv(n_type=self.obj_type, length=sizeof(IpFwObjNTlv)),
2199f44a47fSAlexander V. Chernikov            idx=self.n_idx,
2209f44a47fSAlexander V. Chernikov            n_set=self.n_set,
2219f44a47fSAlexander V. Chernikov            n_type=self.n_type,
2229f44a47fSAlexander V. Chernikov            name=name_bytes[:64],
2239f44a47fSAlexander V. Chernikov        )
2249f44a47fSAlexander V. Chernikov        return bytes(hdr)
2259f44a47fSAlexander V. Chernikov
2269f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
2279f44a47fSAlexander V. Chernikov        return " " + "type={} set={} idx={} name={}".format(
2289f44a47fSAlexander V. Chernikov            self.n_type, self.n_set, self.n_idx, self.n_name
2299f44a47fSAlexander V. Chernikov        )
2309f44a47fSAlexander V. Chernikov
2319f44a47fSAlexander V. Chernikov
2329f44a47fSAlexander V. Chernikovclass IpFwObjCTlv(Structure):
2339f44a47fSAlexander V. Chernikov    _fields_ = [
2349f44a47fSAlexander V. Chernikov        ("head", IpFwObjTlv),
2359f44a47fSAlexander V. Chernikov        ("count", c_uint32),
2369f44a47fSAlexander V. Chernikov        ("objsize", c_ushort),
2379f44a47fSAlexander V. Chernikov        ("version", c_uint8),
2389f44a47fSAlexander V. Chernikov        ("flags", c_uint8),
2399f44a47fSAlexander V. Chernikov    ]
2409f44a47fSAlexander V. Chernikov
2419f44a47fSAlexander V. Chernikov
2429f44a47fSAlexander V. Chernikovclass CTlv(Tlv):
2439f44a47fSAlexander V. Chernikov    def __init__(self, obj_type, obj_list=[]):
2449f44a47fSAlexander V. Chernikov        super().__init__(obj_type)
2459f44a47fSAlexander V. Chernikov        if obj_list:
2469f44a47fSAlexander V. Chernikov            self.obj_list.extend(obj_list)
2479f44a47fSAlexander V. Chernikov
2489f44a47fSAlexander V. Chernikov    @classmethod
2499f44a47fSAlexander V. Chernikov    def _validate(cls, data):
2509f44a47fSAlexander V. Chernikov        if len(data) < sizeof(IpFwObjCTlv):
2519f44a47fSAlexander V. Chernikov            raise ValueError("TLV too short")
2529f44a47fSAlexander V. Chernikov        hdr = IpFwObjCTlv.from_buffer_copy(data[: sizeof(IpFwObjCTlv)])
2539f44a47fSAlexander V. Chernikov        if len(data) != hdr.head.length:
2549f44a47fSAlexander V. Chernikov            raise ValueError("wrong TLV size")
2559f44a47fSAlexander V. Chernikov
2569f44a47fSAlexander V. Chernikov    @classmethod
2579f44a47fSAlexander V. Chernikov    def _parse(cls, data, attr_map):
2589f44a47fSAlexander V. Chernikov        hdr = IpFwObjCTlv.from_buffer_copy(data[: sizeof(IpFwObjCTlv)])
2599f44a47fSAlexander V. Chernikov        tlv_list = cls.parse_tlvs(data[sizeof(IpFwObjCTlv) :], attr_map)
2609f44a47fSAlexander V. Chernikov        if len(tlv_list) != hdr.count:
2619f44a47fSAlexander V. Chernikov            raise ValueError("wrong number of objects")
2629f44a47fSAlexander V. Chernikov        self = cls(hdr.head.n_type, obj_list=tlv_list)
2639f44a47fSAlexander V. Chernikov        return self
2649f44a47fSAlexander V. Chernikov
2659f44a47fSAlexander V. Chernikov    def __bytes__(self):
2669f44a47fSAlexander V. Chernikov        ret = b""
2679f44a47fSAlexander V. Chernikov        for obj in self.obj_list:
2689f44a47fSAlexander V. Chernikov            ret += bytes(obj)
2699f44a47fSAlexander V. Chernikov        length = len(ret) + sizeof(IpFwObjCTlv)
2709f44a47fSAlexander V. Chernikov        if self.obj_list:
2719f44a47fSAlexander V. Chernikov            objsize = len(bytes(self.obj_list[0]))
2729f44a47fSAlexander V. Chernikov        else:
2739f44a47fSAlexander V. Chernikov            objsize = 0
2749f44a47fSAlexander V. Chernikov        hdr = IpFwObjCTlv(
2759f44a47fSAlexander V. Chernikov            head=IpFwObjTlv(n_type=self.obj_type, length=sizeof(IpFwObjNTlv)),
2769f44a47fSAlexander V. Chernikov            count=len(self.obj_list),
2779f44a47fSAlexander V. Chernikov            objsize=objsize,
2789f44a47fSAlexander V. Chernikov        )
2799f44a47fSAlexander V. Chernikov        return bytes(hdr) + ret
2809f44a47fSAlexander V. Chernikov
2819f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
2829f44a47fSAlexander V. Chernikov        return ""
2839f44a47fSAlexander V. Chernikov
2849f44a47fSAlexander V. Chernikov
2859f44a47fSAlexander V. Chernikovclass IpFwRule(Structure):
2869f44a47fSAlexander V. Chernikov    _fields_ = [
2879f44a47fSAlexander V. Chernikov        ("act_ofs", c_ushort),
2889f44a47fSAlexander V. Chernikov        ("cmd_len", c_ushort),
2899f44a47fSAlexander V. Chernikov        ("spare", c_ushort),
2909f44a47fSAlexander V. Chernikov        ("n_set", c_uint8),
2919f44a47fSAlexander V. Chernikov        ("flags", c_uint8),
2929f44a47fSAlexander V. Chernikov        ("rulenum", c_uint32),
2939f44a47fSAlexander V. Chernikov        ("n_id", c_uint32),
2949f44a47fSAlexander V. Chernikov    ]
2959f44a47fSAlexander V. Chernikov
2969f44a47fSAlexander V. Chernikov
2979f44a47fSAlexander V. Chernikovclass RawRule(Tlv):
2989f44a47fSAlexander V. Chernikov    def __init__(self, obj_type=0, n_set=0, rulenum=0, obj_list=[]):
2999f44a47fSAlexander V. Chernikov        super().__init__(obj_type)
3009f44a47fSAlexander V. Chernikov        self.n_set = n_set
3019f44a47fSAlexander V. Chernikov        self.rulenum = rulenum
3029f44a47fSAlexander V. Chernikov        if obj_list:
3039f44a47fSAlexander V. Chernikov            self.obj_list.extend(obj_list)
3049f44a47fSAlexander V. Chernikov
3059f44a47fSAlexander V. Chernikov    @classmethod
3069f44a47fSAlexander V. Chernikov    def _validate(cls, data):
3079f44a47fSAlexander V. Chernikov        min_size = sizeof(IpFwRule)
3089f44a47fSAlexander V. Chernikov        if len(data) < min_size:
3099f44a47fSAlexander V. Chernikov            raise ValueError("rule TLV too short")
3109f44a47fSAlexander V. Chernikov        rule = IpFwRule.from_buffer_copy(data[:min_size])
3119f44a47fSAlexander V. Chernikov        if len(data) != min_size + rule.cmd_len * 4:
3129f44a47fSAlexander V. Chernikov            raise ValueError("rule TLV cmd_len incorrect")
3139f44a47fSAlexander V. Chernikov
3149f44a47fSAlexander V. Chernikov    @classmethod
3159f44a47fSAlexander V. Chernikov    def _parse(cls, data, attr_map):
3169f44a47fSAlexander V. Chernikov        hdr = IpFwRule.from_buffer_copy(data[: sizeof(IpFwRule)])
3179f44a47fSAlexander V. Chernikov        self = cls(
3189f44a47fSAlexander V. Chernikov            n_set=hdr.n_set,
3199f44a47fSAlexander V. Chernikov            rulenum=hdr.rulenum,
3209f44a47fSAlexander V. Chernikov            obj_list=BaseInsn.parse_insns(data[sizeof(IpFwRule) :], insn_attrs),
3219f44a47fSAlexander V. Chernikov        )
3229f44a47fSAlexander V. Chernikov        return self
3239f44a47fSAlexander V. Chernikov
3249f44a47fSAlexander V. Chernikov    def __bytes__(self):
3259f44a47fSAlexander V. Chernikov        act_ofs = 0
3269f44a47fSAlexander V. Chernikov        cmd_len = 0
3279f44a47fSAlexander V. Chernikov        ret = b""
3289f44a47fSAlexander V. Chernikov        for obj in self.obj_list:
3299f44a47fSAlexander V. Chernikov            if obj.is_action and act_ofs == 0:
3309f44a47fSAlexander V. Chernikov                act_ofs = cmd_len
3319f44a47fSAlexander V. Chernikov            obj_bytes = bytes(obj)
3329f44a47fSAlexander V. Chernikov            cmd_len += len(obj_bytes) // 4
3339f44a47fSAlexander V. Chernikov            ret += obj_bytes
3349f44a47fSAlexander V. Chernikov
3359f44a47fSAlexander V. Chernikov        hdr = IpFwRule(
3369f44a47fSAlexander V. Chernikov            act_ofs=act_ofs,
3379f44a47fSAlexander V. Chernikov            cmd_len=cmd_len,
3389f44a47fSAlexander V. Chernikov            n_set=self.n_set,
3399f44a47fSAlexander V. Chernikov            rulenum=self.rulenum,
3409f44a47fSAlexander V. Chernikov        )
3419f44a47fSAlexander V. Chernikov        return bytes(hdr) + ret
3429f44a47fSAlexander V. Chernikov
3439f44a47fSAlexander V. Chernikov    @property
3449f44a47fSAlexander V. Chernikov    def obj_name(self):
3459f44a47fSAlexander V. Chernikov        return "rule#{}".format(self.rulenum)
3469f44a47fSAlexander V. Chernikov
3479f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
3489f44a47fSAlexander V. Chernikov        cmd_len = sum([len(bytes(obj)) for obj in self.obj_list]) // 4
3499f44a47fSAlexander V. Chernikov        return " set={} cmd_len={}".format(self.n_set, cmd_len)
3509f44a47fSAlexander V. Chernikov
3519f44a47fSAlexander V. Chernikov
3529f44a47fSAlexander V. Chernikovclass CTlvRule(CTlv):
3539f44a47fSAlexander V. Chernikov    def __init__(self, obj_type=IpFwTlvType.IPFW_TLV_RULE_LIST, obj_list=[]):
3549f44a47fSAlexander V. Chernikov        super().__init__(obj_type, obj_list)
3559f44a47fSAlexander V. Chernikov
3569f44a47fSAlexander V. Chernikov    @classmethod
3579f44a47fSAlexander V. Chernikov    def _parse(cls, data, attr_map):
3589f44a47fSAlexander V. Chernikov        chdr = IpFwObjCTlv.from_buffer_copy(data[: sizeof(IpFwObjCTlv)])
3599f44a47fSAlexander V. Chernikov        rule_list = []
3609f44a47fSAlexander V. Chernikov        off = sizeof(IpFwObjCTlv)
3619f44a47fSAlexander V. Chernikov        while off + sizeof(IpFwRule) <= len(data):
3629f44a47fSAlexander V. Chernikov            hdr = IpFwRule.from_buffer_copy(data[off : off + sizeof(IpFwRule)])
3639f44a47fSAlexander V. Chernikov            rule_len = sizeof(IpFwRule) + hdr.cmd_len * 4
3649f44a47fSAlexander V. Chernikov            # print("FOUND RULE len={} cmd_len={}".format(rule_len, hdr.cmd_len))
3659f44a47fSAlexander V. Chernikov            if off + rule_len > len(data):
3669f44a47fSAlexander V. Chernikov                raise ValueError("wrong rule size")
3679f44a47fSAlexander V. Chernikov            rule = RawRule.from_bytes(data[off : off + rule_len])
3689f44a47fSAlexander V. Chernikov            rule_list.append(rule)
3699f44a47fSAlexander V. Chernikov            off += align8(rule_len)
3709f44a47fSAlexander V. Chernikov        if off != len(data):
3719f44a47fSAlexander V. Chernikov            raise ValueError("rule bytes left: off={} len={}".format(off, len(data)))
3729f44a47fSAlexander V. Chernikov        return cls(chdr.head.n_type, obj_list=rule_list)
3739f44a47fSAlexander V. Chernikov
3749f44a47fSAlexander V. Chernikov    # XXX: _validate
3759f44a47fSAlexander V. Chernikov
3769f44a47fSAlexander V. Chernikov    def __bytes__(self):
3779f44a47fSAlexander V. Chernikov        ret = b""
3789f44a47fSAlexander V. Chernikov        for rule in self.obj_list:
3799f44a47fSAlexander V. Chernikov            rule_bytes = bytes(rule)
3809f44a47fSAlexander V. Chernikov            remainder = len(rule_bytes) % 8
3819f44a47fSAlexander V. Chernikov            if remainder > 0:
3829f44a47fSAlexander V. Chernikov                rule_bytes += b"\0" * (8 - remainder)
3839f44a47fSAlexander V. Chernikov            ret += rule_bytes
3849f44a47fSAlexander V. Chernikov        hdr = IpFwObjCTlv(
3859f44a47fSAlexander V. Chernikov            head=IpFwObjTlv(
3869f44a47fSAlexander V. Chernikov                n_type=self.obj_type, length=len(ret) + sizeof(IpFwObjCTlv)
3879f44a47fSAlexander V. Chernikov            ),
3889f44a47fSAlexander V. Chernikov            count=len(self.obj_list),
3899f44a47fSAlexander V. Chernikov        )
3909f44a47fSAlexander V. Chernikov        return bytes(hdr) + ret
3919f44a47fSAlexander V. Chernikov
3929f44a47fSAlexander V. Chernikov
3939f44a47fSAlexander V. Chernikovclass BaseIpFwMessage(object):
3949f44a47fSAlexander V. Chernikov    messages = []
3959f44a47fSAlexander V. Chernikov
3969f44a47fSAlexander V. Chernikov    def __init__(self, msg_type, obj_list=[]):
3979f44a47fSAlexander V. Chernikov        if isinstance(msg_type, Enum):
3989f44a47fSAlexander V. Chernikov            self.obj_type = msg_type.value
3999f44a47fSAlexander V. Chernikov            self._enum = msg_type
4009f44a47fSAlexander V. Chernikov        else:
4019f44a47fSAlexander V. Chernikov            self.obj_type = msg_type
4029f44a47fSAlexander V. Chernikov            self._enum = enum_from_int(self.messages, self.obj_type)
4039f44a47fSAlexander V. Chernikov        self.obj_list = []
4049f44a47fSAlexander V. Chernikov        if obj_list:
4059f44a47fSAlexander V. Chernikov            self.obj_list.extend(obj_list)
4069f44a47fSAlexander V. Chernikov
4079f44a47fSAlexander V. Chernikov    def add_obj(self, obj):
4089f44a47fSAlexander V. Chernikov        self.obj_list.append(obj)
4099f44a47fSAlexander V. Chernikov
4109f44a47fSAlexander V. Chernikov    def get_obj(self, obj_type):
4119f44a47fSAlexander V. Chernikov        obj_type_raw = enum_or_int(obj_type)
4129f44a47fSAlexander V. Chernikov        for obj in self.obj_list:
4139f44a47fSAlexander V. Chernikov            if obj.obj_type == obj_type_raw:
4149f44a47fSAlexander V. Chernikov                return obj
4159f44a47fSAlexander V. Chernikov        return None
4169f44a47fSAlexander V. Chernikov
4179f44a47fSAlexander V. Chernikov    @staticmethod
4189f44a47fSAlexander V. Chernikov    def parse_header(data: bytes):
4199f44a47fSAlexander V. Chernikov        if len(data) < sizeof(IpFw3OpHeader):
4209f44a47fSAlexander V. Chernikov            raise ValueError("length less than op3 message header")
4219f44a47fSAlexander V. Chernikov        return IpFw3OpHeader.from_buffer_copy(data), sizeof(IpFw3OpHeader)
4229f44a47fSAlexander V. Chernikov
4239f44a47fSAlexander V. Chernikov    def parse_obj_list(self, data: bytes):
4249f44a47fSAlexander V. Chernikov        off = 0
4259f44a47fSAlexander V. Chernikov        while off < len(data):
4269f44a47fSAlexander V. Chernikov            # print("PARSE off={} rem={}".format(off, len(data) - off))
4279f44a47fSAlexander V. Chernikov            hdr = IpFwObjTlv.from_buffer_copy(data[off : off + sizeof(IpFwObjTlv)])
4289f44a47fSAlexander V. Chernikov            # print(" tlv len {}".format(hdr.length))
4299f44a47fSAlexander V. Chernikov            if hdr.length + off > len(data):
4309f44a47fSAlexander V. Chernikov                raise ValueError("TLV too big")
4319f44a47fSAlexander V. Chernikov            tlv = Tlv(hdr.n_type, data[off : off + hdr.length])
4329f44a47fSAlexander V. Chernikov            self.add_obj(tlv)
4339f44a47fSAlexander V. Chernikov            off += hdr.length
4349f44a47fSAlexander V. Chernikov
4359f44a47fSAlexander V. Chernikov    def is_type(self, msg_type):
4369f44a47fSAlexander V. Chernikov        return enum_or_int(msg_type) == self.msg_type
4379f44a47fSAlexander V. Chernikov
4389f44a47fSAlexander V. Chernikov    @property
4399f44a47fSAlexander V. Chernikov    def obj_name(self):
4409f44a47fSAlexander V. Chernikov        if self._enum is not None:
4419f44a47fSAlexander V. Chernikov            return self._enum.name
4429f44a47fSAlexander V. Chernikov        else:
4439f44a47fSAlexander V. Chernikov            return "msg#{}".format(self.obj_type)
4449f44a47fSAlexander V. Chernikov
4459f44a47fSAlexander V. Chernikov    def print_hdr(self, prepend=""):
4469f44a47fSAlexander V. Chernikov        print("{}len={}, type={}".format(prepend, len(bytes(self)), self.obj_name))
4479f44a47fSAlexander V. Chernikov
4489f44a47fSAlexander V. Chernikov    @classmethod
4499f44a47fSAlexander V. Chernikov    def from_bytes(cls, data):
4509f44a47fSAlexander V. Chernikov        try:
4519f44a47fSAlexander V. Chernikov            hdr, hdrlen = cls.parse_header(data)
4529f44a47fSAlexander V. Chernikov            self = cls(hdr.opcode)
4539f44a47fSAlexander V. Chernikov            self._orig_data = data
4549f44a47fSAlexander V. Chernikov        except ValueError as e:
4559f44a47fSAlexander V. Chernikov            print("Failed to parse op3 header: {}".format(e))
4569f44a47fSAlexander V. Chernikov            cls.print_as_bytes(data)
4579f44a47fSAlexander V. Chernikov            raise
4589f44a47fSAlexander V. Chernikov        tlv_list = Tlv.parse_tlvs(data[hdrlen:], self.attr_map)
4599f44a47fSAlexander V. Chernikov        self.obj_list.extend(tlv_list)
4609f44a47fSAlexander V. Chernikov        return self
4619f44a47fSAlexander V. Chernikov
4629f44a47fSAlexander V. Chernikov    def __bytes__(self):
4639f44a47fSAlexander V. Chernikov        ret = bytes(IpFw3OpHeader(opcode=self.obj_type))
4649f44a47fSAlexander V. Chernikov        for obj in self.obj_list:
4659f44a47fSAlexander V. Chernikov            ret += bytes(obj)
4669f44a47fSAlexander V. Chernikov        return ret
4679f44a47fSAlexander V. Chernikov
4689f44a47fSAlexander V. Chernikov    def print_obj(self):
4699f44a47fSAlexander V. Chernikov        self.print_hdr()
4709f44a47fSAlexander V. Chernikov        for obj in self.obj_list:
4719f44a47fSAlexander V. Chernikov            obj.print_obj("  ")
4729f44a47fSAlexander V. Chernikov
4739f44a47fSAlexander V. Chernikov    @staticmethod
4749f44a47fSAlexander V. Chernikov    def print_as_bytes(data: bytes, descr: str):
4759f44a47fSAlexander V. Chernikov        print("===vv {} (len:{:3d}) vv===".format(descr, len(data)))
4769f44a47fSAlexander V. Chernikov        off = 0
4779f44a47fSAlexander V. Chernikov        step = 16
4789f44a47fSAlexander V. Chernikov        while off < len(data):
4799f44a47fSAlexander V. Chernikov            for i in range(step):
4809f44a47fSAlexander V. Chernikov                if off + i < len(data):
4819f44a47fSAlexander V. Chernikov                    print(" {:02X}".format(data[off + i]), end="")
4829f44a47fSAlexander V. Chernikov            print("")
4839f44a47fSAlexander V. Chernikov            off += step
4849f44a47fSAlexander V. Chernikov        print("--------------------")
4859f44a47fSAlexander V. Chernikov
4869f44a47fSAlexander V. Chernikov
4879f44a47fSAlexander V. Chernikovrule_attrs = prepare_attrs_map(
4889f44a47fSAlexander V. Chernikov    [
4899f44a47fSAlexander V. Chernikov        AttrDescr(
4909f44a47fSAlexander V. Chernikov            IpFwTlvType.IPFW_TLV_TBLNAME_LIST,
4919f44a47fSAlexander V. Chernikov            CTlv,
4929f44a47fSAlexander V. Chernikov            [
4939f44a47fSAlexander V. Chernikov                AttrDescr(IpFwTlvType.IPFW_TLV_TBL_NAME, NTlv),
4949f44a47fSAlexander V. Chernikov                AttrDescr(IpFwTlvType.IPFW_TLV_STATE_NAME, NTlv),
495*84b41342SAlexander V. Chernikov                AttrDescr(IpFwTlvType.IPFW_TLV_EACTION, NTlv),
4969f44a47fSAlexander V. Chernikov            ],
4979f44a47fSAlexander V. Chernikov            True,
4989f44a47fSAlexander V. Chernikov        ),
4999f44a47fSAlexander V. Chernikov        AttrDescr(IpFwTlvType.IPFW_TLV_RULE_LIST, CTlvRule),
5009f44a47fSAlexander V. Chernikov    ]
5019f44a47fSAlexander V. Chernikov)
5029f44a47fSAlexander V. Chernikov
5039f44a47fSAlexander V. Chernikov
5049f44a47fSAlexander V. Chernikovclass IpFwXRule(BaseIpFwMessage):
5059f44a47fSAlexander V. Chernikov    messages = [Op3CmdType.IP_FW_XADD]
5069f44a47fSAlexander V. Chernikov    attr_map = rule_attrs
5079f44a47fSAlexander V. Chernikov
5089f44a47fSAlexander V. Chernikov
5099f44a47fSAlexander V. Chernikovlegacy_classes = []
5109f44a47fSAlexander V. Chernikovset3_classes = []
5119f44a47fSAlexander V. Chernikovget3_classes = [IpFwXRule]
512