def _ReadFromTap(): try: recvBytesNum = _tapFD_read.readinto(_TAPpacketBuffer[ARCHANET_HEADER_MAX_SIZE:]) ethernetPkt = _TAPpacketBuffer[ARCHANET_HEADER_MAX_SIZE:ARCHANET_HEADER_MAX_SIZE+recvBytesNum] #MacDst = EUI(int.from_bytes(ethernetPkt[0:6].tobytes(), "big")) MacSrc = EUI(int.from_bytes(ethernetPkt[6:12].tobytes(), "big")) EthType = ethernetPkt[12:14] ethernetPayload = ethernetPkt[14:recvBytesNum] ethernetPayload_len = len(ethernetPayload) #if EthType == 0x0806: # ARP Protocol if EthType == b'\x08\x06': # ARP Protocol # struct.unpack("!HHBBH", ethernetPayload[0:8]) #HardwareType = ethernetPayload[0:2] ARPProtocolType = ethernetPayload[2:4].tobytes() #HardwareAddressLength = ethernetPayload[4] #ProtocolAddressLength = ethernetPayload[5] #Operation = int.from_bytes(ethernetPayload[6:8], 'big') Operation = ethernetPayload[6:8].tobytes() if Operation == b'\x00\x01': if ARPProtocolType == b'\x08\x00': # 0x0800: #IPv4 #strFormat = str.format("!6s4s6s4s", HardwareAddressLength, ProtocolAddressLength, HardwareAddressLength, ProtocolAddressLength) SenderHardwareAddress = ethernetPayload[8:14] #SenderProtocolAddress = ethernetPayload[14:18] #TargetHardwareAddress = ethernetPayload[18:24] TargetProtocolAddress = ethernetPayload[24:28] targetIPv4 = IPv4Address(TargetProtocolAddress.tobytes()) if not (targetIPv4 in _tapIPv4Network): _TapLogger.debug(str.format("IPv4 {:s} does not belong to this network ({:s}).", str(targetIPv4), _tapIPv4Network)) return peer_client = _DB.QueryReverseDNSTranslation(IPv4Address(TargetProtocolAddress.tobytes())) if not peer_client: _TapLogger.debug(str.format("No ARP Solution for IPv4 {:s}.", str(targetIPv4))) return (client_mac, _, _) = _DB.QueryDNSTranslation(*peer_client) ethernetPkt[0:6] = SenderHardwareAddress ethernetPkt[6:12] = _tapMAC_bytes ethernetPayload[6:8] = b'\x00\x02' #int(2).to_bytes(2, 'big') ethernetPayload[18:24] = ethernetPayload[8:14] ethernetPayload[8:14] = client_mac.packed tmp = ethernetPayload[14:18].tobytes() ethernetPayload[14:18] = ethernetPayload[24:28] ethernetPayload[24:28] = tmp _tapFD_write.write(ethernetPkt[0:42]) return # _tapFD_write.write(struct.pack("!6s6sH HHBBH 6s4s6s4s", # SenderHardwareAddress, _tapMAC_bytes, 0x0806, # HardwareType, ARPProtocolType, HardwareAddressLength, ProtocolAddressLength, 2, # client_mac, TargetProtocolAddress, SenderHardwareAddress, SenderProtocolAddress) # ) if ARPProtocolType == b'\x86\xDD': #0x86DD: #IPv6 _TapLogger.debug("ARP IPv6 not Implemented - Discarding Packet") return #Other ARP Type -> Ignore _TapLogger.debug(str.format("Ignoring ARP Packet: Type {:d}", int.from_bytes(ARPProtocolType, 'big'))) return #Other ARP Operation Type -> Ignore _TapLogger.debug(str.format("Ignoring ARP Operation: {:d}", int.from_bytes(Operation, 'big'))) return ####################################################################################################################### #0x0800: # IPv4 Protocol Ingress if EthType == b'\x08\x00': IPv4_pkt = ethernetPayload IPv4Hdr_len = (IPv4_pkt[0] & 0x0F) * 4 srcIpAddr = IPv4_pkt[12:16] dstIpAddr = IPv4_pkt[16:20] srcIPv4obj = IPv4Address(srcIpAddr.tobytes()) dstIPv4obj = IPv4Address(dstIpAddr.tobytes()) if dstIPv4obj == _tapIPv4Network.broadcast_address: _TapLogger.debug("Broadcast IPv4 was detected. Ignoring Packet since Broadcast IPv4 is not Implemented.") return if dstIPv4obj.is_multicast: _TapLogger.debug("Multicast IPv4 was detected. Ignoring Packet since Multicast IPv4 is not Implemented.") return src_peerUUID_clientID = _DB.QueryReverseDNSTranslation(srcIPv4obj) if not src_peerUUID_clientID: ## Local Client is not registered. Sends an event to cognition to register the local client. _eventQueue.put_nowait((SYS_CONTROL_EVENT_FOUND_LAN_CLIENT, MacSrc, srcIPv4obj)) return (_, srcClientID) = src_peerUUID_clientID dst_peerUUID_clientID = _DB.QueryReverseDNSTranslation(dstIPv4obj) pathID = None if dst_peerUUID_clientID: (dstAgentUUID, dstClientID) = dst_peerUUID_clientID pathID = _PM.QueryBestPathToPeer(dstAgentUUID) #### If there is no translation available or no pathID available, return to sender an ICMP Host Unreachable if not (dst_peerUUID_clientID and pathID): # http://www.firewall.cx/networking-topics/protocols/icmp-protocol/153-icmp-destination-unreachable.html #Ethernet Header (14) + IPv4 (20) ICMP (12) + Original IPv4 Header + 8 bytes of Original data icmpBuf = memoryview(bytearray(IPv4Hdr_len + 54)) # MacHdr+IPHdr+ICMPHdr icmpBuf[0:6] = ethernetPkt[6:12] icmpBuf[6:12] = _tapMAC_bytes icmpBuf[12] = 0x08 #; icmpBuf[13] = (0x00) - unecessary. Bytearray already initialized with zero values #IPv4Hdr = struct.pack("!BBHHBBBBH4s4s", (0x04<<4)| 0x05, 0, len(icmpBuf)-14 , 0, (0x1<<6),0,0,1, 0 , dstIpAddr.tobytes(), _tapIPv4_bytes) IPv4Hdr = struct.pack("!BBHHBBBBH4s4s", (0x04<<4)| 0x05, 0, len(icmpBuf)-14 , 0, (0x1<<6),0,0,1, 0 , _tapIPv4_bytes, srcIpAddr.tobytes()) cs16Value = _checksum16(IPv4Hdr) icmpBuf[14:24] = IPv4Hdr[0:10] icmpBuf[24:26] = cs16Value.to_bytes(2, 'big') icmpBuf[26:34] = IPv4Hdr[12:20] strFrmt = str.format("!BBHHH{:d}s", IPv4Hdr_len+8) icmpBytes = struct.pack(strFrmt, 3, 1, 0, 0, 0, IPv4_pkt[0:IPv4Hdr_len+8].tobytes()) ICMPChecksum = _checksum16(icmpBytes) icmpBuf[34:34 + len(icmpBytes)] = icmpBytes icmpBuf[36:38] = ICMPChecksum.to_bytes(2, 'big') _tapFD_write.write(icmpBuf) if not dst_peerUUID_clientID: _TapLogger.debug(str.format("No Translation available for IPv4 {:s}. Dropping Packet and returning ICMP Host Unreachable.", str(dstIPv4obj))) else: _TapLogger.debug(str.format("No path available to peer {:s}. Dropping Packet and returning ICMP Host Unreachable.", str(dstAgentUUID))) return ######## ## TODO: Fragmentation needs to be implemented for variable MTU. Here it is the place ## Check Don't Fragment bit in the IPv4 header. If active, return the ICMP #// Check if the path max MTU can receive de packet. If not, send an ICMP with type 3 code 4 #// PathMTU already contemplates the minimum Archanet Header size. #// If the path requires to use a next path id, then it will require an archanet header with a minimum size of 52 bytes. #// http://tools.ietf.org/html/rfc1191 #// http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol - Destination Unreachable next hop mtu #// http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly # # REMEMBER - The Path MTU needs to be compared with the size of the future archanet packet length. It includes Archa header size and switch list size if not _DB.isPathIDRegistered(pathID): _TapLogger.debug(str.format("No Translation available for PathID {:s}. Dropping Packet.", byteStr2HexStr(pathID))) return (nextpathID, _, switchIDlist, _, pathMTU) = _DB.QueryPathID(pathID) (isActive, interfaceName, interfaceMAC, dstPeerMAC, _) = _DB.QuerySwitchIDInfo(switchIDlist[0]) if not isActive: _TapLogger.debug(str.format("Switch ID ({:d}) is not Active. Dropping Packet during Ingress.", switchIDlist[0])) return pktSwitchList = list(switchIDlist[1:]) pktSwitchList.reverse() archaNetFlags = 0 if nextpathID: archaNetPkt = _TAPpacketBuffer[0:] archaNetPkt[14] = archaNetFlags | ARCHA_FLAG_PATHID archaNetPkt[16:16+PATHID_HASH_WSIZE] = nextpathID pp_pivot = 16+PATHID_HASH_WSIZE else: archaNetPkt = _TAPpacketBuffer[16:] archaNetPkt[14] = archaNetFlags pp_pivot = 16 archaNetPkt[0:6] = dstPeerMAC.packed archaNetPkt[6:12] = interfaceMAC.packed archaNetPkt[12] = 0xAA; archaNetPkt[13] = 0xAA archaNetPkt[15] = len(pktSwitchList) archaNetPkt[pp_pivot:pp_pivot+16] = _systemUUID.bytes archaNetPkt[pp_pivot+16:pp_pivot+18] = ethernetPayload_len.to_bytes(2, 'big') archaNetPkt[pp_pivot+18] = 0x08 archaNetPkt[pp_pivot+19] = 0x00 archaNet_pkt_end = pp_pivot+20+ethernetPayload_len+len(pktSwitchList) archaNetPkt[pp_pivot+20+ethernetPayload_len:archaNet_pkt_end] = bytes(pktSwitchList) newIPv4_bytes = struct.pack("!II",srcClientID,dstClientID) #newSum = (IPv4_pkt[10] << 8) + IPv4_pkt[11] # The newSum here initialized, is actually the oldSum (C) newSum = int.from_bytes(IPv4_pkt[10:12], 'big') ### We get the old values oldStuff = sum((int.from_bytes(IPv4_pkt[12:14], 'big'), int.from_bytes(IPv4_pkt[14:16], 'big'), int.from_bytes(IPv4_pkt[16:18], 'big'), int.from_bytes(IPv4_pkt[18:20], 'big'), )) ### Prepare the new ones newStuff = srcClientID + dstClientID ### Calculating the new Checksum via incremental update newSum = _incr_check_s(newSum, oldStuff, newStuff) IPv4_pkt[12:20] = newIPv4_bytes IPv4_pkt[10] = (newSum >> 8) & 0xFF IPv4_pkt[11] = newSum & 0xFF if IPv4_pkt[9] == 0x6: #TCP Packet - Must recalculate checksum, otherwise the kernel will discard it ## Calculating new TCP Checksum through incremental Update - just because it uses a pseudo header... #tcpStartIndex = IPv4Hdr_len newSum = int.from_bytes(IPv4_pkt[IPv4Hdr_len+16:IPv4Hdr_len+18], 'big') newSum = _incr_check_s(newSum, oldStuff, newStuff) IPv4_pkt[IPv4Hdr_len+16] = (newSum >> 8) & 0xFF IPv4_pkt[IPv4Hdr_len+17] = newSum & 0xFF elif IPv4_pkt[9] == 0x11: # UDP Packet - Must recalculate checksum if different than zero. THIS IS NOT WORKING WELL!!! # Since I'm unable to understand why the incremental update works for the IP and TCP header but fails for the UDP header, # the checksum field will just be zeroed. # UDP checksum can be discarded IPv4_pkt[IPv4Hdr_len+6] = 0 IPv4_pkt[IPv4Hdr_len+7] = 0 # udpStartIndex = IPv4Hdr_len # udpCS = IPv4_pkt[udpStartIndex+6:udpStartIndex+8] # if udpCS != b'\x00\x00': # ## Calculating new UDP Checksum through incremental Update - checksum field is != 0 # newSum = _incr_check_s(int.from_bytes(udpCS, 'big'), oldStuff, newStuff) # if newSum == 0: # newSum = 0xFFFF # IPv4_pkt[udpStartIndex+6] = (newSum >> 8) & 0xFF # IPv4_pkt[udpStartIndex+7] = newSum & 0xFF _nameToInterfaceProp[interfaceName][0].send(archaNetPkt[0:archaNet_pkt_end]) #pktBytes = archaNetPkt[0:archaNet_pkt_end].tobytes() #_nameToInterfaceQueue[interfaceName].put_nowait((10, id(pktBytes), pktBytes)) return if EthType == b'\x86\xdd': #0x86DD: # IPv6 Protocol - Not Yet Implemented... _TapLogger.debug("IPv6 not Implemented - Discarding Packet") return _TapLogger.debug(str.format("Cannot Parse packet with type: {:d}", int.from_bytes(EthType, "big"))) return except Exception: LogCustomTraceBack(_TapLogger, logging.ERROR, *sys.exc_info())
def _ReadFromInterface(interfaceName): assert type(interfaceName) is str and len(interfaceName) > 0 and len(interfaceName) <= 16, str.format("interfaceName expected to be str with len > 0 and len <= 16. Got {:s}", repr(interfaceName)) try: (_, sckFD, interfaceMAC, buf, InterfaceLogger, _) = _nameToInterfaceProp[interfaceName] #recvBytesNum = sckFD.readinto(buf) sckFD.readinto(buf) #MacDst = EUI(int.from_bytes(buf[0:6].tobytes(), "big")) MacSrc = EUI(int.from_bytes(buf[6:12].tobytes(), "big")) #EthType = buf[12:14] hasPathID = buf[14] & ARCHA_FLAG_PATHID #switchIDs_num = buf[15] ### IF there are still switches in the list. Fast Packet Switch if buf[15]: archaHdr_len = ARCHANET_HEADER_MAX_SIZE if hasPathID else ARCHANET_HEADER_MIN_SIZE archaData_len = int.from_bytes(buf[48:50], 'big') if hasPathID else int.from_bytes(buf[32:34], 'big') nextSwitchPos = 14 + archaHdr_len + archaData_len + buf[15]-1 switchID = buf[nextSwitchPos] (isActive, interfaceName, interfaceMAC, dstMACaddr, _) = _DB.QuerySwitchIDInfo(switchID) if not isActive: InterfaceLogger.debug(str.format("Packet cannot be switched. Next Switch ({:d}) at buf position {:d} in line is not Active. Dropping Pkt.", switchID, nextSwitchPos)) return buf[0:6] = dstMACaddr.packed buf[6:12] = interfaceMAC.packed buf[15] = buf[15] - 1 _nameToInterfaceProp[interfaceName][0].send(buf[0:nextSwitchPos]) #pktBytes = buf.tobytes() #_nameToInterfaceQueue[interfaceName].put_nowait((10, id(pktBytes), pktBytes)) return ### If there is a PathID but there aren't any switch IDs left in the list. Route the packet. if hasPathID: #archaHdr_len = ARCHANET_HEADER_MAX_SIZE archaData_len = int.from_bytes(buf[48:50], 'big') pathID = bytes(buf[16:16+PATHID_HASH_WSIZE]) (nextpathID, _, switchIDlist, _, _) = _DB.QueryPathID(pathID) ### We can perform a double verification on the Path MTU... but this may be useless... switchID = switchIDlist[0] (isActive, interfaceName, interfaceMAC, dstMACaddr, _) = _DB.QuerySwitchIDInfo(switchID) if not isActive: InterfaceLogger.debug(str.format("Packet cannot be routed. Next Switch ({:d}) in line is not Active. Dropping Pkt.", switchID)) return pktSwitchList = list(switchIDlist[1:]) pktSwitchList.reverse() pktSwitchList_len = len(pktSwitchList) startSwIDlist_pos = 14 + ARCHANET_HEADER_MAX_SIZE + archaData_len # Because the packet has a pathID. if nextpathID: buf[0:6] = dstMACaddr.packed buf[6:12] = interfaceMAC.packed buf[15] = pktSwitchList_len buf[16:16+PATHID_HASH_WSIZE] = nextpathID buf[startSwIDlist_pos:startSwIDlist_pos+pktSwitchList_len] = bytes(pktSwitchList) _nameToInterfaceProp[interfaceName][0].send(buf[0:startSwIDlist_pos+pktSwitchList_len]) #pktBytes = buf[0:startSwIDlist_pos+pktSwitchList_len].tobytes() #_nameToInterfaceQueue[interfaceName].put_nowait((10, id(pktBytes), pktBytes)) else: buf[16:22] = dstMACaddr.packed buf[22:28] = interfaceMAC.packed buf[28] = 0xAA; buf[29] = 0xAA buf[30] = buf[14] & ((~ARCHA_FLAG_PATHID)&0xFF) # Remember... its buf[14] because we are moving the bitflag field, since this packet will not have a pathID buf[31] = pktSwitchList_len buf[startSwIDlist_pos:startSwIDlist_pos+pktSwitchList_len] = bytes(pktSwitchList) _nameToInterfaceProp[interfaceName][0].send(buf[16:startSwIDlist_pos+pktSwitchList_len]) #pktBytes = buf[16:startSwIDlist_pos+pktSwitchList_len].tobytes() #_nameToInterfaceQueue[interfaceName].put_nowait((10, id(pktBytes), pktBytes)) return ### No Switch IDs left and no Path ID, can only mean that it is meant for this peer. srcPeerUUID = UUID(bytes=bytes(buf[16:16+PATHID_HASH_WSIZE])) archaData_pos = 14 + ARCHANET_HEADER_MIN_SIZE archaData_len = int.from_bytes(buf[archaData_pos-4:archaData_pos-2], 'big') archaData = buf[archaData_pos:archaData_pos+archaData_len] if archaData_len < 6: InterfaceLogger.debug(str.format("Control Packet sent from {:s} has an invalid size (length {:d}). Discarding packet.", str(srcPeerUUID), archaData_len)) return # If the Packet is a control packet, send it to Cognition if buf[14] & ARCHA_FLAG_CONTROL: # First thing to do is to validate the control data. eventType = int.from_bytes(archaData[0:2], 'big') if eventType not in _acceptedControlTypes: InterfaceLogger.debug(str.format("Control Packet sent from {:s} contains an invalid Control Message Type ({:d}). Only {:s} can be accepted through Layer 2. Discarding packet.", str(srcPeerUUID), eventType, str(_acceptedControlTypes))) return control_crc32 = int.from_bytes(archaData[-4:], 'big') current_crc32_val = zlib.crc32(archaData[0:-4], 0) if control_crc32 != current_crc32_val: InterfaceLogger.debug(str.format("Control Packet sent from {:s} failed CRC32 check. Got {:s} when it should be {:s}. Discarding packet.", \ str(srcPeerUUID), byteStr2HexStr(current_crc32_val.to_bytes(4, 'big')), byteStr2HexStr(control_crc32.to_bytes(4, 'big')))) return if eventType == L2_CONTROL_PONG: pingID = int.from_bytes(archaData[2:6].tobytes(), byteorder='big') if pingID in _pingPeerFutures: if not _pingPeerFutures[pingID].done(): _pingPeerFutures[pingID].set_result(True) del _pingPeerFutures[pingID] return # If CRC checks out, send it to cognition. _eventQueue.put_nowait((eventType, interfaceName, srcPeerUUID, MacSrc, archaData[2:-4].tobytes())) return # If the Packet is a data packet, egress it to TAP Device #archaData_type = buf[34:36].tobytes() if buf[34:36] == b'\x08\x00': ## IPv4 Packet newPkt = buf[ARCHANET_HEADER_MIN_SIZE:archaData_pos+archaData_len] IPv4PacketPayload = buf[archaData_pos:archaData_pos+archaData_len] IPv4Hdr_len = (IPv4PacketPayload[0] & 0x0F) * 4 srcClientID = int.from_bytes(IPv4PacketPayload[12:16], 'big') dstClientID = int.from_bytes(IPv4PacketPayload[16:20], 'big') src_protocols = _DB.QueryDNSTranslation(srcPeerUUID, srcClientID) if not src_protocols: InterfaceLogger.debug(str.format("Received Packet from peer {:s} with client ID {:d} but no registration was found. Discarding Packet.", str(srcPeerUUID), srcClientID)) return dst_protocols = _DB.QueryDNSTranslation(_systemUUID, dstClientID) if not dst_protocols: InterfaceLogger.debug(str.format("Received Packet to Local Client ID {:d} but no registration was found for the local client. Discarding Packet.", dstClientID)) return (srcMAC, srcIPv4, _) = src_protocols (dstMAC, dstIPv4, _) = dst_protocols newPkt[0:6] = dstMAC.packed newPkt[6:12] = srcMAC.packed newIPv4_bytes = srcIPv4.packed + dstIPv4.packed #newSum = (IPv4PacketPayload[10] << 8) + IPv4PacketPayload[11] # The newSum here initialized, is actually the oldSum (C) newSum = int.from_bytes(IPv4PacketPayload[10:12], 'big') ### We get the old values oldStuff = sum((int.from_bytes(IPv4PacketPayload[12:14], 'big'), int.from_bytes(IPv4PacketPayload[14:16], 'big'), int.from_bytes(IPv4PacketPayload[16:18], 'big'), int.from_bytes(IPv4PacketPayload[18:20], 'big'), )) ### Prepare the new ones newStuff = int(srcIPv4) + int(dstIPv4) ### Calculating the new Checksum via incremental update newSum = _incr_check_s(newSum, oldStuff, newStuff) IPv4PacketPayload[12:20] = newIPv4_bytes IPv4PacketPayload[10] = (newSum >> 8) & 0xFF IPv4PacketPayload[11] = newSum & 0xFF if IPv4PacketPayload[9] == 0x6: #TCP Packet - Must recalculate checksum, otherwise the kernel will discard it #tcpStartIndex = IPv4Hdr_len ## Calculating new TCP Checksum through incremental Update - just because it uses a pseudo header... newSum = (IPv4PacketPayload[IPv4Hdr_len+16] << 8) + IPv4PacketPayload[IPv4Hdr_len+17] newSum = _incr_check_s(newSum, oldStuff, newStuff) IPv4PacketPayload[IPv4Hdr_len+16] = (newSum >> 8) & 0xFF IPv4PacketPayload[IPv4Hdr_len+17] = newSum & 0xFF elif IPv4PacketPayload[9] == 0x11: # UDP Packet - Must recalculate checksum if different than zero. THIS IS NOT WORKING WELL!!! # UDP Packet - Must recalculate checksum if different than zero. THIS IS NOT WORKING WELL!!! # Since I'm unable to understand why the incremental update works for the IP and TCP header but fails for the UDP header, # the checksum field will just be zeroed. # UDP checksum can be discarded IPv4PacketPayload[IPv4Hdr_len+6] = 0 IPv4PacketPayload[IPv4Hdr_len+7] = 0 # udpStartIndex = IPv4Hdr_len # udpCS = IPv4PacketPayload[udpStartIndex+6:udpStartIndex+8] # if udpCS != b'\x00\x00': # ## Calculating new UDP Checksum through incremental Update - checksum field is != 0 # newSum = _incr_check_s(int.from_bytes(udpCS, 'big'), oldStuff, newStuff) # if newSum == 0: # newSum = 0xFFFF # IPv4PacketPayload[udpStartIndex+6] = (newSum >> 8) & 0xFF # IPv4PacketPayload[udpStartIndex+7] = newSum & 0xFF _tapFD_write.write(newPkt) return if buf[34:36] == b'\x86\xdd': # IPv6 Protocol - Not Yet Implemented... InterfaceLogger.debug("IPv6 not Implemented - Discarding Packet") return except Exception: LogCustomTraceBack(InterfaceLogger, logging.ERROR, *sys.exc_info())