From 1dd35ff059ecd9cfa73591c104f43bfbc6451fad Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 8 Mar 2021 13:15:22 -1000 Subject: [PATCH] Catch dhcp setup permission errors sooner (#47639) This solves an unexpected thread exception on macs when running as a user intead of root --- homeassistant/components/dhcp/__init__.py | 18 ++++++++++-------- tests/components/dhcp/test_init.py | 10 ++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/dhcp/__init__.py b/homeassistant/components/dhcp/__init__.py index d33c6159888..304eea24fd4 100644 --- a/homeassistant/components/dhcp/__init__.py +++ b/homeassistant/components/dhcp/__init__.py @@ -207,8 +207,11 @@ class DHCPWatcher(WatcherBase): async def async_start(self): """Start watching for dhcp packets.""" + # disable scapy promiscuous mode as we do not need it + conf.sniff_promisc = 0 + try: - _verify_l2socket_creation_permission() + await self.hass.async_add_executor_job(_verify_l2socket_setup, FILTER) except (Scapy_Exception, OSError) as ex: if os.geteuid() == 0: _LOGGER.error("Cannot watch for dhcp packets: %s", ex) @@ -219,7 +222,7 @@ class DHCPWatcher(WatcherBase): return try: - await _async_verify_working_pcap(self.hass, FILTER) + await self.hass.async_add_executor_job(_verify_working_pcap, FILTER) except (Scapy_Exception, ImportError) as ex: _LOGGER.error( "Cannot watch for dhcp packets without a functional packet filter: %s", @@ -233,6 +236,7 @@ class DHCPWatcher(WatcherBase): prn=self.handle_dhcp_packet, store=0, ) + self._sniffer.start() def handle_dhcp_packet(self, packet): @@ -283,7 +287,7 @@ def _format_mac(mac_address): return format_mac(mac_address).replace(":", "") -def _verify_l2socket_creation_permission(): +def _verify_l2socket_setup(cap_filter): """Create a socket using the scapy configured l2socket. Try to create the socket @@ -292,15 +296,13 @@ def _verify_l2socket_creation_permission(): thread so we will not be able to capture any permission or bind errors. """ - # disable scapy promiscuous mode as we do not need it - conf.sniff_promisc = 0 - conf.L2socket() + conf.L2socket(filter=cap_filter) -async def _async_verify_working_pcap(hass, cap_filter): +def _verify_working_pcap(cap_filter): """Verify we can create a packet filter. If we cannot create a filter we will be listening for all traffic which is too intensive. """ - await hass.async_add_executor_job(compile_filter, cap_filter) + compile_filter(cap_filter) diff --git a/tests/components/dhcp/test_init.py b/tests/components/dhcp/test_init.py index fc24c8201e2..f5cc5f1728d 100644 --- a/tests/components/dhcp/test_init.py +++ b/tests/components/dhcp/test_init.py @@ -281,7 +281,7 @@ async def test_setup_and_stop(hass): await hass.async_block_till_done() with patch("homeassistant.components.dhcp.AsyncSniffer.start") as start_call, patch( - "homeassistant.components.dhcp._verify_l2socket_creation_permission", + "homeassistant.components.dhcp._verify_l2socket_setup", ), patch( "homeassistant.components.dhcp.compile_filter", ): @@ -307,7 +307,7 @@ async def test_setup_fails_as_root(hass, caplog): wait_event = threading.Event() with patch("os.geteuid", return_value=0), patch( - "homeassistant.components.dhcp._verify_l2socket_creation_permission", + "homeassistant.components.dhcp._verify_l2socket_setup", side_effect=Scapy_Exception, ): hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) @@ -330,7 +330,7 @@ async def test_setup_fails_non_root(hass, caplog): await hass.async_block_till_done() with patch("os.geteuid", return_value=10), patch( - "homeassistant.components.dhcp._verify_l2socket_creation_permission", + "homeassistant.components.dhcp._verify_l2socket_setup", side_effect=Scapy_Exception, ): hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) @@ -351,9 +351,7 @@ async def test_setup_fails_with_broken_libpcap(hass, caplog): ) await hass.async_block_till_done() - with patch( - "homeassistant.components.dhcp._verify_l2socket_creation_permission", - ), patch( + with patch("homeassistant.components.dhcp._verify_l2socket_setup",), patch( "homeassistant.components.dhcp.compile_filter", side_effect=ImportError, ) as compile_filter, patch(