xref: /freebsd/tests/atf_python/sys/netpfil/ipfw/insns.py (revision 32cd3ee5901ea33d41ff550e5f40ce743c8d4165)
1#!/usr/bin/env python3
2import os
3import socket
4import struct
5import subprocess
6import sys
7from ctypes import c_byte
8from ctypes import c_char
9from ctypes import c_int
10from ctypes import c_long
11from ctypes import c_uint32
12from ctypes import c_uint8
13from ctypes import c_ulong
14from ctypes import c_ushort
15from ctypes import sizeof
16from ctypes import Structure
17from enum import Enum
18from typing import Any
19from typing import Dict
20from typing import List
21from typing import NamedTuple
22from typing import Optional
23from typing import Union
24
25from atf_python.sys.netpfil.ipfw.insn_headers import IpFwOpcode
26from atf_python.sys.netpfil.ipfw.insn_headers import IcmpRejectCode
27from atf_python.sys.netpfil.ipfw.insn_headers import Icmp6RejectCode
28from atf_python.sys.netpfil.ipfw.insn_headers import IpFwTableLookupType
29from atf_python.sys.netpfil.ipfw.insn_headers import IpFwTableValueType
30from atf_python.sys.netpfil.ipfw.utils import AttrDescr
31from atf_python.sys.netpfil.ipfw.utils import enum_or_int
32from atf_python.sys.netpfil.ipfw.utils import enum_from_int
33from atf_python.sys.netpfil.ipfw.utils import prepare_attrs_map
34
35
36insn_actions = (
37    IpFwOpcode.O_CHECK_STATE.value,
38    IpFwOpcode.O_REJECT.value,
39    IpFwOpcode.O_UNREACH6.value,
40    IpFwOpcode.O_ACCEPT.value,
41    IpFwOpcode.O_DENY.value,
42    IpFwOpcode.O_COUNT.value,
43    IpFwOpcode.O_NAT.value,
44    IpFwOpcode.O_QUEUE.value,
45    IpFwOpcode.O_PIPE.value,
46    IpFwOpcode.O_SKIPTO.value,
47    IpFwOpcode.O_NETGRAPH.value,
48    IpFwOpcode.O_NGTEE.value,
49    IpFwOpcode.O_DIVERT.value,
50    IpFwOpcode.O_TEE.value,
51    IpFwOpcode.O_CALLRETURN.value,
52    IpFwOpcode.O_FORWARD_IP.value,
53    IpFwOpcode.O_FORWARD_IP6.value,
54    IpFwOpcode.O_SETFIB.value,
55    IpFwOpcode.O_SETDSCP.value,
56    IpFwOpcode.O_REASS.value,
57    IpFwOpcode.O_SETMARK.value,
58    IpFwOpcode.O_EXTERNAL_ACTION.value,
59)
60
61
62class IpFwInsn(Structure):
63    _fields_ = [
64        ("opcode", c_uint8),
65        ("length", c_uint8),
66        ("arg1", c_ushort),
67    ]
68
69
70class BaseInsn(object):
71    obj_enum_class = IpFwOpcode
72
73    def __init__(self, opcode, is_or, is_not, arg1):
74        if isinstance(opcode, Enum):
75            self.obj_type = opcode.value
76            self._enum = opcode
77        else:
78            self.obj_type = opcode
79            self._enum = enum_from_int(self.obj_enum_class, self.obj_type)
80        self.is_or = is_or
81        self.is_not = is_not
82        self.arg1 = arg1
83        self.is_action = self.obj_type in insn_actions
84        self.ilen = 1
85        self.obj_list = []
86
87    @property
88    def obj_name(self):
89        if self._enum is not None:
90            return self._enum.name
91        else:
92            return "opcode#{}".format(self.obj_type)
93
94    @staticmethod
95    def get_insn_len(data: bytes) -> int:
96        (opcode_len,) = struct.unpack("@B", data[1:2])
97        return opcode_len & 0x3F
98
99    @classmethod
100    def _validate_len(cls, data, valid_options=None):
101        if len(data) < 4:
102            raise ValueError("opcode too short")
103        opcode_type, opcode_len = struct.unpack("@BB", data[:2])
104        if len(data) != ((opcode_len & 0x3F) * 4):
105            raise ValueError("wrong length")
106        if valid_options and len(data) not in valid_options:
107            raise ValueError(
108                "len {} not in {} for {}".format(
109                    len(data), valid_options,
110                    enum_from_int(cls.obj_enum_class, data[0])
111                )
112            )
113
114    @classmethod
115    def _validate(cls, data):
116        cls._validate_len(data)
117
118    @classmethod
119    def _parse(cls, data):
120        insn = IpFwInsn.from_buffer_copy(data[:4])
121        is_or = (insn.length & 0x40) != 0
122        is_not = (insn.length & 0x80) != 0
123        return cls(opcode=insn.opcode, is_or=is_or, is_not=is_not, arg1=insn.arg1)
124
125    @classmethod
126    def from_bytes(cls, data, attr_type_enum):
127        cls._validate(data)
128        opcode = cls._parse(data)
129        opcode._enum = attr_type_enum
130        return opcode
131
132    def __bytes__(self):
133        raise NotImplementedError()
134
135    def print_obj(self, prepend=""):
136        is_or = ""
137        if self.is_or:
138            is_or = " [OR]\\"
139        is_not = ""
140        if self.is_not:
141            is_not = "[!] "
142        print(
143            "{}{}len={} type={}({}){}{}".format(
144                prepend,
145                is_not,
146                len(bytes(self)),
147                self.obj_name,
148                self.obj_type,
149                self._print_obj_value(),
150                is_or,
151            )
152        )
153
154    def _print_obj_value(self):
155        raise NotImplementedError()
156
157    def print_obj_hex(self, prepend=""):
158        print(prepend)
159        print()
160        print(" ".join(["x{:02X}".format(b) for b in bytes(self)]))
161
162    @staticmethod
163    def parse_insns(data, attr_map):
164        ret = []
165        off = 0
166        while off + sizeof(IpFwInsn) <= len(data):
167            hdr = IpFwInsn.from_buffer_copy(data[off : off + sizeof(IpFwInsn)])
168            insn_len = (hdr.length & 0x3F) * 4
169            if off + insn_len > len(data):
170                raise ValueError("wrong length")
171            # print("GET insn type {} len {}".format(hdr.opcode, insn_len))
172            attr = attr_map.get(hdr.opcode, None)
173            if attr is None:
174                cls = InsnUnknown
175                type_enum = enum_from_int(BaseInsn.obj_enum_class, hdr.opcode)
176            else:
177                cls = attr["ad"].cls
178                type_enum = attr["ad"].val
179            insn = cls.from_bytes(data[off : off + insn_len], type_enum)
180            ret.append(insn)
181            off += insn_len
182
183        if off != len(data):
184            raise ValueError("empty space")
185        return ret
186
187
188class Insn(BaseInsn):
189    def __init__(self, opcode, is_or=False, is_not=False, arg1=0):
190        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
191
192    @classmethod
193    def _validate(cls, data):
194        cls._validate_len(data, [4])
195
196    def __bytes__(self):
197        length = self.ilen
198        if self.is_or:
199            length |= 0x40
200        if self.is_not:
201            length | 0x80
202        insn = IpFwInsn(opcode=self.obj_type, length=length, arg1=enum_or_int(self.arg1))
203        return bytes(insn)
204
205    def _print_obj_value(self):
206        return " arg1={}".format(self.arg1)
207
208
209class InsnUnknown(Insn):
210    @classmethod
211    def _validate(cls, data):
212        cls._validate_len(data)
213
214    @classmethod
215    def _parse(cls, data):
216        self = super()._parse(data)
217        self._data = data
218        return self
219
220    def __bytes__(self):
221        return self._data
222
223    def _print_obj_value(self):
224        return " " + " ".join(["x{:02X}".format(b) for b in self._data])
225
226
227class InsnEmpty(Insn):
228    @classmethod
229    def _validate(cls, data):
230        cls._validate_len(data, [4])
231        insn = IpFwInsn.from_buffer_copy(data[:4])
232        if insn.arg1 != 0:
233            raise ValueError("arg1 should be empty")
234
235    def _print_obj_value(self):
236        return ""
237
238
239class InsnComment(Insn):
240    def __init__(self, opcode=IpFwOpcode.O_NOP, is_or=False, is_not=False, arg1=0, comment=""):
241        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
242        if comment:
243            self.comment = comment
244        else:
245            self.comment = ""
246
247    @classmethod
248    def _validate(cls, data):
249        cls._validate_len(data)
250        if len(data) > 88:
251            raise ValueError("comment too long")
252
253    @classmethod
254    def _parse(cls, data):
255        self = super()._parse(data)
256        # Comment encoding can be anything,
257        # use utf-8 to ease debugging
258        max_len = 0
259        for b in range(4, len(data)):
260            if data[b] == b"\0":
261                break
262            max_len += 1
263        self.comment = data[4:max_len].decode("utf-8")
264        return self
265
266    def __bytes__(self):
267        ret = super().__bytes__()
268        comment_bytes = self.comment.encode("utf-8") + b"\0"
269        if len(comment_bytes) % 4 > 0:
270            comment_bytes += b"\0" * (4 - (len(comment_bytes) % 4))
271        ret += comment_bytes
272        return ret
273
274    def _print_obj_value(self):
275        return " comment='{}'".format(self.comment)
276
277
278class InsnProto(Insn):
279    def __init__(self, opcode=IpFwOpcode.O_PROTO, is_or=False, is_not=False, arg1=0):
280        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
281
282    def _print_obj_value(self):
283        known_map = {6: "TCP", 17: "UDP", 41: "IPV6"}
284        proto = self.arg1
285        if proto in known_map:
286            return " proto={}".format(known_map[proto])
287        else:
288            return " proto=#{}".format(proto)
289
290
291class InsnU32(Insn):
292    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0):
293        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
294        self.u32 = u32
295        self.ilen = 2
296
297    @classmethod
298    def _validate(cls, data):
299        cls._validate_len(data, [8])
300
301    @classmethod
302    def _parse(cls, data):
303        self = super()._parse(data[:4])
304        self.u32 = struct.unpack("@I", data[4:8])[0]
305        return self
306
307    def __bytes__(self):
308        return super().__bytes__() + struct.pack("@I", self.u32)
309
310    def _print_obj_value(self):
311        return " arg1={} u32={}".format(self.arg1, self.u32)
312
313
314class InsnKidx(InsnU32):
315    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, kidx=0):
316        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1, u32=kidx)
317
318    @property
319    def kidx(self):
320        return self.u32
321
322    @kidx.setter
323    def kidx(self, kidx):
324        self.u32 = kidx
325
326    def _print_obj_value(self):
327        return " arg1={}, kidx={}".format(self.arg1, self.kidx)
328
329
330class InsnProb(InsnU32):
331    def __init__(
332        self,
333        opcode=IpFwOpcode.O_PROB,
334        is_or=False,
335        is_not=False,
336        arg1=0,
337        u32=0,
338        prob=0.0,
339    ):
340        super().__init__(opcode, is_or=is_or, is_not=is_not)
341        self.prob = prob
342
343    @property
344    def prob(self):
345        return 1.0 * self.u32 / 0x7FFFFFFF
346
347    @prob.setter
348    def prob(self, prob: float):
349        self.u32 = int(prob * 0x7FFFFFFF)
350
351    def _print_obj_value(self):
352        return " prob={}".format(round(self.prob, 5))
353
354
355class InsnIp(InsnU32):
356    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0, ip=None):
357        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1, u32=u32)
358        if ip:
359            self.ip = ip
360
361    @property
362    def ip(self):
363        return socket.inet_ntop(socket.AF_INET, struct.pack("@I", self.u32))
364
365    @ip.setter
366    def ip(self, ip: str):
367        ip_bin = socket.inet_pton(socket.AF_INET, ip)
368        self.u32 = struct.unpack("@I", ip_bin)[0]
369
370    def _print_opcode_value(self):
371        return " ip={}".format(self.ip)
372
373
374class InsnTable(InsnKidx):
375    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, kidx=0, value=None):
376        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1, kidx=kidx)
377        self.val = value
378
379    @classmethod
380    def _validate(cls, data):
381        cls._validate_len(data, [8, 12])
382
383    @classmethod
384    def _parse(cls, data):
385        self = super()._parse(data)
386
387        (self.kidx,) = struct.unpack("@I", data[4:8])
388        if len(data) == 12:
389            (self.val,) = struct.unpack("@I", data[8:12])
390            self.ilen = 3
391        else:
392            self.val = None
393        return self
394
395    def __bytes__(self):
396        ret = super().__bytes__()
397        if getattr(self, "val", None) is not None:
398            ret += struct.pack("@I", self.val)
399        return ret
400
401    def _print_obj_value(self):
402        if getattr(self, "val", None) is not None:
403            return " table={} value={}".format(self.kidx, self.val)
404        else:
405            return " table={}".format(self.kidx)
406
407
408class InsnLookup(InsnKidx):
409    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, kidx=0, value=None, bitmask=None):
410        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1, kidx=kidx)
411        self.val = value
412        self.bm = bitmask
413        self.decompile_arg1()
414
415    @classmethod
416    def _validate(cls, data):
417        cls._validate_len(data, [8, 24])
418
419    @classmethod
420    def _parse(cls, data):
421        self = super()._parse(data)
422
423        if len(data) == 24:
424            self.decompile_arg1()
425            self.ilen = 8
426            if self.do_masking != 0:
427                if self.lookup_type in [IpFwTableLookupType.LOOKUP_DST_IP4, IpFwTableLookupType.LOOKUP_SRC_IP4]:
428                    self.bm = socket.inet_ntop(socket.AF_INET, data[8:12])
429                elif self.lookup_type in [IpFwTableLookupType.LOOKUP_DST_IP6, IpFwTableLookupType.LOOKUP_SRC_IP6]:
430                    self.bm = socket.inet_ntop(socket.AF_INET6, data[8:24])
431                elif self.lookup_type in [IpFwTableLookupType.LOOKUP_DST_MAC, IpFwTableLookupType.LOOKUP_SRC_MAC]:
432                    self.bm = ':'.join(f'{b:02x}' for b in struct.unpack('!6B', data[8:14]))
433                elif self.lookup_type in [IpFwTableLookupType.LOOKUP_DST_IP, IpFwTableLookupType.LOOKUP_SRC_IP]:
434                    raise ValueError(f"maksing LOOKUP_SRC/DST_IP is invalid")
435                else:
436                    (self.bm,) = struct.unpack("@I", data[8:12])
437            elif self.do_matching != 0:
438                match self.tvalue_type:
439                    case IpFwTableValueType.TVALUE_NH4:
440                        self.val = socket.inet_ntop(socket.AF_INET, data[8:12])
441                    case IpFwTableValueType.TVALUE_NH6:
442                        self.val = socket.inet_ntop(socket.AF_INET6, data[8:24])
443                    case _:
444                        (self.val,) = struct.unpack("@I", data[8:12])
445            else:
446                raise ValueError(f"insn_lookup is used but neither masking or matching are set")
447
448        return self
449
450    @classmethod
451    def compile_arg1(cls, value_type=None, value=None, lookup_type=None, bitmask=None):
452        arg1 = 0
453        if value_type is not None:
454            arg1 |= value_type.value << 8
455            arg1 |= 0x8000
456        if lookup_type is not None:
457            arg1 |= lookup_type.value
458        if bitmask is not None:
459            arg1 |= 0x0080
460        return arg1
461
462    def decompile_arg1(self):
463        self.do_masking = ((self.arg1 & 0x0080) != 0)
464        self.do_matching = ((self.arg1 & 0x8000) != 0)
465        self.lookup_int_type = self.arg1 & 0x007F
466        self.tvalue_int_type = (self.arg1 & 0x7F00) >> 8
467        self.lookup_type = enum_from_int(IpFwTableLookupType, self.lookup_int_type)
468        self.tvalue_type = enum_from_int(IpFwTableValueType, self.tvalue_int_type)
469
470        if self.do_matching != 0 and self.do_masking != 0:
471            raise ValueError(f"masking and matching can not be combined")
472        elif self.do_matching == 0 and self.do_masking == 0:
473            return
474        self.ilen = 8
475
476    def _validate_arg1(self):
477        if self.do_matching == 0 and self.val is not None:
478            raise ValueError(f"matching bit is NOT set yet value is set to {self.val}")
479        elif self.do_matching != 0 and self.val is None:
480            raise ValueError("matching bit is set yet value is None")
481        elif self.do_matching != 0 and self.tvalue_type is None:
482            raise ValueError(f"matching bit is set but unknown table value ({self.tvalue_type}) is set")
483
484        if self.do_masking == 0 and self.bm is not None:
485            raise ValueError(f"masking bit is NOT set yet bitmask is set to {self.bm}")
486        elif self.do_masking != 0 and self.bm is None:
487            raise ValueError("masking bit is set yet bitmask is None")
488        elif self.do_masking != 0 and self.lookup_type is None:
489            raise ValueError(f"masking bit is set but unknown lookup type ({self.lookup_int_type}) is set")
490        elif self.do_masking != 0 and self.lookup_type == IpFwTableLookupType.LOOKUP_NONE:
491            raise ValueError(f"masking bit is set but LOOKUP_NONE lookup type is set")
492
493    def __bytes__(self):
494        # perform sanity check
495        self._validate_arg1()
496        ret = super().__bytes__()
497        if self.do_masking != 0:
498            if (isinstance(self.bm, str) and
499              self.lookup_type in [IpFwTableLookupType.LOOKUP_DST_IP4, IpFwTableLookupType.LOOKUP_SRC_IP4]):
500                ret += socket.inet_pton(socket.AF_INET, self.bm)
501            elif self.lookup_type in [IpFwTableLookupType.LOOKUP_DST_IP6, IpFwTableLookupType.LOOKUP_SRC_IP6]:
502                ret += socket.inet_pton(socket.AF_INET6, self.bm)
503            elif self.lookup_type in [IpFwTableLookupType.LOOKUP_SRC_MAC, IpFwTableLookupType.LOOKUP_SRC_MAC]:
504                ret += struct.pack('!6B10x', *(int(b, 16) for b in self.bm.split(':')))
505            else:
506                ret += struct.pack("@I12x", self.bm)
507        elif self.do_matching != 0:
508            match self.tvalue_type:
509                case IpFwTableValueType.TVALUE_NH4:
510                    ret += socket.inet_pton(socket.AF_INET, self.val)
511                case IpFwTableValueType.TVALUE_NH6:
512                    ret += socket.inet_pton(socket.AF_INET6, self.val)
513                case _:
514                    ret += struct.pack("@I12x", self.val)
515        return ret
516
517    def _print_obj_value(self):
518        # perform sanity check
519        self._validate_arg1()
520        if self.do_matching != 0:
521            return " arg1={}, lookup_type={}, tvalue_type={}, kidx={}, value={}".format(
522              self.arg1, self.lookup_type, self.tvalue_type, self.kidx, self.val)
523        elif self.do_masking != 0:
524            return " arg1={}, lookup_type{}, tvalue_type={}, kidx={}, bitmask={}".format(
525              self.arg1, self.lookup_type, self.tvalue_type, self.kidx, self.bm)
526        else:
527            return " arg1={}, kidx={}".format(self.arg1, self.kidx)
528
529
530class InsnReject(Insn):
531    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, mtu=None):
532        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
533        self.mtu = mtu
534        if self.mtu is not None:
535            self.ilen = 2
536
537    @classmethod
538    def _validate(cls, data):
539        cls._validate_len(data, [4, 8])
540
541    @classmethod
542    def _parse(cls, data):
543        self = super()._parse(data)
544
545        if len(data) == 8:
546            (self.mtu,) = struct.unpack("@I", data[4:8])
547            self.ilen = 2
548        else:
549            self.mtu = None
550        return self
551
552    def __bytes__(self):
553        ret = super().__bytes__()
554        if getattr(self, "mtu", None) is not None:
555            ret += struct.pack("@I", self.mtu)
556        return ret
557
558    def _print_obj_value(self):
559        code = enum_from_int(IcmpRejectCode, self.arg1)
560        if getattr(self, "mtu", None) is not None:
561            return " code={} mtu={}".format(code, self.mtu)
562        else:
563            return " code={}".format(code)
564
565
566class InsnPorts(Insn):
567    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, port_pairs=[]):
568        super().__init__(opcode, is_or=is_or, is_not=is_not)
569        self.port_pairs = []
570        if port_pairs:
571            self.port_pairs = port_pairs
572
573    @classmethod
574    def _validate(cls, data):
575        if len(data) < 8:
576            raise ValueError("no ports specified")
577        cls._validate_len(data)
578
579    @classmethod
580    def _parse(cls, data):
581        self = super()._parse(data)
582
583        off = 4
584        port_pairs = []
585        while off + 4 <= len(data):
586            low, high = struct.unpack("@HH", data[off : off + 4])
587            port_pairs.append((low, high))
588            off += 4
589        self.port_pairs = port_pairs
590        return self
591
592    def __bytes__(self):
593        ret = super().__bytes__()
594        if getattr(self, "val", None) is not None:
595            ret += struct.pack("@I", self.val)
596        return ret
597
598    def _print_obj_value(self):
599        ret = []
600        for p in self.port_pairs:
601            if p[0] == p[1]:
602                ret.append(str(p[0]))
603            else:
604                ret.append("{}-{}".format(p[0], p[1]))
605        return " ports={}".format(",".join(ret))
606
607
608class IpFwInsnIp6(Structure):
609    _fields_ = [
610        ("o", IpFwInsn),
611        ("addr6", c_byte * 16),
612        ("mask6", c_byte * 16),
613    ]
614
615
616class InsnIp6(Insn):
617    def __init__(self, opcode, is_or=False, is_not=False, arg1=0, ip6=None, mask6=None):
618        super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
619        self.ip6 = ip6
620        self.mask6 = mask6
621        if mask6 is not None:
622            self.ilen = 9
623        else:
624            self.ilen = 5
625
626    @classmethod
627    def _validate(cls, data):
628        cls._validate_len(data, [4 + 16, 4 + 16 * 2])
629
630    @classmethod
631    def _parse(cls, data):
632        self = super()._parse(data)
633        self.ip6 = socket.inet_ntop(socket.AF_INET6, data[4:20])
634
635        if len(data) == 4 + 16 * 2:
636            self.mask6 = socket.inet_ntop(socket.AF_INET6, data[20:36])
637            self.ilen = 9
638        else:
639            self.mask6 = None
640            self.ilen = 5
641        return self
642
643    def __bytes__(self):
644        ret = super().__bytes__() + socket.inet_pton(socket.AF_INET6, self.ip6)
645        if self.mask6 is not None:
646            ret += socket.inet_pton(socket.AF_INET6, self.mask6)
647        return ret
648
649    def _print_obj_value(self):
650        if self.mask6:
651            return " ip6={}/{}".format(self.ip6, self.mask6)
652        else:
653            return " ip6={}".format(self.ip6)
654
655
656insn_attrs = prepare_attrs_map(
657    [
658        AttrDescr(IpFwOpcode.O_CHECK_STATE, InsnKidx),
659        AttrDescr(IpFwOpcode.O_ACCEPT, InsnEmpty),
660        AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),
661
662        AttrDescr(IpFwOpcode.O_REJECT, InsnReject),
663        AttrDescr(IpFwOpcode.O_UNREACH6, Insn),
664        AttrDescr(IpFwOpcode.O_DENY, InsnEmpty),
665        AttrDescr(IpFwOpcode.O_DIVERT, Insn),
666        AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),
667        AttrDescr(IpFwOpcode.O_QUEUE, Insn),
668        AttrDescr(IpFwOpcode.O_PIPE, Insn),
669        AttrDescr(IpFwOpcode.O_SKIPTO, InsnU32),
670        AttrDescr(IpFwOpcode.O_NETGRAPH, Insn),
671        AttrDescr(IpFwOpcode.O_NGTEE, Insn),
672        AttrDescr(IpFwOpcode.O_DIVERT, Insn),
673        AttrDescr(IpFwOpcode.O_TEE, Insn),
674        AttrDescr(IpFwOpcode.O_CALLRETURN, InsnU32),
675        AttrDescr(IpFwOpcode.O_SETFIB, Insn),
676        AttrDescr(IpFwOpcode.O_SETDSCP, Insn),
677        AttrDescr(IpFwOpcode.O_REASS, InsnEmpty),
678        AttrDescr(IpFwOpcode.O_SETMARK, Insn),
679        AttrDescr(IpFwOpcode.O_EXTERNAL_ACTION, InsnKidx),
680        AttrDescr(IpFwOpcode.O_EXTERNAL_INSTANCE, InsnKidx),
681
682
683
684        AttrDescr(IpFwOpcode.O_NOP, InsnComment),
685        AttrDescr(IpFwOpcode.O_PROTO, InsnProto),
686        AttrDescr(IpFwOpcode.O_PROB, InsnProb),
687        AttrDescr(IpFwOpcode.O_IP_DST_ME, InsnEmpty),
688        AttrDescr(IpFwOpcode.O_IP_SRC_ME, InsnEmpty),
689        AttrDescr(IpFwOpcode.O_IP6_DST_ME, InsnEmpty),
690        AttrDescr(IpFwOpcode.O_IP6_SRC_ME, InsnEmpty),
691        AttrDescr(IpFwOpcode.O_IP_SRC, InsnIp),
692        AttrDescr(IpFwOpcode.O_IP_DST, InsnIp),
693        AttrDescr(IpFwOpcode.O_IP6_DST, InsnIp6),
694        AttrDescr(IpFwOpcode.O_IP6_SRC, InsnIp6),
695        AttrDescr(IpFwOpcode.O_IP_SRC_LOOKUP, InsnLookup),
696        AttrDescr(IpFwOpcode.O_IP_DST_LOOKUP, InsnLookup),
697        AttrDescr(IpFwOpcode.O_IP_SRCPORT, InsnPorts),
698        AttrDescr(IpFwOpcode.O_IP_DSTPORT, InsnPorts),
699        AttrDescr(IpFwOpcode.O_PROBE_STATE, InsnKidx),
700        AttrDescr(IpFwOpcode.O_KEEP_STATE, InsnKidx),
701        AttrDescr(IpFwOpcode.O_TABLE_LOOKUP, InsnLookup),
702    ]
703)
704