Fix memory leak in dhcp integration (#45378)

* Fix memory leak in dhcp integration

Passing the L2socket to AsyncSniffer caused a memory
leak on some systems. To ensure we can create a socket,
we do a test creation before starting AsyncSniffer
since the sniffer will create it in another thread
and we cannot see any permission error otherwise.

* Update tests

* space

* do not store packets
pull/45165/head^2
J. Nick Koston 2021-01-21 01:26:58 -06:00 committed by GitHub
parent 2925474a5d
commit e8cda598ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 25 additions and 11 deletions

View File

@ -8,7 +8,6 @@ import os
import threading
from scapy.config import conf
from scapy.data import ETH_P_ALL
from scapy.error import Scapy_Exception
from scapy.layers.dhcp import DHCP
from scapy.layers.l2 import Ether
@ -208,14 +207,7 @@ class DHCPWatcher(WatcherBase):
async def async_start(self):
"""Start watching for dhcp packets."""
try:
sniff_socket = conf.L2socket(type=ETH_P_ALL)
self._sniffer = AsyncSniffer(
filter=FILTER,
opened_socket=[sniff_socket],
started_callback=self._started.set,
prn=self.handle_dhcp_packet,
)
self._sniffer.start()
_verify_l2socket_creation_permission()
except (Scapy_Exception, OSError) as ex:
if os.geteuid() == 0:
_LOGGER.error("Cannot watch for dhcp packets: %s", ex)
@ -225,6 +217,14 @@ class DHCPWatcher(WatcherBase):
)
return
self._sniffer = AsyncSniffer(
filter=FILTER,
started_callback=self._started.set,
prn=self.handle_dhcp_packet,
store=0,
)
self._sniffer.start()
def handle_dhcp_packet(self, packet):
"""Process a dhcp packet."""
if DHCP not in packet:
@ -271,3 +271,15 @@ def _decode_dhcp_option(dhcp_options, key):
def _format_mac(mac_address):
"""Format a mac address for matching."""
return format_mac(mac_address).replace(":", "")
def _verify_l2socket_creation_permission():
"""Create a socket using the scapy configured l2socket.
Try to create the socket
to see if we have permissions
since AsyncSniffer will do it another
thread so we will not be able to capture
any permission or bind errors.
"""
conf.L2socket()

View File

@ -303,7 +303,8 @@ async def test_setup_fails_as_root(hass, caplog):
wait_event = threading.Event()
with patch("os.geteuid", return_value=0), patch(
"homeassistant.components.dhcp.AsyncSniffer.start", side_effect=Scapy_Exception
"homeassistant.components.dhcp._verify_l2socket_creation_permission",
side_effect=Scapy_Exception,
):
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
@ -327,7 +328,8 @@ async def test_setup_fails_non_root(hass, caplog):
wait_event = threading.Event()
with patch("os.geteuid", return_value=10), patch(
"homeassistant.components.dhcp.AsyncSniffer.start", side_effect=Scapy_Exception
"homeassistant.components.dhcp._verify_l2socket_creation_permission",
side_effect=Scapy_Exception,
):
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()