xref: /freebsd/tests/atf_python/sys/netpfil/ipfw/insns.py (revision 9f44a47fd07924afc035991af15d84e6585dea4f)
1*9f44a47fSAlexander V. Chernikov#!/usr/bin/env python3
2*9f44a47fSAlexander V. Chernikovimport os
3*9f44a47fSAlexander V. Chernikovimport socket
4*9f44a47fSAlexander V. Chernikovimport struct
5*9f44a47fSAlexander V. Chernikovimport subprocess
6*9f44a47fSAlexander V. Chernikovimport sys
7*9f44a47fSAlexander V. Chernikovfrom ctypes import c_byte
8*9f44a47fSAlexander V. Chernikovfrom ctypes import c_char
9*9f44a47fSAlexander V. Chernikovfrom ctypes import c_int
10*9f44a47fSAlexander V. Chernikovfrom ctypes import c_long
11*9f44a47fSAlexander V. Chernikovfrom ctypes import c_uint32
12*9f44a47fSAlexander V. Chernikovfrom ctypes import c_uint8
13*9f44a47fSAlexander V. Chernikovfrom ctypes import c_ulong
14*9f44a47fSAlexander V. Chernikovfrom ctypes import c_ushort
15*9f44a47fSAlexander V. Chernikovfrom ctypes import sizeof
16*9f44a47fSAlexander V. Chernikovfrom ctypes import Structure
17*9f44a47fSAlexander V. Chernikovfrom enum import Enum
18*9f44a47fSAlexander V. Chernikovfrom typing import Any
19*9f44a47fSAlexander V. Chernikovfrom typing import Dict
20*9f44a47fSAlexander V. Chernikovfrom typing import List
21*9f44a47fSAlexander V. Chernikovfrom typing import NamedTuple
22*9f44a47fSAlexander V. Chernikovfrom typing import Optional
23*9f44a47fSAlexander V. Chernikovfrom typing import Union
24*9f44a47fSAlexander V. Chernikov
25*9f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.insn_headers import IpFwOpcode
26*9f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.insn_headers import IcmpRejectCode
27*9f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.insn_headers import Icmp6RejectCode
28*9f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import AttrDescr
29*9f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import enum_or_int
30*9f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import enum_from_int
31*9f44a47fSAlexander V. Chernikovfrom atf_python.sys.netpfil.ipfw.utils import prepare_attrs_map
32*9f44a47fSAlexander V. Chernikov
33*9f44a47fSAlexander V. Chernikov
34*9f44a47fSAlexander V. Chernikovinsn_actions = (
35*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_CHECK_STATE.value,
36*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_REJECT.value,
37*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_UNREACH6.value,
38*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_ACCEPT.value,
39*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_DENY.value,
40*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_COUNT.value,
41*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_NAT.value,
42*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_QUEUE.value,
43*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_PIPE.value,
44*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_SKIPTO.value,
45*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_NETGRAPH.value,
46*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_NGTEE.value,
47*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_DIVERT.value,
48*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_TEE.value,
49*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_CALLRETURN.value,
50*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_FORWARD_IP.value,
51*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_FORWARD_IP6.value,
52*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_SETFIB.value,
53*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_SETDSCP.value,
54*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_REASS.value,
55*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_SETMARK.value,
56*9f44a47fSAlexander V. Chernikov    IpFwOpcode.O_EXTERNAL_ACTION.value,
57*9f44a47fSAlexander V. Chernikov)
58*9f44a47fSAlexander V. Chernikov
59*9f44a47fSAlexander V. Chernikov
60*9f44a47fSAlexander V. Chernikovclass IpFwInsn(Structure):
61*9f44a47fSAlexander V. Chernikov    _fields_ = [
62*9f44a47fSAlexander V. Chernikov        ("opcode", c_uint8),
63*9f44a47fSAlexander V. Chernikov        ("length", c_uint8),
64*9f44a47fSAlexander V. Chernikov        ("arg1", c_ushort),
65*9f44a47fSAlexander V. Chernikov    ]
66*9f44a47fSAlexander V. Chernikov
67*9f44a47fSAlexander V. Chernikov
68*9f44a47fSAlexander V. Chernikovclass BaseInsn(object):
69*9f44a47fSAlexander V. Chernikov    obj_enum_class = IpFwOpcode
70*9f44a47fSAlexander V. Chernikov
71*9f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or, is_not, arg1):
72*9f44a47fSAlexander V. Chernikov        if isinstance(opcode, Enum):
73*9f44a47fSAlexander V. Chernikov            self.obj_type = opcode.value
74*9f44a47fSAlexander V. Chernikov            self._enum = opcode
75*9f44a47fSAlexander V. Chernikov        else:
76*9f44a47fSAlexander V. Chernikov            self.obj_type = opcode
77*9f44a47fSAlexander V. Chernikov            self._enum = enum_from_int(self.obj_enum_class, self.obj_type)
78*9f44a47fSAlexander V. Chernikov        self.is_or = is_or
79*9f44a47fSAlexander V. Chernikov        self.is_not = is_not
80*9f44a47fSAlexander V. Chernikov        self.arg1 = arg1
81*9f44a47fSAlexander V. Chernikov        self.is_action = self.obj_type in insn_actions
82*9f44a47fSAlexander V. Chernikov        self.ilen = 1
83*9f44a47fSAlexander V. Chernikov        self.obj_list = []
84*9f44a47fSAlexander V. Chernikov
85*9f44a47fSAlexander V. Chernikov    @property
86*9f44a47fSAlexander V. Chernikov    def obj_name(self):
87*9f44a47fSAlexander V. Chernikov        if self._enum is not None:
88*9f44a47fSAlexander V. Chernikov            return self._enum.name
89*9f44a47fSAlexander V. Chernikov        else:
90*9f44a47fSAlexander V. Chernikov            return "opcode#{}".format(self.obj_type)
91*9f44a47fSAlexander V. Chernikov
92*9f44a47fSAlexander V. Chernikov    @staticmethod
93*9f44a47fSAlexander V. Chernikov    def get_insn_len(data: bytes) -> int:
94*9f44a47fSAlexander V. Chernikov        (opcode_len,) = struct.unpack("@B", data[1:2])
95*9f44a47fSAlexander V. Chernikov        return opcode_len & 0x3F
96*9f44a47fSAlexander V. Chernikov
97*9f44a47fSAlexander V. Chernikov    @classmethod
98*9f44a47fSAlexander V. Chernikov    def _validate_len(cls, data, valid_options=None):
99*9f44a47fSAlexander V. Chernikov        if len(data) < 4:
100*9f44a47fSAlexander V. Chernikov            raise ValueError("opcode too short")
101*9f44a47fSAlexander V. Chernikov        opcode_type, opcode_len = struct.unpack("@BB", data[:2])
102*9f44a47fSAlexander V. Chernikov        if len(data) != ((opcode_len & 0x3F) * 4):
103*9f44a47fSAlexander V. Chernikov            raise ValueError("wrong length")
104*9f44a47fSAlexander V. Chernikov        if valid_options and len(data) not in valid_options:
105*9f44a47fSAlexander V. Chernikov            raise ValueError(
106*9f44a47fSAlexander V. Chernikov                "len {} not in {} for {}".format(
107*9f44a47fSAlexander V. Chernikov                    len(data), valid_options,
108*9f44a47fSAlexander V. Chernikov                    enum_from_int(cls.obj_enum_class, data[0])
109*9f44a47fSAlexander V. Chernikov                )
110*9f44a47fSAlexander V. Chernikov            )
111*9f44a47fSAlexander V. Chernikov
112*9f44a47fSAlexander V. Chernikov    @classmethod
113*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
114*9f44a47fSAlexander V. Chernikov        cls._validate_len(data)
115*9f44a47fSAlexander V. Chernikov
116*9f44a47fSAlexander V. Chernikov    @classmethod
117*9f44a47fSAlexander V. Chernikov    def _parse(cls, data):
118*9f44a47fSAlexander V. Chernikov        insn = IpFwInsn.from_buffer_copy(data[:4])
119*9f44a47fSAlexander V. Chernikov        is_or = (insn.length & 0x40) != 0
120*9f44a47fSAlexander V. Chernikov        is_not = (insn.length & 0x80) != 0
121*9f44a47fSAlexander V. Chernikov        return cls(opcode=insn.opcode, is_or=is_or, is_not=is_not, arg1=insn.arg1)
122*9f44a47fSAlexander V. Chernikov
123*9f44a47fSAlexander V. Chernikov    @classmethod
124*9f44a47fSAlexander V. Chernikov    def from_bytes(cls, data, attr_type_enum):
125*9f44a47fSAlexander V. Chernikov        cls._validate(data)
126*9f44a47fSAlexander V. Chernikov        opcode = cls._parse(data)
127*9f44a47fSAlexander V. Chernikov        opcode._enum = attr_type_enum
128*9f44a47fSAlexander V. Chernikov        return opcode
129*9f44a47fSAlexander V. Chernikov
130*9f44a47fSAlexander V. Chernikov    def __bytes__(self):
131*9f44a47fSAlexander V. Chernikov        raise NotImplementedError()
132*9f44a47fSAlexander V. Chernikov
133*9f44a47fSAlexander V. Chernikov    def print_obj(self, prepend=""):
134*9f44a47fSAlexander V. Chernikov        is_or = ""
135*9f44a47fSAlexander V. Chernikov        if self.is_or:
136*9f44a47fSAlexander V. Chernikov            is_or = " [OR]\\"
137*9f44a47fSAlexander V. Chernikov        is_not = ""
138*9f44a47fSAlexander V. Chernikov        if self.is_not:
139*9f44a47fSAlexander V. Chernikov            is_not = "[!] "
140*9f44a47fSAlexander V. Chernikov        print(
141*9f44a47fSAlexander V. Chernikov            "{}{}len={} type={}({}){}{}".format(
142*9f44a47fSAlexander V. Chernikov                prepend,
143*9f44a47fSAlexander V. Chernikov                is_not,
144*9f44a47fSAlexander V. Chernikov                len(bytes(self)),
145*9f44a47fSAlexander V. Chernikov                self.obj_name,
146*9f44a47fSAlexander V. Chernikov                self.obj_type,
147*9f44a47fSAlexander V. Chernikov                self._print_obj_value(),
148*9f44a47fSAlexander V. Chernikov                is_or,
149*9f44a47fSAlexander V. Chernikov            )
150*9f44a47fSAlexander V. Chernikov        )
151*9f44a47fSAlexander V. Chernikov
152*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
153*9f44a47fSAlexander V. Chernikov        raise NotImplementedError()
154*9f44a47fSAlexander V. Chernikov
155*9f44a47fSAlexander V. Chernikov    def print_obj_hex(self, prepend=""):
156*9f44a47fSAlexander V. Chernikov        print(prepend)
157*9f44a47fSAlexander V. Chernikov        print()
158*9f44a47fSAlexander V. Chernikov        print(" ".join(["x{:02X}".format(b) for b in bytes(self)]))
159*9f44a47fSAlexander V. Chernikov
160*9f44a47fSAlexander V. Chernikov    @staticmethod
161*9f44a47fSAlexander V. Chernikov    def parse_insns(data, attr_map):
162*9f44a47fSAlexander V. Chernikov        ret = []
163*9f44a47fSAlexander V. Chernikov        off = 0
164*9f44a47fSAlexander V. Chernikov        while off + sizeof(IpFwInsn) <= len(data):
165*9f44a47fSAlexander V. Chernikov            hdr = IpFwInsn.from_buffer_copy(data[off : off + sizeof(IpFwInsn)])
166*9f44a47fSAlexander V. Chernikov            insn_len = (hdr.length & 0x3F) * 4
167*9f44a47fSAlexander V. Chernikov            if off + insn_len > len(data):
168*9f44a47fSAlexander V. Chernikov                raise ValueError("wrng length")
169*9f44a47fSAlexander V. Chernikov            # print("GET insn type {} len {}".format(hdr.opcode, insn_len))
170*9f44a47fSAlexander V. Chernikov            attr = attr_map.get(hdr.opcode, None)
171*9f44a47fSAlexander V. Chernikov            if attr is None:
172*9f44a47fSAlexander V. Chernikov                cls = InsnUnknown
173*9f44a47fSAlexander V. Chernikov                type_enum = enum_from_int(BaseInsn.obj_enum_class, hdr.opcode)
174*9f44a47fSAlexander V. Chernikov            else:
175*9f44a47fSAlexander V. Chernikov                cls = attr["ad"].cls
176*9f44a47fSAlexander V. Chernikov                type_enum = attr["ad"].val
177*9f44a47fSAlexander V. Chernikov            insn = cls.from_bytes(data[off : off + insn_len], type_enum)
178*9f44a47fSAlexander V. Chernikov            ret.append(insn)
179*9f44a47fSAlexander V. Chernikov            off += insn_len
180*9f44a47fSAlexander V. Chernikov
181*9f44a47fSAlexander V. Chernikov        if off != len(data):
182*9f44a47fSAlexander V. Chernikov            raise ValueError("empty space")
183*9f44a47fSAlexander V. Chernikov        return ret
184*9f44a47fSAlexander V. Chernikov
185*9f44a47fSAlexander V. Chernikov
186*9f44a47fSAlexander V. Chernikovclass Insn(BaseInsn):
187*9f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0):
188*9f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
189*9f44a47fSAlexander V. Chernikov
190*9f44a47fSAlexander V. Chernikov    @classmethod
191*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
192*9f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4])
193*9f44a47fSAlexander V. Chernikov
194*9f44a47fSAlexander V. Chernikov    def __bytes__(self):
195*9f44a47fSAlexander V. Chernikov        length = self.ilen
196*9f44a47fSAlexander V. Chernikov        if self.is_or:
197*9f44a47fSAlexander V. Chernikov            length |= 0x40
198*9f44a47fSAlexander V. Chernikov        if self.is_not:
199*9f44a47fSAlexander V. Chernikov            length | 0x80
200*9f44a47fSAlexander V. Chernikov        insn = IpFwInsn(opcode=self.obj_type, length=length, arg1=enum_or_int(self.arg1))
201*9f44a47fSAlexander V. Chernikov        return bytes(insn)
202*9f44a47fSAlexander V. Chernikov
203*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
204*9f44a47fSAlexander V. Chernikov        return " arg1={}".format(self.arg1)
205*9f44a47fSAlexander V. Chernikov
206*9f44a47fSAlexander V. Chernikov
207*9f44a47fSAlexander V. Chernikovclass InsnUnknown(Insn):
208*9f44a47fSAlexander V. Chernikov    @classmethod
209*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
210*9f44a47fSAlexander V. Chernikov        cls._validate_len(data)
211*9f44a47fSAlexander V. Chernikov
212*9f44a47fSAlexander V. Chernikov    @classmethod
213*9f44a47fSAlexander V. Chernikov    def _parse(cls, data):
214*9f44a47fSAlexander V. Chernikov        self = super()._parse(data)
215*9f44a47fSAlexander V. Chernikov        self._data = data
216*9f44a47fSAlexander V. Chernikov        return self
217*9f44a47fSAlexander V. Chernikov
218*9f44a47fSAlexander V. Chernikov    def __bytes__(self):
219*9f44a47fSAlexander V. Chernikov        return self._data
220*9f44a47fSAlexander V. Chernikov
221*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
222*9f44a47fSAlexander V. Chernikov        return " " + " ".join(["x{:02X}".format(b) for b in self._data])
223*9f44a47fSAlexander V. Chernikov
224*9f44a47fSAlexander V. Chernikov
225*9f44a47fSAlexander V. Chernikovclass InsnEmpty(Insn):
226*9f44a47fSAlexander V. Chernikov    @classmethod
227*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
228*9f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4])
229*9f44a47fSAlexander V. Chernikov        insn = IpFwInsn.from_buffer_copy(data[:4])
230*9f44a47fSAlexander V. Chernikov        if insn.arg1 != 0:
231*9f44a47fSAlexander V. Chernikov            raise ValueError("arg1 should be empty")
232*9f44a47fSAlexander V. Chernikov
233*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
234*9f44a47fSAlexander V. Chernikov        return ""
235*9f44a47fSAlexander V. Chernikov
236*9f44a47fSAlexander V. Chernikov
237*9f44a47fSAlexander V. Chernikovclass InsnComment(Insn):
238*9f44a47fSAlexander V. Chernikov    def __init__(self, opcode=IpFwOpcode.O_NOP, is_or=False, is_not=False, arg1=0, comment=""):
239*9f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
240*9f44a47fSAlexander V. Chernikov        if comment:
241*9f44a47fSAlexander V. Chernikov            self.comment = comment
242*9f44a47fSAlexander V. Chernikov        else:
243*9f44a47fSAlexander V. Chernikov            self.comment = ""
244*9f44a47fSAlexander V. Chernikov
245*9f44a47fSAlexander V. Chernikov    @classmethod
246*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
247*9f44a47fSAlexander V. Chernikov        cls._validate_len(data)
248*9f44a47fSAlexander V. Chernikov        if len(data) > 88:
249*9f44a47fSAlexander V. Chernikov            raise ValueError("comment too long")
250*9f44a47fSAlexander V. Chernikov
251*9f44a47fSAlexander V. Chernikov    @classmethod
252*9f44a47fSAlexander V. Chernikov    def _parse(cls, data):
253*9f44a47fSAlexander V. Chernikov        self = super()._parse(data)
254*9f44a47fSAlexander V. Chernikov        # Comment encoding can be anything,
255*9f44a47fSAlexander V. Chernikov        # use utf-8 to ease debugging
256*9f44a47fSAlexander V. Chernikov        max_len = 0
257*9f44a47fSAlexander V. Chernikov        for b in range(4, len(data)):
258*9f44a47fSAlexander V. Chernikov            if data[b] == b"\0":
259*9f44a47fSAlexander V. Chernikov                break
260*9f44a47fSAlexander V. Chernikov            max_len += 1
261*9f44a47fSAlexander V. Chernikov        self.comment = data[4:max_len].decode("utf-8")
262*9f44a47fSAlexander V. Chernikov        return self
263*9f44a47fSAlexander V. Chernikov
264*9f44a47fSAlexander V. Chernikov    def __bytes__(self):
265*9f44a47fSAlexander V. Chernikov        ret = super().__bytes__()
266*9f44a47fSAlexander V. Chernikov        comment_bytes = self.comment.encode("utf-8") + b"\0"
267*9f44a47fSAlexander V. Chernikov        if len(comment_bytes) % 4 > 0:
268*9f44a47fSAlexander V. Chernikov            comment_bytes += b"\0" * (4 - (len(comment_bytes) % 4))
269*9f44a47fSAlexander V. Chernikov        ret += comment_bytes
270*9f44a47fSAlexander V. Chernikov        return ret
271*9f44a47fSAlexander V. Chernikov
272*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
273*9f44a47fSAlexander V. Chernikov        return " comment='{}'".format(self.comment)
274*9f44a47fSAlexander V. Chernikov
275*9f44a47fSAlexander V. Chernikov
276*9f44a47fSAlexander V. Chernikovclass InsnProto(Insn):
277*9f44a47fSAlexander V. Chernikov    def __init__(self, opcode=IpFwOpcode.O_PROTO, is_or=False, is_not=False, arg1=0):
278*9f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
279*9f44a47fSAlexander V. Chernikov
280*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
281*9f44a47fSAlexander V. Chernikov        known_map = {6: "TCP", 17: "UDP", 41: "IPV6"}
282*9f44a47fSAlexander V. Chernikov        proto = self.arg1
283*9f44a47fSAlexander V. Chernikov        if proto in known_map:
284*9f44a47fSAlexander V. Chernikov            return " proto={}".format(known_map[proto])
285*9f44a47fSAlexander V. Chernikov        else:
286*9f44a47fSAlexander V. Chernikov            return " proto=#{}".format(proto)
287*9f44a47fSAlexander V. Chernikov
288*9f44a47fSAlexander V. Chernikov
289*9f44a47fSAlexander V. Chernikovclass InsnU32(Insn):
290*9f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0):
291*9f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
292*9f44a47fSAlexander V. Chernikov        self.u32 = u32
293*9f44a47fSAlexander V. Chernikov        self.ilen = 2
294*9f44a47fSAlexander V. Chernikov
295*9f44a47fSAlexander V. Chernikov    @classmethod
296*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
297*9f44a47fSAlexander V. Chernikov        cls._validate_len(data, [8])
298*9f44a47fSAlexander V. Chernikov
299*9f44a47fSAlexander V. Chernikov    @classmethod
300*9f44a47fSAlexander V. Chernikov    def _parse(cls, data):
301*9f44a47fSAlexander V. Chernikov        self = super()._parse(data[:4])
302*9f44a47fSAlexander V. Chernikov        self.u32 = struct.unpack("@I", data[4:8])[0]
303*9f44a47fSAlexander V. Chernikov        return self
304*9f44a47fSAlexander V. Chernikov
305*9f44a47fSAlexander V. Chernikov    def __bytes__(self):
306*9f44a47fSAlexander V. Chernikov        return super().__bytes__() + struct.pack("@I", self.u32)
307*9f44a47fSAlexander V. Chernikov
308*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
309*9f44a47fSAlexander V. Chernikov        return " arg1={} u32={}".format(self.arg1, self.u32)
310*9f44a47fSAlexander V. Chernikov
311*9f44a47fSAlexander V. Chernikov
312*9f44a47fSAlexander V. Chernikovclass InsnProb(InsnU32):
313*9f44a47fSAlexander V. Chernikov    def __init__(
314*9f44a47fSAlexander V. Chernikov        self,
315*9f44a47fSAlexander V. Chernikov        opcode=IpFwOpcode.O_PROB,
316*9f44a47fSAlexander V. Chernikov        is_or=False,
317*9f44a47fSAlexander V. Chernikov        is_not=False,
318*9f44a47fSAlexander V. Chernikov        arg1=0,
319*9f44a47fSAlexander V. Chernikov        u32=0,
320*9f44a47fSAlexander V. Chernikov        prob=0.0,
321*9f44a47fSAlexander V. Chernikov    ):
322*9f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not)
323*9f44a47fSAlexander V. Chernikov        self.prob = prob
324*9f44a47fSAlexander V. Chernikov
325*9f44a47fSAlexander V. Chernikov    @property
326*9f44a47fSAlexander V. Chernikov    def prob(self):
327*9f44a47fSAlexander V. Chernikov        return 1.0 * self.u32 / 0x7FFFFFFF
328*9f44a47fSAlexander V. Chernikov
329*9f44a47fSAlexander V. Chernikov    @prob.setter
330*9f44a47fSAlexander V. Chernikov    def prob(self, prob: float):
331*9f44a47fSAlexander V. Chernikov        self.u32 = int(prob * 0x7FFFFFFF)
332*9f44a47fSAlexander V. Chernikov
333*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
334*9f44a47fSAlexander V. Chernikov        return " prob={}".format(round(self.prob, 5))
335*9f44a47fSAlexander V. Chernikov
336*9f44a47fSAlexander V. Chernikov
337*9f44a47fSAlexander V. Chernikovclass InsnIp(InsnU32):
338*9f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0, ip=None):
339*9f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1, u32=u32)
340*9f44a47fSAlexander V. Chernikov        if ip:
341*9f44a47fSAlexander V. Chernikov            self.ip = ip
342*9f44a47fSAlexander V. Chernikov
343*9f44a47fSAlexander V. Chernikov    @property
344*9f44a47fSAlexander V. Chernikov    def ip(self):
345*9f44a47fSAlexander V. Chernikov        return socket.inet_ntop(socket.AF_INET, struct.pack("@I", self.u32))
346*9f44a47fSAlexander V. Chernikov
347*9f44a47fSAlexander V. Chernikov    @ip.setter
348*9f44a47fSAlexander V. Chernikov    def ip(self, ip: str):
349*9f44a47fSAlexander V. Chernikov        ip_bin = socket.inet_pton(socket.AF_INET, ip)
350*9f44a47fSAlexander V. Chernikov        self.u32 = struct.unpack("@I", ip_bin)[0]
351*9f44a47fSAlexander V. Chernikov
352*9f44a47fSAlexander V. Chernikov    def _print_opcode_value(self):
353*9f44a47fSAlexander V. Chernikov        return " ip={}".format(self.ip)
354*9f44a47fSAlexander V. Chernikov
355*9f44a47fSAlexander V. Chernikov
356*9f44a47fSAlexander V. Chernikovclass InsnTable(Insn):
357*9f44a47fSAlexander V. Chernikov    @classmethod
358*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
359*9f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4, 8])
360*9f44a47fSAlexander V. Chernikov
361*9f44a47fSAlexander V. Chernikov    @classmethod
362*9f44a47fSAlexander V. Chernikov    def _parse(cls, data):
363*9f44a47fSAlexander V. Chernikov        self = super()._parse(data)
364*9f44a47fSAlexander V. Chernikov
365*9f44a47fSAlexander V. Chernikov        if len(data) == 8:
366*9f44a47fSAlexander V. Chernikov            (self.val,) = struct.unpack("@I", data[4:8])
367*9f44a47fSAlexander V. Chernikov            self.ilen = 2
368*9f44a47fSAlexander V. Chernikov        else:
369*9f44a47fSAlexander V. Chernikov            self.val = None
370*9f44a47fSAlexander V. Chernikov        return self
371*9f44a47fSAlexander V. Chernikov
372*9f44a47fSAlexander V. Chernikov    def __bytes__(self):
373*9f44a47fSAlexander V. Chernikov        ret = super().__bytes__()
374*9f44a47fSAlexander V. Chernikov        if getattr(self, "val", None) is not None:
375*9f44a47fSAlexander V. Chernikov            ret += struct.pack("@I", self.val)
376*9f44a47fSAlexander V. Chernikov        return ret
377*9f44a47fSAlexander V. Chernikov
378*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
379*9f44a47fSAlexander V. Chernikov        if getattr(self, "val", None) is not None:
380*9f44a47fSAlexander V. Chernikov            return " table={} value={}".format(self.arg1, self.val)
381*9f44a47fSAlexander V. Chernikov        else:
382*9f44a47fSAlexander V. Chernikov            return " table={}".format(self.arg1)
383*9f44a47fSAlexander V. Chernikov
384*9f44a47fSAlexander V. Chernikov
385*9f44a47fSAlexander V. Chernikovclass InsnReject(Insn):
386*9f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, mtu=None):
387*9f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
388*9f44a47fSAlexander V. Chernikov        self.mtu = mtu
389*9f44a47fSAlexander V. Chernikov        if self.mtu is not None:
390*9f44a47fSAlexander V. Chernikov            self.ilen = 2
391*9f44a47fSAlexander V. Chernikov
392*9f44a47fSAlexander V. Chernikov    @classmethod
393*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
394*9f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4, 8])
395*9f44a47fSAlexander V. Chernikov
396*9f44a47fSAlexander V. Chernikov    @classmethod
397*9f44a47fSAlexander V. Chernikov    def _parse(cls, data):
398*9f44a47fSAlexander V. Chernikov        self = super()._parse(data)
399*9f44a47fSAlexander V. Chernikov
400*9f44a47fSAlexander V. Chernikov        if len(data) == 8:
401*9f44a47fSAlexander V. Chernikov            (self.mtu,) = struct.unpack("@I", data[4:8])
402*9f44a47fSAlexander V. Chernikov            self.ilen = 2
403*9f44a47fSAlexander V. Chernikov        else:
404*9f44a47fSAlexander V. Chernikov            self.mtu = None
405*9f44a47fSAlexander V. Chernikov        return self
406*9f44a47fSAlexander V. Chernikov
407*9f44a47fSAlexander V. Chernikov    def __bytes__(self):
408*9f44a47fSAlexander V. Chernikov        ret = super().__bytes__()
409*9f44a47fSAlexander V. Chernikov        if getattr(self, "mtu", None) is not None:
410*9f44a47fSAlexander V. Chernikov            ret += struct.pack("@I", self.mtu)
411*9f44a47fSAlexander V. Chernikov        return ret
412*9f44a47fSAlexander V. Chernikov
413*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
414*9f44a47fSAlexander V. Chernikov        code = enum_from_int(IcmpRejectCode, self.arg1)
415*9f44a47fSAlexander V. Chernikov        if getattr(self, "mtu", None) is not None:
416*9f44a47fSAlexander V. Chernikov            return " code={} mtu={}".format(code, self.mtu)
417*9f44a47fSAlexander V. Chernikov        else:
418*9f44a47fSAlexander V. Chernikov            return " code={}".format(code)
419*9f44a47fSAlexander V. Chernikov
420*9f44a47fSAlexander V. Chernikov
421*9f44a47fSAlexander V. Chernikovclass InsnPorts(Insn):
422*9f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, port_pairs=[]):
423*9f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not)
424*9f44a47fSAlexander V. Chernikov        self.port_pairs = []
425*9f44a47fSAlexander V. Chernikov        if port_pairs:
426*9f44a47fSAlexander V. Chernikov            self.port_pairs = port_pairs
427*9f44a47fSAlexander V. Chernikov
428*9f44a47fSAlexander V. Chernikov    @classmethod
429*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
430*9f44a47fSAlexander V. Chernikov        if len(data) < 8:
431*9f44a47fSAlexander V. Chernikov            raise ValueError("no ports specified")
432*9f44a47fSAlexander V. Chernikov        cls._validate_len(data)
433*9f44a47fSAlexander V. Chernikov
434*9f44a47fSAlexander V. Chernikov    @classmethod
435*9f44a47fSAlexander V. Chernikov    def _parse(cls, data):
436*9f44a47fSAlexander V. Chernikov        self = super()._parse(data)
437*9f44a47fSAlexander V. Chernikov
438*9f44a47fSAlexander V. Chernikov        off = 4
439*9f44a47fSAlexander V. Chernikov        port_pairs = []
440*9f44a47fSAlexander V. Chernikov        while off + 4 <= len(data):
441*9f44a47fSAlexander V. Chernikov            low, high = struct.unpack("@HH", data[off : off + 4])
442*9f44a47fSAlexander V. Chernikov            port_pairs.append((low, high))
443*9f44a47fSAlexander V. Chernikov            off += 4
444*9f44a47fSAlexander V. Chernikov        self.port_pairs = port_pairs
445*9f44a47fSAlexander V. Chernikov        return self
446*9f44a47fSAlexander V. Chernikov
447*9f44a47fSAlexander V. Chernikov    def __bytes__(self):
448*9f44a47fSAlexander V. Chernikov        ret = super().__bytes__()
449*9f44a47fSAlexander V. Chernikov        if getattr(self, "val", None) is not None:
450*9f44a47fSAlexander V. Chernikov            ret += struct.pack("@I", self.val)
451*9f44a47fSAlexander V. Chernikov        return ret
452*9f44a47fSAlexander V. Chernikov
453*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
454*9f44a47fSAlexander V. Chernikov        ret = []
455*9f44a47fSAlexander V. Chernikov        for p in self.port_pairs:
456*9f44a47fSAlexander V. Chernikov            if p[0] == p[1]:
457*9f44a47fSAlexander V. Chernikov                ret.append(str(p[0]))
458*9f44a47fSAlexander V. Chernikov            else:
459*9f44a47fSAlexander V. Chernikov                ret.append("{}-{}".format(p[0], p[1]))
460*9f44a47fSAlexander V. Chernikov        return " ports={}".format(",".join(ret))
461*9f44a47fSAlexander V. Chernikov
462*9f44a47fSAlexander V. Chernikov
463*9f44a47fSAlexander V. Chernikovclass IpFwInsnIp6(Structure):
464*9f44a47fSAlexander V. Chernikov    _fields_ = [
465*9f44a47fSAlexander V. Chernikov        ("o", IpFwInsn),
466*9f44a47fSAlexander V. Chernikov        ("addr6", c_byte * 16),
467*9f44a47fSAlexander V. Chernikov        ("mask6", c_byte * 16),
468*9f44a47fSAlexander V. Chernikov    ]
469*9f44a47fSAlexander V. Chernikov
470*9f44a47fSAlexander V. Chernikov
471*9f44a47fSAlexander V. Chernikovclass InsnIp6(Insn):
472*9f44a47fSAlexander V. Chernikov    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, ip6=None, mask6=None):
473*9f44a47fSAlexander V. Chernikov        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
474*9f44a47fSAlexander V. Chernikov        self.ip6 = ip6
475*9f44a47fSAlexander V. Chernikov        self.mask6 = mask6
476*9f44a47fSAlexander V. Chernikov        if mask6 is not None:
477*9f44a47fSAlexander V. Chernikov            self.ilen = 9
478*9f44a47fSAlexander V. Chernikov        else:
479*9f44a47fSAlexander V. Chernikov            self.ilen = 5
480*9f44a47fSAlexander V. Chernikov
481*9f44a47fSAlexander V. Chernikov    @classmethod
482*9f44a47fSAlexander V. Chernikov    def _validate(cls, data):
483*9f44a47fSAlexander V. Chernikov        cls._validate_len(data, [4 + 16, 4 + 16 * 2])
484*9f44a47fSAlexander V. Chernikov
485*9f44a47fSAlexander V. Chernikov    @classmethod
486*9f44a47fSAlexander V. Chernikov    def _parse(cls, data):
487*9f44a47fSAlexander V. Chernikov        self = super()._parse(data)
488*9f44a47fSAlexander V. Chernikov        self.ip6 = socket.inet_ntop(socket.AF_INET6, data[4:20])
489*9f44a47fSAlexander V. Chernikov
490*9f44a47fSAlexander V. Chernikov        if len(data) == 4 + 16 * 2:
491*9f44a47fSAlexander V. Chernikov            self.mask6 = socket.inet_ntop(socket.AF_INET6, data[20:36])
492*9f44a47fSAlexander V. Chernikov            self.ilen = 9
493*9f44a47fSAlexander V. Chernikov        else:
494*9f44a47fSAlexander V. Chernikov            self.mask6 = None
495*9f44a47fSAlexander V. Chernikov            self.ilen = 5
496*9f44a47fSAlexander V. Chernikov        return self
497*9f44a47fSAlexander V. Chernikov
498*9f44a47fSAlexander V. Chernikov    def __bytes__(self):
499*9f44a47fSAlexander V. Chernikov        ret = super().__bytes__() + socket.inet_pton(socket.AF_INET6, self.ip6)
500*9f44a47fSAlexander V. Chernikov        if self.mask6 is not None:
501*9f44a47fSAlexander V. Chernikov            ret += socket.inet_pton(socket.AF_INET6, self.mask6)
502*9f44a47fSAlexander V. Chernikov        return ret
503*9f44a47fSAlexander V. Chernikov
504*9f44a47fSAlexander V. Chernikov    def _print_obj_value(self):
505*9f44a47fSAlexander V. Chernikov        if self.mask6:
506*9f44a47fSAlexander V. Chernikov            return " ip6={}/{}".format(self.ip6, self.mask6)
507*9f44a47fSAlexander V. Chernikov        else:
508*9f44a47fSAlexander V. Chernikov            return " ip6={}".format(self.ip6)
509*9f44a47fSAlexander V. Chernikov
510*9f44a47fSAlexander V. Chernikov
511*9f44a47fSAlexander V. Chernikovinsn_attrs = prepare_attrs_map(
512*9f44a47fSAlexander V. Chernikov    [
513*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_CHECK_STATE, Insn),
514*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_ACCEPT, InsnEmpty),
515*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),
516*9f44a47fSAlexander V. Chernikov
517*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_REJECT, InsnReject),
518*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_UNREACH6, Insn),
519*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_DENY, InsnEmpty),
520*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_DIVERT, Insn),
521*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),
522*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_QUEUE, Insn),
523*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_PIPE, Insn),
524*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_SKIPTO, Insn),
525*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_NETGRAPH, Insn),
526*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_NGTEE, Insn),
527*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_DIVERT, Insn),
528*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_TEE, Insn),
529*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_CALLRETURN, Insn),
530*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_SETFIB, Insn),
531*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_SETDSCP, Insn),
532*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_REASS, InsnEmpty),
533*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_SETMARK, Insn),
534*9f44a47fSAlexander V. Chernikov
535*9f44a47fSAlexander V. Chernikov
536*9f44a47fSAlexander V. Chernikov
537*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_NOP, InsnComment),
538*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_PROTO, InsnProto),
539*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_PROB, InsnProb),
540*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_DST_ME, InsnEmpty),
541*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_SRC_ME, InsnEmpty),
542*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP6_DST_ME, InsnEmpty),
543*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP6_SRC_ME, InsnEmpty),
544*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_SRC, InsnIp),
545*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_DST, InsnIp),
546*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP6_DST, InsnIp6),
547*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP6_SRC, InsnIp6),
548*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_SRC_LOOKUP, InsnTable),
549*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_DST_LOOKUP, InsnTable),
550*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_SRCPORT, InsnPorts),
551*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_IP_DSTPORT, InsnPorts),
552*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_PROBE_STATE, Insn),
553*9f44a47fSAlexander V. Chernikov        AttrDescr(IpFwOpcode.O_KEEP_STATE, Insn),
554*9f44a47fSAlexander V. Chernikov    ]
555*9f44a47fSAlexander V. Chernikov)
556