-
Notifications
You must be signed in to change notification settings - Fork 354
client mode added #888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
BeigeBox
wants to merge
3
commits into
EFForg:main
Choose a base branch
from
BeigeBox:feature/client-wifi
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+571
−19
Draft
client mode added #888
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| # WiFi Client Mode for Rayhunter (Orbic RC400L) | ||
|
|
||
| Connect the Orbic to an existing WiFi network while keeping its AP running. | ||
| This enables internet access (for ntfy notifications, etc.) and allows | ||
| accessing the Rayhunter web UI from any device on your network. | ||
|
|
||
| ## How It Works | ||
|
|
||
| The Orbic's QCA6174 supports concurrent AP + station mode. `wlan0` runs | ||
| the AP (via hostapd/QCMAP), and `wlan1` is configured as a station using | ||
| a cross-compiled `wpa_supplicant`. | ||
|
|
||
| ## Quick Start | ||
|
|
||
| 1. Build wpa_supplicant (one-time): | ||
| ``` | ||
| cd tools/build-wpa-supplicant | ||
| docker build --platform linux/amd64 --target export --output type=local,dest=./out . | ||
| ``` | ||
|
|
||
| 2. Push files to device: | ||
| ``` | ||
| sh client-mode/scripts/setup-device.sh | ||
| ``` | ||
|
|
||
| 3. Set credentials via the Rayhunter web UI (Settings > WiFi Client Mode), | ||
| or via the installer: | ||
| ``` | ||
| ./installer orbic --admin-password YOUR_PASS --wifi-ssid MyNetwork --wifi-password MyPass | ||
| ``` | ||
|
|
||
| 4. Reboot. WiFi client starts automatically. Check the log: | ||
| ``` | ||
| adb shell cat /tmp/wifi-client.log | ||
| ``` | ||
|
|
||
| ## File Layout on Device | ||
|
|
||
| ``` | ||
| /data/rayhunter/ | ||
| bin/wpa_supplicant # Static ARMv7 binary | ||
| bin/wpa_cli # Static ARMv7 binary | ||
| scripts/wifi-client.sh # Main script (start/stop/status) | ||
| wifi-creds.conf # Credentials (ssid=X / password=Y) | ||
| ``` | ||
|
|
||
| ## What the Script Does | ||
|
|
||
| 1. Waits for wlan1 to appear (up to 30s) | ||
| 2. Sets wlan1 to managed mode, starts wpa_supplicant | ||
| 3. Obtains IP via DHCP | ||
| 4. Fixes routing: replaces bridge0 default route, adds policy routing | ||
| (table 100) so replies from wlan1's IP always exit via wlan1 | ||
| 5. Sets DNS to 8.8.8.8 | ||
| 6. Configures iptables: allows inbound on wlan1, blocks outbound except | ||
| ESTABLISHED/RELATED, DHCP, DNS, and HTTPS (port 443 for ntfy) | ||
|
|
||
| ## AT+SYSCMD | ||
|
|
||
| Commands needing `CAP_NET_ADMIN` (iw, iptables, ip rule) cannot run through | ||
| rootshell -- ADB's capability bounding set is too restrictive. The init | ||
| script triggers wifi-client.sh which runs with full capabilities. | ||
|
|
||
| Key constraint: AT+SYSCMD via `/dev/smd8` is **one-shot per boot**. The | ||
| installer uses USB bulk transfers and can send multiple commands. | ||
|
|
||
| ## Disabling | ||
|
|
||
| Delete or rename the credentials file, then reboot: | ||
| ``` | ||
| adb shell "mv /data/rayhunter/wifi-creds.conf /data/rayhunter/wifi-creds.conf.disabled" | ||
| ``` | ||
|
|
||
| All network changes are runtime-only -- a reboot always restores defaults. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| Check the log first: `adb shell cat /tmp/wifi-client.log` | ||
|
|
||
| - **No log file**: wifi-client.sh didn't run. Check that wifi-creds.conf | ||
| exists and the init script has the PRESTART replacement. | ||
| - **wpa_supplicant connects but no IP**: Check udhcpc uses | ||
| `-s /etc/udhcpc.d/50default`. | ||
| - **Can't reach device from LAN**: Likely a policy routing issue. The | ||
| script handles this, but if bridge0 and wlan1 share a subnet | ||
| (both 192.168.1.0/24), check `ip rule show` and `ip route show table 100`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| #!/bin/sh | ||
| # Pushes all client-mode files to the Orbic device via ADB. | ||
| # Run from the rayhunter repo root. | ||
| set -e | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | ||
| WPA_DIR="$SCRIPT_DIR/../../tools/build-wpa-supplicant/out" | ||
|
|
||
| if ! adb devices | grep -q device$; then | ||
| echo "No ADB device found" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "Pushing scripts..." | ||
| adb shell "mkdir -p /data/rayhunter/scripts /data/rayhunter/bin" | ||
| adb push "$SCRIPT_DIR/wifi-client.sh" /data/rayhunter/scripts/wifi-client.sh | ||
|
|
||
| if [ -f "$WPA_DIR/wpa_supplicant" ]; then | ||
| echo "Pushing wpa_supplicant binaries..." | ||
| adb push "$WPA_DIR/wpa_supplicant" /data/rayhunter/bin/wpa_supplicant | ||
| adb push "$WPA_DIR/wpa_cli" /data/rayhunter/bin/wpa_cli | ||
| else | ||
| echo "wpa_supplicant binaries not found at $WPA_DIR" | ||
| echo "Build them first: see tools/build-wpa-supplicant/Dockerfile" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "" | ||
| echo "Files pushed. Set WiFi credentials via the web UI or installer," | ||
| echo "then reboot. WiFi client starts automatically on boot." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| #!/bin/sh | ||
| # WiFi client mode for Rayhunter - connects wlan1 to an existing network | ||
| # Reads credentials from /data/rayhunter/wifi-creds.conf | ||
| # Format: | ||
| # ssid=YourNetworkName | ||
| # password=YourPassword | ||
|
|
||
| LOG="/tmp/wifi-client.log" | ||
| exec > "$LOG" 2>&1 | ||
|
|
||
| CRED_FILE="/data/rayhunter/wifi-creds.conf" | ||
| WPA_BIN="/data/rayhunter/bin/wpa_supplicant" | ||
| WPA_CONF="/tmp/wpa_sta.conf" | ||
| WPA_PID="/tmp/wpa_sta.pid" | ||
| DHCP_PID="/tmp/udhcpc_wlan1.pid" | ||
| IFACE="wlan1" | ||
| RT_TABLE=100 | ||
|
|
||
| stop() { | ||
| [ -f "$WPA_PID" ] && kill "$(cat "$WPA_PID")" 2>/dev/null && rm -f "$WPA_PID" | ||
| [ -f "$DHCP_PID" ] && kill "$(cat "$DHCP_PID")" 2>/dev/null && rm -f "$DHCP_PID" | ||
| ip link set "$IFACE" down 2>/dev/null | ||
| } | ||
|
|
||
| start() { | ||
| if [ ! -f "$CRED_FILE" ]; then | ||
| echo "No credentials file at $CRED_FILE" | ||
| exit 1 | ||
| fi | ||
|
|
||
| SSID=$(grep '^ssid=' "$CRED_FILE" | cut -d= -f2-) | ||
| PSK=$(grep '^password=' "$CRED_FILE" | cut -d= -f2-) | ||
|
|
||
| if [ -z "$SSID" ] || [ -z "$PSK" ]; then | ||
| echo "Missing ssid or password in $CRED_FILE" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Wait for the wireless interface to appear (created asynchronously by QCMAP/hostapd) | ||
| for i in $(seq 1 30); do | ||
| [ -d "/sys/class/net/$IFACE" ] && break | ||
| [ "$i" = "1" ] && echo "Waiting for $IFACE..." | ||
| sleep 1 | ||
| done | ||
| if [ ! -d "/sys/class/net/$IFACE" ]; then | ||
| echo "$IFACE not found after 30s, giving up" | ||
| exit 1 | ||
| fi | ||
|
|
||
| stop 2>/dev/null | ||
| sleep 1 | ||
|
|
||
| echo "Configuring $IFACE for station mode" | ||
| iw dev "$IFACE" set type managed | ||
| ip link set "$IFACE" up | ||
|
|
||
| cat > "$WPA_CONF" <<WPAEOF | ||
| ctrl_interface=/var/run/wpa_supplicant | ||
| network={ | ||
| ssid="$SSID" | ||
| psk="$PSK" | ||
| key_mgmt=WPA-PSK | ||
| } | ||
| WPAEOF | ||
|
|
||
| echo "Starting wpa_supplicant" | ||
| "$WPA_BIN" -i "$IFACE" -Dnl80211 -c "$WPA_CONF" -B -P "$WPA_PID" | ||
| sleep 5 | ||
|
|
||
| echo "wpa_supplicant status:" | ||
| iw dev "$IFACE" link | ||
|
|
||
| echo "Starting DHCP" | ||
| udhcpc -i "$IFACE" -s /etc/udhcpc.d/50default -p "$DHCP_PID" -t 10 -A 3 -b | ||
| sleep 3 | ||
|
|
||
| WLAN1_IP=$(ip addr show "$IFACE" | grep 'inet ' | awk '{print $2}' | cut -d/ -f1) | ||
| WLAN1_CIDR=$(ip addr show "$IFACE" | grep 'inet ' | awk '{print $2}') | ||
| WLAN1_SUBNET=$(ip route show dev "$IFACE" | grep 'proto kernel' | awk '{print $1}') | ||
| WLAN1_GW=$(ip route show dev "$IFACE" | grep 'proto kernel' | awk '{print $1}' | cut -d/ -f1) | ||
| WLAN1_GW="${WLAN1_GW%.*}.1" | ||
|
|
||
| if [ -z "$WLAN1_IP" ]; then | ||
| echo "Failed to get IP on $IFACE" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "IP: $WLAN1_IP Subnet: $WLAN1_SUBNET CIDR: $WLAN1_CIDR Gateway: $WLAN1_GW" | ||
|
|
||
| # Fix default route: ensure it goes through wlan1, not bridge0 | ||
| GATEWAY=$(ip route show default | grep "dev bridge0" | awk '{print $3}') | ||
| if [ -n "$GATEWAY" ]; then | ||
| echo "Fixing default route: bridge0 -> wlan1" | ||
| ip route del default dev bridge0 2>/dev/null | ||
| fi | ||
| ip route replace default via "$WLAN1_GW" dev "$IFACE" metric 10 | ||
|
|
||
| # Policy routing: force traffic from our DHCP IP out wlan1 | ||
| # (needed because bridge0 shares the same subnet) | ||
| ip rule del from "$WLAN1_IP" table $RT_TABLE 2>/dev/null | ||
| ip route flush table $RT_TABLE 2>/dev/null | ||
| ip rule add from "$WLAN1_IP" table $RT_TABLE | ||
| ip route add "$WLAN1_SUBNET" dev "$IFACE" src "$WLAN1_IP" table $RT_TABLE | ||
| ip route add default via "$WLAN1_GW" dev "$IFACE" table $RT_TABLE | ||
|
|
||
| echo "nameserver 8.8.8.8" > /etc/resolv.conf | ||
|
|
||
| # Allow inbound traffic on wlan1 | ||
| iptables -I INPUT -i "$IFACE" -j ACCEPT | ||
| iptables -I FORWARD -i "$IFACE" -j ACCEPT | ||
|
|
||
| # Block stock Orbic daemons from phoning home (dmclient, upgrade, etc.) | ||
| # Allow only: replies to incoming connections, DHCP renewal, DNS, and HTTPS | ||
| # (needed for ntfy notifications). | ||
| iptables -A OUTPUT -o "$IFACE" -m state --state ESTABLISHED,RELATED -j ACCEPT | ||
| iptables -A OUTPUT -o "$IFACE" -p udp --dport 67:68 -j ACCEPT | ||
| iptables -A OUTPUT -o "$IFACE" -p udp --dport 53 -j ACCEPT | ||
| iptables -A OUTPUT -o "$IFACE" -p tcp --dport 53 -j ACCEPT | ||
| iptables -A OUTPUT -o "$IFACE" -p tcp --dport 443 -j ACCEPT | ||
| iptables -A OUTPUT -o "$IFACE" -j DROP | ||
|
|
||
| echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables | ||
|
|
||
| echo "=== iptables OUTPUT ===" | ||
| iptables -L OUTPUT -v -n 2>&1 | ||
|
|
||
| echo "=== policy routing ===" | ||
| ip rule show | ||
| echo "--- table $RT_TABLE ---" | ||
| ip route show table $RT_TABLE | ||
|
|
||
| echo "=== network state ===" | ||
| ip addr show "$IFACE" | grep 'inet ' | ||
| ip route show | ||
|
|
||
| echo "Internet test:" | ||
| wget -q -O /dev/null http://detectportal.firefox.com/success.txt && echo "OK" || echo "FAILED" | ||
| } | ||
|
|
||
| status() { | ||
| if [ -f "$WPA_PID" ] && kill -0 "$(cat "$WPA_PID")" 2>/dev/null; then | ||
| ip addr show "$IFACE" | grep 'inet ' | awk '{print $2}' | ||
| else | ||
| echo "disconnected" | ||
| return 1 | ||
| fi | ||
| } | ||
|
|
||
| case "$1" in | ||
| start) start ;; | ||
| stop) stop ;; | ||
| status) status ;; | ||
| *) echo "Usage: $0 {start|stop|status}" >&2; exit 1 ;; | ||
| esac | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This, I think, is valid regardless of which WiFi mode we're in, and we should consider it a separate feature entirely.
I am not yet sure that OTA updates is something we should try to prevent though. If the OTA update arrives before rayhunter is installed (e.g. you buy a used device) you have the same problem, and eventually it will come out of the factory like that, so we will have to deal with firmware updates eventually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add it as a separate piece and configurable on the GUI. I wouldn't want to deliver this without that option though since it basically could result in an OTA going out to anyone on wifi which potentially could nuke rayhunter or patch things.