Path: blob/main/tests/atf_python/sys/netpfil/ipfw/insns.py
39536 views
#!/usr/bin/env python31import os2import socket3import struct4import subprocess5import sys6from ctypes import c_byte7from ctypes import c_char8from ctypes import c_int9from ctypes import c_long10from ctypes import c_uint3211from ctypes import c_uint812from ctypes import c_ulong13from ctypes import c_ushort14from ctypes import sizeof15from ctypes import Structure16from enum import Enum17from typing import Any18from typing import Dict19from typing import List20from typing import NamedTuple21from typing import Optional22from typing import Union2324from atf_python.sys.netpfil.ipfw.insn_headers import IpFwOpcode25from atf_python.sys.netpfil.ipfw.insn_headers import IcmpRejectCode26from atf_python.sys.netpfil.ipfw.insn_headers import Icmp6RejectCode27from atf_python.sys.netpfil.ipfw.utils import AttrDescr28from atf_python.sys.netpfil.ipfw.utils import enum_or_int29from atf_python.sys.netpfil.ipfw.utils import enum_from_int30from atf_python.sys.netpfil.ipfw.utils import prepare_attrs_map313233insn_actions = (34IpFwOpcode.O_CHECK_STATE.value,35IpFwOpcode.O_REJECT.value,36IpFwOpcode.O_UNREACH6.value,37IpFwOpcode.O_ACCEPT.value,38IpFwOpcode.O_DENY.value,39IpFwOpcode.O_COUNT.value,40IpFwOpcode.O_NAT.value,41IpFwOpcode.O_QUEUE.value,42IpFwOpcode.O_PIPE.value,43IpFwOpcode.O_SKIPTO.value,44IpFwOpcode.O_NETGRAPH.value,45IpFwOpcode.O_NGTEE.value,46IpFwOpcode.O_DIVERT.value,47IpFwOpcode.O_TEE.value,48IpFwOpcode.O_CALLRETURN.value,49IpFwOpcode.O_FORWARD_IP.value,50IpFwOpcode.O_FORWARD_IP6.value,51IpFwOpcode.O_SETFIB.value,52IpFwOpcode.O_SETDSCP.value,53IpFwOpcode.O_REASS.value,54IpFwOpcode.O_SETMARK.value,55IpFwOpcode.O_EXTERNAL_ACTION.value,56)575859class IpFwInsn(Structure):60_fields_ = [61("opcode", c_uint8),62("length", c_uint8),63("arg1", c_ushort),64]656667class BaseInsn(object):68obj_enum_class = IpFwOpcode6970def __init__(self, opcode, is_or, is_not, arg1):71if isinstance(opcode, Enum):72self.obj_type = opcode.value73self._enum = opcode74else:75self.obj_type = opcode76self._enum = enum_from_int(self.obj_enum_class, self.obj_type)77self.is_or = is_or78self.is_not = is_not79self.arg1 = arg180self.is_action = self.obj_type in insn_actions81self.ilen = 182self.obj_list = []8384@property85def obj_name(self):86if self._enum is not None:87return self._enum.name88else:89return "opcode#{}".format(self.obj_type)9091@staticmethod92def get_insn_len(data: bytes) -> int:93(opcode_len,) = struct.unpack("@B", data[1:2])94return opcode_len & 0x3F9596@classmethod97def _validate_len(cls, data, valid_options=None):98if len(data) < 4:99raise ValueError("opcode too short")100opcode_type, opcode_len = struct.unpack("@BB", data[:2])101if len(data) != ((opcode_len & 0x3F) * 4):102raise ValueError("wrong length")103if valid_options and len(data) not in valid_options:104raise ValueError(105"len {} not in {} for {}".format(106len(data), valid_options,107enum_from_int(cls.obj_enum_class, data[0])108)109)110111@classmethod112def _validate(cls, data):113cls._validate_len(data)114115@classmethod116def _parse(cls, data):117insn = IpFwInsn.from_buffer_copy(data[:4])118is_or = (insn.length & 0x40) != 0119is_not = (insn.length & 0x80) != 0120return cls(opcode=insn.opcode, is_or=is_or, is_not=is_not, arg1=insn.arg1)121122@classmethod123def from_bytes(cls, data, attr_type_enum):124cls._validate(data)125opcode = cls._parse(data)126opcode._enum = attr_type_enum127return opcode128129def __bytes__(self):130raise NotImplementedError()131132def print_obj(self, prepend=""):133is_or = ""134if self.is_or:135is_or = " [OR]\\"136is_not = ""137if self.is_not:138is_not = "[!] "139print(140"{}{}len={} type={}({}){}{}".format(141prepend,142is_not,143len(bytes(self)),144self.obj_name,145self.obj_type,146self._print_obj_value(),147is_or,148)149)150151def _print_obj_value(self):152raise NotImplementedError()153154def print_obj_hex(self, prepend=""):155print(prepend)156print()157print(" ".join(["x{:02X}".format(b) for b in bytes(self)]))158159@staticmethod160def parse_insns(data, attr_map):161ret = []162off = 0163while off + sizeof(IpFwInsn) <= len(data):164hdr = IpFwInsn.from_buffer_copy(data[off : off + sizeof(IpFwInsn)])165insn_len = (hdr.length & 0x3F) * 4166if off + insn_len > len(data):167raise ValueError("wrng length")168# print("GET insn type {} len {}".format(hdr.opcode, insn_len))169attr = attr_map.get(hdr.opcode, None)170if attr is None:171cls = InsnUnknown172type_enum = enum_from_int(BaseInsn.obj_enum_class, hdr.opcode)173else:174cls = attr["ad"].cls175type_enum = attr["ad"].val176insn = cls.from_bytes(data[off : off + insn_len], type_enum)177ret.append(insn)178off += insn_len179180if off != len(data):181raise ValueError("empty space")182return ret183184185class Insn(BaseInsn):186def __init__(self, opcode, is_or=False, is_not=False, arg1=0):187super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)188189@classmethod190def _validate(cls, data):191cls._validate_len(data, [4])192193def __bytes__(self):194length = self.ilen195if self.is_or:196length |= 0x40197if self.is_not:198length | 0x80199insn = IpFwInsn(opcode=self.obj_type, length=length, arg1=enum_or_int(self.arg1))200return bytes(insn)201202def _print_obj_value(self):203return " arg1={}".format(self.arg1)204205206class InsnUnknown(Insn):207@classmethod208def _validate(cls, data):209cls._validate_len(data)210211@classmethod212def _parse(cls, data):213self = super()._parse(data)214self._data = data215return self216217def __bytes__(self):218return self._data219220def _print_obj_value(self):221return " " + " ".join(["x{:02X}".format(b) for b in self._data])222223224class InsnEmpty(Insn):225@classmethod226def _validate(cls, data):227cls._validate_len(data, [4])228insn = IpFwInsn.from_buffer_copy(data[:4])229if insn.arg1 != 0:230raise ValueError("arg1 should be empty")231232def _print_obj_value(self):233return ""234235236class InsnComment(Insn):237def __init__(self, opcode=IpFwOpcode.O_NOP, is_or=False, is_not=False, arg1=0, comment=""):238super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)239if comment:240self.comment = comment241else:242self.comment = ""243244@classmethod245def _validate(cls, data):246cls._validate_len(data)247if len(data) > 88:248raise ValueError("comment too long")249250@classmethod251def _parse(cls, data):252self = super()._parse(data)253# Comment encoding can be anything,254# use utf-8 to ease debugging255max_len = 0256for b in range(4, len(data)):257if data[b] == b"\0":258break259max_len += 1260self.comment = data[4:max_len].decode("utf-8")261return self262263def __bytes__(self):264ret = super().__bytes__()265comment_bytes = self.comment.encode("utf-8") + b"\0"266if len(comment_bytes) % 4 > 0:267comment_bytes += b"\0" * (4 - (len(comment_bytes) % 4))268ret += comment_bytes269return ret270271def _print_obj_value(self):272return " comment='{}'".format(self.comment)273274275class InsnProto(Insn):276def __init__(self, opcode=IpFwOpcode.O_PROTO, is_or=False, is_not=False, arg1=0):277super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)278279def _print_obj_value(self):280known_map = {6: "TCP", 17: "UDP", 41: "IPV6"}281proto = self.arg1282if proto in known_map:283return " proto={}".format(known_map[proto])284else:285return " proto=#{}".format(proto)286287288class InsnU32(Insn):289def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0):290super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)291self.u32 = u32292self.ilen = 2293294@classmethod295def _validate(cls, data):296cls._validate_len(data, [8])297298@classmethod299def _parse(cls, data):300self = super()._parse(data[:4])301self.u32 = struct.unpack("@I", data[4:8])[0]302return self303304def __bytes__(self):305return super().__bytes__() + struct.pack("@I", self.u32)306307def _print_obj_value(self):308return " arg1={} u32={}".format(self.arg1, self.u32)309310311class InsnProb(InsnU32):312def __init__(313self,314opcode=IpFwOpcode.O_PROB,315is_or=False,316is_not=False,317arg1=0,318u32=0,319prob=0.0,320):321super().__init__(opcode, is_or=is_or, is_not=is_not)322self.prob = prob323324@property325def prob(self):326return 1.0 * self.u32 / 0x7FFFFFFF327328@prob.setter329def prob(self, prob: float):330self.u32 = int(prob * 0x7FFFFFFF)331332def _print_obj_value(self):333return " prob={}".format(round(self.prob, 5))334335336class InsnIp(InsnU32):337def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0, ip=None):338super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1, u32=u32)339if ip:340self.ip = ip341342@property343def ip(self):344return socket.inet_ntop(socket.AF_INET, struct.pack("@I", self.u32))345346@ip.setter347def ip(self, ip: str):348ip_bin = socket.inet_pton(socket.AF_INET, ip)349self.u32 = struct.unpack("@I", ip_bin)[0]350351def _print_opcode_value(self):352return " ip={}".format(self.ip)353354355class InsnTable(Insn):356@classmethod357def _validate(cls, data):358cls._validate_len(data, [4, 8])359360@classmethod361def _parse(cls, data):362self = super()._parse(data)363364if len(data) == 8:365(self.val,) = struct.unpack("@I", data[4:8])366self.ilen = 2367else:368self.val = None369return self370371def __bytes__(self):372ret = super().__bytes__()373if getattr(self, "val", None) is not None:374ret += struct.pack("@I", self.val)375return ret376377def _print_obj_value(self):378if getattr(self, "val", None) is not None:379return " table={} value={}".format(self.arg1, self.val)380else:381return " table={}".format(self.arg1)382383384class InsnReject(Insn):385def __init__(self, opcode, is_or=False, is_not=False, arg1=0, mtu=None):386super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)387self.mtu = mtu388if self.mtu is not None:389self.ilen = 2390391@classmethod392def _validate(cls, data):393cls._validate_len(data, [4, 8])394395@classmethod396def _parse(cls, data):397self = super()._parse(data)398399if len(data) == 8:400(self.mtu,) = struct.unpack("@I", data[4:8])401self.ilen = 2402else:403self.mtu = None404return self405406def __bytes__(self):407ret = super().__bytes__()408if getattr(self, "mtu", None) is not None:409ret += struct.pack("@I", self.mtu)410return ret411412def _print_obj_value(self):413code = enum_from_int(IcmpRejectCode, self.arg1)414if getattr(self, "mtu", None) is not None:415return " code={} mtu={}".format(code, self.mtu)416else:417return " code={}".format(code)418419420class InsnPorts(Insn):421def __init__(self, opcode, is_or=False, is_not=False, arg1=0, port_pairs=[]):422super().__init__(opcode, is_or=is_or, is_not=is_not)423self.port_pairs = []424if port_pairs:425self.port_pairs = port_pairs426427@classmethod428def _validate(cls, data):429if len(data) < 8:430raise ValueError("no ports specified")431cls._validate_len(data)432433@classmethod434def _parse(cls, data):435self = super()._parse(data)436437off = 4438port_pairs = []439while off + 4 <= len(data):440low, high = struct.unpack("@HH", data[off : off + 4])441port_pairs.append((low, high))442off += 4443self.port_pairs = port_pairs444return self445446def __bytes__(self):447ret = super().__bytes__()448if getattr(self, "val", None) is not None:449ret += struct.pack("@I", self.val)450return ret451452def _print_obj_value(self):453ret = []454for p in self.port_pairs:455if p[0] == p[1]:456ret.append(str(p[0]))457else:458ret.append("{}-{}".format(p[0], p[1]))459return " ports={}".format(",".join(ret))460461462class IpFwInsnIp6(Structure):463_fields_ = [464("o", IpFwInsn),465("addr6", c_byte * 16),466("mask6", c_byte * 16),467]468469470class InsnIp6(Insn):471def __init__(self, opcode, is_or=False, is_not=False, arg1=0, ip6=None, mask6=None):472super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)473self.ip6 = ip6474self.mask6 = mask6475if mask6 is not None:476self.ilen = 9477else:478self.ilen = 5479480@classmethod481def _validate(cls, data):482cls._validate_len(data, [4 + 16, 4 + 16 * 2])483484@classmethod485def _parse(cls, data):486self = super()._parse(data)487self.ip6 = socket.inet_ntop(socket.AF_INET6, data[4:20])488489if len(data) == 4 + 16 * 2:490self.mask6 = socket.inet_ntop(socket.AF_INET6, data[20:36])491self.ilen = 9492else:493self.mask6 = None494self.ilen = 5495return self496497def __bytes__(self):498ret = super().__bytes__() + socket.inet_pton(socket.AF_INET6, self.ip6)499if self.mask6 is not None:500ret += socket.inet_pton(socket.AF_INET6, self.mask6)501return ret502503def _print_obj_value(self):504if self.mask6:505return " ip6={}/{}".format(self.ip6, self.mask6)506else:507return " ip6={}".format(self.ip6)508509510insn_attrs = prepare_attrs_map(511[512AttrDescr(IpFwOpcode.O_CHECK_STATE, InsnU32),513AttrDescr(IpFwOpcode.O_ACCEPT, InsnEmpty),514AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),515516AttrDescr(IpFwOpcode.O_REJECT, InsnReject),517AttrDescr(IpFwOpcode.O_UNREACH6, Insn),518AttrDescr(IpFwOpcode.O_DENY, InsnEmpty),519AttrDescr(IpFwOpcode.O_DIVERT, Insn),520AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),521AttrDescr(IpFwOpcode.O_QUEUE, Insn),522AttrDescr(IpFwOpcode.O_PIPE, Insn),523AttrDescr(IpFwOpcode.O_SKIPTO, InsnU32),524AttrDescr(IpFwOpcode.O_NETGRAPH, Insn),525AttrDescr(IpFwOpcode.O_NGTEE, Insn),526AttrDescr(IpFwOpcode.O_DIVERT, Insn),527AttrDescr(IpFwOpcode.O_TEE, Insn),528AttrDescr(IpFwOpcode.O_CALLRETURN, InsnU32),529AttrDescr(IpFwOpcode.O_SETFIB, Insn),530AttrDescr(IpFwOpcode.O_SETDSCP, Insn),531AttrDescr(IpFwOpcode.O_REASS, InsnEmpty),532AttrDescr(IpFwOpcode.O_SETMARK, InsnU32),533534AttrDescr(IpFwOpcode.O_EXTERNAL_ACTION, InsnU32),535AttrDescr(IpFwOpcode.O_EXTERNAL_INSTANCE, InsnU32),536537538539AttrDescr(IpFwOpcode.O_NOP, InsnComment),540AttrDescr(IpFwOpcode.O_PROTO, InsnProto),541AttrDescr(IpFwOpcode.O_PROB, InsnProb),542AttrDescr(IpFwOpcode.O_IP_DST_ME, InsnEmpty),543AttrDescr(IpFwOpcode.O_IP_SRC_ME, InsnEmpty),544AttrDescr(IpFwOpcode.O_IP6_DST_ME, InsnEmpty),545AttrDescr(IpFwOpcode.O_IP6_SRC_ME, InsnEmpty),546AttrDescr(IpFwOpcode.O_IP_SRC, InsnIp),547AttrDescr(IpFwOpcode.O_IP_DST, InsnIp),548AttrDescr(IpFwOpcode.O_IP6_DST, InsnIp6),549AttrDescr(IpFwOpcode.O_IP6_SRC, InsnIp6),550AttrDescr(IpFwOpcode.O_IP_SRC_LOOKUP, InsnU32),551AttrDescr(IpFwOpcode.O_IP_DST_LOOKUP, InsnU32),552AttrDescr(IpFwOpcode.O_IP_SRCPORT, InsnPorts),553AttrDescr(IpFwOpcode.O_IP_DSTPORT, InsnPorts),554AttrDescr(IpFwOpcode.O_PROBE_STATE, InsnU32),555AttrDescr(IpFwOpcode.O_KEEP_STATE, InsnU32),556]557)558559560