From e8c6ff1720acd36a34d4bf812d9fe221d14f3edf Mon Sep 17 00:00:00 2001 From: Craig Pratt Date: Fri, 12 Nov 2021 19:12:13 -0800 Subject: [PATCH] Implemented support for setting the hostapd SSID accd to the AP Group SSID (#59, #60) Note that this will only change the SSID on the AP when it's different than the value currently set on the AP, which should avoid connection thrashing. The SSID can currently be over-written by putting an SSID value in the file specified by NETREACH_ADAPTER_SSID_OVERRIDE_FILE - currently "lib/netreach-ssid-override.txt". --- micronets-gw-service/app/hostapd_adapter.py | 27 +++++++++++++++-- micronets-gw-service/app/netreach_adapter.py | 32 ++++++++++++++++++-- micronets-gw-service/config.py | 2 ++ 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/micronets-gw-service/app/hostapd_adapter.py b/micronets-gw-service/app/hostapd_adapter.py index 6610467..1a33227 100644 --- a/micronets-gw-service/app/hostapd_adapter.py +++ b/micronets-gw-service/app/hostapd_adapter.py @@ -192,9 +192,7 @@ def read_cli_output(self): async def process_hostapd_ready(self): logger.info(f"HostapdAdapter:process_hostapd_ready()") - status_cmd = await self.send_command(HostapdAdapter.StatusCLICommand()) - logger.info (f"HostapdAdapter:process_hostapd_ready: Retrieving status...") - self.status_vars = await status_cmd.get_status_dict() + await self.refresh_status_vars() for handler in self.event_handler_table: asyncio.ensure_future(handler.handle_hostapd_ready()) @@ -208,6 +206,11 @@ async def process_event(self, event_data): if handler.event_prefixes is None or event_data.startswith(handler.event_prefixes): asyncio.ensure_future(handler.handle_hostapd_cli_event(event_data)) + async def refresh_status_vars(self): + logger.info(f"HostapdAdapter:refresh_status_vars()") + status_cmd = await self.send_command(HostapdAdapter.StatusCLICommand()) + self.status_vars = await status_cmd.get_status_dict() + def get_status_var(self, var_name): if not self.status_vars: raise Exception("The Hostapd adapter status variables aren't initialized") @@ -617,6 +620,24 @@ async def was_successful(self): await self.get_response() return self.success + class ReloadCLICommand(HostapdCLICommand): + def __init__ (self, event_loop=asyncio.get_event_loop()): + super().__init__(event_loop) + self.success = False + + def get_command_string(self): + return "reload" + + async def process_response_data(self, response): + try: + self.success = "OK" in response + finally: + await super().process_response_data(response) + + async def was_successful(self): + await self.get_response() + return self.success + class ReloadPSKCLICommand(HostapdCLICommand): def __init__ (self, event_loop=asyncio.get_event_loop()): super().__init__(event_loop) diff --git a/micronets-gw-service/app/netreach_adapter.py b/micronets-gw-service/app/netreach_adapter.py index 342201a..551181d 100644 --- a/micronets-gw-service/app/netreach_adapter.py +++ b/micronets-gw-service/app/netreach_adapter.py @@ -26,6 +26,8 @@ def __init__ (self, config): self.pub_key_file = config['NETREACH_ADAPTER_PUBLIC_KEY_FILE'] self.priv_key_file = config['NETREACH_ADAPTER_PRIVATE_KEY_FILE'] self.wifi_interface = config['NETREACH_ADAPTER_WIFI_INTERFACE'] + self.ssid_override_file = config['NETREACH_ADAPTER_SSID_OVERRIDE_FILE'] + self.unassigned_ssid = config['NETREACH_ADAPTER_UNASSIGNED_SSID'] self.geolocation = config.get('NETREACH_ADAPTER_GEOLOCATION') self.management_address = config.get('NETREACH_ADAPTER_MAN_ADDRESS') self.management_interface = config.get('NETREACH_ADAPTER_MAN_INTERFACE') @@ -60,6 +62,7 @@ def __init__ (self, config): if not self.management_address and self.management_interface: logger.info(f"NetreachAdapter: Setting management address to {self.management_interface} address") self.management_address = self._ip_for_interface(self.management_interface) + self.ssid_override = self.ssid_override_file.read_text().strip() if self.ssid_override_file.exists() else None if not self.geolocation: self.geolocation = self._get_geolocation() self.pub_key = None @@ -310,6 +313,7 @@ async def _setup_micronets_for_ap(self): ap_groups = result.json()['results'] if len(ap_groups) == 0: logger.info(f"NetreachAdapter:_setup_micronets_for_ap {self.ap_name} ({self.ap_uuid}) does not have an AP Group. Nothing to setup.") + await self._configure_ap_for_ssids([self.unassigned_ssid]) return ap_group = ap_groups[0] @@ -320,7 +324,8 @@ async def _setup_micronets_for_ap(self): logger.info(f"NetreachAdapter:_setup_micronets_for_ap: apGroup {self.ap_group_name} " f"(apGroup {self.ap_group_uuid})") logger.info(f"NetreachAdapter:_setup_micronets_for_ap: ssid(s) {self.ssid_list}") - # TODO: Configure hostapd with the given ssid(s) + await self._configure_ap_for_ssids(self.ssid_list if not self.ssid_override else [self.ssid_override]) + result = httpx.get(f"{self.controller_base_url}/v1/services/?apGroupUuid={self.ap_group_uuid}", headers={"x-api-token": self.api_token}) if result.is_error: @@ -337,7 +342,7 @@ async def _setup_micronets_for_ap(self): micronet_subnet = IPv4Network(service['micronetSubnet'], strict=True) micronet_vlan = int(service['vlan']) # TODO: Replace this with gateway reference from Service object (see issue #15) - micronet_gateway = str(next(micronet_subnet.hosts())) + micronet_gateway = service['micronetGateway'] logger.info(f"NetreachAdapter:_setup_micronets_for_ap: Found service {service_name} ({service_uuid})") logger.info(f"NetreachAdapter:_setup_micronets_for_ap: micronet id {service_uuid} vlan {micronet_vlan}") if not (micronet_subnet and micronet_vlan): @@ -414,6 +419,29 @@ async def _setup_micronets_for_ap(self): f"for service {service_name} ({service_uuid}) - Result was {result.reason_phrase}") continue + async def _configure_ap_for_ssids(self, ssids): + # Note: This only deals with one SSID currently + logger.info(f"NetreachAdapter:_configure_ap_for_ssids({ssids})") + ssid = ssids[0] + + cur_ssids = self.hostapd_adapter.get_status_var("ssid") + if cur_ssids[0] == ssid: + logger.info(f"NetreachAdapter:_configure_ap_for_ssids({ssids}): AP SSID already set to \"{ssid}\" " + f"- nothing to do") + return + + logger.info(f"NetreachAdapter:_configure_ap_for_ssids({ssids}): Changing AP SSID from \"{cur_ssids[0]}\" " + f"to \"{ssid}\"") + set_cmd = await self.hostapd_adapter.send_command(HostapdAdapter.SetCLICommand("ssid", ssid)) + if not await set_cmd.was_successful(): + response = await set_cmd.get_response() + raise Exception(f"Could not set ssid to {ssid}: {response}") + + reload_cmd = await self.hostapd_adapter.send_command(HostapdAdapter.ReloadCLICommand()) + if not await reload_cmd.was_successful(): + raise Exception(f"Error issuing hostapd reload after setting SSID to {ssid}") + await self.hostapd_adapter.refresh_status_vars() + def _on_mqtt_connect(self, client, userdata, flags, rc): # handles the connecting event of the mqtt broker logger.info(f"NetreachAdapter:_on_mqtt_connect(client:{client},userdata:{userdata},flags:{flags},rc:{rc})") diff --git a/micronets-gw-service/config.py b/micronets-gw-service/config.py index f78976f..3bf51d2 100644 --- a/micronets-gw-service/config.py +++ b/micronets-gw-service/config.py @@ -52,6 +52,8 @@ class NetreachDefaultSettings(): NETREACH_ADAPTER_MAN_INTERFACE = "eth0" # NETREACH_ADAPTER_MAN_ADDRESS = "1.2.3.4" # NETREACH_ADAPTER_GEOLOCATION = {"latitude": "0.0", "longitude": "0.0"} + NETREACH_ADAPTER_SSID_OVERRIDE_FILE = libpath.joinpath('netreach-ssid-override.txt') + NETREACH_ADAPTER_UNASSIGNED_SSID = "netreach-inactive" NETREACH_ADAPTER_CONTROLLER_BASE_URL = "https://staging.api.controller.netreach.in" NETREACH_ADAPTER_API_KEY_FILE = libpath.joinpath('netreach-api-token.txt') NETREACH_ADAPTER_API_KEY_REFRESH_DAYS = 500