def test__build(self): mac = factory.getRandomMACAddress() discover = DHCPDiscoverPacket(mac) discover._build() expected = (b'\x01\x01\x06\x00' + discover.transaction_ID + b'\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + discover.packed_mac + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00' * 67 + b'\x00' * 125 + b'\x63\x82\x53\x63\x35\x01\x01\x3d\x06' + discover.packed_mac + b'\x37\x03\x03\x01\x06\xff') self.assertEqual(expected, discover.packet)
def test__deferDHCPRequests(self): clock = Clock() monitor = DHCPRequestMonitor('lo', clock) mock_addErrback = mock.MagicMock() mock_deferredListResult = mock.MagicMock() mock_deferLater = self.patch(detect_module, 'deferLater') mock_deferLater.return_value = mock.MagicMock() mock_deferLater.return_value.addErrback = mock_addErrback mock_DeferredList = self.patch(detect_module, 'DeferredList') mock_DeferredList.return_value = mock_deferredListResult mock_deferredListResult.addErrback = mock_addErrback expected_calls = [ call( clock, seconds + 0.1, send_dhcp_request_packet, DHCPDiscoverPacket(transaction_id=monitor.transaction_id, seconds=seconds), 'lo') for seconds in DHCP_REQUEST_TIMING ] monitor.deferDHCPRequests() self.assertThat(mock_deferLater, MockCallsMatch(*expected_calls)) self.assertThat( mock_DeferredList, MockCallsMatch( call(monitor.deferredDHCPRequests, fireOnOneErrback=True, consumeErrors=True))) # Expect addErrback to be called both on each individual Deferred, plus # one more time on the DeferredList. expected_errback_calls = [ call(monitor.deferredDHCPRequestErrback) for _ in range(len(DHCP_REQUEST_TIMING) + 1) ] self.assertThat(mock_addErrback, MockCallsMatch(*expected_errback_calls))
def test_init_sets_transaction_ID(self): transaction_id = make_transaction_ID() self.patch(detect_module, 'make_transaction_ID').return_value = (transaction_id) discover = DHCPDiscoverPacket(factory.getRandomMACAddress()) self.assertEqual(transaction_id, discover.transaction_ID)
def test__sends_expected_packet(self): mock_socket = patch_socket(self) mock_socket.bind = mock.MagicMock() mock_socket.sendto = mock.MagicMock() self.patch(detect_module.get_interface_ip).return_value = '127.0.0.1' self.patch( detect_module.get_interface_mac).return_value = '00:00:00:00:00:00' request = DHCPDiscoverPacket() send_dhcp_request_packet(request, 'lo') self.assertThat(mock_socket.bind, MockCallsMatch(call(('127.0.0.1', BOOTP_CLIENT_PORT)))) self.assertThat( mock_socket.sendto, MockCallsMatch( call(request.packet, ('<broadcast>', BOOTP_SERVER_PORT))))
def test__builds_packet(self): mac = factory.make_mac_address() seconds = random.randint(0, 1024) xid = factory.make_bytes(4) discover = DHCPDiscoverPacket(transaction_id=xid, mac=mac, seconds=seconds) seconds_bytes = seconds.to_bytes(2, "big") expected = (b"\x01\x01\x06\x00" + xid + seconds_bytes + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00" + discover.mac_bytes + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00" * 67 + b"\x00" * 125 + b"\x63\x82\x53\x63\x35\x01\x01" + discover.client_uid_option + b"\x37\x03\x03\x01\x06\xff") self.assertEqual(expected, discover.packet)
def test__packet_property_after_init_with_mac_and_no_transaction_id(self): discover = DHCPDiscoverPacket(factory.make_mac_address()) self.assertIsNotNone(discover.packet)
def test_init_sets_mac_bytes(self): mac = factory.make_mac_address() discover = DHCPDiscoverPacket(mac) self.assertEqual(discover.mac_string_to_bytes(mac), discover.mac_bytes)
def test_init_sets_transaction_id(self): transaction_id = make_dhcp_transaction_id() self.patch(detect_module, "make_dhcp_transaction_id").return_value = transaction_id discover = DHCPDiscoverPacket(factory.make_mac_address()) self.assertEqual(transaction_id, discover.transaction_id)
def test__send_requests_and_await_replies(self): # This test is a bit large because it covers the entire functionality # of the `send_requests_and_await_replies()` method. (It could be # split apart into multiple tests, but the large amount of setup work # and interdependencies makes that a maintenance burden.) mock_socket = patch_socket(self) mock_socket.bind = mock.MagicMock() mock_socket.recvfrom = mock.MagicMock() mock_socket.setsockopt = mock.MagicMock() mock_socket.settimeout = mock.MagicMock() # Pretend we were successful at deferring the DHCP requests. self.patch_autospec(detect_module, "blockingCallFromThread") # This method normally blocks for ~10 seconds, so take control of the # monotonic clock and make sure the last call to `recvfrom()` happens # just as we hit the reply timeout. mock_time_monotonic = self.patch(detect_module.time.monotonic) mock_time_monotonic.side_effect = ( # Start time (before loop starts). 10, # First reply (truncated packet). 11, # Second reply (not a match to our transaction). 12, # Third reply (Matching reply with server identifier option). 13, # First socket timeout (need to make sure the loop continues). 14, # Second socket timeout (hey, we're done!). 10 + detect_module.REPLY_TIMEOUT, ) mock_xid = factory.make_bytes(4) valid_dhcp_reply = factory.make_dhcp_packet( transaction_id=mock_xid, include_server_identifier=True, server_ip="127.1.1.1", ) mock_get_xid = self.patch(detect_module.make_dhcp_transaction_id) mock_get_xid.return_value = mock_xid # Valid DHCP packet, but not a match because it doesn't have a # Server Identifier option. valid_non_match = DHCPDiscoverPacket(mac="01:02:03:04:05:06", transaction_id=mock_xid).packet mock_socket.recvfrom.side_effect = ( # Truncated packet, to test logging. (b"", ("127.0.0.1", BOOTP_SERVER_PORT)), (valid_non_match, ("127.0.0.2", BOOTP_SERVER_PORT)), (valid_dhcp_reply, ("127.0.0.3", BOOTP_SERVER_PORT)), socket.timeout, socket.timeout, ) logger = self.useFixture(TwistedLoggerFixture()) monitor = DHCPRequestMonitor("lo", Clock()) result = monitor.send_requests_and_await_replies() self.assertThat( mock_socket.setsockopt, MockCallsMatch( call(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1), call(socket.SOL_SOCKET, socket.SO_BROADCAST, 1), ), ) self.assertThat(mock_socket.bind, MockCallsMatch(call(("", 68)))) self.assertThat( mock_socket.settimeout, MockCallsMatch(call(detect_module.SOCKET_TIMEOUT)), ) self.assertThat( mock_socket.recvfrom, MockCallsMatch(call(2048), call(2048), call(2048), call(2048), call(2048)), ) # One of the response packets was truncated. self.assertThat( logger.output, DocTestMatches("Invalid DHCP response...Truncated..."), ) self.assertThat(result, HasLength(1)) # Ensure we record the fact that the reply packet came from a different # IP address than the server claimed to be. self.assertThat(result, Contains(DHCPServer("127.1.1.1", "127.0.0.3")))
def test_init_sets_packet(self): discover = DHCPDiscoverPacket(factory.getRandomMACAddress()) self.assertIsNotNone(discover.packet)
def test_init_sets_packed_mac(self): mac = factory.getRandomMACAddress() discover = DHCPDiscoverPacket(mac) self.assertEqual(discover.string_mac_to_packed(mac), discover.packed_mac)