def _packet_integrity_check(self) -> str: """Packet integrity check to be run on raw packet prior to parsing to make sure parsing is safe""" if not config.PACKET_INTEGRITY_CHECK: return "" if len(self) < IP4_HEADER_LEN: return "IPv4 integrity - wrong packet length (I)" if not IP4_HEADER_LEN <= self.hlen <= self.plen <= len(self): return "IPv4 integrity - wrong packet length (II)" # Cannot compute checksum earlier because it depends on sanity of hlen field if inet_cksum(self._frame[:self.hlen]): return "IPv4 integriy - wrong packet checksum" optr = IP4_HEADER_LEN while optr < self.hlen: if self._frame[optr] == IP4_OPT_EOL: break if self._frame[optr] == IP4_OPT_NOP: optr += 1 if optr > self.hlen: return "IPv4 integrity - wrong option length (I)" continue if optr + 1 > self.hlen: return "IPv4 integrity - wrong option length (II)" if self._frame[optr + 1] == 0: return "IPv4 integrity - wrong option length (III)" optr += self._frame[optr + 1] if optr > self.hlen: return "IPv4 integrity - wrong option length (IV)" return ""
def _packet_integrity_check(self, pshdr_sum: int) -> str: """Packet integrity check to be run on raw frame prior to parsing to make sure parsing is safe""" if not config.PACKET_INTEGRITY_CHECK: return "" if inet_cksum(self._frame[: self._plen], pshdr_sum): return "TCP integrity - wrong packet checksum" if not TCP_HEADER_LEN <= self._plen <= len(self): return "TCP integrity - wrong packet length (I)" hlen = (self._frame[12] & 0b11110000) >> 2 if not TCP_HEADER_LEN <= hlen <= self._plen <= len(self): return "TCP integrity - wrong packet length (II)" optr = TCP_HEADER_LEN while optr < hlen: if self._frame[optr] == TCP_OPT_EOL: break if self._frame[optr] == TCP_OPT_NOP: optr += 1 if optr > hlen: return "TCP integrity - wrong option length (I)" continue if optr + 1 > hlen: return "TCP integrity - wrong option length (II)" if self._frame[optr + 1] == 0: return "TCP integrity - wrong option length (III)" optr += self._frame[optr + 1] if optr > hlen: return "TCP integrity - wrong option length (IV)" return ""
def _packet_integrity_check(self, pshdr_sum: int) -> str: """Packet integrity check to be run on raw frame prior to parsing to make sure parsing is safe""" if not config.PACKET_INTEGRITY_CHECK: return "" if inet_cksum(self._frame[:self._plen], pshdr_sum): return "ICMPv6 integrity - wrong packet checksum" if not ICMP6_HEADER_LEN <= self._plen <= len(self): return "ICMPv6 integrity - wrong packet length (I)" if self._frame[0] == ICMP6_UNREACHABLE: if not 12 <= self._plen <= len(self): return "ICMPv6 integrity - wrong packet length (II)" elif self._frame[0] in {ICMP6_ECHO_REQUEST, ICMP6_ECHO_REPLY}: if not 8 <= self._plen <= len(self): return "ICMPv6 integrity - wrong packet length (II)" elif self._frame[0] == ICMP6_MLD2_QUERY: if not 28 <= self._plen <= len(self): return "ICMPv6 integrity - wrong packet length (II)" if self._plen != 28 + struct.unpack("!H", self._frame[26:28])[0] * 16: return "ICMPv6 integrity - wrong packet length (III)" elif self._frame[0] == ICMP6_ROUTER_SOLICITATION: if not 8 <= self._plen <= len(self): return "ICMPv6 integrity - wrong packet length (II)" if fail := self._nd_option_integrity_check(8): return fail
def assemble(self, frame: memoryview, pshdr_sum: int) -> None: """Assemble packet into the raw form""" struct.pack_into( f"! HH L L BBH HH {len(self._raw_options)}s {len(self._data)}s", frame, 0, self._sport, self._dport, self._seq, self._ack, self._hlen << 2 | self._flag_ns, self._flag_crw << 7 | self._flag_ece << 6 | self._flag_urg << 5 | self._flag_ack << 4 | self._flag_psh << 3 | self._flag_rst << 2 | self._flag_syn << 1 | self._flag_fin, self._win, 0, self._urp, self._raw_options, self._data, ) struct.pack_into("! H", frame, 16, inet_cksum(frame, pshdr_sum))
def _packet_integrity_check(self, pshdr_sum: int) -> str: """Packet integrity check to be run on raw frame prior to parsing to make sure parsing is safe""" if not config.PACKET_INTEGRITY_CHECK: return "" if inet_cksum(self._frame[:self._plen], pshdr_sum): return "UDP integrity - wrong packet checksum" if not UDP_HEADER_LEN <= self._plen <= len(self): return "UDP integrity - wrong packet length (I)" plen = struct.unpack("!H", self._frame[4:6])[0] if not UDP_HEADER_LEN <= plen == self._plen <= len(self): return "UDP integrity - wrong packet length (II)" return ""
def _packet_integrity_check(self) -> str: """Packet integrity check to be run on raw frame prior to parsing to make sure parsing is safe""" if not config.PACKET_INTEGRITY_CHECK: return "" if inet_cksum(self._frame[:self._plen]): return "ICMPv4 integrity - wrong packet checksum" if not ICMP4_HEADER_LEN <= self._plen <= len(self): return "ICMPv4 integrity - wrong packet length (I)" if self._frame[0] in {ICMP4_ECHO_REQUEST, ICMP4_ECHO_REPLY}: if not 8 <= self._plen <= len(self): return "ICMPv6 integrity - wrong packet length (II)" elif self._frame[0] == ICMP4_UNREACHABLE: if not 12 <= self._plen <= len(self): return "ICMPv6 integrity - wrong packet length (II)" return ""
def assemble(self, frame: memoryview) -> None: """Assemble packet into the raw form""" struct.pack_into( f"! BBH HH BBH 4s 4s {len(self._raw_options)}s {len(self._data)}s", frame, 0, self._ver << 4 | self._hlen >> 2, self._dscp << 2 | self._ecn, self._plen, self._id, self._flag_df << 14 | self._flag_mf << 13 | self._offset >> 3, self._ttl, self._proto, 0, bytes(self._src), bytes(self._dst), bytes(self._raw_options), # memoryview: conversion to bytes requir bytes(self._data), # memoryview: conversion to bytes requir ) struct.pack_into("! H", frame, 10, inet_cksum(frame[:self._hlen]))
def assemble(self, frame: memoryview, _: int = 0) -> None: """Assemble packet into the raw form""" if self._type == ICMP4_ECHO_REPLY: # memoryview: bytes conversion required struct.pack_into(f"! BBH HH {len(self._ec_data)}s", frame, 0, self._type, self._code, 0, self._ec_id, self._ec_seq, bytes(self._ec_data)) elif self._type == ICMP4_UNREACHABLE and self._code == ICMP4_UNREACHABLE__PORT: # memoryview: bytes conversion required struct.pack_into(f"! BBH L {len(self._un_data)}s", frame, 0, self._type, self._code, 0, 0, bytes(self._un_data)) elif self._type == ICMP4_ECHO_REQUEST: # memoryview: bytes conversion required struct.pack_into(f"! BBH HH {len(self._ec_data)}s", frame, 0, self._type, self._code, 0, self._ec_id, self._ec_seq, bytes(self._ec_data)) struct.pack_into("! H", frame, 2, inet_cksum(frame))
def assemble(self, frame: memoryview) -> None: """Assemble packet into the raw form""" struct.pack_into( f"! BBH HH BBH 4s 4s {len(self._raw_options)}s", frame, 0, self._ver << 4 | self._hlen >> 2, self._dscp << 2 | self._ecn, self._plen, self._id, self._flag_df << 14 | self._flag_mf << 13 | self._offset >> 3, self._ttl, self._proto, 0, bytes(self._src), bytes(self._dst), self._raw_options, ) struct.pack_into("! H", frame, 10, inet_cksum(frame[:self._hlen])) self._carried_packet.assemble(frame[self._hlen:], self.pshdr_sum)
def assemble(self, frame: memoryview, pshdr_sum: int) -> None: """Assemble packet into the raw form""" # memoryview: bytes conversion requir struct.pack_into(f"! HH HH {len(self._data)}s", frame, 0, self._sport, self._dport, self._plen, 0, bytes(self._data)) struct.pack_into("! H", frame, 6, inet_cksum(frame, pshdr_sum))
def _defragment_ip4_packet(self, packet_rx: PacketRx) -> Optional[PacketRx]: """Defragment IPv4 packet""" # Cleanup expir flows self.ip4_frag_flows = { _: self.ip4_frag_flows[_] for _ in self.ip4_frag_flows if self.ip4_frag_flows[_]["timestamp"] - time() < config.IP4_FRAG_FLOW_TIMEOUT } if __debug__: log( "ip4", f"{packet_rx.tracker} - IPv4 packet fragment, offset {packet_rx.ip4.offset}, dlen {packet_rx.ip4.dlen}" + f"{'' if packet_rx.ip4.flag_mf else ', last'}", ) flow_id = (packet_rx.ip4.src, packet_rx.ip4.dst, packet_rx.ip4.id) # Update flow db if self.ip4_frag_flows.get(flow_id, None): self.ip4_frag_flows[flow_id]["data"][ packet_rx.ip4.offset] = packet_rx.ip4.data_copy else: self.ip4_frag_flows[flow_id] = { "header": packet_rx.ip4.header_copy, "timestamp": time(), "last": False, "data": { packet_rx.ip4.offset: packet_rx.ip4.data_copy }, } if not packet_rx.ip4.flag_mf: self.ip4_frag_flows[flow_id]["last"] = True # Test if we received all fragments if not self.ip4_frag_flows[flow_id]["last"]: return None data_len = 0 for offset in sorted(self.ip4_frag_flows[flow_id]["data"]): if offset > data_len: return None data_len = offset + len(self.ip4_frag_flows[flow_id]["data"][offset]) # Defragment packet header = bytearray(self.ip4_frag_flows[flow_id]["header"]) data = bytearray(data_len) for offset in sorted(self.ip4_frag_flows[flow_id]["data"]): struct.pack_into( f"{len(self.ip4_frag_flows[flow_id]['data'][offset])}s", data, offset, self.ip4_frag_flows[flow_id]["data"][offset]) del self.ip4_frag_flows[flow_id] header[0] = 0x45 struct.pack_into("!H", header, 2, IP4_HEADER_LEN + len(data)) header[6] = header[7] = header[10] = header[11] = 0 struct.pack_into("!H", header, 10, inet_cksum(memoryview(header))) packet_rx = PacketRx(bytes(header) + data) Ip4Parser(packet_rx) if __debug__: log( "ip4", f"{packet_rx.tracker} - Reasembled fragmented IPv4 packet, dlen {len(data)} bytes" ) return packet_rx
def assemble(self, frame: memoryview, pshdr_sum: int) -> None: """Assemble packet into the raw form""" if self._type == ICMP6_UNREACHABLE: struct.pack_into(f"! BBH L {len(self._un_data)}s", frame, 0, self._type, self._code, 0, self._un_reserved, self._un_data) elif self._type == ICMP6_ECHO_REQUEST: struct.pack_into(f"! BBH HH {len(self._ec_data)}s", frame, 0, self._type, self._code, 0, self._ec_id, self._ec_seq, self._ec_data) elif self._type == ICMP6_ECHO_REPLY: # memoryview: bytes conversion required struct.pack_into(f"! BBH HH {len(self._ec_data)}s", frame, 0, self._type, self._code, 0, self._ec_id, self._ec_seq, bytes(self._ec_data)) elif self._type == ICMP6_ROUTER_SOLICITATION: struct.pack_into(f"! BBH L {len(self._raw_nd_options)}s", frame, 0, self._type, self._code, 0, self._rs_reserved, self._raw_nd_options) elif self._type == ICMP6_ROUTER_ADVERTISEMENT: assert self._ra_flag_m is not None assert self._ra_flag_o is not None struct.pack_into( f"! BBH BBH L L {len(self._raw_nd_options)}s", frame, 0, self._type, self._code, 0, self._ra_hop, (self._ra_flag_m << 7) | (self._ra_flag_o << 6), self._ra_router_lifetime, self._ra_reachable_time, self._ra_retrans_timer, self._raw_nd_options, ) elif self._type == ICMP6_NEIGHBOR_SOLICITATION: assert self._ns_target_address is not None struct.pack_into( f"! BBH L 16s {len(self._raw_nd_options)}s", frame, 0, self._type, self._code, 0, self._ns_reserved, bytes(self._ns_target_address), self._raw_nd_options, ) elif self._type == ICMP6_NEIGHBOR_ADVERTISEMENT: assert self._na_flag_r is not None assert self._na_flag_s is not None assert self._na_flag_o is not None assert self._na_target_address is not None struct.pack_into( f"! BBH L 16s {len(self._raw_nd_options)}s", frame, 0, self._type, self._code, 0, (self._na_flag_r << 31) | (self._na_flag_s << 30) | (self._na_flag_o << 29) | self._na_reserved, bytes(self._na_target_address), self._raw_nd_options, ) elif self._type == ICMP6_MLD2_REPORT: struct.pack_into( f"! BBH HH {sum([len(_) for _ in self._mlr2_multicast_address_record])}s", frame, 0, self._type, self._code, 0, self._mlr2_reserved, self._mlr2_number_of_multicast_address_records, b"".join([ _.raw_record for _ in self._mlr2_multicast_address_record ]), ) struct.pack_into("! H", frame, 2, inet_cksum(frame, pshdr_sum))