Skip to content

Commit 25f7cd2

Browse files
committed
Implement DHCP lease maintenance with renew / rebind.
- Now can use `socket.bind` to set source port to be consistent with CPython socket behavior. - DHCP object is now persistent in `WIZNET5K` because we want to keep it around to automatically renew and rebind. - Added `maintain_dhcp_lease` to `WIZNET5K` to properly maintain the DHCP lease with renew / rebind behavior. - Implement correct DHCP transaction ID behavior so we don't accidentally grab an IP that was supposed to be assigned to another device. - Now keeps track of automatically assigned source ports to prevent duplication. - Renamed `WIZNET5K._src_port` to `WIZNET5K.src_port` because it's externally accessed from `socket.connect`. - Removed setting source port for DNS lookup, moved to `bind` before socket connect. - `WIZNET5K.get_socket` now only returns fully closed sockets. Previous behavior caused problems with stray old data from previous connections confusing new connections when the socket wasn't fully closed yet. - Improved WSGI server in how it deals with socket removal and allocation. - Fixed some bugs and comments.
1 parent 0ac4cb0 commit 25f7cd2

File tree

5 files changed

+237
-155
lines changed

5 files changed

+237
-155
lines changed

adafruit_wiznet5k/adafruit_wiznet5k.py

Lines changed: 33 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@
121121
# UDP socket struct.
122122
UDP_SOCK = {"bytes_remaining": 0, "remote_ip": 0, "remote_port": 0}
123123

124+
# Source ports in use
125+
SRC_PORTS = [0] * W5200_W5500_MAX_SOCK_NUM
126+
124127

125128
class WIZNET5K: # pylint: disable=too-many-public-methods
126129
"""Interface for WIZNET5K module.
@@ -173,69 +176,48 @@ def __init__(
173176
assert self._w5100_init() == 1, "Failed to initialize WIZnet module."
174177
# Set MAC address
175178
self.mac_address = mac
176-
self._src_port = 0
179+
self.src_port = 0
177180
self._dns = 0
178181
# Set DHCP
182+
self._dhcp_client = None
179183
if is_dhcp:
180184
ret = self.set_dhcp(hostname, dhcp_timeout)
185+
if ret != 0:
186+
self._dhcp_client = None
181187
assert ret == 0, "Failed to configure DHCP Server!"
182188

183-
def set_dhcp(self, hostname=None, response_timeout=3):
189+
def set_dhcp(self, hostname=None, response_timeout=30):
184190
"""Initializes the DHCP client and attempts to retrieve
185191
and set network configuration from the DHCP server.
186-
Returns True if DHCP configured, False otherwise.
192+
Returns 0 if DHCP configured, -1 otherwise.
187193
:param str hostname: The desired hostname, with optional {} to fill in MAC.
188194
:param int response_timeout: Time to wait for server to return packet, in seconds.
189195
190196
"""
191197
if self._debug:
192198
print("* Initializing DHCP")
193-
self._src_port = 68
194199
# Return IP assigned by DHCP
195-
_dhcp_client = dhcp.DHCP(
200+
self._dhcp_client = dhcp.DHCP(
196201
self, self.mac_address, hostname, response_timeout, debug=self._debug
197202
)
198-
ret = _dhcp_client.request_dhcp_lease()
203+
ret = self._dhcp_client.request_dhcp_lease()
199204
if ret == 1:
200-
_ip = (
201-
_dhcp_client.local_ip[0],
202-
_dhcp_client.local_ip[1],
203-
_dhcp_client.local_ip[2],
204-
_dhcp_client.local_ip[3],
205-
)
206-
207-
_subnet_mask = (
208-
_dhcp_client.subnet_mask[0],
209-
_dhcp_client.subnet_mask[1],
210-
_dhcp_client.subnet_mask[2],
211-
_dhcp_client.subnet_mask[3],
212-
)
213-
214-
_gw_addr = (
215-
_dhcp_client.gateway_ip[0],
216-
_dhcp_client.gateway_ip[1],
217-
_dhcp_client.gateway_ip[2],
218-
_dhcp_client.gateway_ip[3],
219-
)
220-
221-
self._dns = (
222-
_dhcp_client.dns_server_ip[0],
223-
_dhcp_client.dns_server_ip[1],
224-
_dhcp_client.dns_server_ip[2],
225-
_dhcp_client.dns_server_ip[3],
226-
)
227-
self.ifconfig = (_ip, _subnet_mask, _gw_addr, self._dns)
228205
if self._debug:
206+
_ifconfig = self.ifconfig
229207
print("* Found DHCP Server:")
230208
print(
231209
"IP: {}\nSubnet Mask: {}\nGW Addr: {}\nDNS Server: {}".format(
232-
_ip, _subnet_mask, _gw_addr, self._dns
210+
*_ifconfig
233211
)
234212
)
235-
self._src_port = 0
236213
return 0
237214
return -1
238215

216+
def maintain_dhcp_lease(self):
217+
"""Maintain DHCP lease"""
218+
if self._dhcp_client is not None:
219+
self._dhcp_client.maintain_dhcp_lease()
220+
239221
def get_host_by_name(self, hostname):
240222
"""Convert a hostname to a packed 4-byte IP Address.
241223
Returns a 4 bytearray.
@@ -244,14 +226,12 @@ def get_host_by_name(self, hostname):
244226
print("* Get host by name")
245227
if isinstance(hostname, str):
246228
hostname = bytes(hostname, "utf-8")
247-
self._src_port = int(time.monotonic()) & 0xFFFF
248229
# Return IP assigned by DHCP
249230
_dns_client = dns.DNS(self, self._dns, debug=self._debug)
250231
ret = _dns_client.gethostbyname(hostname)
251232
if self._debug:
252233
print("* Resolved IP: ", ret)
253234
assert ret != -1, "Failed to resolve hostname!"
254-
self._src_port = 0
255235
return ret
256236

257237
@property
@@ -469,7 +449,11 @@ def socket_available(self, socket_num, sock_type=SNMR_TCP):
469449
:param int sock_type: Socket type, defaults to TCP.
470450
"""
471451
if self._debug:
472-
print("* socket_available called with protocol", sock_type)
452+
print(
453+
"* socket_available called on socket {}, protocol {}".format(
454+
socket_num, sock_type
455+
)
456+
)
473457
assert socket_num <= self.max_sockets, "Provided socket exceeds max_sockets."
474458

475459
res = self._get_rx_rcv_size(socket_num)
@@ -549,13 +533,7 @@ def get_socket(self):
549533
sock = SOCKET_INVALID
550534
for _sock in range(self.max_sockets):
551535
status = self.socket_status(_sock)[0]
552-
if status in (
553-
SNSR_SOCK_CLOSED,
554-
SNSR_SOCK_TIME_WAIT,
555-
SNSR_SOCK_FIN_WAIT,
556-
SNSR_SOCK_CLOSE_WAIT,
557-
SNSR_SOCK_CLOSING,
558-
):
536+
if status == SNSR_SOCK_CLOSED:
559537
sock = _sock
560538
break
561539

@@ -576,8 +554,9 @@ def socket_listen(self, socket_num, port):
576554
)
577555
)
578556
# Initialize a socket and set the mode
579-
self._src_port = port
557+
self.src_port = port
580558
res = self.socket_open(socket_num, conn_mode=SNMR_TCP)
559+
self.src_port = 0
581560
if res == 1:
582561
raise RuntimeError("Failed to initalize the socket.")
583562
# Send listen command
@@ -627,11 +606,15 @@ def socket_open(self, socket_num, conn_mode=SNMR_TCP):
627606
self._write_snmr(socket_num, conn_mode)
628607
self._write_snir(socket_num, 0xFF)
629608

630-
if self._src_port > 0:
609+
if self.src_port > 0:
631610
# write to socket source port
632-
self._write_sock_port(socket_num, self._src_port)
611+
self._write_sock_port(socket_num, self.src_port)
633612
else:
634-
self._write_sock_port(socket_num, randint(49152, 65535))
613+
s_port = randint(49152, 65535)
614+
while s_port in SRC_PORTS:
615+
s_port = randint(49152, 65535)
616+
self._write_sock_port(socket_num, s_port)
617+
SRC_PORTS[socket_num] = s_port
635618

636619
# open socket
637620
self._write_sncr(socket_num, CMD_SOCK_OPEN)

0 commit comments

Comments
 (0)