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. Chernikovfrom atf_python.sys.netpfil.ipfw.insn_headers import IpFwOpcode
269f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.insn_headers import IcmpRejectCode
279f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.insn_headers import Icmp6RejectCode
289f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import AttrDescr
299f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import enum_or_int
309f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import enum_from_int
319f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import prepare_attrs_map
329f44a47fSAlexander V. Chernikov
339f44a47fSAlexander V. Chernikov
349f44a47fSAlexander V. Chernikovinsn_actions = (
359f44a47fSAlexander V. Chernikov    IpFwOpcode.O_CHECK_STATE.value,
369f44a47fSAlexander V. Chernikov    IpFwOpcode.O_REJECT.value,
379f44a47fSAlexander V. Chernikov    IpFwOpcode.O_UNREACH6.value,
389f44a47fSAlexander V. Chernikov    IpFwOpcode.O_ACCEPT.value,
399f44a47fSAlexander V. Chernikov    IpFwOpcode.O_DENY.value,
409f44a47fSAlexander V. Chernikov    IpFwOpcode.O_COUNT.value,
419f44a47fSAlexander V. Chernikov    IpFwOpcode.O_NAT.value,
429f44a47fSAlexander V. Chernikov    IpFwOpcode.O_QUEUE.value,
439f44a47fSAlexander V. Chernikov    IpFwOpcode.O_PIPE.value,
449f44a47fSAlexander V. Chernikov    IpFwOpcode.O_SKIPTO.value,
459f44a47fSAlexander V. Chernikov    IpFwOpcode.O_NETGRAPH.value,
469f44a47fSAlexander V. Chernikov    IpFwOpcode.O_NGTEE.value,
479f44a47fSAlexander V. Chernikov    IpFwOpcode.O_DIVERT.value,
489f44a47fSAlexander V. Chernikov    IpFwOpcode.O_TEE.value,
499f44a47fSAlexander V. Chernikov    IpFwOpcode.O_CALLRETURN.value,
509f44a47fSAlexander V. Chernikov    IpFwOpcode.O_FORWARD_IP.value,
519f44a47fSAlexander V. Chernikov    IpFwOpcode.O_FORWARD_IP6.value,
529f44a47fSAlexander V. Chernikov    IpFwOpcode.O_SETFIB.value,
539f44a47fSAlexander V. Chernikov    IpFwOpcode.O_SETDSCP.value,
549f44a47fSAlexander V. Chernikov    IpFwOpcode.O_REASS.value,
559f44a47fSAlexander V. Chernikov    IpFwOpcode.O_SETMARK.value,
569f44a47fSAlexander V. Chernikov    IpFwOpcode.O_EXTERNAL_ACTION.value,
579f44a47fSAlexander V. Chernikov)
589f44a47fSAlexander V. Chernikov
599f44a47fSAlexander V. Chernikov
609f44a47fSAlexander V. Chernikovclass IpFwInsn(Structure):
619f44a47fSAlexander V. Chernikov    _fields_ = [
629f44a47fSAlexander V. Chernikov        ("opcode", c_uint8),
639f44a47fSAlexander V. Chernikov        ("length", c_uint8),
649f44a47fSAlexander V. Chernikov        ("arg1", c_ushort),
659f44a47fSAlexander V. Chernikov    ]
669f44a47fSAlexander V. Chernikov
679f44a47fSAlexander V. Chernikov
689f44a47fSAlexander V. Chernikovclass BaseInsn(object):
699f44a47fSAlexander V. Chernikov    obj_enum_class = IpFwOpcode
709f44a47fSAlexander V. Chernikov
719f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or, is_not, arg1):
729f44a47fSAlexander V. Chernikov        if isinstance(opcode, Enum):
739f44a47fSAlexander V. Chernikov            self.obj_type = opcode.value
749f44a47fSAlexander V. Chernikov            self._enum = opcode
759f44a47fSAlexander V. Chernikov        else:
769f44a47fSAlexander V. Chernikov            self.obj_type = opcode
779f44a47fSAlexander V. Chernikov            self._enum = enum_from_int(self.obj_enum_class, self.obj_type)
789f44a47fSAlexander V. Chernikov        self.is_or = is_or
799f44a47fSAlexander V. Chernikov        self.is_not = is_not
809f44a47fSAlexander V. Chernikov        self.arg1 = arg1
819f44a47fSAlexander V. Chernikov        self.is_action = self.obj_type in insn_actions
829f44a47fSAlexander V. Chernikov        self.ilen = 1
839f44a47fSAlexander V. Chernikov        self.obj_list = []
849f44a47fSAlexander V. Chernikov
859f44a47fSAlexander V. Chernikov    @property
869f44a47fSAlexander V. Chernikov    def obj_name(self):
879f44a47fSAlexander V. Chernikov        if self._enum is not None:
889f44a47fSAlexander V. Chernikov            return self._enum.name
899f44a47fSAlexander V. Chernikov        else:
909f44a47fSAlexander V. Chernikov            return "opcode#{}".format(self.obj_type)
919f44a47fSAlexander V. Chernikov
929f44a47fSAlexander V. Chernikov    @staticmethod
939f44a47fSAlexander V. Chernikov    def get_insn_len(data: bytes) -> int:
949f44a47fSAlexander V. Chernikov        (opcode_len,) = struct.unpack("@B", data[1:2])
959f44a47fSAlexander V. Chernikov        return opcode_len & 0x3F
969f44a47fSAlexander V. Chernikov
979f44a47fSAlexander V. Chernikov    @classmethod
989f44a47fSAlexander V. Chernikov    def _validate_len(cls, data, valid_options=None):
999f44a47fSAlexander V. Chernikov        if len(data) < 4:
1009f44a47fSAlexander V. Chernikov            raise ValueError("opcode too short")
1019f44a47fSAlexander V. Chernikov        opcode_type, opcode_len = struct.unpack("@BB", data[:2])
1029f44a47fSAlexander V. Chernikov        if len(data) != ((opcode_len & 0x3F) * 4):
1039f44a47fSAlexander V. Chernikov            raise ValueError("wrong length")
1049f44a47fSAlexander V. Chernikov        if valid_options and len(data) not in valid_options:
1059f44a47fSAlexander V. Chernikov            raise ValueError(
1069f44a47fSAlexander V. Chernikov                "len {} not in {} for {}".format(
1079f44a47fSAlexander V. Chernikov                    len(data), valid_options,
1089f44a47fSAlexander V. Chernikov                    enum_from_int(cls.obj_enum_class, data[0])
1099f44a47fSAlexander V. Chernikov                )
1109f44a47fSAlexander V. Chernikov            )
1119f44a47fSAlexander V. Chernikov
1129f44a47fSAlexander V. Chernikov    @classmethod
1139f44a47fSAlexander V. Chernikov    def _validate(cls, data):
1149f44a47fSAlexander V. Chernikov        cls._validate_len(data)
1159f44a47fSAlexander V. Chernikov
1169f44a47fSAlexander V. Chernikov    @classmethod
1179f44a47fSAlexander V. Chernikov    def _parse(cls, data):
1189f44a47fSAlexander V. Chernikov        insn = IpFwInsn.from_buffer_copy(data[:4])
1199f44a47fSAlexander V. Chernikov        is_or = (insn.length & 0x40) != 0
1209f44a47fSAlexander V. Chernikov        is_not = (insn.length & 0x80) != 0
1219f44a47fSAlexander V. Chernikov        return cls(opcode=insn.opcode, is_or=is_or, is_not=is_not, arg1=insn.arg1)
1229f44a47fSAlexander V. Chernikov
1239f44a47fSAlexander V. Chernikov    @classmethod
1249f44a47fSAlexander V. Chernikov    def from_bytes(cls, data, attr_type_enum):
1259f44a47fSAlexander V. Chernikov        cls._validate(data)
1269f44a47fSAlexander V. Chernikov        opcode = cls._parse(data)
1279f44a47fSAlexander V. Chernikov        opcode._enum = attr_type_enum
1289f44a47fSAlexander V. Chernikov        return opcode
1299f44a47fSAlexander V. Chernikov
1309f44a47fSAlexander V. Chernikov    def __bytes__(self):
1319f44a47fSAlexander V. Chernikov        raise NotImplementedError()
1329f44a47fSAlexander V. Chernikov
1339f44a47fSAlexander V. Chernikov    def print_obj(self, prepend=""):
1349f44a47fSAlexander V. Chernikov        is_or = ""
1359f44a47fSAlexander V. Chernikov        if self.is_or:
1369f44a47fSAlexander V. Chernikov            is_or = " [OR]\\"
1379f44a47fSAlexander V. Chernikov        is_not = ""
1389f44a47fSAlexander V. Chernikov        if self.is_not:
1399f44a47fSAlexander V. Chernikov            is_not = "[!] "
1409f44a47fSAlexander V. Chernikov        print(
1419f44a47fSAlexander V. Chernikov            "{}{}len={} type={}({}){}{}".format(
1429f44a47fSAlexander V. Chernikov                prepend,
1439f44a47fSAlexander V. Chernikov                is_not,
1449f44a47fSAlexander V. Chernikov                len(bytes(self)),
1459f44a47fSAlexander V. Chernikov                self.obj_name,
1469f44a47fSAlexander V. Chernikov                self.obj_type,
1479f44a47fSAlexander V. Chernikov                self._print_obj_value(),
1489f44a47fSAlexander V. Chernikov                is_or,
1499f44a47fSAlexander V. Chernikov            )
1509f44a47fSAlexander V. Chernikov        )
1519f44a47fSAlexander V. Chernikov
1529f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
1539f44a47fSAlexander V. Chernikov        raise NotImplementedError()
1549f44a47fSAlexander V. Chernikov
1559f44a47fSAlexander V. Chernikov    def print_obj_hex(self, prepend=""):
1569f44a47fSAlexander V. Chernikov        print(prepend)
1579f44a47fSAlexander V. Chernikov        print()
1589f44a47fSAlexander V. Chernikov        print(" ".join(["x{:02X}".format(b) for b in bytes(self)]))
1599f44a47fSAlexander V. Chernikov
1609f44a47fSAlexander V. Chernikov    @staticmethod
1619f44a47fSAlexander V. Chernikov    def parse_insns(data, attr_map):
1629f44a47fSAlexander V. Chernikov        ret = []
1639f44a47fSAlexander V. Chernikov        off = 0
1649f44a47fSAlexander V. Chernikov        while off + sizeof(IpFwInsn) <= len(data):
1659f44a47fSAlexander V. Chernikov            hdr = IpFwInsn.from_buffer_copy(data[off : off + sizeof(IpFwInsn)])
1669f44a47fSAlexander V. Chernikov            insn_len = (hdr.length & 0x3F) * 4
1679f44a47fSAlexander V. Chernikov            if off + insn_len > len(data):
1689f44a47fSAlexander V. Chernikov                raise ValueError("wrng length")
1699f44a47fSAlexander V. Chernikov            # print("GET insn type {} len {}".format(hdr.opcode, insn_len))
1709f44a47fSAlexander V. Chernikov            attr = attr_map.get(hdr.opcode, None)
1719f44a47fSAlexander V. Chernikov            if attr is None:
1729f44a47fSAlexander V. Chernikov                cls = InsnUnknown
1739f44a47fSAlexander V. Chernikov                type_enum = enum_from_int(BaseInsn.obj_enum_class, hdr.opcode)
1749f44a47fSAlexander V. Chernikov            else:
1759f44a47fSAlexander V. Chernikov                cls = attr["ad"].cls
1769f44a47fSAlexander V. Chernikov                type_enum = attr["ad"].val
1779f44a47fSAlexander V. Chernikov            insn = cls.from_bytes(data[off : off + insn_len], type_enum)
1789f44a47fSAlexander V. Chernikov            ret.append(insn)
1799f44a47fSAlexander V. Chernikov            off += insn_len
1809f44a47fSAlexander V. Chernikov
1819f44a47fSAlexander V. Chernikov        if off != len(data):
1829f44a47fSAlexander V. Chernikov            raise ValueError("empty space")
1839f44a47fSAlexander V. Chernikov        return ret
1849f44a47fSAlexander V. Chernikov
1859f44a47fSAlexander V. Chernikov
1869f44a47fSAlexander V. Chernikovclass Insn(BaseInsn):
1879f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0):
1889f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
1899f44a47fSAlexander V. Chernikov
1909f44a47fSAlexander V. Chernikov    @classmethod
1919f44a47fSAlexander V. Chernikov    def _validate(cls, data):
1929f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4])
1939f44a47fSAlexander V. Chernikov
1949f44a47fSAlexander V. Chernikov    def __bytes__(self):
1959f44a47fSAlexander V. Chernikov        length = self.ilen
1969f44a47fSAlexander V. Chernikov        if self.is_or:
1979f44a47fSAlexander V. Chernikov            length |= 0x40
1989f44a47fSAlexander V. Chernikov        if self.is_not:
1999f44a47fSAlexander V. Chernikov            length | 0x80
2009f44a47fSAlexander V. Chernikov        insn = IpFwInsn(opcode=self.obj_type, length=length, arg1=enum_or_int(self.arg1))
2019f44a47fSAlexander V. Chernikov        return bytes(insn)
2029f44a47fSAlexander V. Chernikov
2039f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
2049f44a47fSAlexander V. Chernikov        return " arg1={}".format(self.arg1)
2059f44a47fSAlexander V. Chernikov
2069f44a47fSAlexander V. Chernikov
2079f44a47fSAlexander V. Chernikovclass InsnUnknown(Insn):
2089f44a47fSAlexander V. Chernikov    @classmethod
2099f44a47fSAlexander V. Chernikov    def _validate(cls, data):
2109f44a47fSAlexander V. Chernikov        cls._validate_len(data)
2119f44a47fSAlexander V. Chernikov
2129f44a47fSAlexander V. Chernikov    @classmethod
2139f44a47fSAlexander V. Chernikov    def _parse(cls, data):
2149f44a47fSAlexander V. Chernikov        self = super()._parse(data)
2159f44a47fSAlexander V. Chernikov        self._data = data
2169f44a47fSAlexander V. Chernikov        return self
2179f44a47fSAlexander V. Chernikov
2189f44a47fSAlexander V. Chernikov    def __bytes__(self):
2199f44a47fSAlexander V. Chernikov        return self._data
2209f44a47fSAlexander V. Chernikov
2219f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
2229f44a47fSAlexander V. Chernikov        return " " + " ".join(["x{:02X}".format(b) for b in self._data])
2239f44a47fSAlexander V. Chernikov
2249f44a47fSAlexander V. Chernikov
2259f44a47fSAlexander V. Chernikovclass InsnEmpty(Insn):
2269f44a47fSAlexander V. Chernikov    @classmethod
2279f44a47fSAlexander V. Chernikov    def _validate(cls, data):
2289f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4])
2299f44a47fSAlexander V. Chernikov        insn = IpFwInsn.from_buffer_copy(data[:4])
2309f44a47fSAlexander V. Chernikov        if insn.arg1 != 0:
2319f44a47fSAlexander V. Chernikov            raise ValueError("arg1 should be empty")
2329f44a47fSAlexander V. Chernikov
2339f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
2349f44a47fSAlexander V. Chernikov        return ""
2359f44a47fSAlexander V. Chernikov
2369f44a47fSAlexander V. Chernikov
2379f44a47fSAlexander V. Chernikovclass InsnComment(Insn):
2389f44a47fSAlexander V. Chernikov    def __init__(self, opcode=IpFwOpcode.O_NOP, is_or=False, is_not=False, arg1=0, comment=""):
2399f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
2409f44a47fSAlexander V. Chernikov        if comment:
2419f44a47fSAlexander V. Chernikov            self.comment = comment
2429f44a47fSAlexander V. Chernikov        else:
2439f44a47fSAlexander V. Chernikov            self.comment = ""
2449f44a47fSAlexander V. Chernikov
2459f44a47fSAlexander V. Chernikov    @classmethod
2469f44a47fSAlexander V. Chernikov    def _validate(cls, data):
2479f44a47fSAlexander V. Chernikov        cls._validate_len(data)
2489f44a47fSAlexander V. Chernikov        if len(data) > 88:
2499f44a47fSAlexander V. Chernikov            raise ValueError("comment too long")
2509f44a47fSAlexander V. Chernikov
2519f44a47fSAlexander V. Chernikov    @classmethod
2529f44a47fSAlexander V. Chernikov    def _parse(cls, data):
2539f44a47fSAlexander V. Chernikov        self = super()._parse(data)
2549f44a47fSAlexander V. Chernikov        # Comment encoding can be anything,
2559f44a47fSAlexander V. Chernikov        # use utf-8 to ease debugging
2569f44a47fSAlexander V. Chernikov        max_len = 0
2579f44a47fSAlexander V. Chernikov        for b in range(4, len(data)):
2589f44a47fSAlexander V. Chernikov            if data[b] == b"\0":
2599f44a47fSAlexander V. Chernikov                break
2609f44a47fSAlexander V. Chernikov            max_len += 1
2619f44a47fSAlexander V. Chernikov        self.comment = data[4:max_len].decode("utf-8")
2629f44a47fSAlexander V. Chernikov        return self
2639f44a47fSAlexander V. Chernikov
2649f44a47fSAlexander V. Chernikov    def __bytes__(self):
2659f44a47fSAlexander V. Chernikov        ret = super().__bytes__()
2669f44a47fSAlexander V. Chernikov        comment_bytes = self.comment.encode("utf-8") + b"\0"
2679f44a47fSAlexander V. Chernikov        if len(comment_bytes) % 4 > 0:
2689f44a47fSAlexander V. Chernikov            comment_bytes += b"\0" * (4 - (len(comment_bytes) % 4))
2699f44a47fSAlexander V. Chernikov        ret += comment_bytes
2709f44a47fSAlexander V. Chernikov        return ret
2719f44a47fSAlexander V. Chernikov
2729f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
2739f44a47fSAlexander V. Chernikov        return " comment='{}'".format(self.comment)
2749f44a47fSAlexander V. Chernikov
2759f44a47fSAlexander V. Chernikov
2769f44a47fSAlexander V. Chernikovclass InsnProto(Insn):
2779f44a47fSAlexander V. Chernikov    def __init__(self, opcode=IpFwOpcode.O_PROTO, is_or=False, is_not=False, arg1=0):
2789f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
2799f44a47fSAlexander V. Chernikov
2809f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
2819f44a47fSAlexander V. Chernikov        known_map = {6: "TCP", 17: "UDP", 41: "IPV6"}
2829f44a47fSAlexander V. Chernikov        proto = self.arg1
2839f44a47fSAlexander V. Chernikov        if proto in known_map:
2849f44a47fSAlexander V. Chernikov            return " proto={}".format(known_map[proto])
2859f44a47fSAlexander V. Chernikov        else:
2869f44a47fSAlexander V. Chernikov            return " proto=#{}".format(proto)
2879f44a47fSAlexander V. Chernikov
2889f44a47fSAlexander V. Chernikov
2899f44a47fSAlexander V. Chernikovclass InsnU32(Insn):
2909f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0):
2919f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
2929f44a47fSAlexander V. Chernikov        self.u32 = u32
2939f44a47fSAlexander V. Chernikov        self.ilen = 2
2949f44a47fSAlexander V. Chernikov
2959f44a47fSAlexander V. Chernikov    @classmethod
2969f44a47fSAlexander V. Chernikov    def _validate(cls, data):
2979f44a47fSAlexander V. Chernikov        cls._validate_len(data, [8])
2989f44a47fSAlexander V. Chernikov
2999f44a47fSAlexander V. Chernikov    @classmethod
3009f44a47fSAlexander V. Chernikov    def _parse(cls, data):
3019f44a47fSAlexander V. Chernikov        self = super()._parse(data[:4])
3029f44a47fSAlexander V. Chernikov        self.u32 = struct.unpack("@I", data[4:8])[0]
3039f44a47fSAlexander V. Chernikov        return self
3049f44a47fSAlexander V. Chernikov
3059f44a47fSAlexander V. Chernikov    def __bytes__(self):
3069f44a47fSAlexander V. Chernikov        return super().__bytes__() + struct.pack("@I", self.u32)
3079f44a47fSAlexander V. Chernikov
3089f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
3099f44a47fSAlexander V. Chernikov        return " arg1={} u32={}".format(self.arg1, self.u32)
3109f44a47fSAlexander V. Chernikov
3119f44a47fSAlexander V. Chernikov
3129f44a47fSAlexander V. Chernikovclass InsnProb(InsnU32):
3139f44a47fSAlexander V. Chernikov    def __init__(
3149f44a47fSAlexander V. Chernikov        self,
3159f44a47fSAlexander V. Chernikov        opcode=IpFwOpcode.O_PROB,
3169f44a47fSAlexander V. Chernikov        is_or=False,
3179f44a47fSAlexander V. Chernikov        is_not=False,
3189f44a47fSAlexander V. Chernikov        arg1=0,
3199f44a47fSAlexander V. Chernikov        u32=0,
3209f44a47fSAlexander V. Chernikov        prob=0.0,
3219f44a47fSAlexander V. Chernikov    ):
3229f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not)
3239f44a47fSAlexander V. Chernikov        self.prob = prob
3249f44a47fSAlexander V. Chernikov
3259f44a47fSAlexander V. Chernikov    @property
3269f44a47fSAlexander V. Chernikov    def prob(self):
3279f44a47fSAlexander V. Chernikov        return 1.0 * self.u32 / 0x7FFFFFFF
3289f44a47fSAlexander V. Chernikov
3299f44a47fSAlexander V. Chernikov    @prob.setter
3309f44a47fSAlexander V. Chernikov    def prob(self, prob: float):
3319f44a47fSAlexander V. Chernikov        self.u32 = int(prob * 0x7FFFFFFF)
3329f44a47fSAlexander V. Chernikov
3339f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
3349f44a47fSAlexander V. Chernikov        return " prob={}".format(round(self.prob, 5))
3359f44a47fSAlexander V. Chernikov
3369f44a47fSAlexander V. Chernikov
3379f44a47fSAlexander V. Chernikovclass InsnIp(InsnU32):
3389f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0, ip=None):
3399f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1, u32=u32)
3409f44a47fSAlexander V. Chernikov        if ip:
3419f44a47fSAlexander V. Chernikov            self.ip = ip
3429f44a47fSAlexander V. Chernikov
3439f44a47fSAlexander V. Chernikov    @property
3449f44a47fSAlexander V. Chernikov    def ip(self):
3459f44a47fSAlexander V. Chernikov        return socket.inet_ntop(socket.AF_INET, struct.pack("@I", self.u32))
3469f44a47fSAlexander V. Chernikov
3479f44a47fSAlexander V. Chernikov    @ip.setter
3489f44a47fSAlexander V. Chernikov    def ip(self, ip: str):
3499f44a47fSAlexander V. Chernikov        ip_bin = socket.inet_pton(socket.AF_INET, ip)
3509f44a47fSAlexander V. Chernikov        self.u32 = struct.unpack("@I", ip_bin)[0]
3519f44a47fSAlexander V. Chernikov
3529f44a47fSAlexander V. Chernikov    def _print_opcode_value(self):
3539f44a47fSAlexander V. Chernikov        return " ip={}".format(self.ip)
3549f44a47fSAlexander V. Chernikov
3559f44a47fSAlexander V. Chernikov
3569f44a47fSAlexander V. Chernikovclass InsnTable(Insn):
3579f44a47fSAlexander V. Chernikov    @classmethod
3589f44a47fSAlexander V. Chernikov    def _validate(cls, data):
3599f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4, 8])
3609f44a47fSAlexander V. Chernikov
3619f44a47fSAlexander V. Chernikov    @classmethod
3629f44a47fSAlexander V. Chernikov    def _parse(cls, data):
3639f44a47fSAlexander V. Chernikov        self = super()._parse(data)
3649f44a47fSAlexander V. Chernikov
3659f44a47fSAlexander V. Chernikov        if len(data) == 8:
3669f44a47fSAlexander V. Chernikov            (self.val,) = struct.unpack("@I", data[4:8])
3679f44a47fSAlexander V. Chernikov            self.ilen = 2
3689f44a47fSAlexander V. Chernikov        else:
3699f44a47fSAlexander V. Chernikov            self.val = None
3709f44a47fSAlexander V. Chernikov        return self
3719f44a47fSAlexander V. Chernikov
3729f44a47fSAlexander V. Chernikov    def __bytes__(self):
3739f44a47fSAlexander V. Chernikov        ret = super().__bytes__()
3749f44a47fSAlexander V. Chernikov        if getattr(self, "val", None) is not None:
3759f44a47fSAlexander V. Chernikov            ret += struct.pack("@I", self.val)
3769f44a47fSAlexander V. Chernikov        return ret
3779f44a47fSAlexander V. Chernikov
3789f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
3799f44a47fSAlexander V. Chernikov        if getattr(self, "val", None) is not None:
3809f44a47fSAlexander V. Chernikov            return " table={} value={}".format(self.arg1, self.val)
3819f44a47fSAlexander V. Chernikov        else:
3829f44a47fSAlexander V. Chernikov            return " table={}".format(self.arg1)
3839f44a47fSAlexander V. Chernikov
3849f44a47fSAlexander V. Chernikov
3859f44a47fSAlexander V. Chernikovclass InsnReject(Insn):
3869f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, mtu=None):
3879f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
3889f44a47fSAlexander V. Chernikov        self.mtu = mtu
3899f44a47fSAlexander V. Chernikov        if self.mtu is not None:
3909f44a47fSAlexander V. Chernikov            self.ilen = 2
3919f44a47fSAlexander V. Chernikov
3929f44a47fSAlexander V. Chernikov    @classmethod
3939f44a47fSAlexander V. Chernikov    def _validate(cls, data):
3949f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4, 8])
3959f44a47fSAlexander V. Chernikov
3969f44a47fSAlexander V. Chernikov    @classmethod
3979f44a47fSAlexander V. Chernikov    def _parse(cls, data):
3989f44a47fSAlexander V. Chernikov        self = super()._parse(data)
3999f44a47fSAlexander V. Chernikov
4009f44a47fSAlexander V. Chernikov        if len(data) == 8:
4019f44a47fSAlexander V. Chernikov            (self.mtu,) = struct.unpack("@I", data[4:8])
4029f44a47fSAlexander V. Chernikov            self.ilen = 2
4039f44a47fSAlexander V. Chernikov        else:
4049f44a47fSAlexander V. Chernikov            self.mtu = None
4059f44a47fSAlexander V. Chernikov        return self
4069f44a47fSAlexander V. Chernikov
4079f44a47fSAlexander V. Chernikov    def __bytes__(self):
4089f44a47fSAlexander V. Chernikov        ret = super().__bytes__()
4099f44a47fSAlexander V. Chernikov        if getattr(self, "mtu", None) is not None:
4109f44a47fSAlexander V. Chernikov            ret += struct.pack("@I", self.mtu)
4119f44a47fSAlexander V. Chernikov        return ret
4129f44a47fSAlexander V. Chernikov
4139f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
4149f44a47fSAlexander V. Chernikov        code = enum_from_int(IcmpRejectCode, self.arg1)
4159f44a47fSAlexander V. Chernikov        if getattr(self, "mtu", None) is not None:
4169f44a47fSAlexander V. Chernikov            return " code={} mtu={}".format(code, self.mtu)
4179f44a47fSAlexander V. Chernikov        else:
4189f44a47fSAlexander V. Chernikov            return " code={}".format(code)
4199f44a47fSAlexander V. Chernikov
4209f44a47fSAlexander V. Chernikov
4219f44a47fSAlexander V. Chernikovclass InsnPorts(Insn):
4229f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, port_pairs=[]):
4239f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not)
4249f44a47fSAlexander V. Chernikov        self.port_pairs = []
4259f44a47fSAlexander V. Chernikov        if port_pairs:
4269f44a47fSAlexander V. Chernikov            self.port_pairs = port_pairs
4279f44a47fSAlexander V. Chernikov
4289f44a47fSAlexander V. Chernikov    @classmethod
4299f44a47fSAlexander V. Chernikov    def _validate(cls, data):
4309f44a47fSAlexander V. Chernikov        if len(data) < 8:
4319f44a47fSAlexander V. Chernikov            raise ValueError("no ports specified")
4329f44a47fSAlexander V. Chernikov        cls._validate_len(data)
4339f44a47fSAlexander V. Chernikov
4349f44a47fSAlexander V. Chernikov    @classmethod
4359f44a47fSAlexander V. Chernikov    def _parse(cls, data):
4369f44a47fSAlexander V. Chernikov        self = super()._parse(data)
4379f44a47fSAlexander V. Chernikov
4389f44a47fSAlexander V. Chernikov        off = 4
4399f44a47fSAlexander V. Chernikov        port_pairs = []
4409f44a47fSAlexander V. Chernikov        while off + 4 <= len(data):
4419f44a47fSAlexander V. Chernikov            low, high = struct.unpack("@HH", data[off : off + 4])
4429f44a47fSAlexander V. Chernikov            port_pairs.append((low, high))
4439f44a47fSAlexander V. Chernikov            off += 4
4449f44a47fSAlexander V. Chernikov        self.port_pairs = port_pairs
4459f44a47fSAlexander V. Chernikov        return self
4469f44a47fSAlexander V. Chernikov
4479f44a47fSAlexander V. Chernikov    def __bytes__(self):
4489f44a47fSAlexander V. Chernikov        ret = super().__bytes__()
4499f44a47fSAlexander V. Chernikov        if getattr(self, "val", None) is not None:
4509f44a47fSAlexander V. Chernikov            ret += struct.pack("@I", self.val)
4519f44a47fSAlexander V. Chernikov        return ret
4529f44a47fSAlexander V. Chernikov
4539f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
4549f44a47fSAlexander V. Chernikov        ret = []
4559f44a47fSAlexander V. Chernikov        for p in self.port_pairs:
4569f44a47fSAlexander V. Chernikov            if p[0] == p[1]:
4579f44a47fSAlexander V. Chernikov                ret.append(str(p[0]))
4589f44a47fSAlexander V. Chernikov            else:
4599f44a47fSAlexander V. Chernikov                ret.append("{}-{}".format(p[0], p[1]))
4609f44a47fSAlexander V. Chernikov        return " ports={}".format(",".join(ret))
4619f44a47fSAlexander V. Chernikov
4629f44a47fSAlexander V. Chernikov
4639f44a47fSAlexander V. Chernikovclass IpFwInsnIp6(Structure):
4649f44a47fSAlexander V. Chernikov    _fields_ = [
4659f44a47fSAlexander V. Chernikov        ("o", IpFwInsn),
4669f44a47fSAlexander V. Chernikov        ("addr6", c_byte * 16),
4679f44a47fSAlexander V. Chernikov        ("mask6", c_byte * 16),
4689f44a47fSAlexander V. Chernikov    ]
4699f44a47fSAlexander V. Chernikov
4709f44a47fSAlexander V. Chernikov
4719f44a47fSAlexander V. Chernikovclass InsnIp6(Insn):
4729f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, ip6=None, mask6=None):
4739f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
4749f44a47fSAlexander V. Chernikov        self.ip6 = ip6
4759f44a47fSAlexander V. Chernikov        self.mask6 = mask6
4769f44a47fSAlexander V. Chernikov        if mask6 is not None:
4779f44a47fSAlexander V. Chernikov            self.ilen = 9
4789f44a47fSAlexander V. Chernikov        else:
4799f44a47fSAlexander V. Chernikov            self.ilen = 5
4809f44a47fSAlexander V. Chernikov
4819f44a47fSAlexander V. Chernikov    @classmethod
4829f44a47fSAlexander V. Chernikov    def _validate(cls, data):
4839f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4 + 16, 4 + 16 * 2])
4849f44a47fSAlexander V. Chernikov
4859f44a47fSAlexander V. Chernikov    @classmethod
4869f44a47fSAlexander V. Chernikov    def _parse(cls, data):
4879f44a47fSAlexander V. Chernikov        self = super()._parse(data)
4889f44a47fSAlexander V. Chernikov        self.ip6 = socket.inet_ntop(socket.AF_INET6, data[4:20])
4899f44a47fSAlexander V. Chernikov
4909f44a47fSAlexander V. Chernikov        if len(data) == 4 + 16 * 2:
4919f44a47fSAlexander V. Chernikov            self.mask6 = socket.inet_ntop(socket.AF_INET6, data[20:36])
4929f44a47fSAlexander V. Chernikov            self.ilen = 9
4939f44a47fSAlexander V. Chernikov        else:
4949f44a47fSAlexander V. Chernikov            self.mask6 = None
4959f44a47fSAlexander V. Chernikov            self.ilen = 5
4969f44a47fSAlexander V. Chernikov        return self
4979f44a47fSAlexander V. Chernikov
4989f44a47fSAlexander V. Chernikov    def __bytes__(self):
4999f44a47fSAlexander V. Chernikov        ret = super().__bytes__() + socket.inet_pton(socket.AF_INET6, self.ip6)
5009f44a47fSAlexander V. Chernikov        if self.mask6 is not None:
5019f44a47fSAlexander V. Chernikov            ret += socket.inet_pton(socket.AF_INET6, self.mask6)
5029f44a47fSAlexander V. Chernikov        return ret
5039f44a47fSAlexander V. Chernikov
5049f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
5059f44a47fSAlexander V. Chernikov        if self.mask6:
5069f44a47fSAlexander V. Chernikov            return " ip6={}/{}".format(self.ip6, self.mask6)
5079f44a47fSAlexander V. Chernikov        else:
5089f44a47fSAlexander V. Chernikov            return " ip6={}".format(self.ip6)
5099f44a47fSAlexander V. Chernikov
5109f44a47fSAlexander V. Chernikov
5119f44a47fSAlexander V. Chernikovinsn_attrs = prepare_attrs_map(
5129f44a47fSAlexander V. Chernikov    [
5139f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_CHECK_STATE, Insn),
5149f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_ACCEPT, InsnEmpty),
5159f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),
5169f44a47fSAlexander V. Chernikov
5179f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_REJECT, InsnReject),
5189f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_UNREACH6, Insn),
5199f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_DENY, InsnEmpty),
5209f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_DIVERT, Insn),
5219f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),
5229f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_QUEUE, Insn),
5239f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_PIPE, Insn),
5249f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_SKIPTO, Insn),
5259f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_NETGRAPH, Insn),
5269f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_NGTEE, Insn),
5279f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_DIVERT, Insn),
5289f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_TEE, Insn),
5299f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_CALLRETURN, Insn),
5309f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_SETFIB, Insn),
5319f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_SETDSCP, Insn),
5329f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_REASS, InsnEmpty),
5339f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_SETMARK, Insn),
5349f44a47fSAlexander V. Chernikov
5359f44a47fSAlexander V. Chernikov
5369f44a47fSAlexander V. Chernikov
5379f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_NOP, InsnComment),
5389f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_PROTO, InsnProto),
5399f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_PROB, InsnProb),
5409f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_DST_ME, InsnEmpty),
5419f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_SRC_ME, InsnEmpty),
5429f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP6_DST_ME, InsnEmpty),
5439f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP6_SRC_ME, InsnEmpty),
5449f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_SRC, InsnIp),
5459f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_DST, InsnIp),
5469f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP6_DST, InsnIp6),
5479f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP6_SRC, InsnIp6),
5489f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_SRC_LOOKUP, InsnTable),
5499f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_DST_LOOKUP, InsnTable),
5509f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_SRCPORT, InsnPorts),
5519f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_DSTPORT, InsnPorts),
5529f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_PROBE_STATE, Insn),
5539f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_KEEP_STATE, Insn),
5549f44a47fSAlexander V. Chernikov    ]
5559f44a47fSAlexander V. Chernikov)
556