def start(self, transactionType: TransactionType) -> DhcpPacket: """Begin a DHCP transaction of given type. Returns the DHCP packet to send to the server. """ if transactionType == TransactionType.DISCOVER: self.transactionType = transactionType return DhcpPacket.fromArgs( OpCode.REQUEST, #OpCode self.transactionId, 0, #secondsElapsed self.clientIp, #clientIp IPv4Address('0.0.0.0'), #yourIp IPv4Address('255.255.255.255'), #serverIp self.clientHardwareAddr, MessageType.DISCOVER, int(DEFAULT_LEASE_TIME)) elif transactionType == TransactionType.RENEW: return DhcpPacket.fromArgs( OpCode.REQUEST, #OpCode self.transactionId, 0, #secondsElapsed self.clientIp, #clientIp self.yourIp, #yourIp IPv4Address('255.255.255.255'), #serverIp self.clientHardwareAddr, MessageType.REQUEST, int(DEFAULT_LEASE_TIME))
def recv(self, packet: DhcpPacket) -> Tuple[bool, Optional[DhcpPacket]]: """Recieve a reply from the server and returns the response packet. The response packet may be None to indicate conclusion of the session. """ self._phase += 1 if self._phase == 1: if packet.clientHardwareAddr == self.clientHardwareAddr: if packet.messageType == MessageType.OFFER: print("Client: Received offer message") print("Client: Sending request message") return False, DhcpPacket.fromArgs( OpCode.REQUEST, #opCode packet.transactionId, int(time.time() - self.transactionStartTime), packet.clientIp, #clientIP packet.yourIp, packet.serverIp, packet.clientHardwareAddr, MessageType.REQUEST, int(DEFAULT_LEASE_TIME)) if packet.messageType == MessageType.ACK: print("Client: Received ACK message") print("Client: IP has already been issued to client: " + str(packet.yourIp)) return True, None else: print("Client: Error: Phase-MessageType mismatch.") return False, None elif packet.messageType == MessageType.DECLINE: print("Client: REQUEST DECLINED") return False, None elif packet.messageType == MessageType.ACK: if packet.clientHardwareAddr == self.clientHardwareAddr: if self._phase == 3: if packet.messageType == MessageType.ACK: self.clientIp = packet.yourIp print("Client: Received ACK message") print(f"Client: Renewed IP {self.clientIp}") return True, DhcpPacket.fromArgs( OpCode.REQUEST, #opCode packet.transactionId, int(time.time() - self.transactionStartTime), packet.clientIp, #clientIP packet.yourIp, packet.serverIp, packet.clientHardwareAddr, MessageType.ACK, int(DEFAULT_LEASE_TIME)) else: print("Client: Error: Phase-MessageType mismatch") else: print("Client: MAC address does not match, could not ACK.") return True, None return True, None
def __gen( self, packet: DhcpPacket ) -> Generator[DhcpPacket, DhcpPacket, Optional[DhcpPacket]]: """Implements self.recv as a coroutine using a python generator.""" self.transactionId = packet.transactionId self.clientHardwareAddr = packet.clientHardwareAddr if packet.messageType is MessageType.DISCOVER: self.transactionType = TransactionType.DISCOVER log.info('Start DISCOVER transaction') log.info('DISCOVER transaction: Reply with OFFER of ' f'{self.yourIp} for {self.leaseTime} seconds') packet = yield DhcpPacket.fromArgs( OpCode.REPLY, self.transactionId, packet.secondsElapsed, packet.clientIp, self.yourIp, self.serverIp, packet.clientHardwareAddr, MessageType.OFFER, self.leaseTime) if packet.messageType is MessageType.REQUEST: log.info('DISCOVER transaction: Recieved REQUEST of ' f'{packet.yourIp} for {packet.leaseTime} seconds') self.requestIp = packet.yourIp return DhcpPacket.fromArgs(OpCode.REPLY, self.transactionId, packet.secondsElapsed, packet.clientIp, self.yourIp, self.serverIp, packet.clientHardwareAddr, MessageType.ACK, self.leaseTime) else: raise ValueError( f'Expected DHCP REQUEST. Got {packet.messageType.name}.') elif packet.messageType is MessageType.REQUEST: self.transactionType = TransactionType.RENEW log.info('Start RENEW transaction') log.info('RENEW transaction: Recieved REQUEST of ' f'{packet.yourIp} for {packet.leaseTime} seconds') packet = yield DhcpPacket.fromArgs( OpCode.REPLY, self.transactionId, packet.secondsElapsed, packet.clientIp, self.yourIp, self.serverIp, packet.clientHardwareAddr, MessageType.ACK, self.leaseTime) if packet.messageType is MessageType.ACK: self.requestIp = packet.yourIp return None else: raise ValueError( f'Expected DHCP ACK. Got {packet.messageType.name}.') else: raise ValueError('Unsupported transaction.')
def release(self) -> DhcpPacket: return DhcpPacket.fromArgs( OpCode.REQUEST, #opCode 0, #ID 0, #elapsed time IPv4Address('0.0.0.0'), #clientIP IPv4Address('0.0.0.0'), #yourIP IPv4Address('255.255.255.255'), #serverIP self.clientHardwareAddr, MessageType.RELEASE, int(DEFAULT_LEASE_TIME))
import logging from typing import cast from ipaddress import IPv4Address, IPv4Interface logging.basicConfig( level=logging.DEBUG, format='%(levelname)s: %(name)s - %(message)s') if __name__ == '__main__': server = DhcpServer(IPv4Interface('192.168.0.255/24')) packet1 = DhcpPacket.fromArgs( OpCode.REQUEST, 8, 34, IPv4Address(0), IPv4Address(0), IPv4Address(0), 4, MessageType.DISCOVER) ret1 = cast(DhcpPacket, server.recv(packet1)) leaseTime = cast(int, ret1.leaseTime) print() packet2 = DhcpPacket.fromArgs( OpCode.REQUEST, 8, 34, IPv4Address(0), ret1.yourIp,
def recv(self, packet: DhcpPacket) -> Optional[DhcpPacket]: """Recieves a DHCP packet and returns a response packet. The response packet may be None to indicate to not reply. """ log.info(f'Recieved {packet.messageType.name} ' f'with MAC of {packet.clientHardwareAddr} and ' f'ID of {packet.transactionId}') log.debug(f'Recieved packet: {packet}') # self.__timeoutTransactions() transaction: Optional[ServerTransaction] = None returnPacket: Optional[DhcpPacket] = None if packet.transactionId not in self.__curTransactions: if packet.messageType is MessageType.DISCOVER: if packet.clientHardwareAddr not in self.__leasedIpsByMacs: self.__timeoutIps() self.__setNextIp() if self.__nextIp is not None: self.__markIp(self.__nextIp) transaction = ServerTransaction() transaction.transactionId = packet.transactionId transaction.yourIp = self.__nextIp transaction.serverIp = self.interface.ip transaction.leaseTime = int(DEFAULT_LEASE_TIME) self.__registerTransaction(transaction) else: leaseTime = int(self.__leasedIps[self.__leasedIpsByMacs[ packet.clientHardwareAddr]][0] - time.time()) returnPacket = DhcpPacket.fromArgs( OpCode.REPLY, packet.transactionId, packet.secondsElapsed, self.__leasedIpsByMacs[packet.clientHardwareAddr], self.__leasedIpsByMacs[packet.clientHardwareAddr], self.interface.ip, packet.clientHardwareAddr, MessageType.ACK, leaseTime if leaseTime >= 0 else 0) elif packet.messageType is MessageType.REQUEST: if packet.yourIp not in self.__leasedIps: self.__markIp(packet.yourIp) transaction = ServerTransaction() transaction.transactionId = packet.transactionId transaction.yourIp = packet.yourIp transaction.serverIp = self.interface.ip transaction.leaseTime = int(DEFAULT_LEASE_TIME) self.__registerTransaction(transaction) else: returnPacket = DhcpPacket.fromArgs( OpCode.REPLY, packet.transactionId, packet.secondsElapsed, IPv4Address(0), IPv4Address(0), self.interface.ip, packet.clientHardwareAddr, MessageType.NAK) elif packet.messageType is MessageType.RELEASE: if packet.clientHardwareAddr in self.__leasedIpsByMacs: self.__freeIp( self.__leasedIpsByMacs[packet.clientHardwareAddr]) else: log.info( f'No IP to release for {packet.clientHardwareAddr}') else: transaction = self.__curTransactions[packet.transactionId] if transaction is not None: try: isTransactionOver, returnPacket = transaction.recv(packet) except ValueError as ve: log.error(f'Transaction error: {ve}') self.__unmarkIp(transaction.yourIp) self.__freeTransaction(transaction.transactionId) returnPacket = DhcpPacket.fromArgs( OpCode.REPLY, transaction.transactionId, packet.secondsElapsed, IPv4Address(0), IPv4Address(0), self.interface.ip, transaction.clientHardwareAddr, MessageType.NAK) else: if isTransactionOver: self.__unmarkIp(transaction.yourIp) if transaction.transactionType is TransactionType.DISCOVER: if transaction.requestIp not in self.__leasedIps: self.__leaseIp(transaction.yourIp, transaction.clientHardwareAddr) else: returnPacket = DhcpPacket.fromArgs( OpCode.REPLY, transaction.transactionId, packet.secondsElapsed, packet.yourIp, IPv4Address(0), self.interface.ip, transaction.clientHardwareAddr, MessageType.NAK) elif transaction.transactionType is TransactionType.RENEW: self.__leaseIp(transaction.yourIp, transaction.clientHardwareAddr) self.__freeTransaction(transaction.transactionId) log.debug(f'Return packet: {returnPacket}') return returnPacket