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