ESP8266 - lwip state is not renitialized on soft-reset
wendlers opened this issue · comments
When using bind
on a lwip
socket, it looks like the binding is not removed after soft-reset. Binding again to the same port is only possible after hard-reset (when calling close
before the soft reset, it is unbound, but I think the socket instances should be closed on soft reset automatically).
import os
>>> os.uname()
(sysname='ESP8266', nodename='ESP8266', release='1.5.2(80914727)', version='v1.6-217-g99fc0d1-dirty on 2016-03-13', machine='ESP module with ESP8266')
>>> import lwip
>>> so = lwip.socket()
>>> so.bind(("", 80))
>>>
PYB: soft reboot
could not find module 'boot'
>>>
MicroPython v1.6-217-g99fc0d1-dirty on 2016-03-13; ESP module with ESP8266
Type "help()" for more information.
>>> print(so)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name not defined
>>> import lwip
>>> so = lwip.socket()
>>> so.bind(("", 80))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: 128
>>> so.bind(("", 81))
Yes, proper soft-reset is not implemented. You'll need to use hard reset for now.
OK. I found kind of a "work-around" to avoid hard-reset (I mostly access the serial line of the ESP remotely):
esptool.py --port /dev/ttyUSB0 run
The esptool seams to restart whats programmed to the flash, and after that, the socket is unbound :-)
There is a slightly more severe problem caused by this bug: if you soft-reset and there is an open socket with a callback set (via setsockopt(socket.SOL_SOCKET, 20, cb)), the callback will be invalid after the soft reset and the interpreter may crash if it's triggered. Tested on an esp8266, but I think any port using lwip would be affected.
To reproduce, in micropython run:
import socket
l = socket.socket()
l.bind(socket.getaddrinfo('0.0.0.0', 1234)[0][4])
l.listen(1)
l.setsockopt(socket.SOL_SOCKET, 20, lambda x: x.accept())
Then, connect to port 1234 (with telnet or netcat or whatever) from another host.
Then, soft-reset.
Then, send data on or close the connection to port 1234.
At best, micropython will produce a bogus exception, e.g.:
TypeError: 'int' object is not callable
or
TypeError: 'tuple' object is not callable
At worst, micropython will cause an OS exception:
Fatal exception 9(LoadStoreAlignmentCause):
epc1=0x40211c62, epc2=0x00000000, epc3=0x00000000, excvaddr=0x00000002, depc=0x00000000
ets Jan 8 2013,rst cause:2, boot mode:(3,7)
In commit b2fa1b5 a call to gc_sweep_all()
is added to close any remaining sockets upon soft reset. This should help to fix the issues here.