From d186cbe04fd3521088acaaddcd6acec9b046a156 Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Thu, 15 Dec 2022 06:19:06 +0300 Subject: [PATCH 1/7] Renamed wiznet5k_wsgiserver_test.py to wiznet5k_wsgiservertest.py to avoid problems with Pytest collecting tests. Began tests. --- .../{wiznet5k_wsgiserver_test.py => wiznet5k_wsgiservertest.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{wiznet5k_wsgiserver_test.py => wiznet5k_wsgiservertest.py} (100%) diff --git a/examples/wiznet5k_wsgiserver_test.py b/examples/wiznet5k_wsgiservertest.py similarity index 100% rename from examples/wiznet5k_wsgiserver_test.py rename to examples/wiznet5k_wsgiservertest.py From b55bdd23c96b8293784b1ef0d462ba3a1e47823e Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Thu, 15 Dec 2022 12:13:28 +0300 Subject: [PATCH 2/7] Completed tests for __init__ and send_dhcp_message. Fixed two off-by-one errors in send_dhcp_message. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 4 +- tests/test_dhcp.py | 276 ++++++++++++++++++++ 2 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 tests/test_dhcp.py diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index 3243f77..001cb26 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -179,7 +179,7 @@ def send_dhcp_message( # Transaction ID (xid) self._initial_xid = htonl(self._transaction_id) self._initial_xid = self._initial_xid.to_bytes(4, "big") - _BUFF[4:7] = self._initial_xid + _BUFF[4:8] = self._initial_xid # seconds elapsed _BUFF[8] = (int(time_elapsed) & 0xFF00) >> 8 @@ -195,7 +195,7 @@ def send_dhcp_message( # as they're already set to 0.0.0.0 # Except when renewing, then fill in ciaddr if renew: - _BUFF[12:15] = bytes(self.local_ip) + _BUFF[12:16] = bytes(self.local_ip) # chaddr _BUFF[28:34] = self._mac_address diff --git a/tests/test_dhcp.py b/tests/test_dhcp.py new file mode 100644 index 0000000..4fe633b --- /dev/null +++ b/tests/test_dhcp.py @@ -0,0 +1,276 @@ +# SPDX-FileCopyrightText: 2022 Martin Stephens +# +# SPDX-License-Identifier: MIT +"""Tests to confirm that there are no changes in behaviour to public methods and functions.""" + +# pylint: disable=no-self-use, redefined-outer-name, protected-access, invalid-name, too-many-arguments +import pytest + +from micropython import const +import adafruit_wiznet5k.adafruit_wiznet5k_dhcp as wiz_dhcp + +# +DEFAULT_DEBUG_ON = True + + +@pytest.fixture +def wiznet(mocker): + return mocker.patch("adafruit_wiznet5k.adafruit_wiznet5k.WIZNET5K", autospec=True) + + +@pytest.fixture +def wrench(mocker): + return mocker.patch( + "adafruit_wiznet5k.adafruit_wiznet5k_dhcp.socket", autospec=True + ) + + +class TestDHCPInit: + def test_constants(self): + # DHCP State Machine + assert wiz_dhcp.STATE_DHCP_START == const(0x00) + assert wiz_dhcp.STATE_DHCP_DISCOVER == const(0x01) + assert wiz_dhcp.STATE_DHCP_REQUEST == const(0x02) + assert wiz_dhcp.STATE_DHCP_LEASED == const(0x03) + assert wiz_dhcp.STATE_DHCP_REREQUEST == const(0x04) + assert wiz_dhcp.STATE_DHCP_RELEASE == const(0x05) + assert wiz_dhcp.STATE_DHCP_WAIT == const(0x06) + assert wiz_dhcp.STATE_DHCP_DISCONN == const(0x07) + + # DHCP wait time between attempts + assert wiz_dhcp.DHCP_WAIT_TIME == const(60) + + # DHCP Message Types + assert wiz_dhcp.DHCP_DISCOVER == const(1) + assert wiz_dhcp.DHCP_OFFER == const(2) + assert wiz_dhcp.DHCP_REQUEST == const(3) + assert wiz_dhcp.DHCP_DECLINE == const(4) + assert wiz_dhcp.DHCP_ACK == const(5) + assert wiz_dhcp.DHCP_NAK == const(6) + assert wiz_dhcp.DHCP_RELEASE == const(7) + assert wiz_dhcp.DHCP_INFORM == const(8) + + # DHCP Message OP Codes + assert wiz_dhcp.DHCP_BOOT_REQUEST == const(0x01) + assert wiz_dhcp.DHCP_BOOT_REPLY == const(0x02) + + assert wiz_dhcp.DHCP_HTYPE10MB == const(0x01) + assert wiz_dhcp.DHCP_HTYPE100MB == const(0x02) + + assert wiz_dhcp.DHCP_HLENETHERNET == const(0x06) + assert wiz_dhcp.DHCP_HOPS == const(0x00) + + assert wiz_dhcp.MAGIC_COOKIE == const(0x63825363) + assert wiz_dhcp.MAX_DHCP_OPT == const(0x10) + + # Default DHCP Server port + assert wiz_dhcp.DHCP_SERVER_PORT == const(67) + # DHCP Lease Time, in seconds + assert wiz_dhcp.DEFAULT_LEASE_TIME == const(900) + assert wiz_dhcp.BROADCAST_SERVER_ADDR == (255, 255, 255, 255) + + # DHCP Response Options + assert wiz_dhcp.MSG_TYPE == 53 + assert wiz_dhcp.SUBNET_MASK == 1 + assert wiz_dhcp.ROUTERS_ON_SUBNET == 3 + assert wiz_dhcp.DNS_SERVERS == 6 + assert wiz_dhcp.DHCP_SERVER_ID == 54 + assert wiz_dhcp.T1_VAL == 58 + assert wiz_dhcp.T2_VAL == 59 + assert wiz_dhcp.LEASE_TIME == 51 + assert wiz_dhcp.OPT_END == 255 + + # Packet buffer + assert wiz_dhcp._BUFF == bytearray(318) + + @pytest.mark.parametrize( + "mac_address", + ( + [1, 2, 3, 4, 5, 6], + (7, 8, 9, 10, 11, 12), + bytes([1, 2, 4, 6, 7, 8]), + ), + ) + def test_dhcp_setup_default(self, mocker, wiznet, wrench, mac_address): + # Test with mac address as tuple, list and bytes with default values. + mock_randint = mocker.patch( + "adafruit_wiznet5k.adafruit_wiznet5k_dhcp.randint", autospec=True + ) + mock_randint.return_value = 0x1234567 + dhcp_client = wiz_dhcp.DHCP(wiznet, mac_address) + assert dhcp_client._eth == wiznet + assert dhcp_client._response_timeout == 30.0 + assert dhcp_client._debug is False + assert dhcp_client._mac_address == mac_address + wrench.set_interface.assert_called_once_with(wiznet) + assert dhcp_client._sock is None + assert dhcp_client._dhcp_state == wiz_dhcp.STATE_DHCP_START + assert dhcp_client._initial_xid == 0 + mock_randint.assert_called_once() + assert dhcp_client._transaction_id == 0x1234567 + assert dhcp_client._start_time == 0 + assert dhcp_client.dhcp_server_ip == wiz_dhcp.BROADCAST_SERVER_ADDR + assert dhcp_client.local_ip == 0 + assert dhcp_client.gateway_ip == 0 + assert dhcp_client.subnet_mask == 0 + assert dhcp_client.dns_server_ip == 0 + assert dhcp_client._lease_time == 0 + assert dhcp_client._last_lease_time == 0 + assert dhcp_client._renew_in_sec == 0 + assert dhcp_client._rebind_in_sec == 0 + assert dhcp_client._t1 == 0 + assert dhcp_client._t2 == 0 + mac_string = "".join("{:02X}".format(o) for o in mac_address) + assert dhcp_client._hostname == bytes( + "WIZnet{}".split(".", maxsplit=1)[0].format(mac_string)[:42], "utf-8" + ) + + def test_dhcp_setup_other_args(self, wiznet): + mac_address = (7, 8, 9, 10, 11, 12) + dhcp_client = wiz_dhcp.DHCP( + wiznet, mac_address, hostname="fred.com", response_timeout=25.0, debug=True + ) + + assert dhcp_client._response_timeout == 25.0 + assert dhcp_client._debug is True + mac_string = "".join("{:02X}".format(o) for o in mac_address) + assert dhcp_client._hostname == bytes( + "fred.com".split(".", maxsplit=1)[0].format(mac_string)[:42], "utf-8" + ) + + +class TestSendDHCPMessage: + DHCP_SEND_01 = bytearray( + b"\x01\x01\x06\x00\xff\xff\xffo\x00\x17\x80\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x05\x06\x07" + b"\x08\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x01=" + b"\x07\x01\x04\x05\x06\x07\x08\t\x0c\x12WIZnet040506070809" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x007\x06\x01\x03" + b"\x06\x0f:;\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + + DHCP_SEND_02 = bytearray( + b"\x01\x01\x06\x00\xff\xff\xffo\x00#\x80\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18#.9DO\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x02=\x07\x01\x18#.9DO" + b"\x0c\x04bert\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x007\x06" + b"\x01\x03\x06\x0f:;\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + + DHCP_SEND_03 = bytearray( + b"\x01\x01\x06\x00\xff\xff\xffo\x00#\x80\x00\n\n\n+\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\xffa$e*c\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00c\x82Sc5\x01\x02=\x07\x01\xffa$e*c\x0c\x05cl" + b"ash\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x007" + b"\x06\x01\x03\x06\x0f:;\xff\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + + def test_send_with_defaults(self, wiznet, wrench): + assert len(wiz_dhcp._BUFF) == 318 + dhcp_client = wiz_dhcp.DHCP(wiznet, (4, 5, 6, 7, 8, 9)) + dhcp_client._sock = wrench.socket(type=wrench.SOCK_DGRAM) + dhcp_client._transaction_id = 0x6FFFFFFF + dhcp_client.send_dhcp_message(1, 23.4) + dhcp_client._sock.send.assert_called_once_with(self.DHCP_SEND_01) + assert len(wiz_dhcp._BUFF) == 318 + + @pytest.mark.parametrize( + "mac_address, hostname, state, time_elapsed, renew, local_ip, server_ip, result", + ( + ( + (4, 5, 6, 7, 8, 9), + None, + wiz_dhcp.STATE_DHCP_DISCOVER, + 23.4, + False, + 0, + 0, + DHCP_SEND_01, + ), + ( + (24, 35, 46, 57, 68, 79), + "bert.co.uk", + wiz_dhcp.STATE_DHCP_REQUEST, + 35.5, + False, + (192, 168, 3, 4), + (222, 123, 23, 10), + DHCP_SEND_02, + ), + ( + (255, 97, 36, 101, 42, 99), + "clash.net", + wiz_dhcp.STATE_DHCP_REQUEST, + 35.5, + True, + (10, 10, 10, 43), + (145, 66, 45, 22), + DHCP_SEND_03, + ), + ), + ) + def test_send_dhcp_message( + self, + wiznet, + wrench, + mac_address, + hostname, + state, + time_elapsed, + renew, + local_ip, + server_ip, + result, + ): + dhcp_client = wiz_dhcp.DHCP(wiznet, mac_address, hostname=hostname) + # Mock out socket to check what is sent + dhcp_client._sock = wrench.socket(type=wrench.SOCK_DGRAM) + # Set client attributes for test + dhcp_client.local_ip = local_ip + dhcp_client.dhcp_server_ip = server_ip + dhcp_client._transaction_id = 0x6FFFFFFF + # Test + dhcp_client.send_dhcp_message(state, time_elapsed, renew=renew) + dhcp_client._sock.send.assert_called_once_with(result) + assert len(wiz_dhcp._BUFF) == 318 From 6469eace1ac23e54b666e12fb8eb795bb70b7bc2 Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Thu, 15 Dec 2022 21:11:57 +0300 Subject: [PATCH 3/7] Completed tests for parse_dhcp_message. Fixed an off-by-one error in parse_dhcp_message and simplified MAGIC_COOKIE. Fixed router IP assignment to allow for multiple routers. Updated type hints. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 17 ++- tests/test_dhcp.py | 110 +++++++++++++++++++- 2 files changed, 116 insertions(+), 11 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index 001cb26..f3fb4ce 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -65,7 +65,7 @@ DHCP_HLENETHERNET = const(0x06) DHCP_HOPS = const(0x00) -MAGIC_COOKIE = const(0x63825363) +MAGIC_COOKIE = b"c\x82Sc" # Four bytes 99.130.83.99 MAX_DHCP_OPT = const(0x10) # Default DHCP Server port @@ -203,10 +203,7 @@ def send_dhcp_message( # NOTE: 192 octets of 0's, BOOTP legacy # Magic Cookie - _BUFF[236] = (MAGIC_COOKIE >> 24) & 0xFF - _BUFF[237] = (MAGIC_COOKIE >> 16) & 0xFF - _BUFF[238] = (MAGIC_COOKIE >> 8) & 0xFF - _BUFF[239] = MAGIC_COOKIE & 0xFF + _BUFF[236:240] = MAGIC_COOKIE # Option - DHCP Message Type _BUFF[240] = 53 @@ -262,10 +259,10 @@ def send_dhcp_message( # pylint: disable=too-many-branches, too-many-statements def parse_dhcp_response( self, - ) -> Union[Tuple[int, bytes], Tuple[int, int]]: + ) -> Union[Tuple[int, bytearray], Tuple[int, int]]: """Parse DHCP response from DHCP server. - :return Union[Tuple[int, bytes], Tuple[int, int]]: DHCP packet type. + :return Union[Tuple[int, bytearray], Tuple[int, int]]: DHCP packet type. """ # store packet in buffer _BUFF = self._sock.recv() @@ -288,7 +285,7 @@ def parse_dhcp_response( if _BUFF[28:34] == 0: return 0, 0 - if int.from_bytes(_BUFF[235:240], "big") != MAGIC_COOKIE: + if _BUFF[236:240] != MAGIC_COOKIE: return 0, 0 # -- Parse Packet, VARIABLE -- # @@ -322,8 +319,8 @@ def parse_dhcp_response( ptr += 1 opt_len = _BUFF[ptr] ptr += 1 - self.gateway_ip = tuple(_BUFF[ptr : ptr + opt_len]) - ptr += opt_len + self.gateway_ip = tuple(_BUFF[ptr : ptr + 4]) + ptr += opt_len # still increment even though we only read 1 addr. elif _BUFF[ptr] == DNS_SERVERS: ptr += 1 opt_len = _BUFF[ptr] diff --git a/tests/test_dhcp.py b/tests/test_dhcp.py index 4fe633b..4b2ab87 100644 --- a/tests/test_dhcp.py +++ b/tests/test_dhcp.py @@ -60,7 +60,7 @@ def test_constants(self): assert wiz_dhcp.DHCP_HLENETHERNET == const(0x06) assert wiz_dhcp.DHCP_HOPS == const(0x00) - assert wiz_dhcp.MAGIC_COOKIE == const(0x63825363) + assert wiz_dhcp.MAGIC_COOKIE == b"c\x82Sc" assert wiz_dhcp.MAX_DHCP_OPT == const(0x10) # Default DHCP Server port @@ -274,3 +274,111 @@ def test_send_dhcp_message( dhcp_client.send_dhcp_message(state, time_elapsed, renew=renew) dhcp_client._sock.send.assert_called_once_with(result) assert len(wiz_dhcp._BUFF) == 318 + + +class TestParseDhcpMessage: + + GOOD_DATA_01 = bytearray( + b"\x02\x00\x00\x00\xff\xff\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\xc0" + b"\xa8\x05\x16\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x05\x07\t\x0b\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01" + b"\x02\x01\x04\xc0\xa8\x06\x026\x04\xeao\xde{3\x04\x00\x01\x01\x00\x03" + b'\x04yy\x04\x05\x06\x04\x05\x06\x07\x08:\x04\x00""\x00;\x04\x0033\x00' + b"\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + GOOD_DATA_02 = bytearray( + b"\x02\x00\x00\x00\x9axV4\x00\x00\x00\x00\x00\x00\x00\x00\x12$@\n\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5" + b"\x01\x05<\x05\x01\x02\x03\x04\x05\x01\x04\n\x0b\x07\xde6\x04zN\x91\x03\x03" + b"\x08\n\x0b\x0e\x0f\xff\x00\xff\x00\x06\x08\x13\x11\x0b\x07****3\x04\x00\x00" + b"=;:\x04\x00\x0e\x17@;\x04\x02\x92]\xde\xff\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + + @pytest.mark.parametrize( + "xid, local_ip, msg_type, subnet, dhcp_ip, gate_ip, dns_ip, lease, t1, t2, response", + ( + ( + 0x7FFFFFFF, + (192, 168, 5, 22), + 2, + (192, 168, 6, 2), + (234, 111, 222, 123), + (121, 121, 4, 5), + (5, 6, 7, 8), + 65792, + 2236928, + 3355392, + GOOD_DATA_01, + ), + ( + 0x3456789A, + (18, 36, 64, 10), + 5, + (10, 11, 7, 222), + (122, 78, 145, 3), + (10, 11, 14, 15), + (19, 17, 11, 7), + 15675, + 923456, + 43146718, + GOOD_DATA_02, + ), + ), + ) + # pylint: disable=too-many-locals + def test_parse_good_data( + self, + wiznet, + wrench, + xid, + local_ip, + msg_type, + subnet, + dhcp_ip, + gate_ip, + dns_ip, + lease, + t1, + t2, + response, + ): + dhcp_client = wiz_dhcp.DHCP(wiznet, (1, 2, 3, 4, 5, 6)) + dhcp_client._sock = wrench.socket(type=wrench.SOCK_DGRAM) + dhcp_client._transaction_id = xid + dhcp_client._initial_xid = dhcp_client._transaction_id.to_bytes(4, "little") + dhcp_client._sock.recv.return_value = response + response_type, response_id = dhcp_client.parse_dhcp_response() + assert response_type == msg_type + assert response_id == bytearray(xid.to_bytes(4, "little")) + assert dhcp_client.local_ip == local_ip + assert dhcp_client.subnet_mask == subnet + assert dhcp_client.dhcp_server_ip == dhcp_ip + assert dhcp_client.gateway_ip == gate_ip + assert dhcp_client.dns_server_ip == dns_ip + assert dhcp_client._lease_time == lease + assert dhcp_client._t1 == t1 + assert dhcp_client._t2 == t2 From 2161e7f9ae9e08460ac252d391f2391ec2583c63 Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Thu, 15 Dec 2022 22:03:37 +0300 Subject: [PATCH 4/7] Added tested for parse_dhcp_response failures. Fixed the logic for checking server ID. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 5 ++- tests/test_dhcp.py | 43 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index f3fb4ce..911bd7e 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -277,12 +277,13 @@ def parse_dhcp_response( DHCP message OP is not expected BOOT Reply." xid = _BUFF[4:8] - if bytes(xid) < self._initial_xid: + if bytes(xid) != self._initial_xid: print("f") return 0, 0 self.local_ip = tuple(_BUFF[16:20]) - if _BUFF[28:34] == 0: + # Check that there is a server ID. + if _BUFF[28:34] == b"\x00\x00\x00\x00\x00\x00": return 0, 0 if _BUFF[236:240] != MAGIC_COOKIE: diff --git a/tests/test_dhcp.py b/tests/test_dhcp.py index 4b2ab87..566d8cd 100644 --- a/tests/test_dhcp.py +++ b/tests/test_dhcp.py @@ -278,6 +278,7 @@ def test_send_dhcp_message( class TestParseDhcpMessage: + # Basic case, no extra fields, one each of router and DNS. GOOD_DATA_01 = bytearray( b"\x02\x00\x00\x00\xff\xff\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\xc0" b"\xa8\x05\x16\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x05\x07\t\x0b\x00" @@ -298,6 +299,7 @@ class TestParseDhcpMessage: b"\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) + # Complex case, extra field, 2 each router and DNS. GOOD_DATA_02 = bytearray( b"\x02\x00\x00\x00\x9axV4\x00\x00\x00\x00\x00\x00\x00\x00\x12$@\n\x00\x00" b"\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" @@ -382,3 +384,44 @@ def test_parse_good_data( assert dhcp_client._lease_time == lease assert dhcp_client._t1 == t1 assert dhcp_client._t2 == t2 + + BAD_DATA = bytearray( + b"\x02\x00\x00\x00\xff\xff\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x12$@\n\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc" + ) + + def test_parsing_failures(self, wiznet, wrench): + # Test for bad OP code, ID mismatch, no server ID, bad Magic Cookie + dhcp_client = wiz_dhcp.DHCP(wiznet, (1, 2, 3, 4, 5, 6)) + dhcp_client._sock = wrench.socket(type=wrench.SOCK_DGRAM) + dhcp_client._sock.recv.return_value = self.BAD_DATA + # Transaction ID mismatch. + dhcp_client._transaction_id = 0x42424242 + dhcp_client._initial_xid = dhcp_client._transaction_id.to_bytes(4, "little") + assert dhcp_client.parse_dhcp_response() == (0, 0) + # Bad OP code. + self.BAD_DATA[0] = 0 + dhcp_client._transaction_id = 0x7FFFFFFF + dhcp_client._initial_xid = dhcp_client._transaction_id.to_bytes(4, "little") + with pytest.raises(AssertionError): + # pylint: disable=expression-not-assigned + dhcp_client.parse_dhcp_response() == (0, 0) + self.BAD_DATA[0] = 2 # Reset to good value + # No server ID. + self.BAD_DATA[28:34] = (0, 0, 0, 0, 0, 0) + assert dhcp_client.parse_dhcp_response() == (0, 0) + self.BAD_DATA[28:34] = (1, 1, 1, 1, 1, 1) # Reset to good value + # Bad Magic Cookie. + self.BAD_DATA[236] = 0 + assert dhcp_client.parse_dhcp_response() == (0, 0) From 449010a1230a5053cfbe303c8697bdbf63cb289f Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Thu, 15 Dec 2022 22:21:13 +0300 Subject: [PATCH 5/7] Refactored parse_dhcp_response to raise ValueError instead of returning 0, 0 when a problem occurs. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 17 +++++++---------- tests/test_dhcp.py | 14 ++++++++------ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index 911bd7e..52e0bba 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -259,10 +259,10 @@ def send_dhcp_message( # pylint: disable=too-many-branches, too-many-statements def parse_dhcp_response( self, - ) -> Union[Tuple[int, bytearray], Tuple[int, int]]: + ) -> Tuple[int, bytearray]: """Parse DHCP response from DHCP server. - :return Union[Tuple[int, bytearray], Tuple[int, int]]: DHCP packet type. + :return Tuple[int, bytearray]: DHCP packet type and ID. """ # store packet in buffer _BUFF = self._sock.recv() @@ -271,23 +271,20 @@ def parse_dhcp_response( # -- Parse Packet, FIXED -- # # Validate OP - assert ( - _BUFF[0] == DHCP_BOOT_REPLY - ), "Malformed Packet - \ - DHCP message OP is not expected BOOT Reply." + if _BUFF[0] != DHCP_BOOT_REPLY: + raise ValueError("DHCP message OP is not expected BOOTP Reply.") xid = _BUFF[4:8] if bytes(xid) != self._initial_xid: - print("f") - return 0, 0 + raise ValueError("DHCP response ID mismatch.") self.local_ip = tuple(_BUFF[16:20]) # Check that there is a server ID. if _BUFF[28:34] == b"\x00\x00\x00\x00\x00\x00": - return 0, 0 + raise ValueError("No DHCP server ID in the response.") if _BUFF[236:240] != MAGIC_COOKIE: - return 0, 0 + raise ValueError("No DHCP Magic Cookie in the response.") # -- Parse Packet, VARIABLE -- # ptr = 240 diff --git a/tests/test_dhcp.py b/tests/test_dhcp.py index 566d8cd..ccd46da 100644 --- a/tests/test_dhcp.py +++ b/tests/test_dhcp.py @@ -409,19 +409,21 @@ def test_parsing_failures(self, wiznet, wrench): # Transaction ID mismatch. dhcp_client._transaction_id = 0x42424242 dhcp_client._initial_xid = dhcp_client._transaction_id.to_bytes(4, "little") - assert dhcp_client.parse_dhcp_response() == (0, 0) + with pytest.raises(ValueError): + dhcp_client.parse_dhcp_response() # Bad OP code. self.BAD_DATA[0] = 0 dhcp_client._transaction_id = 0x7FFFFFFF dhcp_client._initial_xid = dhcp_client._transaction_id.to_bytes(4, "little") - with pytest.raises(AssertionError): - # pylint: disable=expression-not-assigned - dhcp_client.parse_dhcp_response() == (0, 0) + with pytest.raises(ValueError): + dhcp_client.parse_dhcp_response() self.BAD_DATA[0] = 2 # Reset to good value # No server ID. self.BAD_DATA[28:34] = (0, 0, 0, 0, 0, 0) - assert dhcp_client.parse_dhcp_response() == (0, 0) + with pytest.raises(ValueError): + dhcp_client.parse_dhcp_response() self.BAD_DATA[28:34] = (1, 1, 1, 1, 1, 1) # Reset to good value # Bad Magic Cookie. self.BAD_DATA[236] = 0 - assert dhcp_client.parse_dhcp_response() == (0, 0) + with pytest.raises(ValueError): + dhcp_client.parse_dhcp_response() From caa5136643cb6e468803c83d128e380423a7502e Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Fri, 16 Dec 2022 11:33:04 +0300 Subject: [PATCH 6/7] Altered the _dhcp_state_machine to handle exceptions raised by send_dhcp_message. Added pytest to optional_requirements.txt. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 12 ++- optional_requirements.txt | 1 + tests/test_dhcp.py | 89 ++++++++++++++++++++- 3 files changed, 97 insertions(+), 5 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index 52e0bba..a2fb006 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -421,7 +421,11 @@ def _dhcp_state_machine(self) -> None: if self._sock.available(): if self._debug: print("* DHCP: Parsing OFFER") - msg_type, xid = self.parse_dhcp_response() + try: + msg_type, xid = self.parse_dhcp_response() + except ValueError as error: + if self._debug: + print(error) if msg_type == DHCP_OFFER: # Check if transaction ID matches, otherwise it may be an offer # for another device @@ -446,7 +450,11 @@ def _dhcp_state_machine(self) -> None: if self._sock.available(): if self._debug: print("* DHCP: Parsing ACK") - msg_type, xid = self.parse_dhcp_response() + try: + msg_type, xid = self.parse_dhcp_response() + except ValueError as error: + if self._debug: + print(error) # Check if transaction ID matches, otherwise it may be # for another device if htonl(self._transaction_id) == int.from_bytes(xid, "big"): diff --git a/optional_requirements.txt b/optional_requirements.txt index d4e27c4..47e65a6 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -1,3 +1,4 @@ # SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries # # SPDX-License-Identifier: Unlicense +pytest diff --git a/tests/test_dhcp.py b/tests/test_dhcp.py index ccd46da..005e20f 100644 --- a/tests/test_dhcp.py +++ b/tests/test_dhcp.py @@ -2,10 +2,8 @@ # # SPDX-License-Identifier: MIT """Tests to confirm that there are no changes in behaviour to public methods and functions.""" - # pylint: disable=no-self-use, redefined-outer-name, protected-access, invalid-name, too-many-arguments import pytest - from micropython import const import adafruit_wiznet5k.adafruit_wiznet5k_dhcp as wiz_dhcp @@ -277,7 +275,6 @@ def test_send_dhcp_message( class TestParseDhcpMessage: - # Basic case, no extra fields, one each of router and DNS. GOOD_DATA_01 = bytearray( b"\x02\x00\x00\x00\xff\xff\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\xc0" @@ -427,3 +424,89 @@ def test_parsing_failures(self, wiznet, wrench): self.BAD_DATA[236] = 0 with pytest.raises(ValueError): dhcp_client.parse_dhcp_response() + + +class TestStateMachine: + @pytest.mark.parametrize( + "dhcp_state, socket_state", + ( + (wiz_dhcp.STATE_DHCP_START, "Socket"), + (wiz_dhcp.STATE_DHCP_DISCOVER, None), + (wiz_dhcp.STATE_DHCP_REQUEST, None), + (wiz_dhcp.STATE_DHCP_LEASED, None), + (wiz_dhcp.STATE_DHCP_REREQUEST, None), + (wiz_dhcp.STATE_DHCP_RELEASE, None), + (wiz_dhcp.STATE_DHCP_WAIT, None), + ), + ) + def test_link_is_down_state_not_disconnected( + self, mocker, wiznet, dhcp_state, socket_state + ): + dhcp_client = wiz_dhcp.DHCP(wiznet, (1, 2, 3, 4, 5, 6)) + dhcp_client._eth.link_status = False + dhcp_client._eth.ifconfig = ( + (1, 1, 1, 1), + (1, 1, 1, 1), + (1, 1, 1, 1), + (1, 1, 1, 1), + ) + dhcp_client._last_lease_time = 1 + dhcp_client.dhcp_server_ip = (192, 234, 1, 75) + dhcp_client._dhcp_state = dhcp_state + # If a socket exists, close() will be called, so add a Mock. + if socket_state is not None: + dhcp_client._sock = mocker.MagicMock() + else: + dhcp_client._dhcp_state = None + # Test. + dhcp_client._dhcp_state_machine() + # DHCP state machine in correct state. + assert dhcp_client._dhcp_state == wiz_dhcp.STATE_DHCP_DISCONN + # Check that configurations are returned to defaults. + assert dhcp_client._eth.ifconfig == ( + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + ) + assert dhcp_client._last_lease_time == 0 + assert dhcp_client.dhcp_server_ip == wiz_dhcp.BROADCAST_SERVER_ADDR + assert dhcp_client._sock is None + + def test_link_is_down_state_disconnected(self, wiznet): + dhcp_client = wiz_dhcp.DHCP(wiznet, (1, 2, 3, 4, 5, 6)) + dhcp_client._eth.link_status = False + dhcp_client._eth.ifconfig = ( + (1, 1, 1, 1), + (1, 1, 1, 1), + (1, 1, 1, 1), + (1, 1, 1, 1), + ) + dhcp_client._last_lease_time = 1 + dhcp_client.dhcp_server_ip = (192, 234, 1, 75) + dhcp_client._sock = "socket" + dhcp_client._dhcp_state = wiz_dhcp.STATE_DHCP_DISCONN + # Test. + dhcp_client._dhcp_state_machine() + # DHCP state machine in correct state. + assert dhcp_client._dhcp_state == wiz_dhcp.STATE_DHCP_DISCONN + # Check that configurations are not altered because state has not changed. + assert dhcp_client._eth.ifconfig == ( + (1, 1, 1, 1), + (1, 1, 1, 1), + (1, 1, 1, 1), + (1, 1, 1, 1), + ) + assert dhcp_client._last_lease_time == 1 + assert dhcp_client.dhcp_server_ip == (192, 234, 1, 75) + assert dhcp_client._sock == "socket" + + def test_link_is_up_state_disconnected(self, wiznet, wrench): + dhcp_client = wiz_dhcp.DHCP(wiznet, (1, 2, 3, 4, 5, 6)) + wrench.socket.side_effect = [RuntimeError] + dhcp_client._eth.link_status = True + dhcp_client._dhcp_state = wiz_dhcp.STATE_DHCP_DISCONN + # Test. + dhcp_client._dhcp_state_machine() + # Assume state is set to START then becomes WAIT after START fails to set a socket + assert dhcp_client._dhcp_state == wiz_dhcp.STATE_DHCP_WAIT From 26069eada77889ae409b4adb2c8283dcad572317 Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Fri, 16 Dec 2022 11:48:05 +0300 Subject: [PATCH 7/7] Added an else to the try statements. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 100 +++++++++++--------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index a2fb006..1797a4c 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -426,25 +426,30 @@ def _dhcp_state_machine(self) -> None: except ValueError as error: if self._debug: print(error) - if msg_type == DHCP_OFFER: - # Check if transaction ID matches, otherwise it may be an offer - # for another device - if htonl(self._transaction_id) == int.from_bytes(xid, "big"): - if self._debug: - print( - "* DHCP: Send request to {}".format(self.dhcp_server_ip) + else: + if msg_type == DHCP_OFFER: + # Check if transaction ID matches, otherwise it may be an offer + # for another device + if htonl(self._transaction_id) == int.from_bytes(xid, "big"): + if self._debug: + print( + "* DHCP: Send request to {}".format( + self.dhcp_server_ip + ) + ) + self._transaction_id = ( + self._transaction_id + 1 + ) & 0x7FFFFFFF + self.send_dhcp_message( + DHCP_REQUEST, (time.monotonic() - self._start_time) ) - self._transaction_id = (self._transaction_id + 1) & 0x7FFFFFFF - self.send_dhcp_message( - DHCP_REQUEST, (time.monotonic() - self._start_time) - ) - self._dhcp_state = STATE_DHCP_REQUEST + self._dhcp_state = STATE_DHCP_REQUEST + else: + if self._debug: + print("* DHCP: Received OFFER with non-matching xid") else: if self._debug: - print("* DHCP: Received OFFER with non-matching xid") - else: - if self._debug: - print("* DHCP: Received DHCP Message is not OFFER") + print("* DHCP: Received DHCP Message is not OFFER") elif self._dhcp_state == STATE_DHCP_REQUEST: if self._sock.available(): @@ -455,39 +460,40 @@ def _dhcp_state_machine(self) -> None: except ValueError as error: if self._debug: print(error) - # Check if transaction ID matches, otherwise it may be - # for another device - if htonl(self._transaction_id) == int.from_bytes(xid, "big"): - if msg_type == DHCP_ACK: - if self._debug: - print("* DHCP: Successful lease") - self._sock.close() - self._sock = None - self._dhcp_state = STATE_DHCP_LEASED - self._last_lease_time = self._start_time - if self._lease_time == 0: - self._lease_time = DEFAULT_LEASE_TIME - if self._t1 == 0: - # T1 is 50% of _lease_time - self._t1 = self._lease_time >> 1 - if self._t2 == 0: - # T2 is 87.5% of _lease_time - self._t2 = self._lease_time - (self._lease_time >> 3) - self._renew_in_sec = self._t1 - self._rebind_in_sec = self._t2 - self._eth.ifconfig = ( - self.local_ip, - self.subnet_mask, - self.gateway_ip, - self.dns_server_ip, - ) - gc.collect() + else: + # Check if transaction ID matches, otherwise it may be + # for another device + if htonl(self._transaction_id) == int.from_bytes(xid, "big"): + if msg_type == DHCP_ACK: + if self._debug: + print("* DHCP: Successful lease") + self._sock.close() + self._sock = None + self._dhcp_state = STATE_DHCP_LEASED + self._last_lease_time = self._start_time + if self._lease_time == 0: + self._lease_time = DEFAULT_LEASE_TIME + if self._t1 == 0: + # T1 is 50% of _lease_time + self._t1 = self._lease_time >> 1 + if self._t2 == 0: + # T2 is 87.5% of _lease_time + self._t2 = self._lease_time - (self._lease_time >> 3) + self._renew_in_sec = self._t1 + self._rebind_in_sec = self._t2 + self._eth.ifconfig = ( + self.local_ip, + self.subnet_mask, + self.gateway_ip, + self.dns_server_ip, + ) + gc.collect() + else: + if self._debug: + print("* DHCP: Received DHCP Message is not ACK") else: if self._debug: - print("* DHCP: Received DHCP Message is not ACK") - else: - if self._debug: - print("* DHCP: Received non-matching xid") + print("* DHCP: Received non-matching xid") elif self._dhcp_state == STATE_DHCP_WAIT: if time.monotonic() > (self._start_time + DHCP_WAIT_TIME):