Configure network interface for AP mode
(self)
| 2641 | |
| 2642 | engine = self._wardriving_engine() |
| 2643 | self._wardrive_ap_lent_iface = None |
| 2644 | |
| 2645 | # 1) Prefer a genuine spare (multi-adapter rigs). |
| 2646 | iface = self._find_wardrive_ap_interface() |
| 2647 | # 2) Otherwise borrow one from wardriving's scan set — preferring the |
| 2648 | # built-in radio (the designed AP interface), so a premium USB scanner |
| 2649 | # like an Alfa keeps scanning and the AP lands on a radio that can |
| 2650 | # actually run hostapd. |
| 2651 | if iface is None and engine is not None: |
| 2652 | iface = engine.lend_interface_for_ap(prefer=self.default_wifi_interface) |
| 2653 | self._wardrive_ap_lent_iface = iface |
| 2654 | # 3) Last resort. |
| 2655 | if iface is None: |
| 2656 | iface = self.default_wifi_interface |
| 2657 | |
| 2658 | # If we're taking the live WiFi uplink, drop it cleanly first — a single |
| 2659 | # chip can't be both a station and an AP. |
| 2660 | if getattr(self, 'wifi_connected', False) and iface == self.default_wifi_interface: |
| 2661 | self.logger.info(f"Wardrive AP: reclaiming {iface} from the WiFi uplink (WiFi will drop)") |
| 2662 | try: |
| 2663 | self.disconnect_wifi() |
| 2664 | except Exception as e: |
| 2665 | self.logger.warning(f"Wardrive AP: disconnect_wifi failed: {e}") |
| 2666 | |
| 2667 | self._wardrive_ap_prev_interface = self.ap_interface |
| 2668 | self.ap_interface = iface |
| 2669 | self.logger.info(f"Wardrive AP: starting on {iface} (SSID={self.ap_ssid})") |
| 2670 | # captive=False: this AP is a viewer for the wardriving page, not an |
| 2671 | # internet path. Advertising no gateway/DNS keeps the phone's cellular |
| 2672 | # data working while it's joined (see _dnsmasq_config_no_internet). |
| 2673 | if self.start_ap_mode(captive=False): |
| 2674 | self.shared_data.wardrive_ap_active = True |
| 2675 | self.shared_data.wardrive_ap_url = f"{self.ap_ip}:8000" |
| 2676 | self.shared_data.wardrive_ap_iface = iface |
| 2677 | self.logger.info( |
| 2678 | f"Wardrive AP up: join '{self.ap_ssid}' then open http://{self.ap_ip}:8000/") |
| 2679 | return True |
| 2680 | |
| 2681 | # Start failed — restore prior interface and hand the radio back. |
| 2682 | self.ap_interface = self._wardrive_ap_prev_interface or self.default_wifi_interface |
| 2683 | if self._wardrive_ap_lent_iface and engine is not None: |
| 2684 | engine.reclaim_interface_from_ap(self._wardrive_ap_lent_iface) |
| 2685 | self._wardrive_ap_lent_iface = None |
| 2686 | self.logger.error("Wardrive AP: failed to start AP mode") |
| 2687 | return False |
| 2688 | |
| 2689 | def stop_wardrive_ap(self): |
| 2690 | """Tear down the wardriving AP and hand the radio back to wardriving, |
| 2691 | which automatically claims it for scanning again.""" |
| 2692 | iface = getattr(self.shared_data, 'wardrive_ap_iface', '') or self.ap_interface |
| 2693 | try: |
| 2694 | self.stop_ap_mode() |
| 2695 | finally: |
| 2696 | if self._wardrive_ap_prev_interface: |
| 2697 | self.ap_interface = self._wardrive_ap_prev_interface |
| 2698 | self._wardrive_ap_prev_interface = None |
| 2699 | self.shared_data.wardrive_ap_active = False |