def _ParseMsgControl(buf): """Parse a raw control buffer into a list of tuples.""" msglist = [] while len(buf) > 0: cmsghdr, buf = cstruct.Read(buf, CMsgHdr) datalen = cmsghdr.len - len(CMsgHdr) data, buf = buf[:datalen], buf[PaddedLength(datalen):] if cmsghdr.level == socket.IPPROTO_IP: if cmsghdr.type == IP_PKTINFO: data = InPktinfo(data) elif cmsghdr.type == IP_TTL: data = struct.unpack("@I", data)[0] if cmsghdr.level == socket.IPPROTO_IPV6: if cmsghdr.type == IPV6_PKTINFO: data = In6Pktinfo(data) elif cmsghdr.type == IPV6_RECVERR: err, source = cstruct.Read(data, SockExtendedErr) if err.origin == SO_ORIGIN_ICMP6: source, pad = cstruct.Read(source, SockaddrIn6) data = (err, source) elif cmsghdr.type == IPV6_HOPLIMIT: data = struct.unpack("@I", data)[0] # If not, leave data as just the raw bytes. msglist.append((cmsghdr.level, cmsghdr.type, data)) return msglist
def DecodeBytecode(bytecode): instructions = [] try: while bytecode: op, rest = cstruct.Read(bytecode, InetDiagBcOp) if op.code in [INET_DIAG_BC_NOP, INET_DIAG_BC_JMP, INET_DIAG_BC_AUTO]: arg = None elif op.code in [INET_DIAG_BC_S_GE, INET_DIAG_BC_S_LE, INET_DIAG_BC_D_GE, INET_DIAG_BC_D_LE]: op, rest = cstruct.Read(rest, InetDiagBcOp) arg = op.no elif op.code in [INET_DIAG_BC_S_COND, INET_DIAG_BC_D_COND]: cond, rest = cstruct.Read(rest, InetDiagHostcond) if cond.family == 0: arg = (None, cond.prefix_len, cond.port) else: addrlen = 4 if cond.family == AF_INET else 16 addr, rest = rest[:addrlen], rest[addrlen:] addr = inet_ntop(cond.family, addr) arg = (addr, cond.prefix_len, cond.port) elif op.code == INET_DIAG_BC_DEV_COND: attrlen = struct.calcsize("=I") attr, rest = rest[:attrlen], rest[attrlen:] arg = struct.unpack("=I", attr) elif op.code == INET_DIAG_BC_MARK_COND: arg, rest = cstruct.Read(rest, InetDiagMarkcond) else: raise ValueError("Unknown opcode %d" % op.code) instructions.append((op, arg)) bytecode = rest return instructions except (TypeError, ValueError): return "???"
def _Decode(self, command, unused_msg, nla_type, nla_data): """Decodes netlink attributes to Python types.""" name = self._GetConstantName(nla_type, "XFRMA_") if name in ["XFRMA_ALG_CRYPT", "XFRMA_ALG_AUTH"]: data = cstruct.Read(nla_data, XfrmAlgo)[0] elif name == "XFRMA_ALG_AUTH_TRUNC": data = cstruct.Read(nla_data, XfrmAlgoAuth)[0] elif name == "XFRMA_ENCAP": data = cstruct.Read(nla_data, XfrmEncapTmpl)[0] else: data = nla_data return name, data
def ParseExtension(exttype, data): struct_type = None if exttype == SADB_EXT_SA: struct_type = SadbSa elif exttype in [SADB_EXT_LIFETIME_CURRENT, SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT]: struct_type = SadbLifetime elif exttype in [SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY]: struct_type = SadbAddress elif exttype in [SADB_EXT_KEY_AUTH, SADB_EXT_KEY_ENCRYPT]: struct_type = SadbKey elif exttype == SADB_X_EXT_SA2: struct_type = SadbXSa2 elif exttype == SADB_X_EXT_NAT_T_TYPE: struct_type = SadbXNatTType elif exttype in [SADB_X_EXT_NAT_T_SPORT, SADB_X_EXT_NAT_T_DPORT]: struct_type = SadbXNatTPort if struct_type: ext, attrs = cstruct.Read(data, struct_type) else: ext, attrs, = data, "" return exttype, ext, attrs
def _ExpectEspPacketOn(self, netid, spi, seq, length, src_addr, dst_addr): """Read a packet from a netid and verify its properties. Args: netid: netid from which to read an ESP packet spi: SPI of the ESP packet in host byte order seq: sequence number of the ESP packet length: length of the packet's ESP payload or None to skip this check src_addr: source address of the packet or None to skip this check dst_addr: destination address of the packet or None to skip this check Returns: scapy.IP/IPv6: the read packet """ packets = self.ReadAllPacketsOn(netid) self.assertEquals(1, len(packets)) packet = packets[0] if length is not None: self.assertEquals(length, len(packet.payload)) if dst_addr is not None: self.assertEquals(dst_addr, packet.dst) if src_addr is not None: self.assertEquals(src_addr, packet.src) # extract the ESP header esp_hdr, _ = cstruct.Read(str(packet.payload), xfrm.EspHdr) self.assertEquals(xfrm.EspHdr((spi, seq)), esp_hdr) return packet
def AllocSpi(self, dst, proto, min_spi, max_spi): """Allocate (reserve) an SPI. This sends an XFRM_MSG_ALLOCSPI message and returns the resulting XfrmUsersaInfo struct. Args: dst: A string, the destination IP address. Forms part of the XFRM ID, and must match the destination address of the packets sent by this SA. proto: the protocol DB of the SA, such as IPPROTO_ESP. min_spi: The minimum value of the acceptable SPI range (inclusive). max_spi: The maximum value of the acceptable SPI range (inclusive). """ spi = XfrmUserSpiInfo("\x00" * len(XfrmUserSpiInfo)) spi.min = min_spi spi.max = max_spi spi.info.id.daddr = PaddedAddress(dst) spi.info.id.proto = proto spi.info.family = AF_INET6 if ":" in dst else AF_INET msg = spi.Pack() flags = netlink.NLM_F_REQUEST self._SendNlRequest(XFRM_MSG_ALLOCSPI, msg, flags) # Read the response message. data = self._Recv() nl_hdr, data = cstruct.Read(data, netlink.NLMsgHdr) if nl_hdr.type == XFRM_MSG_NEWSA: return XfrmUsersaInfo(data) if nl_hdr.type == netlink.NLMSG_ERROR: error = netlink.NLMsgErr(data).error raise IOError(error, os.strerror(-error)) raise ValueError("Unexpected netlink message type: %d" % nl_hdr.type)
def ParseExtensions(data): """Parses the extensions in a SADB message.""" extensions = [] while data: ext, data = cstruct.Read(data, SadbExt) datalen = PfKey.ExtensionsLength(ext, SadbExt) extdata, data = data[:datalen], data[datalen:] extensions.append(ParseExtension(ext.exttype, extdata)) return extensions
def _ParseNLMsg(self, data, msgtype): """Parses a Netlink message into a header and a dictionary of attributes.""" nlmsghdr, data = cstruct.Read(data, NLMsgHdr) self._Debug(" %s" % nlmsghdr) if nlmsghdr.type == NLMSG_ERROR or nlmsghdr.type == NLMSG_DONE: print "done" return (None, None), data nlmsg, data = cstruct.Read(data, msgtype) self._Debug(" %s" % nlmsg) # Parse the attributes in the nlmsg. attrlen = nlmsghdr.length - len(nlmsghdr) - len(nlmsg) attributes = self._ParseAttributes(nlmsghdr.type, nlmsg, data[:attrlen]) data = data[attrlen:] return (nlmsg, attributes), data
def _ParseAck(self, response): # Find the error code. hdr, data = cstruct.Read(response, NLMsgHdr) if hdr.type == NLMSG_ERROR: error = NLMsgErr(data).error if error: raise IOError(error, os.strerror(-error)) else: raise ValueError("Expected ACK, got type %d" % hdr.type)
def assertIsUdpEncapEsp(self, packet, spi, seq, length): self.assertEquals(IPPROTO_UDP, packet.proto) udp_hdr = packet[scapy.UDP] self.assertEquals(4500, udp_hdr.dport) self.assertEquals(length, len(udp_hdr)) esp_hdr, _ = cstruct.Read(str(udp_hdr.payload), xfrm.EspHdr) # FIXME: this file currently swaps SPI byte order manually, so SPI needs to # be double-swapped here. self.assertEquals(xfrm.EspHdr((spi, seq)), esp_hdr)
def _ReadNlAttr(self, data): # Read the nlattr header. nla, data = cstruct.Read(data, NLAttr) # Read the data. datalen = nla.nla_len - len(nla) padded_len = util.GetPadLength(NLA_ALIGNTO, datalen) + datalen nla_data, data = data[:datalen], data[padded_len:] return nla, nla_data, data
def GetIfinfo(self, dev_name): """Fetches information about the specified interface. Args: dev_name: A string, the name of the interface. Returns: A tuple containing an IfinfoMsg struct and raw, undecoded attributes. """ ifinfo = IfinfoMsg().Pack() ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name) self._SendNlRequest(RTM_GETLINK, ifinfo) hdr, data = cstruct.Read(self._Recv(), netlink.NLMsgHdr) if hdr.type == RTM_NEWLINK: return cstruct.Read(data, IfinfoMsg) elif hdr.type == netlink.NLMSG_ERROR: error = netlink.NLMsgErr(data).error raise IOError(error, os.strerror(-error)) else: raise ValueError("Unknown Netlink Message Type %d" % hdr.type)
def DumpSaInfo(self): """Returns a list of (SadbMsg, [(extension, attr), ...], ...) tuples.""" dump = [] msg = self.MakeSadbMsg(SADB_DUMP, SADB_TYPE_UNSPEC) received = self.SendAndRecv(msg, "") while received: msg, data = cstruct.Read(received, SadbMsg) extlen = self.ExtensionsLength(msg, SadbMsg) extensions, data = data[:extlen], data[extlen:] dump.append((msg, self.ParseExtensions(extensions))) if msg.seq == 0: # End of dump. break received = self.Recv() return dump
def _Decode(self, command, msg, nla_type, nla_data): """Decodes netlink attributes to Python types.""" if msg.family == AF_INET or msg.family == AF_INET6: if isinstance(msg, InetDiagReqV2): prefix = "INET_DIAG_REQ" else: prefix = "INET_DIAG" name = self._GetConstantName(__name__, nla_type, prefix) else: # Don't know what this is. Leave it as an integer. name = nla_type if name in [ "INET_DIAG_SHUTDOWN", "INET_DIAG_TOS", "INET_DIAG_TCLASS", "INET_DIAG_SKV6ONLY" ]: data = ord(nla_data) elif name == "INET_DIAG_CONG": data = nla_data.strip("\x00") elif name == "INET_DIAG_MEMINFO": data = InetDiagMeminfo(nla_data) elif name == "INET_DIAG_INFO": # TODO: Catch the exception and try something else if it's not TCP. data = TcpInfo(nla_data) elif name == "INET_DIAG_SKMEMINFO": data = SkMeminfo(nla_data) elif name == "INET_DIAG_MARK": data = struct.unpack("=I", nla_data)[0] elif name == "INET_DIAG_REQ_BYTECODE": data = self.DecodeBytecode(nla_data) elif name in ["INET_DIAG_LOCALS", "INET_DIAG_PEERS"]: data = [] while len(nla_data): # The SCTP diag code always appears to copy sizeof(sockaddr_storage) # bytes, but does so from a union sctp_addr which is at most as long # as a sockaddr_in6. addr, nla_data = cstruct.Read(nla_data, csocket.SockaddrStorage) if addr.family == AF_INET: addr = csocket.SockaddrIn(addr.Pack()) elif addr.family == AF_INET6: addr = csocket.SockaddrIn6(addr.Pack()) data.append(addr) else: data = nla_data return name, data
def DecryptPacketWithNull(packet): """Apply null decryption to a packet. This performs ESP decapsulation on the given packet. The input packet is assumed to be a UDP packet. This function will remove the ESP header and trailer bytes from an ESP packet. TODO: Support TCP Args: packet: a scapy.IPv6 or scapy.IP packet Returns: A tuple of decrypted packet (scapy.IPv6 or scapy.IP) and EspHdr """ esp_hdr, esp_data = cstruct.Read(str(packet.payload), xfrm.EspHdr) # Parse and strip ESP trailer. pad_len, esp_nexthdr = struct.unpack("BB", esp_data[-2:]) trailer_len = pad_len + 2 # Add the size of the pad_len and next_hdr fields. LayerType = { IPPROTO_IPIP: scapy.IP, IPPROTO_IPV6: scapy.IPv6, IPPROTO_UDP: scapy.UDP}[esp_nexthdr] next_layer = LayerType(esp_data[:-trailer_len]) if esp_nexthdr in [IPPROTO_IPIP, IPPROTO_IPV6]: # Tunnel mode decap is simple. Return the inner packet. return next_layer, esp_hdr # Cut out the ESP header. packet.payload = next_layer # Fix the IPv4/IPv6 headers. if type(packet) is scapy.IPv6: packet.nh = IPPROTO_UDP packet.plen = None # Recompute packet length. packet = scapy.IPv6(str(packet)) elif type(packet) is scapy.IP: packet.proto = IPPROTO_UDP packet.len = None # Recompute packet length. packet.chksum = None # Recompute IPv4 checksum. packet = scapy.IP(str(packet)) else: raise ValueError("First layer in packet should be IPv4 or IPv6: " + repr(packet)) return packet, esp_hdr
def _ParseAttributes(self, command, family, msg, data): """Parses and decodes netlink attributes. Takes a block of NLAttr data structures, decodes them using Decode, and returns the result in a dict keyed by attribute number. Args: command: An integer, the rtnetlink command being carried out. family: The address family. msg: A Struct, the type of the data after the netlink header. data: A byte string containing a sequence of NLAttr data structures. Returns: A dictionary mapping attribute types (integers) to decoded values. Raises: ValueError: There was a duplicate attribute type. """ attributes = {} while data: # Read the nlattr header. nla, data = cstruct.Read(data, NLAttr) # Read the data. datalen = nla.nla_len - len(nla) padded_len = PaddedLength(nla.nla_len) - len(nla) nla_data, data = data[:datalen], data[padded_len:] # If it's an attribute we know about, try to decode it. nla_name, nla_data = self._Decode(command, msg, nla.nla_type, nla_data) # We only support unique attributes for now, except for INET_DIAG_NONE, # which can appear more than once but doesn't seem to contain any data. if nla_name in attributes and nla_name != "INET_DIAG_NONE": raise ValueError("Duplicate attribute %s" % nla_name) attributes[nla_name] = nla_data self._Debug(" %s" % str((nla_name, nla_data))) return attributes