diff --git a/luci-app-easymesh/.gitignore b/luci-app-easymesh/.gitignore new file mode 100644 index 000000000..e60e445db --- /dev/null +++ b/luci-app-easymesh/.gitignore @@ -0,0 +1,16 @@ +# Editor backups +*.bak +*.swp +*.tmp + +# Local test artifacts +/tmp/ +/.config + +# Python +__pycache__/ +*.pyc + +# OS files +.DS_Store +Thumbs.db diff --git a/luci-app-easymesh/CHANGELOG.md b/luci-app-easymesh/CHANGELOG.md new file mode 100644 index 000000000..659738ea0 --- /dev/null +++ b/luci-app-easymesh/CHANGELOG.md @@ -0,0 +1,124 @@ +# EasyMesh 2.4.1-r1 + +LuCI interface for Batman-adv 802.11s mesh on OpenWrt / ImmortalWrt. + +## Changes since upstream + +- De-integration of DAWN for Usteer pre-installed devices (conflict-free install, better performance) +- Optional cron fallback for priority reapplication (UI checkbox, default off) +- Live latency monitoring for wireless peers (smoothed over 3 pings) +- Live RX/TX bandwidth rates for wireless and wired links +- Neighbor MAC + last-seen for wired backhaul (from batman originator table) +- Robust priority read (tries multiple batctl syntaxes) +- Hotplug integration for priority reapplication on interface up +- 90-second priority retry loop + second-pass catchup in init script +- Removed `+wpad-mesh` dependency (fixed kconfig recursive dependency) +- Meta packages for `wpad-openssl` / `wpad-wolfssl` / `wpad-mbedtls` variants +- WPS handler `/etc/rc.wps/easymesh-pair` (no conflict with wifi-scripts) +- বাংলা (Bengali) Language → now supports full Bengali UI + +**Mesh features** + +- Editable link priority weights in UI — wired, 6 GHz, 5 GHz, 2.4 GHz +- Priority Weight column for wireless peers (previously wired-only) +- Mesh VLANs — extend guest / IoT / hotspot networks over the mesh via 802.1Q +- Comma-separated VLAN IDs in UI (e.g. `10,20,30`) +- Auto-attach to `br-` / `br-guest` / `br-iot` bridges +- Safe re-apply (removes previous 802.1Q devices first) + +**Documentation** + +- English README with screenshots +- Bengali (বাংলা) README — `docs/bn` +- Bengali glossary — 80+ networking terms +- Language switcher on both READMEs + +## Bundle + +`easymesh-pkg-2.4.1-r1.zip` (1.6 MB): 6 IPK + 6 APK + full source + Bengali docs (README, glossary, 7 screenshots) + Bengali/Chinese `.po`. + +## Notes + +- GitHub replaces `~` with `.` in **asset names on this page** (`…37259.7cad107…`). Files inside the bundle keep the original `~`. Package metadata is unchanged — `opkg` / `apk` read the version from inside the package. +- Tag `2.4.1-r1` was force-moved. Re-download if you pulled an earlier build. +- SAE mesh encryption is driver-limited on ath10k / MT7915E — mesh runs unencrypted. + +## Credits + +- **Original authors:** + - dz + - torguardvpn — [torguardvpn/luci-app-easymesh](https://github.com/torguardvpn/luci-app-easymesh) +- **Upstream forks:** + - [ntlf9t/luci-app-easymesh](https://github.com/ntlf9t/luci-app-easymesh) + - [kenzok78/luci-app-easymesh](https://github.com/kenzok78/luci-app-easymesh) + - [torguardvpn/luci-app-easymesh](https://github.com/torguardvpn/luci-app-easymesh) +- **Bugfix branch:** [mobing8/luci-app-easymesh-dawn](https://github.com/mobing8/luci-app-easymesh-dawn) +- **Current maintainer:** Arafat Rahman Zami Mondol — [@arafatrahmanzami](https://github.com/arafatrahmanzami) · [open an issue](https://github.com/arafatrahmanzami/luci-app-easymesh/issues/new) + + +*Preview (screenshots)* +01-dashboard + + + + +07-priority-vlans + + + + +02-quick-setup-1-9 + + + + + +03-quick-setup-10-19 + + + + +04-mesh-settings + + + + +05-client-wifi-ap-mode + + + +06-wired-backhaul-safety + + + +## Installation example + +Pick the wpad meta package matching your TLS backend, then the app, then i18n. + +**OpenWrt ≤ 24.10 (opkg, openssl)** +```sh +cd /tmp +opkg install luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk +opkg install luci-app-easymesh_2.4.1-r1_all.ipk +# optional +opkg install luci-i18n-easymesh-bn_26.184.37259.7cad107_all.ipk +/etc/init.d/uhttpd restart +``` + +**OpenWrt ≥ 25.12 (apk,wolfssl)** + +```sh +cd /tmp +apk --allow-untrusted add /tmp/luci-app-easymesh-wpad-wolfssl-2.4.1-r1.apk +apk --allow-untrusted add /tmp/luci-app-easymesh-2.4.1-r1.apk +# optional +apk --allow-untrusted add /tmp/luci-i18n-easymesh-bn-26.184.37259.7cad107.apk +/etc/init.d/uhttpd restart +``` + +> The meta depends on `wpad-mesh-*` (not `wpad-*`). `apk verify` reports `UNTRUSTED signature` — expected, harmless; that's why `--allow-untrusted` is required. + +**Bengali UI:** System → System → Language and Style → Language → **বাংলা (Bengali)** + +> All packages are `PKG_ARCH=all` — universal across every router CPU. + diff --git a/luci-app-easymesh/Makefile b/luci-app-easymesh/Makefile new file mode 100755 index 000000000..08d7cec46 --- /dev/null +++ b/luci-app-easymesh/Makefile @@ -0,0 +1,49 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-easymesh +PKG_VERSION:=2.4.1 +PKG_RELEASE:=3 + +PKG_LICENSE:=GPL-2.0 +PKG_MAINTAINER:=Arafat Rahman Zami Mondol +PKG_URL:=https://github.com/arafatrahmanzami/luci-app-easymesh + +LUCI_TITLE:=LuCI Management Interface for EasyMesh (Batman-adv mesh) +LUCI_DESCRIPTION:=LuCI app for configuring and monitoring a Batman-adv \ + based 802.11s mesh network. Requires a wpad-mesh-* package \ + (openssl, wolfssl, or mbedtls) to be installed separately. + +# ---- Runtime dependencies -------------------------------------------- +# Wireless stack + +LUCI_DEPENDS:= \ + +kmod-cfg80211 \ + +kmod-batman-adv \ + +luci-proto-batman-adv \ + +batctl-default \ + +luci-compat \ + +luci-lua-runtime \ + +libiwinfo-lua + +# Client steering (works with or without usteer) +LUCI_DEPENDS += \ + +usteer \ + +luci-app-usteer + +LUCI_PKGARCH:=all + +define Package/luci-app-easymesh/conffiles +/etc/config/easymesh +endef + +define Package/luci-app-easymesh/postinst +#!/bin/sh +[ -n "$${IPKG_INSTROOT}" ] && exit 0 +rm -f /tmp/luci-indexcache 2>/dev/null +/etc/init.d/rpcd restart 2>/dev/null +/etc/init.d/uhttpd restart 2>/dev/null +exit 0 +endef + +include $(TOPDIR)/feeds/luci/luci.mk +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-easymesh/README.md b/luci-app-easymesh/README.md new file mode 100644 index 000000000..7b2e3afd0 --- /dev/null +++ b/luci-app-easymesh/README.md @@ -0,0 +1,1169 @@ +🌐 **English** · [**বাংলা**](https://github.com/arafatrahmanzami/luci-app-easymesh/tree/main/docs/bn) + +# luci-app-easymesh + +An EasyMesh plugin for OpenWrt/ImmortalWrt — a lightweight Linux LuCI (OpenWrt's web-based configuration User Interface) interface that simplifies deploying Batman-adv layer-2 mesh networks with 802.11s (IEEE WiFi mesh standard) wireless and wired backhaul. It can also be used standalone without a hotspot or VPN (Virtual Private Network). + +**Release:** `2.4.1-r1` — 2026-09-16 — by [@arafatrahmanzami](https://github.com/arafatrahmanzami) + +With limited skills, I wrote a mesh LuCI setup plugin based on `kmod-batman-adv` + 802.11s wired + wireless backhaul. The addition of quick and automatic settings makes setup more convenient. The plugin has only been tested on OpenWrt and ImmortalWrt 24 and 25. It's very poorly written but usable for now. Please report or resolve any issues with the available source code if needed. + +--- + +## What does this actually do? + +Imagine you have two or more WiFi routers. Normally they create **separate** WiFi networks — your phone has to disconnect and reconnect when you walk between rooms. + +**EasyMesh** makes them behave as **one single WiFi network**. Your phone stays connected as you move around the house — no drops, no reconnections. + +It also lets you **connect the routers with Ethernet cables** for faster backhaul. The mesh uses the cable when possible, wireless when the cable isn't available, and switches between them automatically. + +**You need this if:** + +- ✅ You have 2+ routers running OpenWrt / ImmortalWrt +- ✅ You want one seamless WiFi name across the whole house +- ✅ You want to extend WiFi into a far room without losing performance +- ✅ You have a guest network you want to reach everywhere + +**You don't need this if:** + +- ❌ You have only one router +- ❌ You don't want to install custom firmware +- ❌ You're happy with separate WiFi names per router + +--- + +## Table of Contents + +- [What does this actually do?](#what-does-this-actually-do) +- [Why EasyMesh?](#why-easymesh) +- [Key Features](#key-features) +- [Prerequisites](#prerequisites) +- [Before You Start](#before-you-start) +- [Installation](#installation) +- [After Installation](#after-installation) +- [Quick Start](#quick-start) +- [Detailed Setup](#detailed-setup--basic-mesh-network-1-server--2-nodes) +- [Working with Dumb AP / Client Nodes](#working-with-dumb-ap--client-nodes) +- [Tuning Link Priorities](#tuning-link-priorities) +- [Wired Backhaul — Safety, Fallback & Actions](#wired-backhaul--safety-fallback--actions) +- [Extending Guest / IoT Networks over the Mesh (VLANs)](#extending-guest--iot-networks-over-the-mesh-vlans) +- [More Info — Use Case Scenarios](#more-info--use-case-scenarios) +- [Troubleshooting](#troubleshooting) +- [Changelog](#changelog) +- [Compile from Source](#compile-from-source-openwrt-sdk) +- [Credits](#credits) +- [Glossary](#glossary) +- [License](#license) + +--- + +## Why EasyMesh? + +This is the first fork of the official EasyMesh release. It simplifies deploying and configuring Batman-adv layer-2 mesh networks with wired and/or wireless backhaul, and provides an easy-to-use LuCI interface for configuring mesh servers and client nodes in seconds. + +Whether you're extending Hotspot / VPN LAN + WiFi coverage with "OpenLink Captive Portal" or WireGuard, building a seamless home mesh network, or managing multiple nodes across a homelab setup, EasyMesh is built to make Batman-adv mesh WiFi deployment super-fast and effortless. + +It also makes it faster and easier to deploy an advanced wired + WiFi mesh for extending the range of a Hotspot, with seamless integration with WireGuard / TorGuard's WireGuard apps using OpenWrt/ImmortalWrt `OpenLink-Captive-Portal`, `wireguard` / `torguardvpn`, powered by `batman-adv` (Batman-adv = Better Approach To Mobile Adhoc Networking — Advanced). + +**EasyMesh is ideal for:** + +- ✅ One-click setup/deployment for a pure mesh on OpenWrt — simplifies server and client-node configuration inside the LuCI web interface +- ✔ Deploying a server/client wired + WiFi mesh backhaul network in seconds with OpenWrt/ImmortalWrt & Batman-adv for access-point WiFi and mesh networks +- ✔ Extending / enhancing connectivity with multiple nodes over WiFi or Ethernet with VPN / Hotspot coverage over large areas +- ✔ Simplifying homelab setups behind WireGuard for managing devices across locations + +--- + +## Key Features + +- **Mesh core:** Batman-adv 802.11s mesh with dual-band + wired backhaul fallback and loop prevention (available on both Server and Client roles) +- **Split-band client SSIDs** (Service Set Identifiers — the WiFi network names; split-band prevents sticky-2.4 GHz clients) +- **Backhaul support:** combines `kmod-batman-adv` and 802.11s for mixed wired and wireless connections +- **Wired backhaul port handling:** port is automatically removed from the LAN bridge on apply +- **Link priority:** wired → 6 GHz → 5 GHz → 2.4 GHz (`throughput_override`) +- **GUI-editable link priority weights** — no hardcoded values; tune each band's weight directly in the UI +- **Bonding enabled** for simultaneous multi-link traffic +- **Auto band detection** (frequency-based, works on any radio order) +- **Security:** WPA3 authentication for secure networks, or open networks +- **WPS / mode-button pairing** via `/etc/rc.wps/` dispatcher +- **60-second rollback watchdog** for IP changes +- **AP Mode (Dumb AP)** for client nodes — Dumb AP Mesh Client/Slave nodes can also provide Internet access via their LAN ports +- **Built-in Mesh Status dashboard** — view Neighbor Nodes, Last Seen Time, Interfaces, and more +- **Live RX/TX latency and bandwidth/link-speed** monitoring for wireless and wired links and peers (smoothed over 3 pings) + - Note: not purely live field updates yet — a page refresh is needed +- **Priority Weight column** displayed for wireless peers (previously only wired) +- **Neighbor MAC + last-seen** for wired backhaul (from the batman originator table) +- **Mesh VLANs** — extend guest / IoT / hotspot networks across the mesh via 802.1Q +- **Auto firewall & interface configuration** for servers and clients +- **Flexible node addressing:** DHCP (Dynamic Host Configuration Protocol), Static IP, and Dumb AP modes +- **Roaming optimization:** integrates 802.11k/v/r (fast-roaming standards) with `dawn` / `usteer` daemon support for fast client roaming between access points + - Advanced K/V/R settings + Mobility Domain + - Using one of `dawn` or `usteer` via their respective luci-apps significantly reduces latency when switching between devices/applications +- **Hotplug + init + optional cron fallback** for link priority +- **VPN compatibility:** easy integration with TorGuard's WireGuard and other VPN OpenWrt apps for VPN-based mesh networks +- **Variants:** openssl / wolfssl / mbedtls + +--- + +## Prerequisites + +To function correctly, the app generally requires the following underlying packages: + +- OpenWrt / ImmortalWrt 24.10+ +- `kmod-batman-adv`, `batctl-default`, `luci-proto-batman-adv`, `kmod-cfg80211` (kernel module loaded) +- One of `wpad-mesh-openssl` / `wpad-mesh-wolfssl` / `wpad-mesh-mbedtls` (for WPA3 encryption) +- One of `dawn` / `usteer` (for AP steering / fast roaming) +- `luci-compat` (if encountering missing Lua runtime dependencies) + +**Hardware requirements:** routers/devices with wireless network cards that support 802.11s mesh (such as MediaTek MT76, Qualcomm Atheros, etc.). + +--- + +## Before You Start + +Answer these five questions **before** installing: + +- [ ] **Do I have 2+ routers running OpenWrt or ImmortalWrt?** — If no, this app won't help yet. Install OpenWrt first. +- [ ] **Do I know my router's IP address?** — Usually `192.168.1.1` or `192.168.10.1`. Check your phone's WiFi settings under "Router" or "Gateway". +- [ ] **Do I have SSH access?** — Try `ssh root@` from your PC. Password is the same as LuCI login. +- [ ] **Do I have a working internet connection on my main router?** — The install downloads packages. +- [ ] **Have I backed up my current config?** — In LuCI: **System → Backup / Flash Firmware → Generate archive**. Do this on every router. + +If all five are ✅, proceed. If any is ❌, fix that first. + +--- + +## Installation + +Run these entirely from `/tmp` — nothing clutters the root filesystem. + +**No architecture detection needed.** EasyMesh is `PKG_ARCH=all` — works on every router CPU (x86, MIPS, ARM, AArch64, RISC-V). + +You can quickly build a completely standard mesh wireless network using only OpenWrt/ImmortalWrt. + +### 1. Check wpad — do you need a specific wpad variant? + +EasyMesh requires a **mesh-capable wpad** (Wireless Protected Access Daemon) to create the 802.11s backhaul. Most routers already have one installed. Check first: + +```sh +# On the router — run this before installing EasyMesh +opkg list-installed 2>/dev/null | grep wpad +# or for OpenWrt 25.12+: +apk list --installed 2>/dev/null | grep wpad +``` + +**Interpreting the output:** + +| What you see | What to do | +|---|---| +| `wpad-mesh-openssl`, `wpad-mesh-wolfssl`, or `wpad-mesh-mbedtls` | ✅ You already have it. **Skip to Step 2.** | +| `wpad-openssl`, `wpad-wolfssl`, `wpad-mbedtls` (no "mesh") | ⚠️ Install a mesh variant — see Step 1b | +| `wpad-basic`, `wpad-basic-mbedtls`, or `wpad-mini` | ❌ Cannot do mesh. Install a mesh variant — see Step 1b | +| Nothing (no wpad) | ❌ Install a mesh variant — see Step 1b | + +### 1b. Install a wpad variant (only if needed) + +Pick **one** variant. Match what your distribution uses by default: + +| Variant | Recommended for | +|---|---| +| `wpad-openssl` | Most OpenWrt builds (default TLS) | +| `wpad-wolfssl` | ImmortalWrt 25.12+ (default TLS) | +| `wpad-mbedtls` | OpenWrt 24.10 default when openssl is unavailable | + +```sh +# opkg (OpenWrt 24.10 and older) +opkg install wpad-openssl # or wpad-wolfssl / wpad-mbedtls +opkg remove wpad-basic-mbedtls # if you're replacing a basic variant + +# apk (OpenWrt 25.12+) +apk add wpad-openssl # or wpad-wolfssl / wpad-mbedtls +apk del wpad-basic-mbedtls # if you're replacing a basic variant +``` + +After installing or replacing wpad, reboot once: + +```sh +reboot +``` + +> **Note:** Do not install two mesh-capable wpad variants at the same time — they conflict. If you switch variants, remove the old one first. + +### 2. Install EasyMesh — choose any ONE method + +#### Option A — Meta-package (includes wpad) — fastest path + +These install the app **and** the wpad in one shot. If you're unsure, use this. + +```sh +# OpenWrt ≤ 24.10 (opkg) — pick ONE +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk && opkg install luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk + +# OpenWrt ≥ 25.12 (apk) — pick ONE +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-wpad-openssl-2.4.1-r1.apk && apk add --allow-untrusted luci-app-easymesh-wpad-openssl-2.4.1-r1.apk +``` + +Swap `openssl` for `wolfssl` or `mbedtls` in both the URL and the package name if you prefer a different variant. + +#### Option B — Install the main EasyMesh package only + +**Skip this step if you already installed a `-wpad-*` meta-package above — it includes the main app.** + +**OpenWrt ≤ 24.10 — opkg** + +```sh +cd /tmp && \ +opkg update && \ +wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh_2.4.1-r1_all.ipk && \ +opkg install luci-app-easymesh_2.4.1-r1_all.ipk && \ +rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* && \ +/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart +``` + +**OpenWrt ≥ 25.12 — apk** + +```sh +cd /tmp && \ +wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-2.4.1-r1.apk && \ +apk add --allow-untrusted luci-app-easymesh-2.4.1-r1.apk && \ +rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* && \ +/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart +``` + +#### Option C — ⚡ Single auto-detect command (opkg vs apk) + +Run this one-liner. It picks `apk` or `opkg` automatically based on which is present. + +```sh +cd /tmp && \ +if command -v apk >/dev/null 2>&1; then \ + echo "Detected apk — OpenWrt 25.12+" && \ + wget -O easymesh.pkg https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-2.4.1-r1.apk && \ + apk add --allow-untrusted easymesh.pkg; \ +else \ + echo "Detected opkg — OpenWrt 24.10 or older" && \ + opkg update && \ + wget -O easymesh.pkg https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh_2.4.1-r1_all.ipk && \ + opkg install easymesh.pkg; \ +fi && \ +rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* && \ +/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart +``` + +### 3. Optional — language packs + +EasyMesh ships with English (default). Bengali and Chinese (Simplified) translations are available as separate packages. + +**Bengali (বাংলা)** + +```sh +# opkg (≤ 24.10) +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-i18n-easymesh-bn_26.184.37259.7cad107_all.ipk && opkg install luci-i18n-easymesh-bn_26.184.37259.7cad107_all.ipk + +# apk (≥ 25.12) +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-i18n-easymesh-bn-26.184.37259.7cad107.apk && apk add --allow-untrusted luci-i18n-easymesh-bn-26.184.37259.7cad107.apk +``` + +Then switch the UI language: **System → System → Language and Style → Language → বাংলা (Bengali)**. + +**Chinese (Simplified) / 简体中文** + +```sh +# opkg (≤ 24.10) +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-i18n-easymesh-zh-cn_26.184.37259.7cad107_all.ipk && opkg install luci-i18n-easymesh-zh-cn_26.184.37259.7cad107_all.ipk + +# apk (≥ 25.12) +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-i18n-easymesh-zh-cn-26.184.37259.7cad107.apk && apk add --allow-untrusted luci-i18n-easymesh-zh-cn-26.184.37259.7cad107.apk +``` + +Then switch the UI language: **System → System → Language and Style → Language → 中文 (Chinese)**. + +> **Note on filenames:** GitHub replaces `~` with `.` in release asset names. The i18n version string above uses `.7cad107` — download URLs must use this form. The package metadata (version `26.184.37259~7cad107`) is unchanged. + + + +### 4. Offline ipk / apk / tarball install (without internet on router/AP) + +**Option 1 — Install via OpenWrt UI (24.10.8 only)** + +*A .ipk +Download the latest `luci-app-easymesh_*_all.ipk` from the Releases section if not already downloaded (any device connected to the router via the same IP subnet). Navigate to **System → Software** in OpenWrt's LuCI UI. Click **Upload Package**, select `luci-app-easymesh_*_all.ipk`, and install it. + +*B .apk +Follow `Option 2b`, There is no direct "one-click" way to do this exclusively using the default LuCI software tabs + +The LuCI web frontend deliberately prevents untrusted local .apk installations. +Because it lacks a backend flag like --allow-untrusted when processing your uploaded files via the System > Software menu, it triggers a signature error and stops the execution. + + +**Option 2 — Install via CLI (SSH / Terminal)** +Use WinSCP / FileZilla (on PC) or a mobile SFTP manager like Solid Explorer or Documents by Readdle (on smartphone) to copy your downloaded .ipk/.apk package (and any required dependency .ipk/.apk files) into the /tmp folder on the router +After the files are ready, enter the commands to install them by logging into the router via SSH: + +2a OpenWrt ≤ 24.10 (opkg) +On router + +```sh +cd /tmp +opkg install luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk +opkg install luci-app-easymesh_2.4.1-r1_all.ipk +# optional +opkg install luci-i18n-easymesh-bn_26.184.37259.7cad107_all.ipk +/etc/init.d/uhttpd restart +``` + +2b OpenWrt ≥ 25.12 (apk) +On router + +```sh +cd /tmp +apk --allow-untrusted add /tmp/luci-app-easymesh-wpad-wolfssl-2.4.1-r1.apk +apk --allow-untrusted add /tmp/luci-app-easymesh-2.4.1-r1.apk +# optional +apk --allow-untrusted add /tmp/luci-i18n-easymesh-bn-26.184.37259.7cad107.apk +/etc/init.d/uhttpd restart +``` + + +**Option 3 — Tarball install** + +Download to your PC, Copy or move to the /tmp folder on the router. Extract + +```sh +# On PC +wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-2.4.1-r1-full.tar.gz +scp luci-app-easymesh-2.4.1-r1-full.tar.gz root@192.168.1.1:/tmp/ +``` + +-Or Copy / Transfer + +Using an SFTP manager like option 2, copy or transfer the previously downloaded tar.gz file to the /tmp folder of the router. +After preparing the file, install it by logging into the router via SSH: + + +```sh +# On router +cd / && tar xzf /tmp/luci-app-easymesh-2.4.1-r1-full.tar.gz +chmod +x /etc/init.d/easymesh /etc/hotplug.d/iface/30-easymesh-priority /etc/rc.wps/easymesh-pair +rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* +/etc/init.d/rpcd restart +/etc/init.d/uhttpd restart +``` + +### Source code + +- [Download `.zip`](https://github.com/arafatrahmanzami/luci-app-easymesh/archive/refs/tags/2.4.1-r1.zip) +- [Download `.tar.gz`](https://github.com/arafatrahmanzami/luci-app-easymesh/archive/refs/tags/2.4.1-r1.tar.gz) + +--- + +## After Installation + +1. Open LuCI: `http:///cgi-bin/luci/` +2. Go to **Network → EasyMesh** +3. Pick a role (**Server**, **Client**, **Node**) and a preset +4. Click **Save & Apply** +5. If the menu doesn't show, hard-refresh: **Ctrl+Shift+R** + +**What gets installed:** + +| Path | Purpose | +|---|---| +| `/usr/lib/lua/luci/controller/easymesh.lua` | LuCI controller | +| `/usr/lib/lua/luci/model/cbi/easymesh.lua` | Configuration UI | +| `/etc/init.d/easymesh` | Init script (bat0, mesh VIFs, wired backhaul, priority) | +| `/etc/config/easymesh` | UCI (Unified Configuration Interface) config | +| `/etc/hotplug.d/iface/30-easymesh-priority` | Reapplies link priority on interface up | +| `/etc/rc.wps/easymesh-pair` | WPS button handler | +| `/etc/uci-defaults/luci-easymesh` | First-run setup | +| `/usr/share/luci/menu.d/luci-app-easymesh.json` | Menu entry | +| `/usr/share/rpcd/acl.d/luci-app-easymesh.json` | ACL permissions | + +--- + +## Quick Start + +1. **Server** (has internet): EasyMesh → role: **Server**, band mode: **Dual-band** +2. **Client** (extender): role: **Client**, same **Mesh ID**, **AP Mode: ON**, static IP +3. **Both**: same passwords, **K/V/R** on + +**Quick Setup presets** — pick a role, click one button, done: + +![Quick Setup presets 1-9](docs/screenshots/02-quick-setup-1-9.png) + +![Quick Setup presets 10-19](docs/screenshots/03-quick-setup-10-19.png) + +After Save & Apply on both routers, wait ~30 seconds. Open **Network → EasyMesh** on the Server — you should see the Client's MAC in the peers table: + +![EasyMesh dashboard](docs/screenshots/01-dashboard.png) + +--- + +## Detailed Setup — Basic Mesh Network (1 Server + 2 Nodes) + +### Step 1 — Setup the Mesh Server + +1. Disable/delete any active wireless networks in OpenWrt (**Network → Wireless**). +2. Go to **Network → EasyMesh** and select **"Server"** for Mesh Mode. + + ![Mesh settings](docs/screenshots/04-mesh-settings.png) + +3. Enter your WiFi SSID (this is the main WiFi network all devices will connect to). +4. Select the WiFi Radio for the regular AP. (Recommended: use a different radio than the mesh backhaul for best performance.) +5. Select the Mesh Radio and enter a separate SSID. (The app automatically appends `-mesh` to your mesh SSID.) +6. Enable Password Protection, enter a Mesh Password, and click **Save & Apply**. +7. Click **"Reapply EasyMesh Settings"** to deploy the APs and activate mesh networking. + +**Verify setup:** + +- Go to **Network → Wireless** to check that the WiFi networks were added. +- Go to **Network → Interfaces** to confirm the Batman (`bat0`) device and the `mesh_batman` interface were added. + +### Step 2 — Setup a Mesh Node + +1. Go to **Network → EasyMesh** on the second router. +2. Select **"Client"** for Mesh Mode. +3. Enter the same WiFi SSID, Mesh SSID, and Password as the server. +4. Ensure you select the same WiFi radio type (AX, AC, b/g/n) for both WiFi SSID and Mesh SSID. +5. Click **Save & Apply**, then click **"Reapply EasyMesh Settings"**. +6. Go to the **AP Mode** tab and select a hostname (e.g., `node2`, `node3`, …). + + ![Client WiFi + AP Mode](docs/screenshots/05-client-wifi-ap-mode.png) + +7. Set to **DHCP** (recommended for Dumb AP nodes) or configure a Static IP in the same range as your Mesh Server. +8. Click **Save & Apply**, then click **"Enable Dumb AP Mode"**. + +### Step 3 — Repeat for Additional Mesh Nodes + +- Use the same WiFi SSID, Mesh SSID, and Password for every node. +- Ensure all nodes use the same WiFi radio type (AX, AC, b/g/n). + +### Step 4 — Verify Neighbor Nodes + +- Go to **Network → EasyMesh** on the Mesh Server and check that nearby nodes are listed under **Mesh Status**. +- Go to **Network → Wireless** and verify that Mesh Backhaul Networks are communicating with the server. +- Find your Mesh Nodes' IPs under **Status → Overview → DHCP Devices**. +- Access a node by entering its IP in a browser. + +### Step 5 — (Optional) Activate TorGuard WireGuard VPN on the Mesh Server + +1. Go to **Network → TorGuard WireGuard**. +2. Enter your WireGuard Username & Password. +3. Select your preferred WireGuard server location. +4. Enable WireGuard and click **Save & Apply**. +5. Click **"Start WireGuard"** to tunnel all mesh network traffic through the VPN. + +🔥 Now your OpenWrt Mesh WiFi is fully set up! 🚀 + +--- + +## Working with Dumb AP / Client Nodes + +**Log in to child nodes after enabling Thin / Dumb AP mode.** + +**Recover a node after enabling Dumb AP mode:** + +If you cannot access the site via WiFi, connect the sub-node's LAN port directly to the master node LAN using an Ethernet cable (LAN-to-LAN via an RJ45 cable). Then query the sub-node's IP address in the master node's DHCP list and enter the IP into your browser to log in to the client node backend. + +*Find the node IP under the status page's DHCP section and open the IP in a browser (Chromium / Firefox / Brave, etc.).* + +**Best practice for modifying Mesh wireless parameters:** + +1. First modify / change the configuration of each child node. +2. Finally, modify / update the Mesh master node parameters / settings. +3. Reapply settings to reconnect all nodes. All devices are reconfigured and node reconnection completes. + +--- + +![Wired Backhaul — Safety, Fallback & Actions](docs/screenshots/06-wired-backhaul-safety.png) + +## Wired Backhaul — Safety, Fallback & Actions + +EasyMesh makes wired backhaul safe and automatic — plug in a cable between two nodes, and the mesh treats it as the primary path. If the cable fails, it falls back to wireless without manual intervention. + + + +### Actions on apply + +- **Port filtering** — only physical `ethN` / `lanN` ports are selectable. CPU-internal ports and macvlan interfaces are hidden, so you can't accidentally break the router by picking the wrong interface. +- **Automatic bridge removal** — the chosen port is removed from the LAN bridge (`br-lan`) on apply. This prevents a Layer-2 loop between the wired path and the mesh path. +- **Priority assignment** — the port receives `throughput_override = 1000000` (1 Gbit), so batman-adv prefers it over any wireless link. +- **Status labels** — before you commit, each port shows `(in bridge - will be removed on apply)` or `(free)` so you know exactly what will happen. + +### Fallback to wireless + +When the wired link drops (cable unplugged, switch reboots, port failure): + +- batman-adv detects the loss within a few seconds +- Traffic automatically reroutes through the 5 GHz (600 Mbit priority) or 2.4 GHz (30 Mbit priority) mesh backhaul +- No manual intervention needed + +When the cable comes back: + +- The wired port re-attaches automatically +- Traffic switches back to wired — wired always wins while it's available + +### Security + +- **Loop prevention** — removing the wired port from the LAN bridge stops a Layer-2 loop between wired and mesh paths, which would otherwise flood the network. +- **WPA3 on the wireless backhaul** — mesh links use WPA3 when configured; the wired side is a direct L2 link between trusted nodes only. +- **Rollback watchdog** — if a wired-backhaul change breaks connectivity, a 60-second watchdog automatically restores the previous working configuration. + +### Quick checks + +Verify the wired backhaul is active: + +```sh +batctl hardif eth0 throughput_override # expect 1000.0 MBit +batctl o # wired neighbor at the top +ip link show eth0 # UP, no `master br-lan` +``` + +Confirm the port was removed from the bridge: + +```sh +brctl show br-lan # eth0 should NOT be listed +``` + +Force a wireless fallback for testing: + +```sh +ip link set eth0 down +sleep 10 +batctl o # traffic should now use 5 GHz +ip link set eth0 up +``` + +--- + +## Tuning Link Priorities + +Batman-adv chooses the best path by multiplying link quality by `throughput_override`. EasyMesh exposes these values in the UI: + +**Defaults:** + +| Field | Default (Kbit/s) | Meaning | +|---|---|---| +| Wired | `1000000` | Prefer wired over any wireless | +| 6 GHz | `900000` | Fast wireless, near-wired | +| 5 GHz | `600000` | Standard 5 GHz backhaul | +| 2.4 GHz | `30000` | Slow but long range | + +Higher value = more likely to be chosen. Set to `0` to let batman decide purely on link quality. + +**Typical tuning scenarios:** + +- **Wired-first, wireless-fallback (default):** wired=1000000, 5 GHz=600000, 2.4 GHz=30000 +- **Force wired only** (ignore wireless unless wired drops): wired=10000000, 5 GHz=100000, 2.4 GHz=10000 +- **Force 5 GHz primary** (prefer wireless bandwidth): wired=500000, 5 GHz=900000, 2.4 GHz=20000 +- **Long-range preference** (2.4 GHz better through walls): wired=1000000, 5 GHz=400000, 2.4 GHz=200000 + +**Verify current values in real time:** + +The **Priority weight** column in the mesh tables shows the actual `throughput_override` applied to each interface, refreshed every page load. See the dashboard screenshot in [Quick Start](#quick-start). + +**Apply:** + +Click **Save & Apply**, or run: + +```sh +/etc/init.d/easymesh restart +``` + +Changes take effect within ~15 seconds. + +--- + + +![Link priority weights + Mesh VLANs](docs/screenshots/07-priority-vlans.png) + +--- + +## Extending Guest / IoT Networks over the Mesh (VLANs) + +### What is this, in 30 seconds? + +Imagine your home has **three separate networks**: + +1. **Main WiFi** — your phone, laptop, TV +2. **Guest WiFi** — visitors (no access to your files) +3. **IoT WiFi** — smart bulbs, cameras, plugs (isolated for security) + +Normally, when you add a mesh extender, only the **main network** spreads to the extender. Guests connecting to the extender still get main-network privileges, which is a security hole. + +**EasyMesh's Mesh VLANs** (Virtual LANs) feature lets all three networks spread across the mesh — so a Guest on an extender node is still a Guest, and an IoT device on an extender is still on the isolated IoT network. + + + +### Do you need this? + +| Your situation | Do you need VLANs? | +|---|---| +| You only have a single WiFi network | ❌ No — skip this section | +| You have guest WiFi and want it on all extenders | ✅ Yes | +| You have smart-home devices on a separate network | ✅ Yes | +| You run a hotspot / captive portal | ✅ Yes — required to spread it over the mesh | +| You just want WiFi to work everywhere | ❌ No — the mesh already handles that | + +### Prerequisites + +Before configuring VLANs, you need: + +- ✅ **A working mesh** — two or more nodes already connected (see Quick Start) +- ✅ **At least one extra network** already created — e.g., a `guest` interface in **Network → Interfaces** +- ✅ **EasyMesh 2.4.1-r1 or newer** installed +- ⚠️ If you use a **wired backhaul**, your Ethernet cable must be able to carry 802.1Q-tagged frames. Simple unmanaged switches do this by default; some older managed switches need to be configured to allow VLAN 10/20/etc. to pass through. + +### Step 1 — Find your network's VLAN ID + +If you already have a guest network, find out what VLAN number it uses: + +**Via LuCI UI:** + +1. Go to **Network → Interfaces** +2. Click **Edit** on your guest network +3. Look for the **Device** field — it might say something like `br-guest` or `br-lan.10` +4. If the name contains a number after a dot (e.g., `br-lan.10`), that number is the VLAN ID +5. If there's no number, your guest network uses the default — commonly **10** + +**Via SSH:** + +```sh +# List all network devices with their VLAN IDs +uci show network | grep -E "8021q|vid" +``` + +Example output: + +``` +network.guest=interface +network.guest.device='br-lan.10' +network.guest.proto='static' +``` + +In this case, VLAN ID is **10**. + +> **Don't have a guest network yet?** Create one first in **Network → Interfaces → Add new interface** → name it `guest` → protocol `Static address` → IP `192.168.30.1/24`. Then continue here. + +### Step 2 — Enter the VLAN IDs in EasyMesh + +1. Open **Network → EasyMesh** +2. Scroll down to the **Mesh VLANs** section +3. In the **VLAN IDs (comma-separated)** field, enter the VLAN IDs you want to extend + - Single network: `10` + - Two networks: `10,20` + - Three networks: `10,20,30` +4. Click **Save & Apply** at the bottom of the page + +### Step 3 — Wait for the mesh to rebuild + +The mesh takes about **15–20 seconds** to rebuild the network devices. Watch progress: + +```sh +# Open a terminal and watch the log +logread -f | grep easymesh +``` + +You'll see lines like: + +``` +easymesh: Mesh VLAN: bat0.10 created +easymesh: Mesh VLAN 10: attached to br-guest +easymesh: Mesh VLAN: bat0.20 created +easymesh: Mesh VLAN 20: attached to br-iot +``` + +### Step 4 — Verify it worked + +**Check the devices were created:** + +```sh +uci show network | grep 8021q +``` + +Expected output (for VLANs 10 and 20): + +``` +network.@device[5].name='bat0.10' +network.@device[5].type='8021q' +network.@device[5].ifname='bat0' +network.@device[5].vid='10' +network.@device[6].name='bat0.20' +network.@device[6].type='8021q' +network.@device[6].ifname='bat0' +network.@device[6].vid='20' +``` + +**Check the bridges:** + +```sh +uci show network | grep -E "br-guest|br-iot" +``` + +Look for `ports='...' bat0.10` in the guest bridge and `ports='...' bat0.20` in the IoT bridge. + +**Test end-to-end:** + +1. Connect a device to your guest WiFi on the **server** node — confirm it gets a `192.168.30.x` IP +2. Connect the **same device** to the guest WiFi on a **client** node — it should **also** get a `192.168.30.x` IP +3. That proves the guest VLAN is now stretching across the mesh + +### Common scenarios + +**Guest network only** + +```sh +uci set easymesh.config.mesh_vlans='10' +uci commit easymesh +/etc/init.d/easymesh restart +``` + +**Guest + IoT** + +```sh +uci set easymesh.config.mesh_vlans='10,20' +uci commit easymesh +/etc/init.d/easymesh restart +``` + +**Guest + IoT + Hotspot** + +```sh +uci set easymesh.config.mesh_vlans='10,20,30' +uci commit easymesh +/etc/init.d/easymesh restart +``` + +**Auto-attach — no manual bridging needed** + +If your existing bridges are named with the VLAN ID at the end, EasyMesh attaches the VLAN device automatically. Recognized patterns: + +| Bridge name | Auto-attach for VLAN | +|---|---| +| `br-10` | VLAN 10 | +| `br-lan10` | VLAN 10 | +| `br-vlan10` | VLAN 10 | +| `br-guest10` | VLAN 10 | +| `br-iot10` | VLAN 10 | + +Example: rename `br-guest` to `br-guest10` in **Network → Interfaces → Devices** and EasyMesh will add `bat0.10` automatically on next restart. No manual step. + +### Troubleshooting VLANs + +**Problem: VLAN device isn't showing up** + +```sh +# Check what the script logged +logread | grep -i "mesh vlan" | tail -20 +``` + +Look for "Skipping invalid VLAN" or "Mesh VLAN: bat0.X created". If the log shows creation but the device isn't there: + +```sh +# Manually recreate +/etc/init.d/network reload +sleep 5 +ip link show bat0.10 +``` + +**Problem: Guest device gets a main-network IP (192.168.1.x) instead of guest (192.168.30.x)** + +The bridge on the client node doesn't have the VLAN device. Add it manually: + +1. **Network → Interfaces → Devices** +2. Click **Edit** on your guest bridge (e.g., `br-guest`) +3. Add `bat0.10` to the **Ports** list +4. Click **Save & Apply** + +Do this on **every node** in the mesh. + +**Problem: The wired backhaul stopped carrying guest traffic after enabling VLANs** + +Your physical switch between nodes may be stripping tags. Check: + +- If using an unmanaged switch — it usually passes all VLANs +- If using a managed switch — configure it to allow tagged traffic on ports 10, 20, 30 (or use 802.1Q trunk mode) + +Quick test: bypass the switch. Connect the two nodes directly with a single Ethernet cable and retest. If it works, the switch is the problem. + +**Problem: WiFi works on server but not on client after enabling VLANs** + +The client node didn't rebuild. Force it: + +```sh +ssh root@ +/etc/init.d/easymesh restart +sleep 15 +uci show network | grep 8021q +``` + +### Disabling VLANs + +To remove all VLAN devices: + +```sh +uci set easymesh.config.mesh_vlans='' +uci commit easymesh +/etc/init.d/easymesh restart +sleep 15 +uci show network | grep 8021q # should print nothing +``` + +The script removes all `8021q` devices on `bat0` automatically. + +### How it works under the hood + +If you're curious: + +- Each VLAN ID creates an **802.1Q sub-interface** on `bat0` — for example, `bat0.10` +- Batman-adv carries the tagged frames transparently across the mesh +- The 802.1Q sub-interface is added as a **port** to the corresponding bridge (e.g., `br-guest`) +- Client devices on the extender that connect to the guest AP get tagged traffic that reaches the server's guest bridge +- No additional configuration is needed on the client nodes — the mesh distributes tags end-to-end + +You don't need to understand this to use it, but it helps when debugging. + +--- + +## More Info — Use Case Scenarios + +1. **No VPN required.** You can completely and quickly build / deploy a standard mesh wired + wireless network using only OpenWrt/ImmortalWrt routers without a VPN — in seconds. + +2. **Shared-radio operation.** You can run both the main WiFi AP and Mesh AP on the same radio hardware, but it is **not recommended** — performance will be significantly reduced. The best solution is for each network to use an independent radio frequency. + - If running both on the same radio (single shared frequency), the following limitations apply: limited channels reduce bandwidth & performance, and fewer available channels lead to decreased network speed and stability. + - In that case, Batman advanced features such as bonding and fragmentation need to be disabled to reduce overhead / hardware load. + +3. **Wired devices and third-party routers.** EasyMesh can connect to wired devices or third-party routers that do not support wireless Mesh. If using a wired OpenWrt device (x86 soft router) without wireless Mesh functionality, go to **Network → Interfaces** and manually bind the `mesh_batman` interface to the `bat0` virtual device (select `bat0` as the device on the `mesh_batman` interface). + +4. **WireGuard / VPN.** It is also possible to use WireGuard (VPN) in conjunction with this plugin. + +--- + +## Troubleshooting + +### I lost access to my router after applying settings + +1. **Unplug the router.** Wait 10 seconds. Plug back in. +2. **Immediately tap the reset button every 0.3 seconds** until the LED blinks rapidly. +3. **Connect a LAN cable** from your PC to a LAN port. +4. Set your PC's IP to **192.168.1.2 / 255.255.255.0**. +5. SSH in: `ssh root@192.168.1.1` +6. Type `mount_root` +7. Restore from last backup: + + ```sh + /etc/init.d/easymesh restore + reboot + ``` + +### Mesh shows 0 peers + +- Check both routers are within WiFi range +- Confirm **Mesh ID** and **password** are identical on both +- Run `batctl n` on both routers — if empty, the mesh link isn't formed +- Wait 2 minutes after a reboot before checking + +### No internet on client nodes + +- The Server must be the only router running DHCP (client nodes are set to "Dumb AP" mode, which disables DHCP locally) +- Check **Status → Overview** — does the client node have an IP? +- Try `ping 1.1.1.1` from the client node + +### WiFi SSID disappears + +- Check **Network → Wireless** — is the interface enabled? +- Run `wifi reload` on the router +- Check `logread | grep hostapd` for errors + +### "Priority weight" shows `?` or `0.0 MBit` + +The init script may not have finished applying priorities. Wait 30 seconds, then: + +```sh +/etc/init.d/easymesh restart +sleep 15 +batctl hardif eth0 throughput_override # should show 1000.0 MBit +``` + +Or enable the **Cron fallback** checkbox in **Network → EasyMesh → Boot-time reliability**. + +This makes the priority reapply every minute — safe, idempotent, and useful on routers where the wireless driver loads slowly. + +--- + +## Changelog + +### [2.4.1-r1] — 2026-09-16 + +**Added** + +- Wired backhaul dropdown now shown for Server role too (was Client/Node only) +- Port status labels: `(in bridge - will be removed on apply)` / `(free)` +- Filter to only `ethN` / `lanN` — excludes CPU ports and macvlan interfaces +- Link priority via `throughput_override` (wired 1 Gbit, 6 GHz 900 Mbit, 5 GHz 600 Mbit, 2.4 GHz 30 Mbit) +- **GUI-editable link priority weights** — no more hardcoded values in init script +- **Priority Weight column** for wireless peers (previously only wired) +- **Mesh VLANs** — extend guest / IoT / hotspot networks across the mesh via 802.1Q + - New "Mesh VLANs" section in the UI: enter comma-separated VLAN IDs (e.g. `10,20,30`) + - Init script creates a `bat0.` 802.1Q device for each ID on every node + - Auto-attaches the VLAN device to a bridge if it's named `br-`, `br-lan`, `br-vlan`, `br-guest`, or `br-iot` + - Removes previous 802.1Q devices on `bat0` before applying — safe to change IDs or clear the field + - Beginner guide in README: prerequisites, step-by-step, examples, troubleshooting +- Auto band detection via frequency (`iw dev`) +- Bonding enabled in `bat0` for simultaneous multi-link traffic +- WPS pairing via `/etc/rc.wps/easymesh-pair` dispatcher (no conflict with wifi-scripts) +- 60-second rollback watchdog for IP changes +- Split-band client SSIDs (2.4 / 5 GHz separate) +- `wait_and_attach_mesh()` — 60s timeout + manual `batctl` attach fallback +- Role-based sanitation: server keeps `wired_if` +- Added UI checkbox for optional cron fallback (default off) +- Live latency monitoring for wireless peers (smoothed over 3 pings) +- Live RX/TX bandwidth rates for wireless and wired links +- Neighbor MAC + last-seen for wired backhaul (from batman originator table) +- Robust priority read (tries multiple `batctl` syntaxes) +- Hotplug integration for priority reapplication on interface up +- 90-second priority retry loop + second-pass catchup in init script + +**Fixed** + +- Init script wrote escaped quotes to UCI, breaking wireless +- `apply_on_parse=true` in CBI wrote UCI on every page load (lockout) +- Server role could accidentally enable AP mode, disabling DHCP +- Removed `+wpad-mesh` dependency (was causing kconfig recursive dependency) +- `pair()` used `sleep 0.3` — broken on BusyBox +- `pair()` peer check counted blank lines +- `sleep 3; ifup bat0` too short for ath9k mesh +- Count display double-counted wired interfaces + +**Changed** + +- Split into 4 packages: main + 3 wpad metas +- IPK built with SDK's `ipkg-build` (gzipped-tar, OpenWrt-compatible) + +--- + +## Compile from Source (OpenWrt SDK) + +You can execute the following commands in the OpenWrt/ImmortalWrt source code root directory. + +### Step 1 — Setup OpenWrt SDK + +Download and install the OpenWrt SDK for your target platform: + +```sh +git clone https://git.openwrt.org/openwrt/openwrt.git +cd openwrt +./scripts/feeds update -a +./scripts/feeds install -a +``` + +### Step 2 — Add the EasyMesh App to OpenWrt Package Sources + +```sh +cd package +git clone https://github.com/arafatrahmanzami/luci-app-easymesh.git +``` + +### Step 3 — Compile the Package + +Return to OpenWrt's root directory: + +```sh +cd ../ +``` + +Select the package using `make menuconfig`: + +```sh +make menuconfig +``` + +Navigate to **LuCI → Applications → luci-app-easymesh**, then select `` to compile it as a module. + +Compile the package: + +```sh +make package/luci-app-easymesh/compile V=s +``` + +Once compiled, the `.ipk` package will be available in `bin/packages/.../base/`. + +**Short version (all in one):** + +```sh +git clone https://github.com/arafatrahmanzami/luci-app-easymesh.git package/luci-app-easymesh +make menuconfig # choose LUCI -> Applications -> luci-app-easymesh +make package/luci-app-easymesh/compile V=s +# Output: bin/packages/xxx/base/luci-app-easymesh_xxx.ipk +``` + +--- + +## Credits + +- **Original authors:** + - dz + - torguardvpn — [torguardvpn/luci-app-easymesh](https://github.com/torguardvpn/luci-app-easymesh) +- **Upstream forks:** + - [ntlf9t/luci-app-easymesh](https://github.com/ntlf9t/luci-app-easymesh) + - [kenzok78/luci-app-easymesh](https://github.com/kenzok78/luci-app-easymesh) + - [torguardvpn/luci-app-easymesh](https://github.com/torguardvpn/luci-app-easymesh) +- **Bugfix branch:** [mobing8/luci-app-easymesh-dawn](https://github.com/mobing8/luci-app-easymesh-dawn) +- **Current maintainer:** Arafat Rahman Zami Mondol — [@arafatrahmanzami](https://github.com/arafatrahmanzami) · [open an issue](https://github.com/arafatrahmanzami/luci-app-easymesh/issues/new) + +For further exploration, check the source code or track releases on the reference repositories listed above. + +--- + +## Glossary + +Terms used in this README that may be unfamiliar, especially if you're new to OpenWrt or networking. Terms that lose meaning when translated are kept in English. + +### Network fundamentals + +| Term | Full form | Meaning | +|---|---|---| +| **WiFi** | Wireless Fidelity | The standard name for wireless network connectivity. | +| **LAN** | Local Area Network | The network inside your home or office — connects devices locally. | +| **WAN** | Wide Area Network | The broader network — usually the internet connection from your ISP. | +| **AP** | Access Point | Broadcasts WiFi that devices can connect to. | +| **IP** | Internet Protocol | Each network device's unique address (e.g. `192.168.1.1`). | +| **MAC** | Media Access Control | Each network card's unique hardware identifier (e.g. `00:11:22:33:44:55`). | +| **DHCP** | Dynamic Host Configuration Protocol | Service that automatically assigns IP addresses to devices. | +| **DNS** | Domain Name System | Translates domain names to IP addresses (e.g. `google.com` → `142.250.185.78`). | +| **SSID** | Service Set Identifier | The WiFi network name you see on your phone (e.g. `EasyMesh_WiFi`). | +| **VLAN** | Virtual LAN | Technique to create multiple logical networks on one physical network (e.g. guest network). | +| **VPN** | Virtual Private Network | An encrypted connection over the internet that protects privacy. | + +### Wireless standards & protocols + +| Term | Meaning | +|---|---| +| **802.11s** | IEEE standard for mesh networking — routers connect directly to each other. | +| **802.11k/v/r** | Three IEEE standards that together provide fast roaming — your phone automatically moves to the nearest router as you walk around. | +| **802.11k** | Shares information about nearby access points. | +| **802.11v** | Suggests to a client which access point to connect to. | +| **802.11r** | Fast roaming — move to another router without dropping the connection. | +| **802.1Q** | VLAN tagging standard. Adds a tag to data packets to mark which network they belong to. | +| **WPA2** | WiFi Protected Access 2 — older but still-used network encryption standard. | +| **WPA3** | WiFi Protected Access 3 — successor to WPA2, stronger encryption. | +| **SAE** | Simultaneous Authentication of Equals — the password-based authentication method used in WPA3. | +| **K/V/R** | Short for 802.11k/v/r above. | +| **AX / AC / N** | WiFi 6 (AX), WiFi 5 (AC), WiFi 4 (N) — wireless standard generations. | +| **2.4 GHz / 5 GHz / 6 GHz** | Wireless signal frequencies — 2.4 GHz longer range but slower; 5/6 GHz faster but shorter range. | +| **MIMO** | Multiple-Input Multiple-Output — sending and receiving data simultaneously via multiple antennas. | +| **MU-MIMO** | Multi-User MIMO — communicating with multiple devices at the same time. | + +### OpenWrt ecosystem + +| Term | Meaning | +|---|---| +| **OpenWrt** | Open-source router firmware, Linux-based. Gives full control over the router. | +| **ImmortalWrt** | A fork of OpenWrt maintained by the Chinese community, with some extra packages and default settings. | +| **LuCI** | OpenWrt's web-based configuration interface / Lua Configuration Interface (or sometimes Lua Control Interface) — what opens when you visit `http://` in a browser. | +| **UCI** | Unified Configuration Interface — OpenWrt's system for storing all configuration in one place, under `/etc/config/`. | +| **opkg** | Package manager used by OpenWrt 24.10 and older. Installs `.ipk` files. | +| **apk** | Newer package manager used by OpenWrt 25.12+ and Alpine Linux. Installs `.apk` files. | +| **procd** | OpenWrt's service manager — controls when a service starts. | +| **rpcd** | The service behind LuCI that processes web interface requests. | +| **uhttpd** | The lightweight HTTP server that serves LuCI web pages. | +| **Batman-adv** | Better Approach To Mobile Adhoc Networking — Advanced — mesh routing protocol. Decides which path data takes. | +| **batctl** | Command-line tool to control and diagnose Batman-adv. | +| **bat0** | Batman-adv's virtual network interface through which mesh traffic flows. | +| **hardif** | Hardware Interface — the real interface Batman-adv uses (e.g. `eth0`, `phy0-mesh0`). | +| **wpad** | Wireless Protected Access Daemon — the service managing WiFi on OpenWrt. Has multiple variants (openssl/wolfssl/mbedtls). | +| **wpad-mesh** | A special variant of wpad that supports 802.11s mesh. | +| **kmod** | Kernel Module — additional code loadable into the kernel (e.g. `kmod-batman-adv`). | +| **dawn** | An access-point steering daemon — guides clients to the best AP. | +| **usteer** | User-space steerer — alternative to dawn, lighter and faster. | + +### Shell commands + +| Command | Meaning | +|---|---| +| `uci set` | Set a value in UCI config. | +| `uci get` | Read a value from UCI config. | +| `uci commit` | Save changed values permanently. | +| `uci show` | Display current UCI config. | +| `opkg install` | Install a package with opkg. | +| `opkg update` | Refresh package list. | +| `apk add` | Install a package with apk. | +| `apk del` | Remove a package with apk. | +| `batctl n` | Show Batman-adv neighbor list. | +| `batctl if` | Show Batman-adv interface list. | +| `batctl o` | Show Batman-adv originator table (full mesh topology). | +| `batctl ping` | Ping at the Batman-adv layer. | +| `iw dev` | List wireless interfaces. | +| `wget` | Download a file. | +| `scp` | Secure file copy (over SSH). | +| `ssh` | Secure remote shell connection. | +| `reboot` | Restart the router. | +| `logread` | Read the system log. | +| `ip link` | Show network interface status. | +| `ifup` / `ifdown` | Bring an interface up / down. | +| `chmod +x` | Make a file executable (runnable). | + +### Units & measurements + +| Unit | Meaning | +|---|---| +| **bit** | The smallest unit of digital information (0 or 1). | +| **Byte** | 8 bits. Written with capital `B`. | +| **Kbit/s** | Kilobits per second — rate of data transfer. | +| **Mbit** | Megabit = 1000 kilobits. | +| **Gbit** | Gigabit = 1000 megabits. | +| **Mbps** | Megabits per second = 1000 Kbit/s. | +| **MHz / GHz** | Megahertz / gigahertz — radio frequency. 1 GHz = 1000 MHz. | +| **ms** | Millisecond = 1/1000 of a second. Used for latency measurements. | + +### Interface naming + +| Name | Meaning | +|---|---| +| **eth0, eth1** | Ethernet interface (wired). On some routers, the internal CPU port. | +| **lan1, lan2, lan3** | LAN physical ports (wired). Usually where you plug devices in. | +| **wan** | WAN port — for internet/ISP connection. | +| **phy0-mesh0** | Mesh virtual wireless interface — carries 802.11s traffic. | +| **phy0, phy1** | Wireless radio hardware identifiers. | +| **radio0, radio1** | OpenWrt's names for radios. | +| **bat0** | Batman-adv's virtual mesh interface. | +| **br-lan** | LAN bridge interface — combines multiple ports into one network. | +| **br-guest** | Guest network bridge. | +| **wlan0, wlan1** | Wireless client/AP interfaces. | + +### UI & setup terms + +| Term | Meaning | +|---|---| +| **Server** | The main mesh router that provides internet. Has the internet connection. | +| **Client** | Extender router — receives internet via the mesh. | +| **Node** | Relay-only — only forwards traffic, doesn't receive or provide internet itself. | +| **Dumb AP** | "Dumb" access point — DHCP and firewall disabled, only broadcasts WiFi. | +| **Mesh ID** | Mesh network name — must be identical on all nodes. | +| **Mobility Domain** | A 4-character hexadecimal identifier used in 802.11r fast roaming. | +| **Save & Apply** | Button to save and apply changes. | +| **Reapply EasyMesh Settings** | Button to re-apply current settings to the mesh. | +| **Preset** | A pre-defined setting — one-click common configuration. | +| **Rollback** | Reverting to the previous state if something breaks after a change. | +| **Watchdog** | A monitoring process that performs a specific action at a specific time. | + +### Platform + +| Term | Meaning | +|---|---| +| **Git** | Version control system — tracks changes to code. | +| **GitHub** | Git repository hosting platform. For sharing and collaborating on code. | +| **Repository (Repo)** | A collection of code and files. | +| **Commit** | A saved snapshot of code changes. | +| **Branch** | A parallel version of the code. | +| **Fork** | Your own copy of someone else's project, freely modifiable. | +| **Release** | A specific version published for users. | +| **Tag** | A named marker for a commit (e.g. `2.4.1-r1`). | +| **Linux** | The open-source operating system kernel — foundation of OpenWrt and ImmortalWrt. | + +--- + +## License + +GPL-2.0 (inherited from upstream) diff --git a/luci-app-easymesh/build-scripts/build-apk-all.sh b/luci-app-easymesh/build-scripts/build-apk-all.sh new file mode 100755 index 000000000..fdff7358c --- /dev/null +++ b/luci-app-easymesh/build-scripts/build-apk-all.sh @@ -0,0 +1,77 @@ +#!/bin/bash +set -e + +SRC="/home/zamimondol/easymesh-build/src/luci-app-easymesh" +OUT="/home/zamimondol/easymesh-build/output" +BUILD="/tmp/easymesh-apk" +APK="/home/zamimondol/easymesh-build/sdk-25.12/staging_dir/host/bin/apk" +PO2LMO="/home/zamimondol/easymesh-build/sdk-24.10/staging_dir/hostpkg/bin/po2lmo" + +VERSION="2.4.1-r1" + +BASE_DEPS="libc luci-base luci-compat luci-lua-runtime libiwinfo-lua kmod-cfg80211 kmod-batman-adv luci-proto-batman-adv batctl-default usteer luci-app-usteer" + +mkdir -p "$OUT" +rm -rf "$BUILD" +mkdir -p "$BUILD" + +stage_main() { + local DEST="$1" + mkdir -p "$DEST/usr/lib/lua/luci" + cp -a "$SRC/luasrc/." "$DEST/usr/lib/lua/luci/" + [ -d "$SRC/htdocs" ] && { mkdir -p "$DEST/www"; cp -a "$SRC/htdocs/." "$DEST/www/"; } + [ -d "$SRC/root" ] && cp -a "$SRC/root/." "$DEST/" + find "$DEST" -name '*.luadoc' -delete +} + +build_apk() { + local NAME="$1" DIR="$2" DEPS="$3" DESC="$4" + local OUTFILE="$OUT/${NAME}-${VERSION}.apk" + rm -f "$OUTFILE" + SOURCE_DATE_EPOCH=0 "$APK" mkpkg \ + --info "name:$NAME" \ + --info "version:$VERSION" \ + --info "arch:noarch" \ + --info "description:$DESC" \ + --info "maintainer:Arafat Rahman Zami Mondol " \ + --info "depends:$DEPS" \ + --info "origin:luci-app-easymesh" \ + --files "$DIR" \ + --output "$OUTFILE" + local MAGIC=$(head -c 4 "$OUTFILE" | od -An -tx1 | tr -d ' \n') + [ "$MAGIC" = "41444264" ] && echo " OK: $NAME" || echo " WARN: $NAME" +} + +echo "==> Building APK variants" + +# Main (no wpad) +MAIN="$BUILD/main" +mkdir -p "$MAIN" +stage_main "$MAIN" +build_apk "luci-app-easymesh" "$MAIN" "$BASE_DEPS" \ + "LuCI Management Interface for EasyMesh (Batman-adv mesh)" + +# Meta variants (empty package with just a wpad dep) +for v in openssl wolfssl mbedtls; do + M="$BUILD/meta-$v" + mkdir -p "$M" + build_apk "luci-app-easymesh-wpad-$v" "$M" \ + "luci-app-easymesh wpad-mesh-$v" \ + "EasyMesh meta-package — installs with wpad-mesh-$v" +done + +# i18n +if [ -x "$PO2LMO" ] && [ -f "$SRC/po/zh_Hans/easymesh.po" ]; then + I18N="$BUILD/i18n" + mkdir -p "$I18N/usr/lib/lua/luci/i18n" "$I18N/etc/uci-defaults" + "$PO2LMO" "$SRC/po/zh_Hans/easymesh.po" \ + "$I18N/usr/lib/lua/luci/i18n/easymesh.zh-cn.lmo" + echo "uci set luci.languages.zh_cn='简体中文'; uci commit luci" \ + > "$I18N/etc/uci-defaults/luci-i18n-easymesh-zh-cn" + build_apk "luci-i18n-easymesh-zh-cn" "$I18N" "libc luci-app-easymesh" \ + "Chinese Simplified translation for luci-app-easymesh" +fi + +echo "" +echo "============================================================" +ls -lh "$OUT"/*.apk diff --git a/luci-app-easymesh/build-scripts/build-ipk-dpkg.sh b/luci-app-easymesh/build-scripts/build-ipk-dpkg.sh new file mode 100755 index 000000000..0e699d934 --- /dev/null +++ b/luci-app-easymesh/build-scripts/build-ipk-dpkg.sh @@ -0,0 +1,104 @@ +#!/bin/bash +set -e + +SRC="/home/zamimondol/easymesh-build/src/luci-app-easymesh" +OUT="/home/zamimondol/easymesh-build/output" +BUILD="/tmp/easymesh-ipk-dpkg" +PO2LMO="/home/zamimondol/easymesh-build/sdk-24.10/staging_dir/hostpkg/bin/po2lmo" + +V="2.4.1-r1" +PKG="luci-app-easymesh" +I18N="luci-i18n-easymesh-zh-cn" +BASE_DEPS="libc, luci-base, luci-compat, luci-lua-runtime, libiwinfo-lua, kmod-cfg80211, kmod-batman-adv, luci-proto-batman-adv, batctl-default, usteer, luci-app-usteer" + +mkdir -p "$OUT" +rm -rf "$BUILD" +mkdir -p "$BUILD" + +build_ipk() { + local NAME="$1" DATADIR="$2" DEPS="$3" DESC="$4" + local WORK="$BUILD/$NAME" + rm -rf "$WORK" + mkdir -p "$WORK/DEBIAN" + + # Copy data files into deb root + if [ -d "$DATADIR" ]; then + cp -a "$DATADIR/." "$WORK/" + fi + rm -rf "$WORK/CONTROL" "$WORK/.git" + + # DEBIAN/control + cat > "$WORK/DEBIAN/control" < +Section: luci +Priority: optional +Description: $DESC +EOF + + if [ "$NAME" = "$PKG" ]; then + echo "/etc/config/easymesh" > "$WORK/DEBIAN/conffiles" + cat > "$WORK/DEBIAN/postinst" <<'POST' +#!/bin/sh +[ -n "${IPKG_INSTROOT}" ] && exit 0 +rm -f /tmp/luci-indexcache 2>/dev/null +/etc/init.d/rpcd restart 2>/dev/null +/etc/init.d/uhttpd restart 2>/dev/null +exit 0 +POST + chmod 0755 "$WORK/DEBIAN/postinst" + fi + + # Build .deb (which IS an ar archive, same format as .ipk) + rm -f "$OUT/${NAME}_${V}_all.ipk" + dpkg-deb --build -Zgzip -z9 "$WORK" "$OUT/${NAME}_${V}_all.ipk" >/dev/null + + # Verify + local MAGIC=$(head -c 8 "$OUT/${NAME}_${V}_all.ipk" | od -An -tx1 | tr -d ' \n') + if [ "$MAGIC" = "213c617263683e0a" ]; then + echo " OK: $NAME ($(stat -c%s "$OUT/${NAME}_${V}_all.ipk") bytes)" + else + echo " FAIL: $NAME — magic $MAGIC" + fi +} + +# Main package +MAIN_DATA="$BUILD/main-data" +mkdir -p "$MAIN_DATA/usr/lib/lua/luci" +cp -a "$SRC/luasrc/." "$MAIN_DATA/usr/lib/lua/luci/" +[ -d "$SRC/root" ] && cp -a "$SRC/root/." "$MAIN_DATA/" +find "$MAIN_DATA" -name '*.luadoc' -delete + +echo "==> Main" +build_ipk "$PKG" "$MAIN_DATA" "$BASE_DEPS" \ + "LuCI Management Interface for EasyMesh (Batman-adv mesh)" + +# Meta packages +for v in openssl wolfssl mbedtls; do + echo "==> Meta $v" + EMPTY="$BUILD/empty" + rm -rf "$EMPTY"; mkdir -p "$EMPTY" + build_ipk "luci-app-easymesh-wpad-$v" "$EMPTY" \ + "libc, $PKG, wpad-mesh-$v" \ + "EasyMesh meta — installs with wpad-mesh-$v" +done + +# i18n +if [ -x "$PO2LMO" ] && [ -f "$SRC/po/zh_Hans/easymesh.po" ]; then + echo "==> i18n" + I18N_DATA="$BUILD/i18n-data" + rm -rf "$I18N_DATA" + mkdir -p "$I18N_DATA/usr/lib/lua/luci/i18n" "$I18N_DATA/etc/uci-defaults" + "$PO2LMO" "$SRC/po/zh_Hans/easymesh.po" \ + "$I18N_DATA/usr/lib/lua/luci/i18n/easymesh.zh-cn.lmo" + echo "uci set luci.languages.zh_cn='简体中文'; uci commit luci" \ + > "$I18N_DATA/etc/uci-defaults/luci-i18n-easymesh-zh-cn" + build_ipk "$I18N" "$I18N_DATA" "libc, $PKG" "Chinese Simplified" +fi + +echo "" +echo "==> Output:" +ls -lh "$OUT"/*.ipk diff --git a/luci-app-easymesh/build-scripts/build-tarball.sh b/luci-app-easymesh/build-scripts/build-tarball.sh new file mode 100755 index 000000000..66ebb4e48 --- /dev/null +++ b/luci-app-easymesh/build-scripts/build-tarball.sh @@ -0,0 +1,121 @@ +#!/bin/bash +set -e + +SRC="/home/zamimondol/easymesh-build/src/luci-app-easymesh" +OUT="/home/zamimondol/easymesh-build/output" +BUILD="/tmp/easymesh-tarball" +PO2LMO="/home/zamimondol/easymesh-build/sdk-24.10/staging_dir/hostpkg/bin/po2lmo" + +VERSION="2.4.1-r1" + +mkdir -p "$OUT" +rm -rf "$BUILD" +mkdir -p "$BUILD" + +# ========================================================= +# Main app tarball +# ========================================================= +echo "==> Building main tarball..." + +STAGE="$BUILD/stage-main" +mkdir -p "$STAGE" + +# luasrc/ -> usr/lib/lua/luci/ +mkdir -p "$STAGE/usr/lib/lua/luci" +cp -a "$SRC/luasrc/." "$STAGE/usr/lib/lua/luci/" + +# root/ -> ./ (etc/init.d/easymesh, etc/config/easymesh, etc/uci-defaults/, usr/share/...) +if [ -d "$SRC/root" ]; then + cp -a "$SRC/root/." "$STAGE/" +fi + +# htdocs/ (if present) -> www/ +if [ -d "$SRC/htdocs" ]; then + mkdir -p "$STAGE/www" + cp -a "$SRC/htdocs/." "$STAGE/www/" +fi + +# Remove stray files +find "$STAGE" -name '*.luadoc' -delete +find "$STAGE" -name '.git*' -delete 2>/dev/null || true + +# Build the tarball with clean permissions +cd "$STAGE" +find . -type d -exec chmod 755 {} \; +find . -type f -exec chmod 644 {} \; +chmod 755 etc/init.d/easymesh 2>/dev/null || true + +tar czf "$OUT/luci-app-easymesh-${VERSION}-all.tar.gz" \ + --numeric-owner --owner=0 --group=0 \ + --sort=name \ + --mtime='2026-09-11 00:00:00' \ + -C "$STAGE" \ + ./etc ./usr 2>/dev/null || \ +tar czf "$OUT/luci-app-easymesh-${VERSION}-all.tar.gz" \ + --numeric-owner --owner=0 --group=0 \ + -C "$STAGE" . + +echo " Created: $OUT/luci-app-easymesh-${VERSION}-all.tar.gz" +ls -lh "$OUT/luci-app-easymesh-${VERSION}-all.tar.gz" + +# Show what's inside +echo "" +echo " Contents:" +tar tzf "$OUT/luci-app-easymesh-${VERSION}-all.tar.gz" | head -25 + +# ========================================================= +# i18n tarball +# ========================================================= +if [ -x "$PO2LMO" ] && [ -f "$SRC/po/zh_Hans/easymesh.po" ]; then + echo "" + echo "==> Building i18n tarball..." + + STAGE_I18N="$BUILD/stage-i18n" + mkdir -p "$STAGE_I18N/usr/lib/lua/luci/i18n" + mkdir -p "$STAGE_I18N/etc/uci-defaults" + + "$PO2LMO" "$SRC/po/zh_Hans/easymesh.po" \ + "$STAGE_I18N/usr/lib/lua/luci/i18n/easymesh.zh-cn.lmo" + + echo "uci set luci.languages.zh_cn='简体中文 (Chinese Simplified)'; uci commit luci" \ + > "$STAGE_I18N/etc/uci-defaults/luci-i18n-easymesh-zh-cn" + + chmod -R 755 "$STAGE_I18N/etc" + chmod 644 "$STAGE_I18N/usr/lib/lua/luci/i18n/easymesh.zh-cn.lmo" + + tar czf "$OUT/luci-i18n-easymesh-zh-cn-${VERSION}-all.tar.gz" \ + --numeric-owner --owner=0 --group=0 \ + --sort=name \ + --mtime='2026-09-11 00:00:00' \ + -C "$STAGE_I18N" . + + echo " Created: $OUT/luci-i18n-easymesh-zh-cn-${VERSION}-all.tar.gz" + ls -lh "$OUT/luci-i18n-easymesh-zh-cn-${VERSION}-all.tar.gz" +else + echo "!! po2lmo not found, skipping i18n tarball" +fi + +# ========================================================= +# Combined "install everything" tarball +# ========================================================= +echo "" +echo "==> Building combined tarball (main + i18n)..." + +STAGE_ALL="$BUILD/stage-all" +mkdir -p "$STAGE_ALL" +cp -a "$BUILD/stage-main/." "$STAGE_ALL/" +[ -d "$BUILD/stage-i18n" ] && cp -a "$BUILD/stage-i18n/." "$STAGE_ALL/" + +tar czf "$OUT/luci-app-easymesh-${VERSION}-full.tar.gz" \ + --numeric-owner --owner=0 --group=0 \ + --sort=name \ + --mtime='2026-09-11 00:00:00' \ + -C "$STAGE_ALL" . + +echo " Created: $OUT/luci-app-easymesh-${VERSION}-full.tar.gz" +ls -lh "$OUT/luci-app-easymesh-${VERSION}-full.tar.gz" + +echo "" +echo "============================================================" +echo "Tarballs in $OUT:" +ls -lh "$OUT"/*.tar.gz diff --git a/luci-app-easymesh/build-scripts/make-ipk.py b/luci-app-easymesh/build-scripts/make-ipk.py new file mode 100755 index 000000000..53ab77495 --- /dev/null +++ b/luci-app-easymesh/build-scripts/make-ipk.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Create a Debian/OpenWrt .ipk (ar archive) from debian-binary + control.tar.gz + data.tar.gz.""" + +import sys +import os +import struct + +def write_ar_header(f, name, size): + # ar member header is 60 bytes + # name must end with '/' for GNU/SysV compatibility + name_field = (name + '/')[:16].ljust(16) + header = "{}{:<12}{:<6}{:<6}{:<8}{:<10}`\n".format( + name_field, "0", "0", "0", "100644", size + ) + f.write(header.encode('ascii')) + +def add_file(ar, path, archive_name): + data = open(path, 'rb').read() + write_ar_header(ar, archive_name, len(data)) + ar.write(data) + # Pad to even length + if len(data) % 2: + ar.write(b'\n') + +def main(): + if len(sys.argv) != 5: + print("Usage: make-ipk.py ") + sys.exit(1) + + out, debian_bin, control_tgz, data_tgz = sys.argv[1:] + + with open(out, 'wb') as ar: + # Magic + ar.write(b'!\n') + add_file(ar, debian_bin, 'debian-binary') + add_file(ar, control_tgz, 'control.tar.gz') + add_file(ar, data_tgz, 'data.tar.gz') + +if __name__ == '__main__': + main() diff --git a/luci-app-easymesh/build-scripts/make-meta-apk.sh b/luci-app-easymesh/build-scripts/make-meta-apk.sh new file mode 100755 index 000000000..0d8783867 --- /dev/null +++ b/luci-app-easymesh/build-scripts/make-meta-apk.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Create wpad meta-package APKs (OpenWrt 25.12+ apk format) +set -e + +OUT="${1:-/home/zamimondol/easymesh-build/output}" +V="2.4.1-r1" +PKG="luci-app-easymesh" +MAINTAINER="Arafat Rahman Zami Mondol " +APK="/home/zamimondol/easymesh-build/sdk-25.12-fresh/staging_dir/host/bin/apk" + +if [ ! -x "$APK" ]; then + echo "ERROR: apk tool not found at $APK" + exit 1 +fi + +mkdir -p "$OUT" + +create_meta_apk() { + local NAME="$1" DEPS="$2" DESC="$3" + local EMPTY="/tmp/meta-apk-empty" + rm -rf "$EMPTY" + mkdir -p "$EMPTY" + + rm -f "$OUT/${NAME}-${V}.apk" + + SOURCE_DATE_EPOCH=0 "$APK" mkpkg \ + --info "name:$NAME" \ + --info "version:$V" \ + --info "arch:noarch" \ + --info "description:$DESC" \ + --info "maintainer:$MAINTAINER" \ + --info "depends:libc $PKG $DEPS" \ + --info "origin:luci-app-easymesh" \ + --files "$EMPTY" \ + --output "$OUT/${NAME}-${V}.apk" 2>&1 | tail -2 + + rm -rf "$EMPTY" + + local SIZE=$(stat -c%s "$OUT/${NAME}-${V}.apk" 2>/dev/null || echo 0) + local MAGIC=$(head -c 4 "$OUT/${NAME}-${V}.apk" 2>/dev/null | od -An -tx1 | tr -d ' \n') + + if [ "$MAGIC" = "41444264" ]; then + echo " OK: $NAME (${SIZE} bytes, ADBd magic)" + else + echo " FAIL: $NAME (magic=$MAGIC)" + fi +} + +echo "==> Creating wpad MESH meta APKs" +create_meta_apk "luci-app-easymesh-wpad-openssl" \ + "wpad-mesh-openssl" \ + "EasyMesh meta - installs with wpad-mesh-openssl (default OpenWrt)" + +create_meta_apk "luci-app-easymesh-wpad-wolfssl" \ + "wpad-mesh-wolfssl" \ + "EasyMesh meta - installs with wpad-mesh-wolfssl (ImmortalWrt default)" + +create_meta_apk "luci-app-easymesh-wpad-mbedtls" \ + "wpad-mesh-mbedtls" \ + "EasyMesh meta - installs with wpad-mesh-mbedtls (lightweight)" + +echo "" +ls -lh "$OUT"/luci-app-easymesh-wpad-*.apk diff --git a/luci-app-easymesh/build-scripts/package-all.sh b/luci-app-easymesh/build-scripts/package-all.sh new file mode 100755 index 000000000..2906d8838 --- /dev/null +++ b/luci-app-easymesh/build-scripts/package-all.sh @@ -0,0 +1,78 @@ +#!/bin/bash +set -e +OUT="/home/zamimondol/easymesh-build/output" +PKG="/home/zamimondol/easymesh-build/easymesh-pkg" +SRC="/home/zamimondol/easymesh-build/src/luci-app-easymesh" +META="/home/zamimondol/easymesh-build/meta-packages" +V="2.4.1-r1" + +rm -rf "$PKG" +mkdir -p "$PKG"/{ipk,apk,tarballs,meta-packages/luci-app-easymesh,meta-packages/wpad-openssl,meta-packages/wpad-wolfssl,meta-packages/wpad-mbedtls} + +# Source +cp -a "$SRC/." "$PKG/meta-packages/luci-app-easymesh/" +rm -rf "$PKG/meta-packages/luci-app-easymesh/.git" +cp "$META/openssl/Makefile" "$PKG/meta-packages/wpad-openssl/" +cp "$META/wolfssl/Makefile" "$PKG/meta-packages/wpad-wolfssl/" +cp "$META/mbedtls/Makefile" "$PKG/meta-packages/wpad-mbedtls/" + +# IPKs +cp -v "$OUT"/luci-app-easymesh_${V}_all.ipk "$PKG/ipk/" +cp -v "$OUT"/luci-app-easymesh-wpad-openssl_${V}_all.ipk "$PKG/ipk/" +cp -v "$OUT"/luci-app-easymesh-wpad-wolfssl_${V}_all.ipk "$PKG/ipk/" +cp -v "$OUT"/luci-app-easymesh-wpad-mbedtls_${V}_all.ipk "$PKG/ipk/" +find "$OUT" -name "luci-i18n-easymesh-zh-cn_*_all.ipk" -exec cp {} "$PKG/ipk/" \; + +# APKs +cp -v "$OUT"/luci-app-easymesh-${V}.apk "$PKG/apk/" +cp -v "$OUT"/luci-app-easymesh-wpad-openssl-${V}.apk "$PKG/apk/" +cp -v "$OUT"/luci-app-easymesh-wpad-wolfssl-${V}.apk "$PKG/apk/" +cp -v "$OUT"/luci-app-easymesh-wpad-mbedtls-${V}.apk "$PKG/apk/" +find "$OUT" -name "luci-i18n-easymesh-zh-cn-*_all.apk" -exec cp {} "$PKG/apk/" \; + +# Tarballs +find "$OUT" -name "luci-app-easymesh-${V}-*.tar.gz" -exec cp {} "$PKG/tarballs/" \; + +cat > "$PKG/MANIFEST.txt" </dev/null +check "CBI Lua syntax valid" $? + +sh -n "$SRC/root/etc/init.d/easymesh" +check "Init script syntax valid" $? + +# ============================================================ +section "2. IPK files" +# ============================================================ +for p in luci-app-easymesh luci-app-easymesh-wpad-openssl \ + luci-app-easymesh-wpad-wolfssl luci-app-easymesh-wpad-mbedtls \ + luci-i18n-easymesh-zh-cn +do + F="$OUT/${p}_${V}_all.ipk" + [ -f "$F" ] + check "IPK present: $(basename $F)" $? +done + +# Verify main IPK contents +D="$TMP/ipk"; mkdir -p "$D"; cd "$D" +if ar x "$OUT/luci-app-easymesh_${V}_all.ipk" 2>/dev/null; then + check "Main IPK is valid ar archive" 0 + + tar xzf control.tar.gz 2>/dev/null + grep -q "Package: luci-app-easymesh" control + check "Main IPK control: package name" $? + grep -q "Version: 2.4.1-r1" control + check "Main IPK control: version" $? + grep -q "Architecture: all" control + check "Main IPK control: arch=all" $? + grep -q "Depends:.*kmod-batman-adv" control + check "Main IPK control: has batman dep" $? + ! grep -q "Depends:.*wpad-mesh" control + check "Main IPK control: no wpad dep" $? + + tar tzf data.tar.gz | grep -q "etc/rc.wps/easymesh-pair" + check "Main IPK data: rc.wps handler present" $? + ! tar tzf data.tar.gz | grep -q "etc/rc.button/wps" + check "Main IPK data: no rc.button conflict" $? + tar tzf data.tar.gz | grep -q "etc/init.d/easymesh" + check "Main IPK data: init.d present" $? + tar tzf data.tar.gz | grep -q "usr/lib/lua/luci/model/cbi/easymesh.lua" + check "Main IPK data: CBI present" $? +else + check "Main IPK is valid ar archive" 1 +fi +cd "$TMP" + +# Verify a meta IPK +D="$TMP/meta"; mkdir -p "$D"; cd "$D" +if ar x "$OUT/luci-app-easymesh-wpad-wolfssl_${V}_all.ipk" 2>/dev/null; then + tar xzf control.tar.gz 2>/dev/null + grep -q "Package: luci-app-easymesh-wpad-wolfssl" control + check "Wolfssl meta: correct name" $? + grep -q "Depends:.*wpad-mesh-wolfssl" control + check "Wolfssl meta: depends on wolfssl" $? + grep -q "Depends:.*luci-app-easymesh" control + check "Wolfssl meta: depends on main" $? +fi +cd "$TMP" + +# ============================================================ +section "3. APK files" +# ============================================================ +for p in luci-app-easymesh luci-app-easymesh-wpad-openssl \ + luci-app-easymesh-wpad-wolfssl luci-app-easymesh-wpad-mbedtls \ + luci-i18n-easymesh-zh-cn +do + F="$OUT/${p}-${V}.apk" + [ -f "$F" ] + check "APK present: $(basename $F)" $? + + if [ -f "$F" ]; then + MAGIC=$(head -c 4 "$F" 2>/dev/null | od -An -tx1 | tr -d ' \n') + [ "$MAGIC" = "41444264" ] + check " magic bytes ADBd: $(basename $F)" $? + fi +done + +# ============================================================ +section "4. Tarballs" +# ============================================================ +for t in luci-app-easymesh-${V}-all.tar.gz \ + luci-app-easymesh-${V}-full.tar.gz \ + luci-i18n-easymesh-zh-cn-${V}-all.tar.gz +do + F="$OUT/$t" + [ -f "$F" ] + check "Tarball present: $t" $? + + if [ -f "$F" ]; then + tar tzf "$F" >/dev/null 2>&1 + check " tarball valid: $t" $? + fi +done + +# Verify full tarball contains the right paths +tar tzf "$OUT/luci-app-easymesh-${V}-full.tar.gz" 2>/dev/null | grep -q "etc/rc.wps/easymesh-pair" +check "Full tarball: rc.wps present" $? +tar tzf "$OUT/luci-app-easymesh-${V}-full.tar.gz" 2>/dev/null | grep -q "etc/init.d/easymesh" +check "Full tarball: init.d present" $? + +# ============================================================ +section "5. Bundle folder" +# ============================================================ +[ -d "$PKG" ] +check "Bundle folder exists" $? + +[ -f "$PKG/MANIFEST.txt" ] +check "MANIFEST.txt present" $? + +[ -f "$PKG/ipk/luci-app-easymesh_${V}_all.ipk" ] +check "Bundle has main IPK" $? + +[ -f "$PKG/apk/luci-app-easymesh-${V}.apk" ] +check "Bundle has main APK" $? + +[ -f "$PKG/meta-packages/luci-app-easymesh/Makefile" ] +check "Bundle has source" $? + +[ -f "$PKG/meta-packages/wpad-openssl/Makefile" ] +check "Bundle has openssl meta Makefile" $? + +[ -f "$PKG/meta-packages/wpad-wolfssl/Makefile" ] +check "Bundle has wolfssl meta Makefile" $? + +[ -f "$PKG/meta-packages/wpad-mbedtls/Makefile" ] +check "Bundle has mbedtls meta Makefile" $? + +[ -f "$BUNDLE" ] +check "Bundle tarball exists" $? + +if [ -f "$BUNDLE" ]; then + tar tzf "$BUNDLE" >/dev/null 2>&1 + check "Bundle tarball valid" $? +fi + +# ============================================================ +echo "" +echo "============================================================" +echo "RESULT: $PASS passed, $FAIL failed" +echo "============================================================" + +[ $FAIL -eq 0 ] && exit 0 || exit 1 diff --git a/luci-app-easymesh/docs/bn/README.md b/luci-app-easymesh/docs/bn/README.md new file mode 100644 index 000000000..07b58b14a --- /dev/null +++ b/luci-app-easymesh/docs/bn/README.md @@ -0,0 +1,1181 @@ +🌐 [**English**](https://github.com/arafatrahmanzami/luci-app-easymesh) · **বাংলা** + +# luci-app-easymesh + +OpenWrt/ImmortalWrt-এর জন্য একটি EasyMesh প্লাগইন — একটি হালকা LuCI (OpenWrt/ImmortalWrt Router-এর ওয়েব/তথ্যপাতা ভিত্তিক নিয়ন্ত্রণাতল) যা 802.11s (IEEE বেতার মেশ মান) বেতার এবং তারযুক্ত ব্যাকহলের/মূলসংযোগ সাথে Batman-adv/অগ্রবর্তী-জালক লেয়ার-২ /দ্বিতীয়-স্তর মেশ নেটওয়ার্ক/জালকবিন্যাস স্থাপনকে সহজ করে। এটি হটস্পট/সংযোগস্থল বা VPN (ছদ্মজাল) ছাড়াই স্বতন্ত্রভাবে ব্যবহার করা যায়। + +**প্রকাশ:** `2.4.1-r1` — ২০২৬-০৯-১৬ — [@arafatrahmanzami](https://github.com/arafatrahmanzami) কর্তৃক + +সীমিত দক্ষতা নিয়ে, আমি `kmod-batman-adv` + 802.11s তারযুক্ত + বেতার ব্যাকহলের উপর ভিত্তি করে একটি মেশ LuCI সেটআপ প্লাগইন লিখেছি। দ্রুত এবং স্বয়ংক্রিয় সেটিংসের সংযোজন সেটআপকে আরও সুবিধাজনক করে তোলে। প্লাগইনটি শুধুমাত্র OpenWrt এবং ImmortalWrt 24 এবং 25-এ পরীক্ষা করা হয়েছে। এটি খুব খারাপভাবে লেখা কিন্তু আপাতত ব্যবহারযোগ্য। প্রয়োজনে উপলব্ধ উৎস কোড দিয়ে কোনো সমস্যা রিপোর্ট বা সমাধান করুন। + +--- + +## এটি আসলে কী করে? + +ধরুন আপনার দুটি বা তার বেশি WiFi রাউটার আছে। সাধারণত তারা **আলাদা** WiFi নেটওয়ার্ক তৈরি করে — আপনি যখন ঘর থেকে ঘরে যান, আপনার ফোনকে সংযোগ বিচ্ছিন্ন করে আবার সংযুক্ত হতে হয়। + +**EasyMesh** তাদের **একটি একক WiFi নেটওয়ার্ক** হিসেবে কাজ করায়। আপনি ঘরে চলাফেরা করার সময় আপনার ফোন সংযুক্ত থাকে — কোনো সংযোগ বিচ্ছিন্নতা নেই, কোনো পুনঃসংযোগ নেই। + +এটি আপনাকে **ইথারনেট কেবল দিয়ে রাউটারগুলিকে সংযুক্ত** করার সুবিধাও দেয় — মেশ কেবল ব্যবহার করে যখন সম্ভব, বেতার সংযোগ ব্যবহার করে যখন কেবল উপলব্ধ নয়, এবং স্বয়ংক্রিয়ভাবে তাদের মধ্যে পরিবর্তন হয়। + +**আপনার এটির প্রয়োজন যদি:** + +- ✅ আপনার OpenWrt / ImmortalWrt চালিত ২+ রাউটার থাকে +- ✅ আপনি পুরো বাড়িতে একটি নিরবচ্ছিন্ন WiFi নাম চান +- ✅ আপনি কর্মক্ষমতা না হারিয়ে দূরের ঘরে WiFi প্রসারিত করতে চান +- ✅ আপনার একটি অতিথি নেটওয়ার্ক আছে যা সর্বত্র পৌঁছানো উচিত + +**আপনার এটির প্রয়োজন নেই যদি:** + +- ❌ আপনার শুধুমাত্র একটি রাউটার আছে +- ❌ আপনি কাস্টম ফার্মওয়্যার ইনস্টল করতে চান না +- ❌ আপনি প্রতি রাউটারে আলাদা WiFi নামে সন্তুষ্ট + +--- + +## সূচিপত্র + +- [এটি আসলে কী করে?](#এটি-আসলে-কী-করে) +- [কেন EasyMesh?](#কেন-easymesh) +- [মূল বৈশিষ্ট্য](#মূল-বৈশিষ্ট্য) +- [পূর্বশর্ত](#পূর্বশর্ত) +- [শুরু করার আগে](#শুরু-করার-আগে) +- [ইনস্টলেশন](#ইনস্টলেশন) +- [ইনস্টলেশনের পরে](#ইনস্টলেশনের-পরে) +- [দ্রুত শুরু](#দ্রুত-শুরু) +- [বিস্তারিত সেটআপ](#detailed-setup-anchor) +- [Dumb AP / ক্লায়েন্ট নোডের সাথে কাজ করা](#dumb-ap--ক্লায়েন্ট-নোডের-সাথে-কাজ-করা) +- [সংযোগ অগ্রাধিকার সমন্বয় ](#সংযোগ-অগ্রাধিকার-সমন্বয়) +- [তারযুক্ত ব্যাকহল — নিরাপত্তা, ফলব্যাক ও কার্যক্রম](#wired-backhaul-anchor) +- [মেশ জুড়ে অতিথি / IoT নেটওয়ার্ক প্রসারিত করা (VLAN)](#vlan-anchor) +- [আরও তথ্য — ব্যবহারের পরিস্থিতি](#আরও-তথ্য--ব্যবহারের-পরিস্থিতি) +- [সমস্যা সমাধান](#সমস্যা-সমাধান) +- [পরিবর্তন বিবরণী](#পরিবর্তন-বিবরণী) +- [উৎস থেকে সংকলন](#compile-anchor) +- [কৃতিত্ব](#কৃতিত্ব) +- [পরিভাষা অভিধান](#পরিভাষা-অভিধান) +- [লাইসেন্স](#লাইসেন্স) + +--- + +## কেন EasyMesh? + +এটি অফিসিয়াল EasyMesh প্রকাশের প্রথম শাখা। এটি তারযুক্ত এবং/অথবা বেতার ব্যাকহলের সাথে Batman-adv লেয়ার-২ মেশ নেটওয়ার্ক স্থাপন এবং কনফিগার করা সহজ করে এবং সেকেন্ডের মধ্যে মেশ সার্ভার এবং ক্লায়েন্ট নোড কনফিগার করার জন্য একটি সহজে ব্যবহারযোগ্য LuCI ইন্টারফেস প্রদান করে। + +আপনি "OpenLink Captive Portal" বা WireGuard দিয়ে হটস্পট / VPN LAN + WiFi কভারেজ প্রসারিত করছেন, একটি নিরবচ্ছিন্ন হোম মেশ নেটওয়ার্ক তৈরি করছেন, বা হোমল্যাব সেটআপ জুড়ে একাধিক নোড পরিচালনা করছেন — EasyMesh Batman-adv মেশ WiFi স্থাপনকে অতি-দ্রুত এবং অনায়াস করতে তৈরি। + +এটি WireGuard / TorGuard-এর WireGuard অ্যাপ্লিকেশনগুলির সাথে OpenWrt/ImmortalWrt `OpenLink-Captive-Portal`, `wireguard` / `torguardvpn` ব্যবহার করে `batman-adv` (Batman-adv = Better Approach To Mobile Adhoc Networking — Advanced) দ্বারা চালিত, একটি হটস্পটের পরিসর প্রসারিত করার জন্য একটি উন্নত তারযুক্ত + WiFi মেশ স্থাপন করাও দ্রুত এবং সহজ করে তোলে। + +**EasyMesh আদর্শ:** + +- ✅ OpenWrt-এ একটি বিশুদ্ধ মেশের জন্য এক-ক্লিক সেটআপ/স্থাপন — LuCI ওয়েব ইন্টারফেসের ভিতরে সার্ভার এবং ক্লায়েন্ট-নোড কনফিগারেশন সহজ করে +- ✔ OpenWrt/ImmortalWrt এবং Batman-adv-এর সাথে সেকেন্ডের মধ্যে একটি সার্ভার/ক্লায়েন্ট তারযুক্ত + WiFi মেশ ব্যাকহল নেটওয়ার্ক স্থাপন — অ্যাক্সেস-পয়েন্ট WiFi এবং মেশ নেটওয়ার্কের জন্য +- ✔ বড় এলাকা জুড়ে VPN / হটস্পট কভারেজ সহ WiFi বা ইথারনেটের মাধ্যমে একাধিক নোডের সাথে সংযোগ প্রসারিত / উন্নত করা +- ✔ বিভিন্ন অবস্থানে ডিভাইস পরিচালনার জন্য WireGuard-এর পিছনে হোমল্যাব সেটআপ সহজ করা + +--- + +## মূল বৈশিষ্ট্য + +- **মেশ কোর:** Batman-adv 802.11s মেশ দ্বি-ব্যান্ড + তারযুক্ত ব্যাকহল ফলব্যাক এবং লুপ প্রতিরোধ সহ (সার্ভার এবং ক্লায়েন্ট উভয় ভূমিকাতেই উপলব্ধ) +- **স্প্লিট-ব্যান্ড ক্লায়েন্ট SSID** (Service Set Identifier — WiFi নেটওয়ার্কের নাম; স্টিকি-2.4 GHz ক্লায়েন্ট প্রতিরোধ করে) +- **ব্যাকহল সমর্থন:** মিশ্র তারযুক্ত এবং বেতার সংযোগের জন্য `kmod-batman-adv` এবং 802.11s একত্রিত করে +- **তারযুক্ত ব্যাকহল পোর্ট হ্যান্ডলিং:** প্রয়োগের সময় পোর্ট স্বয়ংক্রিয়ভাবে LAN ব্রিজ থেকে সরানো হয় +- **সংযোগ অগ্রাধিকার:** তারযুক্ত → 6 GHz → 5 GHz → 2.4 GHz (`throughput_override`) +- **GUI-সম্পাদনযোগ্য সংযোগ অগ্রাধিকার** — আর কোনো হার্ডকোড করা মান নেই; UI-তেই প্রতিটি ব্যান্ডের ওজন সেট করুন +- **বন্ডিং সক্রিয়** একযোগে বহু-সংযোগ ট্র্যাফিকের জন্য +- **স্বয়ংক্রিয় ব্যান্ড শনাক্তকরণ** (ফ্রিকোয়েন্সি-ভিত্তিক, যেকোনো রেডিও ক্রমে কাজ করে) +- **নিরাপত্তা:** নিরাপদ নেটওয়ার্কের জন্য WPA3 প্রমাণীকরণ, বা খোলা নেটওয়ার্ক +- **WPS / মোড-বাটন পেয়ারিং** `/etc/rc.wps/` পরিবেশকের মাধ্যমে +- **IP পরিবর্তনের জন্য ৬০-সেকেন্ডের রোলব্যাক ওয়াচডগ** +- **ক্লায়েন্ট নোডের জন্য AP মোড (Dumb AP)** — Dumb AP মেশ ক্লায়েন্ট/স্লেভ নোডগুলি তাদের LAN পোর্টের মাধ্যমে ইন্টারনেট অ্যাক্সেসও প্রদান করতে পারে +- **অন্তর্নির্মিত Mesh Status ড্যাশবোর্ড** — প্রতিবেশী নোড, সর্বশেষ দেখা সময়, ইন্টারফেস এবং আরও দেখুন +- **লাইভ RX/TX লেটেন্সি এবং ব্যান্ডউইথ/সংযোগ-গতি** বেতার এবং তারযুক্ত সংযোগ এবং সঙ্গীদের জন্য (৩টি পিং-এর উপর স্মুথ করা) + - দ্রষ্টব্য: এখনও সম্পূর্ণ লাইভ ফিল্ড আপডেট নয় — পেজ রিফ্রেশ প্রয়োজন +- **Priority Weight কলাম** বেতার সঙ্গীদের জন্য প্রদর্শিত (পূর্বে শুধুমাত্র তারযুক্ত) +- **প্রতিবেশী MAC + সর্বশেষ-দেখা** তারযুক্ত ব্যাকহলের জন্য (batman originator টেবিল থেকে) +- **মেশ VLAN** — 802.1Q-এর মাধ্যমে মেশ জুড়ে অতিথি / IoT / হটস্পট নেটওয়ার্ক প্রসারিত করুন +- **সার্ভার এবং ক্লায়েন্টের জন্য স্বয়ংক্রিয় ফায়ারওয়াল ও ইন্টারফেস কনফিগারেশন** +- **নমনীয় নোড অ্যাড্রেসিং:** DHCP (Dynamic Host Configuration Protocol), Static IP এবং Dumb AP মোড +- **রোমিং অপ্টিমাইজেশন:** দ্রুত ক্লায়েন্ট রোমিংয়ের জন্য `dawn` / `usteer` ডেমন সমর্থন সহ 802.11k/v/r (দ্রুত-রোমিং মান) একত্রিত করে + - উন্নত K/V/R সেটিংস + Mobility Domain + - `dawn` বা `usteer` এর একটির নিজস্ব luci-app ব্যবহার করলে ডিভাইস/অ্যাপ্লিকেশনের মধ্যে স্যুইচ করার সময় লেটেন্সি উল্লেখযোগ্যভাবে হ্রাস পায় +- **হটপ্লাগ + init + ঐচ্ছিক cron ফলব্যাক** সংযোগ অগ্রাধিকারের জন্য +- **VPN সামঞ্জস্য:** VPN-ভিত্তিক মেশ নেটওয়ার্কের জন্য TorGuard-এর WireGuard এবং অন্যান্য VPN OpenWrt অ্যাপের সাথে সহজ একীকরণ +- **ভেরিয়েন্ট:** openssl / wolfssl / mbedtls + +--- + +## পূর্বশর্ত + +সঠিকভাবে কাজ করার জন্য, অ্যাপটির সাধারণত নিম্নলিখিত অন্তর্নিহিত প্যাকেজগুলির প্রয়োজন: + +- OpenWrt / ImmortalWrt 24.10+ +- `kmod-batman-adv`, `batctl-default`, `luci-proto-batman-adv`, `kmod-cfg80211` (কার্নেল মডিউল লোড করা) +- `wpad-mesh-openssl` / `wpad-mesh-wolfssl` / `wpad-mesh-mbedtls` এর একটি (WPA3 এনক্রিপশনের জন্য) +- `dawn` / `usteer` এর একটি (AP স্টিয়ারিং / দ্রুত রোমিংয়ের জন্য) +- `luci-compat` (যদি অনুপস্থিত Lua রানটাইম নির্ভরতা দেখা যায়) + +**হার্ডওয়্যার প্রয়োজনীয়তা:** 802.11s মেশ সমর্থনকারী বেতার নেটওয়ার্ক কার্ড সহ রাউটার/ডিভাইস (যেমন MediaTek MT76, Qualcomm Atheros ইত্যাদি)। + +--- + +## শুরু করার আগে + +ইনস্টল করার **আগে** এই পাঁচটি প্রশ্নের উত্তর দিন: + +- [ ] **আমার কি OpenWrt বা ImmortalWrt চালিত ২+ রাউটার আছে?** — না হলে, প্রথমে OpenWrt ইনস্টল করুন। +- [ ] **আমি আমার রাউটারের IP ঠিকানা জানি?** — সাধারণত `192.168.1.1` বা `192.168.10.1`। আপনার ফোনের WiFi সেটিংসে "Router" বা "Gateway" এর অধীনে দেখুন। +- [ ] **আমার SSH অ্যাক্সেস আছে?** — আপনার PC থেকে `ssh root@` চেষ্টা করুন। পাসওয়ার্ড LuCI লগইনের মতোই। +- [ ] **আমার মূল রাউটারে একটি কার্যকরী ইন্টারনেট সংযোগ আছে?** — ইনস্টল প্যাকেজ ডাউনলোড করে। +- [ ] **আমি আমার বর্তমান কনফিগারেশন ব্যাকআপ করেছি?** — LuCI-তে: **System → Backup / Flash Firmware → Generate archive**। প্রতিটি রাউটারে এটি করুন। + +যদি পাঁচটি ✅ হয়, তবে এগিয়ে যান। যদি কোনোটি ❌ হয়, প্রথমে সেটি ঠিক করুন। + +--- + +## ইনস্টলেশন + +সম্পূর্ণভাবে `/tmp` থেকে এই কমান্ডগুলি চালান — মূল ফাইলসিস্টেমে কিছু জমা হবে না। + +**আর্কিটেকচার শনাক্তকরণের প্রয়োজন নেই।** EasyMesh হল `PKG_ARCH=all` — প্রতিটি রাউটার CPU-তে কাজ করে (x86, MIPS, ARM, AArch64, RISC-V)। + +শুধুমাত্র OpenWrt/ImmortalWrt ব্যবহার করে আপনি দ্রুত একটি সম্পূর্ণ স্ট্যান্ডার্ড মেশ বেতার নেটওয়ার্ক তৈরি করতে পারেন। + +### ১. wpad পরীক্ষা করুন — আপনার কি নির্দিষ্ট wpad ভেরিয়েন্ট দরকার? + +EasyMesh-এর 802.11s ব্যাকহল তৈরি করতে একটি **মেশ-সক্ষম wpad** (Wireless Protected Access Daemon) প্রয়োজন। বেশিরভাগ রাউটারে ইতিমধ্যে একটি ইনস্টল করা আছে। প্রথমে পরীক্ষা করুন: + +```sh +# রাউটারে — EasyMesh ইনস্টল করার আগে এটি চালান +opkg list-installed 2>/dev/null | grep wpad +# বা OpenWrt 25.12+ এর জন্য: +apk list --installed 2>/dev/null | grep wpad +``` + +**আউটপুট ব্যাখ্যা:** + +| আপনি যা দেখেন | কী করবেন | +|---|---| +| `wpad-mesh-openssl`, `wpad-mesh-wolfssl`, বা `wpad-mesh-mbedtls` | ✅ আপনার কাছে ইতিমধ্যে আছে। **ধাপ ২-এ যান।** | +| `wpad-openssl`, `wpad-wolfssl`, `wpad-mbedtls` ("mesh" ছাড়া) | ⚠️ একটি মেশ ভেরিয়েন্ট ইনস্টল করুন — ধাপ ১খ দেখুন | +| `wpad-basic`, `wpad-basic-mbedtls`, বা `wpad-mini` | ❌ মেশ করতে পারে না। একটি মেশ ভেরিয়েন্ট ইনস্টল করুন — ধাপ ১খ দেখুন | +| কিছুই না (কোনো wpad নেই) | ❌ একটি মেশ ভেরিয়েন্ট ইনস্টল করুন — ধাপ ১খ দেখুন | + +### ১খ. একটি wpad ভেরিয়েন্ট ইনস্টল করুন (শুধুমাত্র প্রয়োজনে) + +**একটি** ভেরিয়েন্ট বাছুন। আপনার ডিস্ট্রিবিউশন ডিফল্টের সাথে মিলান: + +| ভেরিয়েন্ট | জন্য সুপারিশ | +|---|---| +| `wpad-openssl` | বেশিরভাগ OpenWrt বিল্ড (ডিফল্ট TLS) | +| `wpad-wolfssl` | ImmortalWrt 25.12+ (ডিফল্ট TLS) | +| `wpad-mbedtls` | OpenWrt 24.10 ডিফল্ট যখন openssl অনুপলব্ধ | + +```sh +# opkg (OpenWrt 24.10 এবং তার পুরানো সংস্করণ) +opkg install wpad-openssl # বা wpad-wolfssl / wpad-mbedtls +opkg remove wpad-basic-mbedtls # যদি আপনি একটি basic ভেরিয়েন্ট প্রতিস্থাপন করছেন + +# apk (OpenWrt 25.12+) +apk add wpad-openssl # বা wpad-wolfssl / wpad-mbedtls +apk del wpad-basic-mbedtls # যদি আপনি একটি basic ভেরিয়েন্ট প্রতিস্থাপন করছেন +``` + +wpad ইনস্টল বা প্রতিস্থাপনের পরে একবার রিবুট করুন: + +```sh +reboot +``` + +> **দ্রষ্টব্য:** একই সময়ে দুটি মেশ-সক্ষম wpad ভেরিয়েন্ট ইনস্টল করবেন না — তারা দ্বন্দ্ব করে। ভেরিয়েন্ট পরিবর্তন করলে, প্রথমে পুরানটি সরান। + +### ২. EasyMesh ইনস্টল করুন — যেকোনো একটি পদ্ধতি বেছে নিন + +#### বিকল্প ক — মেটা-প্যাকেজ (wpad সহ) — দ্রুততম পথ + +এগুলি একবারে অ্যাপ **এবং** wpad ইনস্টল করে। যদি আপনি নিশ্চিত না হন, এটি ব্যবহার করুন। + +```sh +# OpenWrt ≤ 24.10 (opkg) — একটি বাছুন +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk && opkg install luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk + +# OpenWrt ≥ 25.12 (apk) — একটি বাছুন +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-wpad-openssl-2.4.1-r1.apk && apk add --allow-untrusted luci-app-easymesh-wpad-openssl-2.4.1-r1.apk +``` + +ভিন্ন ভেরিয়েন্ট পছন্দ করলে URL এবং প্যাকেজ নাম উভয়েই `openssl` কে `wolfssl` বা `mbedtls` দিয়ে প্রতিস্থাপন করুন। + +#### বিকল্প খ — শুধুমাত্র মূল EasyMesh প্যাকেজ ইনস্টল করুন + +**আপনি যদি উপরে একটি `-wpad-*` মেটা-প্যাকেজ ইনস্টল করে থাকেন তবে এই ধাপটি এড়িয়ে যান — এতে মূল অ্যাপ রয়েছে।** + +**OpenWrt ≤ 24.10 — opkg** + +```sh +cd /tmp && \ +opkg update && \ +wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh_2.4.1-r1_all.ipk && \ +opkg install luci-app-easymesh_2.4.1-r1_all.ipk && \ +rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* && \ +/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart +``` + +**OpenWrt ≥ 25.12 — apk** + +```sh +cd /tmp && \ +wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-2.4.1-r1.apk && \ +apk add --allow-untrusted luci-app-easymesh-2.4.1-r1.apk && \ +rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* && \ +/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart +``` + +#### বিকল্প গ — ⚡ একক স্বয়ংক্রিয়-শনাক্তকরণ কমান্ড (opkg vs apk) + +এই এক-লাইনটি চালান। এটি কোনটি উপস্থিত তার উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে `apk` বা `opkg` বাছাই করে। + +```sh +cd /tmp && \ +if command -v apk >/dev/null 2>&1; then \ + echo "Detected apk — OpenWrt 25.12+" && \ + wget -O easymesh.pkg https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-2.4.1-r1.apk && \ + apk add --allow-untrusted easymesh.pkg; \ +else \ + echo "Detected opkg — OpenWrt 24.10 or older" && \ + opkg update && \ + wget -O easymesh.pkg https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh_2.4.1-r1_all.ipk && \ + opkg install easymesh.pkg; \ +fi && \ +rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* && \ +/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart +``` + +### ৩. ঐচ্ছিক — ভাষা প্যাক + +EasyMesh ডিফল্টভাবে ইংরেজিতে আসে। বাংলা এবং চীনা (সরলীকৃত) অনুবাদ আলাদা প্যাকেজ হিসাবে উপলব্ধ। + +**বাংলা (Bengali)** + +```sh +# opkg (≤ 24.10) +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-i18n-easymesh-bn_26.184.37259.7cad107_all.ipk && opkg install luci-i18n-easymesh-bn_26.184.37259.7cad107_all.ipk + +# apk (≥ 25.12) +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-i18n-easymesh-bn-26.184.37259.7cad107.apk && apk add --allow-untrusted luci-i18n-easymesh-bn-26.184.37259.7cad107.apk +``` + +তারপর UI ভাষা পরিবর্তন করুন: **System → System → Language and Style → Language → বাংলা (Bengali)**। + +**চীনা (সরলীকৃত) / 简体中文** + +```sh +# opkg (≤ 24.10) +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-i18n-easymesh-zh-cn_26.184.37259.7cad107_all.ipk && opkg install luci-i18n-easymesh-zh-cn_26.184.37259.7cad107_all.ipk + +# apk (≥ 25.12) +cd /tmp && wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-i18n-easymesh-zh-cn-26.184.37259.7cad107.apk && apk add --allow-untrusted luci-i18n-easymesh-zh-cn-26.184.37259.7cad107.apk +``` + +তারপর UI ভাষা পরিবর্তন করুন: **System → System → Language and Style → Language → 中文 (Chinese)**। + +> **ফাইলনাম সম্পর্কে নোট:** GitHub রিলিজ asset নামের `~` কে `.` দিয়ে প্রতিস্থাপন করে। উপরের i18n সংস্করণ স্ট্রিং `.7cad107` ব্যবহার করে — ডাউনলোড URL এই ফর্মটি ব্যবহার করতেই হবে। প্যাকেজ মেটাডেটা (সংস্করণ `26.184.37259~7cad107`) অপরিবর্তিত থাকে। + + + +### ৪. অফলাইন ipk / apk/ tarball ইনস্টল (রাউটারে ইন্টারনেট ছাড়া) + +**বিকল্প ১ — OpenWrt UI-এর মাধ্যমে ইনস্টল করুন** + +*ক .ipk +Releases/প্রকাশ বিভাগ থেকে সর্বশেষ `luci-app-easymesh_*_all.ipk` ডাউনলোড করুন। OpenWrt-এর LuCI UI-তে **System → Software** এ যান। **Upload Package** ক্লিক করুন, `luci-app-easymesh_*_all.ipk` নির্বাচন করুন, এবং ইনস্টল করুন। + +*খ .apk +`বিকল্প ২খ` অনুসরণ করুন , শুধুমাত্র ডিফল্ট LuCI সফটওয়্যার ট্যাবগুলো ব্যবহার করে এটি করার কোনো সরাসরি 'এক-ক্লিক' উপায় নেই। + +লুসি (LuCI) ওয়েব ফ্রন্টএন্ড উদ্দেশ্যপ্রণোদিতভাবেই অনির্ভরযোগ্য লোকাল .apk ইনস্টলেশন প্রতিরোধ করে। +কারণ এর সিস্টেম > সফটওয়্যার (System > Software) মেনুর মাধ্যমে আপনার আপলোড করা ফাইলগুলো প্রসেস করার সময় ব্যাকএন্ডে --allow-untrusted এর মতো কোনো ফ্ল্যাগ বা অনুমতি থাকে না, যার ফলে এটি একটি সিগনেচার ত্রুটি (signature error) দেখায় এবং ইনস্টলেশন প্রক্রিয়াটি বন্ধ করে দেয়। + + +**বিকল্প ২ — CLI (SSH / টার্মিনাল)-এর মাধ্যমে ইনস্টল করুন** +পিসিতে WinSCP / FileZilla অথবা স্মার্টফোনে Solid Explorer বা Documents by Readdle-এর মতো একটি মোবাইল SFTP ম্যানেজার ব্যবহার করে আপনার ডাউনলোড করা .ipk/.apk প্যাকেজটি (এবং এর জন্য প্রয়োজনীয় অন্যান্য নির্ভরশীল .ipk/ .apk ফাইলগুলো) রাউটারের /tmp ফোল্ডারে কপি বা স্থানান্তর করুন। +ফাইলগুলো প্রস্তুত করার পর রাউটারে SSH দিয়ে প্রবেশ করে সেগুলো ইনস্টল করার কমান্ড: + +২ক OpenWrt ≤ 24.10 (opkg) +রাউটারে + +```sh +```sh +cd /tmp +opkg install luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk +opkg install luci-app-easymesh_2.4.1-r1_all.ipk +# optional +opkg install luci-i18n-easymesh-bn_26.184.37259.7cad107_all.ipk +/etc/init.d/uhttpd restart +``` + +২খ OpenWrt ≥ 25.12 (apk) +রাউটারে + +```sh +cd /tmp +apk --allow-untrusted add /tmp/luci-app-easymesh-wpad-wolfssl-2.4.1-r1.apk +apk --allow-untrusted add /tmp/luci-app-easymesh-2.4.1-r1.apk +# optional +apk --allow-untrusted add /tmp/luci-i18n-easymesh-bn-26.184.37259.7cad107.apk +/etc/init.d/uhttpd restart +``` + +**বিকল্প ৩ — Tarball ইনস্টল** + +আপনার PC-তে ডাউনলোড করুন, রাউটারের /tmp ফোল্ডারে কপি বা স্থানান্তর করুন। এক্সট্র্যাক্ট করুন + +```sh +# On PC +wget https://github.com/arafatrahmanzami/luci-app-easymesh/releases/download/2.4.1-r1/luci-app-easymesh-2.4.1-r1-full.tar.gz +scp luci-app-easymesh-2.4.1-r1-full.tar.gz root@192.168.1.1:/tmp/ +``` + +-অথবা অনুলিপি / স্থানান্তর + +বিকল্প ২ এর মতো একটি SFTP ম্যানেজার ব্যবহার করে পূর্বে ডাউনলোড করা tar.gz ফাইল টি, রাউটারের /tmp ফোল্ডারে কপি বা স্থানান্তর করুন। +ফাইল প্রস্তুত করার পর রাউটারে SSH দিয়ে প্রবেশ করে ইনস্টল করার কমান্ড: + +রাউটারে + +```sh +# On router +cd / && tar xzf /tmp/luci-app-easymesh-2.4.1-r1-full.tar.gz +chmod +x /etc/init.d/easymesh /etc/hotplug.d/iface/30-easymesh-priority /etc/rc.wps/easymesh-pair +rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* +/etc/init.d/rpcd restart +/etc/init.d/uhttpd restart +``` + +### উৎস কোড (ক্রমানুদেশন) + +- [`.zip` ডাউনলোড করুন](https://github.com/arafatrahmanzami/luci-app-easymesh/archive/refs/tags/2.4.1-r1.zip) +- [`.tar.gz` ডাউনলোড করুন](https://github.com/arafatrahmanzami/luci-app-easymesh/archive/refs/tags/2.4.1-r1.tar.gz) + +--- + +## ইনস্টলেশনের পরে + +1. LuCI খুলুন: `http:///cgi-bin/luci/` +2. **Network → EasyMesh** এ যান +3. একটি ভূমিকা (**Server**, **Client**, **Node**) এবং একটি প্রিসেট বাছুন +4. **Save & Apply** ক্লিক করুন +5. যদি মেনু না দেখায়, হার্ড-রিফ্রেশ করুন: **Ctrl+Shift+R** + +**কী ইনস্টল হয়:** + +| পাথ | উদ্দেশ্য | +|---|---| +| `/usr/lib/lua/luci/controller/easymesh.lua` | LuCI কন্ট্রোলার | +| `/usr/lib/lua/luci/model/cbi/easymesh.lua` | কনফিগারেশন UI | +| `/etc/init.d/easymesh` | init লিপি (bat0, mesh VIFs, তারযুক্ত ব্যাকহল, অগ্রাধিকার) | +| `/etc/config/easymesh` | UCI (Unified Configuration Interface) কনফিগার | +| `/etc/hotplug.d/iface/30-easymesh-priority` | ইন্টারফেস চালু হলে সংযোগ অগ্রাধিকার পুনঃপ্রয়োগ | +| `/etc/rc.wps/easymesh-pair` | WPS বোতাম পরিবেশক | +| `/etc/uci-defaults/luci-easymesh` | প্রথম-চালনার সেটআপ | +| `/usr/share/luci/menu.d/luci-app-easymesh.json` | মেনু এন্ট্রি | +| `/usr/share/rpcd/acl.d/luci-app-easymesh.json` | ACL অনুমতি | + +--- + +## দ্রুত শুরু + +1. **সার্ভার** (ইন্টারনেট আছে): EasyMesh → role: **Server**, band mode: **Dual-band** +2. **ক্লায়েন্ট** (এক্সটেন্ডার): role: **Client**, একই **Mesh ID**, **AP Mode: ON**, static IP +3. **উভয়ে**: একই পাসওয়ার্ড, **K/V/R** চালু + +**দ্রুত সেটআপ পূর্বনির্ধারিত** — একটি ভূমিকা বাছাই করুন, একটি বোতাম ক্লিক করুন, সম্পন্ন: + +![Quick Setup presets 1-9](../screenshots-bn/02-quick-setup-1-9_bn.png) + +![Quick Setup presets 10-19](../screenshots-bn/03-quick-setup-10-19_bn.png) + +উভয় রাউটারে Save & Apply করার পর প্রায় ৩০ সেকেন্ড অপেক্ষা করুন। সার্ভারে **Network → EasyMesh** খুলুন — আপনার সঙ্গী টেবিলে ক্লায়েন্টের MAC দেখা উচিত: + +![EasyMesh dashboard](../screenshots-bn/01-dashboard_bn.png) + +--- + + +## বিস্তারিত সেটআপ — মৌলিক মেশ নেটওয়ার্ক (১ সার্ভার + ২ নোড) + +### ধাপ ১ — জালক সার্ভার সেটআপ করুন + +1. OpenWrt-এ (**Network → Wireless**) যেকোনো সক্রিয় বেতার নেটওয়ার্ক নিষ্ক্রিয়/মুছুন। +2. **Network → EasyMesh**-এ যান এবং Mesh Mode-এর জন্য **"Server"** নির্বাচন করুন। + + ![Mesh settings](../screenshots-bn/04-mesh-settings_bn.png) + +3. আপনার WiFi SSID লিখুন (এটি মূল WiFi নেটওয়ার্ক যা সমস্ত ডিভাইস সংযুক্ত হবে)। +4. নিয়মিত AP-এর জন্য WiFi রেডিও নির্বাচন করুন। (সুপারিশ: সেরা পারফরম্যান্সের জন্য মেশ ব্যাকহল থেকে ভিন্ন রেডিও ব্যবহার করুন।) +5. মেশ রেডিও নির্বাচন করুন এবং একটি পৃথক SSID লিখুন। (অ্যাপ স্বয়ংক্রিয়ভাবে আপনার মেশ SSID-তে `-mesh` যোগ করে।) +6. Password Protection সক্রিয় করুন, একটি Mesh Password লিখুন এবং **Save & Apply** ক্লিক করুন। +7. **"Reapply EasyMesh Settings"** ক্লিক করুন APs স্থাপন এবং মেশ নেটওয়ার্কিং সক্রিয় করতে। + +**সেটআপ যাচাই করুন:** + +- **Network → Wireless** এ যান WiFi নেটওয়ার্ক যোগ হয়েছে কিনা দেখতে। +- **Network → Interfaces** এ যান Batman (`bat0`) ডিভাইস এবং `mesh_batman` ইন্টারফেস যোগ হয়েছে কিনা নিশ্চিত করতে। + +### ধাপ ২ — একটি মেশ নোড সেটআপ করুন + +1. দ্বিতীয় রাউটারে **Network → EasyMesh** এ যান। +2. Mesh Mode-এর জন্য **"Client"** নির্বাচন করুন। +3. সার্ভারের মতো একই WiFi SSID, Mesh SSID এবং Password লিখুন। +4. WiFi SSID এবং Mesh SSID উভয়ের জন্য একই WiFi রেডিও টাইপ (AX, AC, b/g/n) নির্বাচন করছেন তা নিশ্চিত করুন। +5. **Save & Apply** ক্লিক করুন, তারপর **"Reapply EasyMesh Settings"** ক্লিক করুন। +6. **AP Mode** ট্যাবে যান এবং একটি হোস্টনেম নির্বাচন করুন (যেমন `node2`, `node3`)। + + ![Client WiFi + AP Mode](../screenshots-bn/05-client-wifi-ap-mode_bn.png) + +7. **DHCP** (Dumb AP নোডের জন্য সুপারিশকৃত) সেট করুন অথবা আপনার Mesh Server-এর মতো একই রেঞ্জে একটি Static IP কনফিগার করুন। +8. **Save & Apply** ক্লিক করুন, তারপর **"Enable Dumb AP Mode"** ক্লিক করুন। + +### ধাপ ৩ — অতিরিক্ত মেশ নোডের জন্য পুনরাবৃত্তি করুন + +- প্রতিটি নোডের জন্য একই WiFi SSID, Mesh SSID এবং Password ব্যবহার করুন। +- সমস্ত নোড একই WiFi রেডিও টাইপ (AX, AC, b/g/n) ব্যবহার করছে তা নিশ্চিত করুন। + +### ধাপ ৪ — প্রতিবেশী নোড যাচাই করুন + +- Mesh Server-এ **Network → EasyMesh** এ যান এবং কাছাকাছি নোডগুলি **Mesh Status** এর অধীনে তালিকাভুক্ত কিনা দেখুন। +- **Network → Wireless** এ যান এবং Mesh Backhaul নেটওয়ার্কগুলি সার্ভারের সাথে যোগাযোগ করছে কিনা যাচাই করুন। +- **Status → Overview → DHCP Devices** এর অধীনে আপনার Mesh নোডগুলির IP খুঁজুন। +- ব্রাউজারে এর IP লিখে একটি নোড অ্যাক্সেস করুন। + +### ধাপ ৫ — (ঐচ্ছিক) Mesh Server-এ TorGuard WireGuard VPN সক্রিয় করুন + +1. **Network → TorGuard WireGuard** এ যান। +2. আপনার WireGuard ইউজারনেম ও পাসওয়ার্ড লিখুন। +3. আপনার পছন্দের WireGuard সার্ভার অবস্থান নির্বাচন করুন। +4. WireGuard সক্রিয় করুন এবং **Save & Apply** ক্লিক করুন। +5. সমস্ত মেশ নেটওয়ার্ক ট্র্যাফিক VPN টানেলের মাধ্যমে রুট করতে **"Start WireGuard"** ক্লিক করুন। + +🔥 এখন আপনার OpenWrt Mesh WiFi সম্পূর্ণভাবে সেট আপ হয়েছে! 🚀 + +--- + +## Dumb AP / ক্লায়েন্ট নোডের সাথে কাজ করা + +**Thin / Dumb AP মোড সক্রিয় করার পরে চাইল্ড নোডে লগইন করুন।** + +**Dumb AP মোড সক্রিয় করার পরে একটি নোড পুনরুদ্ধার করুন:** + +যদি আপনি WiFi-এর মাধ্যমে সাইট অ্যাক্সেস করতে না পারেন, তাহলে একটি ইথারনেট কেবল (RJ45 কেবলের মাধ্যমে LAN-to-LAN) ব্যবহার করে সাব-নোডের LAN পোর্ট সরাসরি মাস্টার নোড LAN-এর সাথে সংযুক্ত করুন। তারপর মাস্টার নোডের DHCP তালিকায় সাব-নোডের IP ঠিকানা খুঁজুন এবং ক্লায়েন্ট নোড ব্যাকএন্ডে লগইন করতে আপনার ব্রাউজারে IP লিখুন। + +*স্ট্যাটাস পেজের DHCP সেকশনের অধীনে নোড IP খুঁজুন এবং একটি ব্রাউজারে (Chromium / Firefox / Brave, ইত্যাদি) IP খুলুন।* + +**Mesh বেতার প্যারামিটার পরিবর্তনের সর্বোত্তম অনুশীলন:** + +1. প্রথমে প্রতিটি চাইল্ড নোডের কনফিগারেশন পরিবর্তন করুন। +2. অবশেষে, Mesh মাস্টার নোডের প্যারামিটার / সেটিংস পরিবর্তন / আপডেট করুন। +3. সমস্ত নোড পুনঃসংযোগ করতে সেটিংস পুনঃপ্রয়োগ করুন। সমস্ত ডিভাইস পুনরায় কনফিগার হয় এবং নোড পুনঃসংযোগ সম্পন্ন হয়। + +--- + + + + +## তারযুক্ত ব্যাকহল — নিরাপত্তা, পিছু হটা ও কার্যক্রম + +EasyMesh তারযুক্ত ব্যাকহলকে নিরাপদ এবং স্বয়ংক্রিয় করে তোলে — দুটি নোডের মধ্যে একটি কেবল প্লাগ ইন করুন, মেশ এটি প্রধান পথ হিসেবে ব্যবহার করে। কেবল ব্যর্থ হলে, ম্যানুয়াল হস্তক্ষেপ ছাড়াই বেতার সংযোগে ফলব্যাক করে। + +![Wired backhaul safety — port status, priority state, fallback](../screenshots-bn/06-wired-backhaul-safety_bn.png) + +### প্রয়োগের সময় কার্যক্রম + +- **পোর্ট ফিল্টারিং** — শুধুমাত্র ভৌত `ethN` / `lanN` পোর্ট নির্বাচনযোগ্য। CPU-অভ্যন্তরীণ পোর্ট এবং macvlan ইন্টারফেস লুকানো থাকে, তাই ভুল ইন্টারফেস বেছে নিয়ে রাউটার ভাঙার ঝুঁকি নেই। +- **স্বয়ংক্রিয় ব্রিজ অপসারণ** — নির্বাচিত পোর্ট প্রয়োগের সময় LAN ব্রিজ (`br-lan`) থেকে সরানো হয়। এটি তারযুক্ত এবং মেশ পথের মধ্যে একটি লেয়ার-২ লুপ প্রতিরোধ করে। +- **অগ্রাধিকার নির্ধারণ** — পোর্টটি `throughput_override = 1000000` (1 Gbit) পায়, তাই batman-adv যেকোনো বেতার সংযোগের উপর এটিকে পছন্দ করে। +- **অবস্থা লেবেল** — প্রতিটি পোর্ট সংরক্ষণের আগে `(in bridge - will be removed on apply)` বা `(free)` দেখায়, যাতে আপনি জানেন কী ঘটবে। + +### বেতারে ফলব্যাক + +তারযুক্ত সংযোগ বিচ্ছিন্ন হলে (কেবল খুলে ফেলা, সুইচ রিবুট, পোর্ট ব্যর্থতা): + +- batman-adv কয়েক সেকেন্ডের মধ্যে ক্ষতি সনাক্ত করে +- ট্র্যাফিক স্বয়ংক্রিয়ভাবে 5 GHz (600 Mbit অগ্রাধিকার) বা 2.4 GHz (30 Mbit অগ্রাধিকার) মেশ ব্যাকহলে রুট হয় +- ম্যানুয়াল হস্তক্ষেপ প্রয়োজন নেই + +কেবল ফিরে এলে: + +- তারযুক্ত পোর্ট স্বয়ংক্রিয়ভাবে পুনঃসংযুক্ত হয় +- ট্র্যাফিক তারযুক্তে ফিরে যায় — উপলব্ধ থাকলে তারযুক্ত সর্বদা জেতে + +### নিরাপত্তা + +- **লুপ প্রতিরোধ** — LAN ব্রিজ থেকে তারযুক্ত পোর্ট সরানো তারযুক্ত এবং মেশ পথের মধ্যে একটি লেয়ার-২ লুপ বন্ধ করে, যা অন্যথায় নেটওয়ার্ক প্লাবিত করত। +- **বেতার ব্যাকহলে WPA3** — কনফিগার করা হলে মেশ সংযোগগুলি WPA3 ব্যবহার করে; তারযুক্ত দিকটি কেবল বিশ্বস্ত নোডের মধ্যে একটি সরাসরি L2 সংযোগ। +- **রোলব্যাক ওয়াচডগ** — যদি একটি তারযুক্ত-ব্যাকহল পরিবর্তন সংযোগ বিচ্ছিন্ন করে, একটি ৬০-সেকেন্ডের ওয়াচডগ স্বয়ংক্রিয়ভাবে পূর্বের কার্যকর কনফিগারেশন পুনরুদ্ধার করে। + +### দ্রুত পরীক্ষা + +তারযুক্ত ব্যাকহল সক্রিয় কিনা যাচাই করুন: + +```sh +batctl hardif eth0 throughput_override # 1000.0 MBit প্রত্যাশিত +batctl o # শীর্ষে তারযুক্ত প্রতিবেশী +ip link show eth0 # UP, `master br-lan` নেই +``` + +ব্রিজ থেকে পোর্ট সরানো হয়েছে কিনা নিশ্চিত করুন: + +```sh +brctl show br-lan # eth0 তালিকাভুক্ত থাকা উচিত নয় +``` + +পরীক্ষার জন্য বেতারে ফলব্যাক জোর করুন: + +```sh +ip link set eth0 down +sleep 10 +batctl o # ট্র্যাফিক এখন 5 GHz ব্যবহার করা উচিত +ip link set eth0 up +``` + +--- + + + + + + +## সংযোগ অগ্রাধিকার সমন্বয় + +Batman-adv `throughput_override` দ্বারা সংযোগ গুণমান গুণ করে সেরা পথ বেছে নেয়। EasyMesh UI-তে এই মানগুলি প্রকাশ করে: + +**ডিফল্ট:** + +| ফিল্ড | ডিফল্ট (Kbit/s) | অর্থ | +|---|---|---| +| তারযুক্ত | `1000000` | যেকোনো বেতার সংযোগের উপর তারযুক্ত পছন্দ | +| 6 GHz | `900000` | দ্রুত বেতার, প্রায়-তারযুক্ত | +| 5 GHz | `600000` | স্ট্যান্ডার্ড 5 GHz ব্যাকহল | +| 2.4 GHz | `30000` | ধীর কিন্তু দীর্ঘ পরিসর | + +উচ্চ মান = বেছে নেওয়ার সম্ভাবনা বেশি। শুধু সংযোগ গুণমানের উপর batman সিদ্ধান্ত নিতে `0` সেট করুন। + +**সাধারণ সমন্বয় পরিস্থিতি:** + +- **তারযুক্ত-প্রথম, বেতার-ফলব্যাক (পূর্বনির্ধারিত):** wired=1000000, 5 GHz=600000, 2.4 GHz=30000 +- **শুধু তারযুক্ত জোর করুন** (তারযুক্ত বিচ্ছিন্ন না হওয়া পর্যন্ত বেতার উপেক্ষা করুন): wired=10000000, 5 GHz=100000, 2.4 GHz=10000 +- **5 GHz প্রাথমিক পছন্দ করুন** (বেতার ব্যান্ডউইথ পছন্দ): wired=500000, 5 GHz=900000, 2.4 GHz=20000 +- **দীর্ঘ-পরিসর পছন্দ** (2.4 GHz দেয়ালের মধ্য দিয়ে ভাল): wired=1000000, 5 GHz=400000, 2.4 GHz=200000 + +**রিয়েল টাইমে বর্তমান মান যাচাই করুন:** + +মেশ টেবিলে **Priority weight** কলামটি প্রতিটি ইন্টারফেসে প্রয়োগ করা প্রকৃত `throughput_override` দেখায়, প্রতিটি পেজ লোডে রিফ্রেশ হয়। [দ্রুত শুরু](#দ্রুত-শুরু) বিভাগে ড্যাশবোর্ড স্ক্রিনশট দেখুন। + +**প্রয়োগ করুন:** + +**Save & Apply** ক্লিক করুন, বা চালান: + +```sh +/etc/init.d/easymesh restart +``` + +পরিবর্তন ~১৫ সেকেন্ডের মধ্যে কার্যকর হয়। + +--- + +![Link priority weights + Mesh VLANs](../screenshots-bn/07-priority-vlans_bn.png) + +--- + + +## মেশ জুড়ে অতিথি / IoT নেটওয়ার্ক প্রসারিত করা (VLAN) + +### এটি কী, ৩০ সেকেন্ডে? + +ধরুন আপনার বাড়িতে **তিনটি পৃথক নেটওয়ার্ক** আছে: + +1. **মূল WiFi** — আপনার ফোন, ল্যাপটপ, টিভি +2. **অতিথি WiFi** — ভিজিটররা (আপনার ফাইলগুলিতে অ্যাক্সেস নেই) +3. **IoT WiFi** — স্মার্ট বাল্ব, ক্যামেরা, প্লাগ (নিরাপত্তার জন্য বিচ্ছিন্ন) + +সাধারণত, আপনি যখন একটি মেশ এক্সটেন্ডার যোগ করেন, শুধুমাত্র **মূল নেটওয়ার্ক** এক্সটেন্ডারে ছড়িয়ে পড়ে। এক্সটেন্ডারে সংযুক্ত অতিথিরা এখনও মূল-নেটওয়ার্ক বিশেষাধিকার পায়, যা একটি নিরাপত্তা ফাঁক। + +**EasyMesh-এর Mesh VLANs** (Virtual LANs) বৈশিষ্ট্য তিনটি নেটওয়ার্ককেই মেশ জুড়ে ছড়িয়ে দেয় — যাতে একটি এক্সটেন্ডার নোডে একজন অতিথি এখনও অতিথি থাকে, এবং একটি এক্সটেন্ডারে একটি IoT ডিভাইস এখনও বিচ্ছিন্ন IoT নেটওয়ার্কে থাকে। + + + +### আপনার কি এটি প্রয়োজন? + +| আপনার পরিস্থিতি | VLAN প্রয়োজন? | +|---|---| +| আপনার শুধুমাত্র একটি WiFi নেটওয়ার্ক আছে | ❌ না — এই সেকশনটি এড়িয়ে যান | +| আপনার অতিথি WiFi আছে এবং সমস্ত এক্সটেন্ডারে চান | ✅ হ্যাঁ | +| আপনার স্মার্ট-হোম ডিভাইস পৃথক নেটওয়ার্কে আছে | ✅ হ্যাঁ | +| আপনি একটি হটস্পট / ক্যাপটিভ পোর্টাল চালান | ✅ হ্যাঁ — মেশ জুড়ে ছড়িয়ে দিতে প্রয়োজন | +| আপনি শুধু চান WiFi সর্বত্র কাজ করুক | ❌ না — মেশ ইতিমধ্যে এটি পরিচালনা করে | + +### পূর্বশর্ত + +VLAN কনফিগার করার আগে, আপনার প্রয়োজন: + +- ✅ **একটি কার্যকরী মেশ** — দুটি বা তার বেশি নোড ইতিমধ্যে সংযুক্ত (দ্রুত শুরু দেখুন) +- ✅ **অন্তত একটি অতিরিক্ত নেটওয়ার্ক** ইতিমধ্যে তৈরি — যেমন **Network → Interfaces**-এ একটি `guest` ইন্টারফেস +- ✅ **EasyMesh 2.4.1-r1 বা নতুন** ইনস্টল করা +- ⚠️ আপনি যদি একটি **তারযুক্ত ব্যাকহল** ব্যবহার করেন, আপনার ইথারনেট কেবল 802.1Q-ট্যাগযুক্ত ফ্রেম বহন করতে সক্ষম হতে হবে। সাধারণ আনম্যানেজড সুইচ পূর্বনির্ধারিত ভাবে এটি করে; কিছু পুরানো ম্যানেজড সুইচে VLAN 10/20/etc. পাস করার অনুমতি কনফিগার করতে হবে। + +### ধাপ ১ — আপনার নেটওয়ার্কের VLAN ID খুঁজুন + +যদি আপনার ইতিমধ্যে একটি অতিথি নেটওয়ার্ক থাকে, এটি কোন VLAN নম্বর ব্যবহার করে তা খুঁজে বের করুন: + +**LuCI UI-এর মাধ্যমে:** + +1. **Network → Interfaces** এ যান +2. আপনার অতিথি নেটওয়ার্কে **Edit** ক্লিক করুন +3. **Device** ফিল্ডটি খুঁজুন — এটি `br-guest` বা `br-lan.10` এর মতো বলতে পারে +4. যদি নামে একটি ডটের পরে একটি সংখ্যা থাকে (যেমন `br-lan.10`), সেই সংখ্যাটি হল VLAN ID +5. যদি কোনো সংখ্যা না থাকে, আপনার অতিথি নেটওয়ার্ক পূর্বনির্ধারিত ভাবে ব্যবহার করে — সাধারণত **10** + +**SSH-এর মাধ্যমে:** + +```sh +# তাদের VLAN ID সহ সমস্ত নেটওয়ার্ক ডিভাইসের তালিকা +uci show network | grep -E "8021q|vid" +``` + +উদাহরণ আউটপুট: + +``` +network.guest=interface +network.guest.device='br-lan.10' +network.guest.proto='static' +``` + +এই ক্ষেত্রে, VLAN ID হল **10**। + +> **এখনও একটি অতিথি নেটওয়ার্ক নেই?** প্রথমে **Network → Interfaces → Add new interface**-এ একটি তৈরি করুন → নাম `guest` → প্রোটোকল `Static address` → IP `192.168.30.1/24`। তারপর এখানে চালিয়ে যান। + +### ধাপ ২ — EasyMesh-এ VLAN IDগুলি লিখুন + +1. **Network → EasyMesh** খুলুন +2. **Mesh VLAN** সেকশনে স্ক্রল করুন +3. **VLAN IDs (কমা-বিভক্ত)** ফিল্ডে, আপনি যে VLAN IDগুলি প্রসারিত করতে চান সেগুলি লিখুন + - একক নেটওয়ার্ক: `10` + - দুটি নেটওয়ার্ক: `10,20` + - তিনটি নেটওয়ার্ক: `10,20,30` +4. পেজের নীচে **Save & Apply** ক্লিক করুন + +### ধাপ ৩ — মেশ পুনর্নির্মাণের জন্য অপেক্ষা করুন + +মেশটি নেটওয়ার্ক ডিভাইসগুলি পুনর্নির্মাণ করতে প্রায় **১৫–২০ সেকেন্ড** সময় নেয়। অগ্রগতি দেখুন: + +```sh +# একটি টার্মিনাল খুলুন এবং লগ দেখুন +logread -f | grep easymesh +``` + +আপনি নিম্নলিখিত লাইনগুলি দেখবেন: + +``` +easymesh: Mesh VLAN: bat0.10 created +easymesh: Mesh VLAN 10: attached to br-guest +easymesh: Mesh VLAN: bat0.20 created +easymesh: Mesh VLAN 20: attached to br-iot +``` + +### ধাপ ৪ — এটি কাজ করেছে কিনা যাচাই করুন + +**ডিভাইসগুলি তৈরি হয়েছে কিনা দেখুন:** + +```sh +uci show network | grep 8021q +``` + +প্রত্যাশিত আউটপুট (VLAN 10 এবং 20 এর জন্য): + +``` +network.@device[5].name='bat0.10' +network.@device[5].type='8021q' +network.@device[5].ifname='bat0' +network.@device[5].vid='10' +network.@device[6].name='bat0.20' +network.@device[6].type='8021q' +network.@device[6].ifname='bat0' +network.@device[6].vid='20' +``` + +**ব্রিজগুলি পরীক্ষা করুন:** + +```sh +uci show network | grep -E "br-guest|br-iot" +``` + +অতিথি ব্রিজে `ports='...' bat0.10` এবং IoT ব্রিজে `ports='...' bat0.20` খুঁজুন। + +**এন্ড-টু-এন্ড পরীক্ষা:** + +1. **সার্ভার** নোডে আপনার অতিথি WiFi-তে একটি ডিভাইস সংযুক্ত করুন — এটি একটি `192.168.30.x` IP পায় কিনা নিশ্চিত করুন +2. **একটি ক্লায়েন্ট** নোডে **একই ডিভাইস** অতিথি WiFi-তে সংযুক্ত করুন — এটি **কিন্তু** `192.168.30.x` IP পাওয়া উচিত +3. এটি প্রমাণ করে যে অতিথি VLAN এখন মেশ জুড়ে প্রসারিত হয়েছে + +### সাধারণ পরিস্থিতি + +**শুধুমাত্র অতিথি নেটওয়ার্ক** + +```sh +uci set easymesh.config.mesh_vlans='10' +uci commit easymesh +/etc/init.d/easymesh restart +``` + +**অতিথি + IoT** + +```sh +uci set easymesh.config.mesh_vlans='10,20' +uci commit easymesh +/etc/init.d/easymesh restart +``` + +**অতিথি + IoT + হটস্পট** + +```sh +uci set easymesh.config.mesh_vlans='10,20,30' +uci commit easymesh +/etc/init.d/easymesh restart +``` + +**স্বয়ংক্রিয়-সংযুক্তি — ম্যানুয়াল ব্রিজিং প্রয়োজন নেই** + +যদি আপনার বিদ্যমান ব্রিজগুলি শেষে VLAN ID দিয়ে নামকরণ করা হয়, EasyMesh স্বয়ংক্রিয়ভাবে VLAN ডিভাইস সংযুক্ত করে। স্বীকৃত প্যাটার্ন: + +| ব্রিজের নাম | VLAN এর জন্য স্বয়ংক্রিয়-সংযুক্তি | +|---|---| +| `br-10` | VLAN 10 | +| `br-lan10` | VLAN 10 | +| `br-vlan10` | VLAN 10 | +| `br-guest10` | VLAN 10 | +| `br-iot10` | VLAN 10 | + +উদাহরণ: **Network → Interfaces → Devices**-এ `br-guest` এর নাম পরিবর্তন করে `br-guest10` করুন এবং EasyMesh পরবর্তী রিস্টার্টে স্বয়ংক্রিয়ভাবে `bat0.10` যোগ করবে। ম্যানুয়াল ধাপ নেই। + +### VLAN সমস্যা সমাধান + +**সমস্যা: VLAN ডিভাইস দেখাচ্ছে না** + +```sh +# লিপি কী লগ করেছে দেখুন +logread | grep -i "mesh vlan" | tail -20 +``` + +"Skipping invalid VLAN" বা "Mesh VLAN: bat0.X created" খুঁজুন। যদি লগ তৈরি দেখায় কিন্তু ডিভাইস সেখানে না থাকে: + +```sh +# ম্যানুয়ালি পুনরায় তৈরি করুন +/etc/init.d/network reload +sleep 5 +ip link show bat0.10 +``` + +**সমস্যা: অতিথি ডিভাইস মূল-নেটওয়ার্ক IP (192.168.1.x) পায় অতিথি (192.168.30.x) এর পরিবর্তে** + +ক্লায়েন্ট নোডে ব্রিজে VLAN ডিভাইস নেই। ম্যানুয়ালি যোগ করুন: + +1. **Network → Interfaces → Devices** +2. আপনার অতিথি ব্রিজে (যেমন `br-guest`) **Edit** ক্লিক করুন +3. **Ports** তালিকায় `bat0.10` যোগ করুন +4. **Save & Apply** ক্লিক করুন + +মেশে **প্রতিটি নোডে** এটি করুন। + +**সমস্যা: VLAN সক্রিয় করার পরে তারযুক্ত ব্যাকহল অতিথি ট্র্যাফিক বহন করা বন্ধ করেছে** + +নোডগুলির মধ্যে আপনার ফিজিক্যাল সুইচ ট্যাগগুলি সরিয়ে ফেলছে কিনা দেখুন: + +- আনম্যানেজড সুইচ ব্যবহার করলে — এটি সাধারণত সমস্ত VLAN পাস করে +- ম্যানেজড সুইচ ব্যবহার করলে — পোর্ট 10, 20, 30-এ ট্যাগযুক্ত ট্র্যাফিকের অনুমতি কনফিগার করুন (বা 802.1Q ট্রাঙ্ক মোড ব্যবহার করুন) + +দ্রুত পরীক্ষা: সুইচ বাইপাস করুন। দুটি নোডকে একটি একক ইথারনেট কেবল দিয়ে সরাসরি সংযুক্ত করুন এবং পুনরায় পরীক্ষা করুন। যদি এটি কাজ করে, সুইচটি সমস্যা। + +**সমস্যা: VLAN সক্রিয় করার পরে সার্ভারে WiFi কাজ করে কিন্তু ক্লায়েন্টে কাজ করে না** + +ক্লায়েন্ট নোড পুনর্নির্মাণ হয়নি। এটি জোর করুন: + +```sh +ssh root@ +/etc/init.d/easymesh restart +sleep 15 +uci show network | grep 8021q +``` + +### VLAN নিষ্ক্রিয় করা + +সমস্ত VLAN ডিভাইস সরাতে: + +```sh +uci set easymesh.config.mesh_vlans='' +uci commit easymesh +/etc/init.d/easymesh restart +sleep 15 +uci show network | grep 8021q # কিছুই প্রিন্ট করবে না +``` + +লিপি স্বয়ংক্রিয়ভাবে `bat0`-এ সমস্ত `8021q` ডিভাইস সরিয়ে দেয়। + +### এটি অভ্যন্তরীণভাবে কীভাবে কাজ করে + +যদি আপনি কৌতূহলী হন: + +- প্রতিটি VLAN ID `bat0`-এ একটি **802.1Q সাব-ইন্টারফেস** তৈরি করে — উদাহরণস্বরূপ, `bat0.10` +- Batman-adv স্বচ্ছভাবে মেশ জুড়ে ট্যাগযুক্ত ফ্রেম বহন করে +- 802.1Q সাব-ইন্টারফেস সংশ্লিষ্ট ব্রিজে (যেমন `br-guest`) একটি **পোর্ট** হিসাবে যোগ করা হয় +- এক্সটেন্ডারে থাকা ক্লায়েন্ট ডিভাইস যেগুলি অতিথি AP-তে সংযুক্ত হয় তারা ট্যাগযুক্ত ট্র্যাফিক পায় যা সার্ভারের অতিথি ব্রিজে পৌঁছায় +- ক্লায়েন্ট নোডে অতিরিক্ত কনফিগারেশনের প্রয়োজন নেই — মেশ ট্যাগগুলি এন্ড-টু-এন্ড বিতরণ করে + +এটি ব্যবহার করার জন্য আপনাকে এটি বুঝতে হবে না, তবে ডিবাগিং করার সময় এটি সহায়ক। + +--- + +## আরও তথ্য — ব্যবহারের পরিস্থিতি + +1. **VPN প্রয়োজন নেই।** আপনি শুধুমাত্র OpenWrt/ImmortalWrt রাউটার ব্যবহার করে VPN ছাড়াই একটি স্ট্যান্ডার্ড মেশ তারযুক্ত + বেতার নেটওয়ার্ক সম্পূর্ণভাবে এবং দ্রুত তৈরি / স্থাপন করতে পারেন — সেকেন্ডের মধ্যে। + +2. **শেয়ার্ড-রেডিও অপারেশন।** আপনি একই রেডিও হার্ডওয়্যারে মূল WiFi AP এবং Mesh AP উভয়ই চালাতে পারেন, কিন্তু এটি **সুপারিশ করা হয় না** — কর্মক্ষমতা উল্লেখযোগ্যভাবে হ্রাস পাবে। সর্বোত্তম সমাধান হল প্রতিটি নেটওয়ার্ক একটি স্বাধীন রেডিও ফ্রিকোয়েন্সি ব্যবহার করুক। + - যদি একটি একক শেয়ার্ড ফ্রিকোয়েন্সিতে উভয়ই চালান, নিম্নলিখিত সীমাবদ্ধতা প্রযোজ্য: সীমিত চ্যানেল ব্যান্ডউইথ ও কর্মক্ষমতা হ্রাস করে, এবং কম উপলব্ধ চ্যানেল নেটওয়ার্ক গতি ও স্থিতিশীলতা হ্রাস করে। + - সেই ক্ষেত্রে, Batman-এর উন্নত বৈশিষ্ট্য যেমন bonding এবং fragmentation নিষ্ক্রিয় করতে হবে ওভারহেড / হার্ডওয়্যার লোড কমাতে। + +3. **তারযুক্ত ডিভাইস এবং থার্ড-পার্টি রাউটার।** EasyMesh বেতার Mesh সমর্থন করে না এমন তারযুক্ত ডিভাইস বা থার্ড-পার্টি রাউটারের সাথে সংযোগ করতে পারে। বেতার Mesh কার্যকারিতা ছাড়া একটি তারযুক্ত OpenWrt ডিভাইস (x86 সফট রাউটার) ব্যবহার করলে, **Network → Interfaces** এ যান এবং `mesh_batman` ইন্টারফেস ম্যানুয়ালি `bat0` ভার্চুয়াল ডিভাইসে বাইন্ড করুন (`mesh_batman` ইন্টারফেসে ডিভাইস হিসেবে `bat0` নির্বাচন করুন)। + +4. **WireGuard / VPN।** এই প্লাগইনের সাথে মিলিতভাবে WireGuard (VPN) ব্যবহার করাও সম্ভব। + +--- + +## সমস্যা সমাধান + +### সেটিংস প্রয়োগ করার পরে আমার রাউটারে অ্যাক্সেস হারিয়েছি + +1. **রাউটার আনপ্লাগ করুন।** ১০ সেকেন্ড অপেক্ষা করুন। আবার প্লাগ করুন। +2. **প্রতি ০.৩ সেকেন্ডে রিসেট বোতাম ট্যাপ করুন** যতক্ষণ না LED দ্রুত ব্লিংক করে। +3. **একটি LAN কেবল** আপনার PC থেকে একটি LAN পোর্টে সংযুক্ত করুন। +4. আপনার PC-র IP **192.168.1.2 / 255.255.255.0** সেট করুন। +5. SSH করুন: `ssh root@192.168.1.1` +6. `mount_root` লিখুন +7. সর্বশেষ ব্যাকআপ থেকে পুনরুদ্ধার করুন: + + ```sh + /etc/init.d/easymesh restore + reboot + ``` + +### মেশ ০ সঙ্গী দেখায় + +- উভয় রাউটার WiFi পরিসরের মধ্যে আছে কিনা দেখুন +- **Mesh ID** এবং **password** উভয়েই একই কিনা নিশ্চিত করুন +- উভয় রাউটারে `batctl n` চালান — যদি খালি থাকে, মেশ সংযোগ তৈরি হয়নি +- পরীক্ষা করার আগে রিবুটের পরে ২ মিনিট অপেক্ষা করুন + +### ক্লায়েন্ট নোডে ইন্টারনেট নেই + +- সার্ভারই একমাত্র DHCP চালানো রাউটার হতে হবে (ক্লায়েন্ট নোডগুলি "Dumb AP" মোডে সেট থাকে, যা স্থানীয়ভাবে DHCP নিষ্ক্রিয় করে) +- **Status → Overview** পরীক্ষা করুন — ক্লায়েন্ট নোডের একটি IP আছে? +- ক্লায়েন্ট নোড থেকে `ping 1.1.1.1` চেষ্টা করুন + +### WiFi SSID অদৃশ্য হয়ে যায় + +- **Network → Wireless** পরীক্ষা করুন — ইন্টারফেস সক্রিয়? +- রাউটারে `wifi reload` চালান +- ত্রুটির জন্য `logread | grep hostapd` পরীক্ষা করুন + +### "Priority weight" `?` বা `0.0 MBit` দেখায় + +init লিপি অগ্রাধিকার প্রয়োগ সমাপ্ত করেনি। ৩০ সেকেন্ড অপেক্ষা করুন, তারপর: + +```sh +/etc/init.d/easymesh restart +sleep 15 +batctl hardif eth0 throughput_override # 1000.0 MBit দেখানো উচিত +``` + +অথবা **Network → EasyMesh → Boot-time reliability**-এ **Cron fallback** চেকবক্স সক্রিয় করুন। + +এটি প্রতি মিনিটে অগ্রাধিকার পুনঃপ্রয়োগ করে — নিরাপদ, idempotent, এবং রাউটারে যেখানে বেতার ড্রাইভার ধীরে লোড হয় সেখানে সহায়ক। + +--- + +## পরিবর্তন বিবরণী + +### [2.4.1-r1] — ২০২৬-০৯-১৬ + +**যোগ করা হয়েছে** + +- সার্ভার ভূমিকার জন্যও তারযুক্ত ব্যাকহল ড্রপডাউন দেখানো হয় (পূর্বে শুধুমাত্র Client/Node) +- পোর্ট অবস্থা লেবেল: `(in bridge - will be removed on apply)` / `(free)` +- শুধুমাত্র `ethN` / `lanN` এ ফিল্টার — CPU পোর্ট এবং macvlan ইন্টারফেস বাদ দেয় +- `throughput_override` এর মাধ্যমে সংযোগ অগ্রাধিকার (তারযুক্ত 1 Gbit, 6 GHz 900 Mbit, 5 GHz 600 Mbit, 2.4 GHz 30 Mbit) +- **GUI-সম্পাদনযোগ্য সংযোগ অগ্রাধিকার ওজন** — init লিপিতে আর কোনো হার্ডকোড করা মান নেই +- **Priority Weight কলাম** বেতার সঙ্গীদের জন্য (পূর্বে শুধুমাত্র তারযুক্ত) +- **মেশ VLAN** — 802.1Q-এর মাধ্যমে মেশ জুড়ে অতিথি / IoT / হটস্পট নেটওয়ার্ক প্রসারিত করুন + - UI-তে নতুন "Mesh VLAN" সেকশন: কমা-বিভক্ত VLAN ID লিখুন (যেমন `10,20,30`) + - init লিপি প্রতিটি নোডে প্রতিটি ID-এর জন্য একটি `bat0.` 802.1Q ডিভাইস তৈরি করে + - ব্রিজ যদি `br-`, `br-lan`, `br-vlan`, `br-guest`, বা `br-iot` নামে হয় তবে স্বয়ংক্রিয়ভাবে VLAN ডিভাইস সংযুক্ত করে + - প্রয়োগের আগে `bat0`-এ পূর্ববর্তী 802.1Q ডিভাইস সরিয়ে দেয় — ID পরিবর্তন বা ফিল্ড মুছতে নিরাপদ + - README-এ শিক্ষানবিস গাইড: পূর্বশর্ত, ধাপে ধাপে, উদাহরণ, সমস্যা সমাধান +- `iw dev` এর মাধ্যমে স্বয়ংক্রিয় ব্যান্ড শনাক্তকরণ +- একযোগে বহু-সংযোগ ট্র্যাফিকের জন্য `bat0`-এ bonding সক্রিয় +- `/etc/rc.wps/easymesh-pair` পরিবেশকের মাধ্যমে WPS পেয়ারিং (wifi-scripts এর সাথে কোনো দ্বন্দ্ব নেই) +- IP পরিবর্তনের জন্য ৬০-সেকেন্ডের রোলব্যাক ওয়াচডগ +- স্প্লিট-ব্যান্ড ক্লায়েন্ট SSID (2.4 / 5 GHz আলাদা) +- `wait_and_attach_mesh()` — ৬০ সেকেন্ড টাইমআউট + ম্যানুয়াল `batctl` সংযুক্তি ফলব্যাক +- রোল-ভিত্তিক স্যানিটেশন: সার্ভার `wired_if` রাখে +- ঐচ্ছিক cron ফলব্যাকের জন্য UI চেকবক্স যোগ করা হয়েছে (ডিফল্ট বন্ধ) +- বেতার সঙ্গীদের জন্য লাইভ লেটেন্সি মনিটরিং (৩টি পিং-এর উপর স্মুথ করা) +- বেতার এবং তারযুক্ত সংযোগের জন্য লাইভ RX/TX ব্যান্ডউইথ রেট +- তারযুক্ত ব্যাকহলের জন্য প্রতিবেশী MAC + সর্বশেষ-দেখা (batman originator টেবিল থেকে) +- শক্তিশালী অগ্রাধিকার পড়া (একাধিক `batctl` সিনট্যাক্স চেষ্টা করে) +- ইন্টারফেস চালু হলে অগ্রাধিকার পুনঃপ্রয়োগের জন্য হটপ্লাগ একীকরণ +- init লিপিতে ৯০-সেকেন্ডের অগ্রাধিকার পুনঃপ্রচেষ্টা লুপ + দ্বিতীয়-পাস ক্যাচআপ + +**সংশোধন করা হয়েছে** + +- init লিপি UCI-তে escaped quotes লিখেছিল, যার ফলে বেতার সংযোগ ভেঙে যায় +- CBI-তে `apply_on_parse=true` প্রতিটি পেজ লোডে UCI লিখেছিল (লকআউট) +- Server ভূমিকা ভুলবশত AP মোড সক্রিয় করতে পারে, DHCP নিষ্ক্রিয় করে +- `+wpad-mesh` নির্ভরতা সরানো হয়েছে (kconfig পুনরাবৃত্ত নির্ভরতা সৃষ্টি করছিল) +- `pair()` `sleep 0.3` ব্যবহার করেছিল — BusyBox-এ ভাঙা +- `pair()` সঙ্গী পরীক্ষা খালি লাইন গণনা করেছিল +- `sleep 3; ifup bat0` ath9k মেশের জন্য খুব ছোট ছিল +- গণনা প্রদর্শন তারযুক্ত ইন্টারফেস দ্বিগুণ গণনা করেছিল + +**পরিবর্তিত** + +- ৪টি প্যাকেজে বিভক্ত: মূল + 3 wpad মেটা +- IPK SDK-এর `ipkg-build` দিয়ে তৈরি (gzipped-tar, OpenWrt-সামঞ্জস্যপূর্ণ) + +--- + + +## উৎস থেকে সংকলন (OpenWrt SDK) + +আপনি OpenWrt/ImmortalWrt উৎস কোড মূল ডিরেক্টরিতে নিম্নলিখিত কমান্ডগুলি চালাতে পারেন। + +### ধাপ ১ — OpenWrt SDK সেটআপ + +আপনার টার্গেট প্ল্যাটফর্মের জন্য OpenWrt SDK ডাউনলোড এবং ইনস্টল করুন: + +```sh +git clone https://git.openwrt.org/openwrt/openwrt.git +cd openwrt +./scripts/feeds update -a +./scripts/feeds install -a +``` + +### ধাপ ২ — OpenWrt প্যাকেজ উৎসে EasyMesh অ্যাপ যোগ করুন + +```sh +cd package +git clone https://github.com/arafatrahmanzami/luci-app-easymesh.git +``` + +### ধাপ ৩ — প্যাকেজ সংকলন করুন + +OpenWrt-এর মূল ডিরেক্টরিতে ফিরে যান: + +```sh +cd ../ +``` + +`make menuconfig` ব্যবহার করে প্যাকেজ নির্বাচন করুন: + +```sh +make menuconfig +``` + +**LuCI → Applications → luci-app-easymesh** এ নেভিগেট করুন, তারপর এটি একটি মডিউল হিসাবে সংকলন করতে `` নির্বাচন করুন। + +প্যাকেজ সংকলন করুন: + +```sh +make package/luci-app-easymesh/compile V=s +``` + +সংকলন হওয়ার পরে, `.ipk` প্যাকেজটি `bin/packages/.../base/`-এ উপলব্ধ হবে। + +**সংক্ষিপ্ত সংস্করণ (সব একসাথে):** + +```sh +git clone https://github.com/arafatrahmanzami/luci-app-easymesh.git package/luci-app-easymesh +make menuconfig # নির্বাচন করুন LUCI -> Applications -> luci-app-easymesh +make package/luci-app-easymesh/compile V=s +# আউটপুট: bin/packages/xxx/base/luci-app-easymesh_xxx.ipk +``` + +--- + +## কৃতিত্ব + +- **মূল লেখক:** + - dz + - torguardvpn — [torguardvpn/luci-app-easymesh](https://github.com/torguardvpn/luci-app-easymesh) +- **মূল প্রকল্প থেকে শাখা:** + - [ntlf9t/luci-app-easymesh](https://github.com/ntlf9t/luci-app-easymesh) + - [kenzok78/luci-app-easymesh](https://github.com/kenzok78/luci-app-easymesh) + - [torguardvpn/luci-app-easymesh](https://github.com/torguardvpn/luci-app-easymesh) +- **ত্রুটি-সংশোধন শাখা:** [mobing8/luci-app-easymesh-dawn](https://github.com/mobing8/luci-app-easymesh-dawn) +- **বর্তমান রক্ষণাবেক্ষণকারী:** Arafat Rahman Zami Mondol — [@arafatrahmanzami](https://github.com/arafatrahmanzami) · [একটি ইস্যু খুলুন](https://github.com/arafatrahmanzami/luci-app-easymesh/issues/new) + +আরও অন্বেষণের জন্য, উৎস কোড দেখুন বা উপরে তালিকাভুক্ত রেফারেন্স রিপোজিটরিগুলিতে প্রকাশ ট্র্যাক করুন। + +--- + +## পরিভাষা অভিধান + +এই README-তে যে ইংরেজি বা মিশ্র পরিভাষাগুলি ব্যবহার করা হয়েছে সেগুলি এখানে ব্যাখ্যা করা হল। যেসব পরিভাষা অনুবাদ করলে নেটওয়ার্কিংয়ে অর্থ পরিবর্তিত হতে পারে বা আন্তর্জাতিক মানে অপরিচিত হতে পারে, সেগুলি ইংরেজিতেই রাখা হয়েছে। + +### নেটওয়ার্ক মৌলিক পরিভাষা + +| পরিভাষা | পূর্ণ রূপ | ব্যাখ্যা | +|---|---|---| +| **WiFi** | Wireless Fidelity | তারবিহীন নেটওয়ার্ক সংযোগের মানক নাম। বেতার সংযোগের প্রযুক্তি। | +| **LAN** | Local Area Network | স্থানীয় এলাকা নেটওয়ার্ক — আপনার বাড়ি বা অফিসের ভিতরের ডিভাইসগুলির মধ্যে সংযোগ। | +| **WAN** | Wide Area Network | বিস্তৃত এলাকা নেটওয়ার্ক — সাধারণত ইন্টারনেট সংযোগ যা ISP থেকে আসে। | +| **AP** | Access Point | অ্যাক্সেস পয়েন্ট — WiFi সংকেত সম্প্রচার করে যা ডিভাইসগুলি সংযুক্ত হতে পারে। | +| **IP** | Internet Protocol | ইন্টারনেট প্রোটোকল — প্রতিটি নেটওয়ার্ক ডিভাইসের অনন্য ঠিকানা (যেমন `192.168.1.1`)। | +| **MAC** | Media Access Control | মিডিয়া অ্যাক্সেস কন্ট্রোল — প্রতিটি নেটওয়ার্ক কার্ডের অনন্য হার্ডওয়্যার সনাক্তকারী (যেমন `00:11:22:33:44:55`)। | +| **DHCP** | Dynamic Host Configuration Protocol | স্বয়ংক্রিয়ভাবে ডিভাইসে IP ঠিকানা বিতরণকারী সেবা। | +| **DNS** | Domain Name System | ডোমেন নামকে IP ঠিকানায় রূপান্তর করে (যেমন `google.com` → `142.250.185.78`)। | +| **SSID** | Service Set Identifier | WiFi নেটওয়ার্কের নাম, যা আপনি ফোনে দেখেন (যেমন `EasyMesh_WiFi`)। | +| **VLAN** | Virtual LAN | ভার্চুয়াল LAN — একটি ফিজিক্যাল নেটওয়ার্কে একাধিক লজিক্যাল নেটওয়ার্ক তৈরি করার কৌশল (যেমন অতিথি নেটওয়ার্ক)। | +| **VPN** | Virtual Private Network | ভার্চুয়াল প্রাইভেট নেটওয়ার্ক — ইন্টারনেটের উপর এনক্রিপ্টেড সংযোগ যা গোপনীয়তা রক্ষা করে। | + +### বেতার মান ও প্রোটোকল + +| পরিভাষা | ব্যাখ্যা | +|---|---| +| **802.11s** | IEEE মান যা মেশ নেটওয়ার্কিং সমর্থন করে — রাউটারগুলি সরাসরি একে অপরের সাথে সংযোগ করে। | +| **802.11k/v/r** | তিনটি IEEE মান যা একসাথে দ্রুত রোমিং প্রদান করে — আপনি যখন ঘরের মধ্যে চলাচল করেন, ফোন স্বয়ংক্রিয়ভাবে সবচেয়ে কাছের রাউটারে সংযোগ করে। | +| **802.11k** | নিকটবর্তী অ্যাক্সেস পয়েন্ট সম্পর্কে তথ্য শেয়ার করে। | +| **802.11v** | ক্লায়েন্টকে সুপারিশ করে কোন অ্যাক্সেস পয়েন্টে সংযুক্ত হওয়া উচিত। | +| **802.11r** | দ্রুত রোমিং — সংযোগ বিচ্ছিন্ন না করে অন্য রাউটারে চলে যাওয়া। | +| **802.1Q** | VLAN ট্যাগিং মান। ডেটা প্যাকেটে একটি ট্যাগ যোগ করে কোন নেটওয়ার্কের তা চিহ্নিত করে। | +| **WPA2** | WiFi Protected Access 2 — নেটওয়ার্ক এনক্রিপশনের পুরানো কিন্তু এখনও ব্যবহৃত মান। | +| **WPA3** | WiFi Protected Access 3 — WPA2-এর উত্তরসূরি, আরও শক্তিশালী এনক্রিপশন। | +| **SAE** | Simultaneous Authentication of Equals — WPA3-এ ব্যবহৃত পাসওয়ার্ড-ভিত্তিক প্রমাণীকরণ পদ্ধতি। | +| **K/V/R** | উপরের 802.11k/v/r-এর সংক্ষিপ্ত রূপ। | +| **AX / AC / N** | WiFi 6 (AX), WiFi 5 (AC), WiFi 4 (N) — বেতার মান প্রজন্ম। | +| **2.4 GHz / 5 GHz / 6 GHz** | বেতার সংকেতের কম্পাঙ্ক — 2.4 GHz দীর্ঘ পরিসর কিন্তু ধীর, 5/6 GHz দ্রুত কিন্তু ছোট পরিসর। | +| **MIMO** | Multiple-Input Multiple-Output — একাধিক অ্যান্টেনা দিয়ে একসাথে ডেটা পাঠানো ও গ্রহণ। | +| **MU-MIMO** | Multi-User MIMO — একসাথে একাধিক ডিভাইসের সাথে যোগাযোগ। | + +### OpenWrt ইকোসিস্টেম + +| পরিভাষা | ব্যাখ্যা | +|---|---| +| **OpenWrt** | ওপেন-সোর্স রাউটার ফার্মওয়্যার, Linux-ভিত্তিক। রাউটারকে সম্পূর্ণভাবে নিয়ন্ত্রণ দেয়। | +| **ImmortalWrt** | OpenWrt-এর একটি ফর্ক (শাখা), চীনা সম্প্রদায় দ্বারা রক্ষণাবেক্ষণকৃত, কিছু অতিরিক্ত প্যাকেজ ও ডিফল্ট সেটিংস সহ। | +| **LuCI** | OpenWrt-এর ওয়েব-ভিত্তিক কনফিগারেশন ইন্টারফেস। ব্রাউজারে `http://` লিখলে যা খোলে। | +| **UCI** | Unified Configuration Interface — OpenWrt-এর সব কনফিগার এক জায়গায় রাখার ব্যবস্থা। `/etc/config/` ডিরেক্টরিতে থাকে। | +| **opkg** | OpenWrt 24.10 এবং তার পুরানো সংস্করণে ব্যবহৃত প্যাকেজ ম্যানেজার। `.ipk` ফাইল ইনস্টল করে। | +| **apk** | OpenWrt 25.12+ এবং Alpine Linux-এ ব্যবহৃত নতুন প্যাকেজ ম্যানেজার। `.apk` ফাইল ইনস্টল করে। | +| **procd** | OpenWrt-এর সেবা ব্যবস্থাপক — কোন সেবা কখন চালু হবে তা নিয়ন্ত্রণ করে। | +| **rpcd** | LuCI-এর পিছনে থাকা সেবা যা ওয়েব ইন্টারফেসের অনুরোধ প্রক্রিয়া করে। | +| **uhttpd** | LuCI ওয়েব পেজ পরিবেশনকারী হালকা HTTP সার্ভার। | +| **Batman-adv** | Better Approach To Mobile Adhoc Networking — Advanced — মেশ রাউটিং প্রোটোকল। কোন ডেটা কোন পথে যাবে তা নির্ধারণ করে। | +| **batctl** | Batman-adv নিয়ন্ত্রণ ও নির্ণয়ের কমান্ড-লাইন টুল। | +| **bat0** | Batman-adv-এর ভার্চুয়াল নেটওয়ার্ক ইন্টারফেস, যার মাধ্যমে মেশ ট্র্যাফিক প্রবাহিত হয়। | +| **hardif** | Hardware Interface — Batman-adv যে বাস্তব ইন্টারফেস ব্যবহার করে (যেমন `eth0`, `phy0-mesh0`)। | +| **wpad** | Wireless Protected Access Daemon — OpenWrt-এ WiFi পরিচালনার সেবা। বিভিন্ন ভেরিয়েন্ট আছে (openssl/wolfssl/mbedtls)। | +| **wpad-mesh** | wpad-এর বিশেষ সংস্করণ যা 802.11s মেশ সমর্থন করে। | +| **kmod** | Kernel Module — কার্নেলে লোডযোগ্য অতিরিক্ত কোড (যেমন `kmod-batman-adv`)। | +| **dawn** | একটি অ্যাক্সেস পয়েন্ট স্টিয়ারিং ডেমন — ক্লায়েন্টকে সেরা AP-তে পরিচালনা করে। | +| **usteer** | ইউজার-স্পেস স্টিয়ারার — dawn-এর বিকল্প, হালকা ও দ্রুত। | + +### শেল কমান্ড + +| কমান্ড | ব্যাখ্যা | +|---|---| +| `uci set` | UCI কনফিগারেশনে একটি মান সেট করে। | +| `uci get` | UCI কনফিগারেশন থেকে একটি মান পড়ে। | +| `uci commit` | পরিবর্তিত মান স্থায়ীভাবে সংরক্ষণ করে। | +| `uci show` | বর্তমান UCI কনফিগারেশন প্রদর্শন করে। | +| `opkg install` | opkg দিয়ে একটি প্যাকেজ ইনস্টল করে। | +| `opkg update` | প্যাকেজ তালিকা হালনাগাদ করে। | +| `apk add` | apk দিয়ে একটি প্যাকেজ ইনস্টল করে। | +| `apk del` | apk দিয়ে একটি প্যাকেজ সরায়। | +| `batctl n` | Batman-adv প্রতিবেশী তালিকা দেখায়। | +| `batctl if` | Batman-adv ইন্টারফেস তালিকা দেখায়। | +| `batctl o` | Batman-adv originator টেবিল দেখায় (সম্পূর্ণ মেশ টপোলজি)। | +| `batctl ping` | Batman-adv-এর লেয়ারে পিং পাঠায়। | +| `iw dev` | বেতার ইন্টারফেস তালিকা দেখায়। | +| `wget` | ফাইল ডাউনলোড করে। | +| `scp` | নিরাপদ ফাইল কপি (SSH-এর উপর)। | +| `ssh` | নিরাপদ রিমোট শেল সংযোগ। | +| `reboot` | রাউটার পুনরায় চালু করে। | +| `logread` | সিস্টেম লগ পড়ে। | +| `ip link` | নেটওয়ার্ক ইন্টারফেস অবস্থা দেখায়। | +| `ifup` / `ifdown` | ইন্টারফেস চালু / বন্ধ করে। | +| `chmod +x` | ফাইলকে এক্সিকিউটেবল (চালানোর যোগ্য) করে। | + +### একক ও পরিমাপ + +| একক | ব্যাখ্যা | +|---|---| +| **bit** | ডিজিটাল তথ্যের ক্ষুদ্রতম একক (০ বা ১)। | +| **Byte** | ৮ বিট। `B` (বড় হাতের) দিয়ে প্রকাশ করা হয়। | +| **Kbit/s** | কিলোবিট প্রতি সেকেন্ড — ডেটা স্থানান্তরের গতি। | +| **Mbit** | মেগাবিট = ১০০০ কিলোবিট। | +| **Gbit** | গিগাবিট = ১০০০ মেগাবিট। | +| **Mbps** | মেগাবিট প্রতি সেকেন্ড = ১০০০ Kbit/s। | +| **MHz / GHz** | মেগাহার্টজ / গিগাহার্টজ — রেডিও কম্পাঙ্ক। ১ GHz = ১০০০ MHz। | +| **ms** | মিলিসেকেন্ড = সেকেন্ডের ১/১০০০। লেটেন্সি পরিমাপে ব্যবহৃত। | + +### ইন্টারফেস পদবি + +| পদবি | ব্যাখ্যা | +|---|---| +| **eth0, eth1** | ইথারনেট ইন্টারফেস (তারযুক্ত)। কিছু রাউটারে CPU-এর অভ্যন্তরীণ পোর্ট। | +| **lan1, lan2, lan3** | LAN ফিজিক্যাল পোর্ট (তারযুক্ত)। সাধারণত যেগুলিতে আপনি ডিভাইস সংযুক্ত করেন। | +| **wan** | WAN পোর্ট — ইন্টারনেট/ISP-এর সাথে সংযোগের জন্য। | +| **phy0-mesh0** | মেশ ভার্চুয়াল বেতার ইন্টারফেস — 802.11s ট্র্যাফিক বহন করে। | +| **phy0, phy1** | বেতার রেডিও হার্ডওয়্যার সনাক্তকারী। | +| **radio0, radio1** | OpenWrt-এ রেডিওর নাম। | +| **bat0** | Batman-adv-এর ভার্চুয়াল মেশ ইন্টারফেস। | +| **br-lan** | LAN ব্রিজ ইন্টারফেস (সেতুবন্ধন)— একাধিক পোর্টকে একটি নেটওয়ার্কে একত্রিত করে। | +| **br-guest** | অতিথি নেটওয়ার্কের ব্রিজ। | +| **wlan0, wlan1** | বেতার ক্লায়েন্ট/AP ইন্টারফেস। | + +### UI ও সেটআপ পদবি + +| পদবি | ব্যাখ্যা | +|---|---| +| **Server** | মেশের মূল রাউটার যা ইন্টারনেট প্রদান করে। ইন্টারনেট সংযোগ থাকে। | +| **Client** | এক্সটেন্ডার রাউটার — মেশের মাধ্যমে ইন্টারনেট গ্রহণ করে। | +| **Node** | শুধুমাত্র রিলে — কেবল ট্র্যাফিক এগিয়ে পাঠায়, নিজে ইন্টারনেট গ্রহণ বা প্রদান করে না। | +| **Dumb AP** | "বোকা" অ্যাক্সেস পয়েন্ট — DHCP ও ফায়ারওয়াল নিষ্ক্রিয় থাকে, কেবল WiFi সম্প্রচার করে। | +| **Mesh ID** | মেশ নেটওয়ার্কের নাম — সমস্ত নোডে অবশ্যই একই হতে হবে। | +| **Mobility Domain** | ৪-অক্ষরের হেক্সাডেসিমাল সনাক্তকারী যা 802.11r দ্রুত রোমিংয়ে ব্যবহৃত হয়। | +| **Save & Apply** | পরিবর্তন সংরক্ষণ ও প্রয়োগ করার বোতাম। | +| **Reapply EasyMesh Settings** | বর্তমান সেটিংস পুনরায় মেশে প্রয়োগ করার বোতাম। | +| **Preset** | পূর্বনির্ধারিত সেটিংস — এক ক্লিকে সাধারণ কনফিগারেশন। | +| **Rollback** | পরিবর্তনের পর সমস্যা হলে পূর্বের অবস্থায় ফিরে যাওয়া। | +| **Watchdog** | নির্দিষ্ট সময়ে নির্দিষ্ট কাজ সম্পাদনকারী নজরদারি প্রক্রিয়া। | +| **WiFi** | বেতার (তারহীন-সংযোগ) । | + + +### প্ল্যাটফর্ম + +| পরিভাষা | ব্যাখ্যা | +|---|---| +| **Branch** | কোডের একটি সমান্তরাল সংস্করণ। | +| **Commit** | কোড পরিবর্তনের একটি সংরক্ষিত স্ন্যাপশট। | +| **Fork** | অন্য কারও প্রকল্পের নিজস্ব অনুলিপি, স্বাধীনভাবে পরিবর্তনযোগ্য। | +| **Git** | ভার্সন নিয়ন্ত্রণ ব্যবস্থা — কোডের পরিবর্তন ট্র্যাক করে। | +| **GitHub** | Git রিপোজিটরি হোস্টিং প্ল্যাটফর্ম। কোড শেয়ার ও সহযোগিতার জন্য। | +| **Repository or Repo** | কোড ও ফাইলের সংগ্রহস্থল। | +| **Release** | একটি নির্দিষ্ট সংস্করণ যা ব্যবহারকারীদের জন্য প্রকাশ করা হয়। | +| **Source code** | উৎস কোড বা প্রোগ্রামিং নির্দেশনাবলী — সহজভাবে ক্রমানুদেশন বা নির্দিষ্ট ক্রমে নির্দেশ দেওয়া। | +| **Tag** | একটি কমিটের নামসহ চিহ্ন যেমন `2.4.1-r1`। | +| **Linux** | ওপেন-সোর্স অপারেটিং সিস্টেম কার্নেল — OpenWrt ও ImmortalWrt এর ভিত্তি। | + +--- + +## লাইসেন্স + +GPL-2.0 (মূল প্রকল্প থেকে উত্তরাধিকারসূত্রে প্রাপ্ত) diff --git a/luci-app-easymesh/docs/screenshots-bn/01-dashboard_bn.png b/luci-app-easymesh/docs/screenshots-bn/01-dashboard_bn.png new file mode 100644 index 000000000..3915c38e5 Binary files /dev/null and b/luci-app-easymesh/docs/screenshots-bn/01-dashboard_bn.png differ diff --git a/luci-app-easymesh/docs/screenshots-bn/02-quick-setup-1-9_bn.png b/luci-app-easymesh/docs/screenshots-bn/02-quick-setup-1-9_bn.png new file mode 100644 index 000000000..196146345 Binary files /dev/null and b/luci-app-easymesh/docs/screenshots-bn/02-quick-setup-1-9_bn.png differ diff --git a/luci-app-easymesh/docs/screenshots-bn/03-quick-setup-10-19_bn.png b/luci-app-easymesh/docs/screenshots-bn/03-quick-setup-10-19_bn.png new file mode 100644 index 000000000..be6c2edce Binary files /dev/null and b/luci-app-easymesh/docs/screenshots-bn/03-quick-setup-10-19_bn.png differ diff --git a/luci-app-easymesh/docs/screenshots-bn/04-mesh-settings_bn.png b/luci-app-easymesh/docs/screenshots-bn/04-mesh-settings_bn.png new file mode 100644 index 000000000..8daf2eab7 Binary files /dev/null and b/luci-app-easymesh/docs/screenshots-bn/04-mesh-settings_bn.png differ diff --git a/luci-app-easymesh/docs/screenshots-bn/05-client-wifi-ap-mode_bn.png b/luci-app-easymesh/docs/screenshots-bn/05-client-wifi-ap-mode_bn.png new file mode 100644 index 000000000..92a72dc0f Binary files /dev/null and b/luci-app-easymesh/docs/screenshots-bn/05-client-wifi-ap-mode_bn.png differ diff --git a/luci-app-easymesh/docs/screenshots-bn/06-wired-backhaul-safety_bn.png b/luci-app-easymesh/docs/screenshots-bn/06-wired-backhaul-safety_bn.png new file mode 100644 index 000000000..f08f1ad02 Binary files /dev/null and b/luci-app-easymesh/docs/screenshots-bn/06-wired-backhaul-safety_bn.png differ diff --git a/luci-app-easymesh/docs/screenshots-bn/07-priority-vlans_bn.png b/luci-app-easymesh/docs/screenshots-bn/07-priority-vlans_bn.png new file mode 100644 index 000000000..52655c024 Binary files /dev/null and b/luci-app-easymesh/docs/screenshots-bn/07-priority-vlans_bn.png differ diff --git a/luci-app-easymesh/docs/screenshots/01-dashboard.png b/luci-app-easymesh/docs/screenshots/01-dashboard.png new file mode 100755 index 000000000..6822cde1f Binary files /dev/null and b/luci-app-easymesh/docs/screenshots/01-dashboard.png differ diff --git a/luci-app-easymesh/docs/screenshots/02-quick-setup-1-9.png b/luci-app-easymesh/docs/screenshots/02-quick-setup-1-9.png new file mode 100755 index 000000000..186bd76ef Binary files /dev/null and b/luci-app-easymesh/docs/screenshots/02-quick-setup-1-9.png differ diff --git a/luci-app-easymesh/docs/screenshots/03-quick-setup-10-19.png b/luci-app-easymesh/docs/screenshots/03-quick-setup-10-19.png new file mode 100755 index 000000000..51d6edd3e Binary files /dev/null and b/luci-app-easymesh/docs/screenshots/03-quick-setup-10-19.png differ diff --git a/luci-app-easymesh/docs/screenshots/04-mesh-settings.png b/luci-app-easymesh/docs/screenshots/04-mesh-settings.png new file mode 100755 index 000000000..2c63502f3 Binary files /dev/null and b/luci-app-easymesh/docs/screenshots/04-mesh-settings.png differ diff --git a/luci-app-easymesh/docs/screenshots/05-client-wifi-ap-mode.png b/luci-app-easymesh/docs/screenshots/05-client-wifi-ap-mode.png new file mode 100755 index 000000000..a6ba1cb5a Binary files /dev/null and b/luci-app-easymesh/docs/screenshots/05-client-wifi-ap-mode.png differ diff --git a/luci-app-easymesh/docs/screenshots/06-wired-backhaul-safety.png b/luci-app-easymesh/docs/screenshots/06-wired-backhaul-safety.png new file mode 100755 index 000000000..b0711c468 Binary files /dev/null and b/luci-app-easymesh/docs/screenshots/06-wired-backhaul-safety.png differ diff --git a/luci-app-easymesh/docs/screenshots/07-priority-vlans.png b/luci-app-easymesh/docs/screenshots/07-priority-vlans.png new file mode 100755 index 000000000..9d2b3d93b Binary files /dev/null and b/luci-app-easymesh/docs/screenshots/07-priority-vlans.png differ diff --git a/luci-app-easymesh/easymesh-pkg-2.4.1-r1.tar.gz b/luci-app-easymesh/easymesh-pkg-2.4.1-r1.tar.gz new file mode 100644 index 000000000..2967f2cd8 Binary files /dev/null and b/luci-app-easymesh/easymesh-pkg-2.4.1-r1.tar.gz differ diff --git a/luci-app-easymesh/luasrc/controller/easymesh.lua b/luci-app-easymesh/luasrc/controller/easymesh.lua new file mode 100755 index 000000000..2abe074fa --- /dev/null +++ b/luci-app-easymesh/luasrc/controller/easymesh.lua @@ -0,0 +1,24 @@ +local nixio = require "nixio" +require "nixio.fs" +-- Copyright (C) 2021 dz + +module("luci.controller.easymesh", package.seeall) + +local fs = require "nixio.fs" + +function index() + if not fs.access("/etc/config/easymesh") then + return + end + + -- luci 23.05+ 已通过 menu.d JSON 注册菜单,无需重复 + if fs.access("/usr/share/luci/menu.d/luci-app-easymesh.json") then + return + end + + local page + + page = entry({"admin", "network", "easymesh"}, cbi("easymesh"), _("EASY MESH"), 60) + page.dependent = true + page.acl_depends = { "luci-app-easymesh" } +end diff --git a/luci-app-easymesh/luasrc/model/cbi/easymesh.lua b/luci-app-easymesh/luasrc/model/cbi/easymesh.lua new file mode 100755 index 000000000..921ee1bc8 --- /dev/null +++ b/luci-app-easymesh/luasrc/model/cbi/easymesh.lua @@ -0,0 +1,685 @@ +-- Copyright (C) 2021 dz +-- Copyright (C) 2026 Arafat Rahman Zami Mondol +-- v2.8 — wired+wireless priority display, editable priority weights, mesh VLANs + +local m, s, o +local sys = require "luci.sys" +local uci = require "luci.model.uci".cursor() + +m = Map("easymesh", translate("EasyMesh"), + translate("Configure a Batman-adv mesh network.")) + +function m.parse(self, ...) + Map.parse(self, ...) + local role = self:get("config", "role") + local ap_mode = self:get("config", "ap_mode") + if role == "server" and ap_mode == "1" then + self:set("config", "ap_mode", "0") + end + local wired_if = self:get("config", "wired_if") + local preferred = self:get("config", "preferred_wired_port") + if (not wired_if or wired_if == "") and preferred and preferred ~= "" then + self:set("config", "wired_if", preferred) + elseif wired_if and wired_if ~= "" and (not preferred or preferred == "") then + self:set("config", "preferred_wired_port", wired_if) + end +end + +function m.on_after_commit(self) + luci.sys.call("/etc/init.d/easymesh restart >/tmp/easymesh-apply.log 2>&1 &") +end + +-- ========================================================= +-- Helpers +-- ========================================================= +local function get_preferred_wired_port() + local wired_if = uci:get("easymesh", "config", "wired_if") + local preferred = uci:get("easymesh", "config", "preferred_wired_port") + local pick = "" + if wired_if and wired_if ~= "" then pick = wired_if + elseif preferred and preferred ~= "" then pick = preferred end + if pick ~= "" then + local f = io.open("/sys/class/net/" .. pick, "r") + if f then f:close(); return pick end + end + return "" +end + +local function list_eth_ports() + local ports, seen = {}, {} + local fp = io.popen("ls /sys/class/net 2>/dev/null") + if fp then + for d in fp:lines() do + if (d:match("^eth%d+$") or d:match("^lan%d+$")) and not seen[d] then + seen[d] = true; table.insert(ports, d) + end + end + fp:close() + end + uci:foreach("network", "device", function(sect) + local n = sect['.name'] + if n and (n:match("^eth%d+$") or n:match("^lan%d+$")) and not seen[n] then + seen[n] = true; table.insert(ports, n) + end + end) + table.sort(ports) + return ports +end + +local function port_label(p) + local f = io.open("/sys/class/net/" .. p .. "/brport/bridge", "r") + if f then f:close(); return p .. " (in bridge — will be removed on apply)" end + return p .. " (free)" +end + +local function read_bytes(iface) + local rx, tx = 0, 0 + local f = io.open("/sys/class/net/" .. iface .. "/statistics/rx_bytes", "r") + if f then rx = tonumber(f:read("*l")) or 0; f:close() end + f = io.open("/sys/class/net/" .. iface .. "/statistics/tx_bytes", "r") + if f then tx = tonumber(f:read("*l")) or 0; f:close() end + return rx, tx +end + +local function human_rate(bps) + if bps < 0 then bps = 0 end + if bps < 1024 then return string.format("%d B/s", bps) + elseif bps < 1048576 then return string.format("%.1f KB/s", bps / 1024) + elseif bps < 1073741824 then return string.format("%.1f MB/s", bps / 1048576) + else return string.format("%.2f GB/s", bps / 1073741824) end +end + +local function read_override(iface) + -- Try multiple batctl syntaxes — first one that returns a digit wins + local cmds = { + "batctl hardif " .. iface .. " throughput_override", + "batctl hardif " .. iface .. " to", + "batctl meshif bat0 hardif " .. iface .. " throughput_override", + } + for _, cmd in ipairs(cmds) do + local fp = io.popen(cmd .. " 2>/dev/null") + if fp then + local val = fp:read("*l") + fp:close() + if val and val ~= "" and val:match("%d") then + return val + end + end + end + return "?" +end + +local function batman_ping(mac) + if not mac or mac == "" or mac == "—" then return "—" end + + -- 30-second cache — page reloads within that window are instant + local cachepath = "/tmp/easymesh-ping-" .. mac:gsub(":", "") + local f = io.open(cachepath, "r") + if f then + local ts = tonumber(f:read("*l") or "0") + local val = f:read("*l") or "—" + f:close() + if ts and (os.time() - ts) < 30 then + return val + end + end + + local fp = io.popen("batctl ping -c 2 -i 0.2 -t 1 " .. mac .. " 2>/dev/null") + if not fp then return "—" end + local min_ms = nil + for line in fp:lines() do + local t = line:match("time=([%d%.]+)") + if t then + local ms = tonumber(t) + if ms and (not min_ms or ms < min_ms) then min_ms = ms end + end + end + fp:close() + local result = min_ms and string.format("%.1f ms", min_ms) or "—" + + local w = io.open(cachepath, "w") + if w then w:write(os.time(), "\n", result, "\n"); w:close() end + + return result +end + +-- ========================================================= +-- Active hardifs + live bandwidth sampling (1s window) +-- ========================================================= +local active_ifaces = {} +local afl = io.popen("batctl if 2>/dev/null") +if afl then + for line in afl:lines() do + local iface = line:match("^([^:]+):%s*active") + if iface then table.insert(active_ifaces, iface) end + end + afl:close() +end + +local s1, s2 = {}, {} +for _, iface in ipairs(active_ifaces) do + local rx, tx = read_bytes(iface); s1[iface] = { rx = rx, tx = tx } +end +if #active_ifaces > 0 then + os.execute("sleep 1") + for _, iface in ipairs(active_ifaces) do + local rx, tx = read_bytes(iface); s2[iface] = { rx = rx, tx = tx } + end +end + +local rates = {} +for _, iface in ipairs(active_ifaces) do + rates[iface] = { + rx = human_rate(s2[iface].rx - s1[iface].rx), + tx = human_rate(s2[iface].tx - s1[iface].tx), + } +end + +-- ========================================================= +-- Originators (for wired neighbor / last-seen) +-- ========================================================= +local function parse_originators() + local by_iface = {} + local fp = io.popen("batctl o 2>/dev/null | tail -n +2") + if not fp then return by_iface end + for line in fp:lines() do + local starred = line:match("^%s*%*") ~= nil + local mac, seen = line:match("([0-9a-fA-F:]+)%s+(%d+%.%d+s)") + local iface = line:match("%[%s*([%w%-_]+)%s*%]") + if mac and iface then + if not by_iface[iface] then by_iface[iface] = {} end + table.insert(by_iface[iface], { mac = mac, seen = seen, starred = starred }) + end + end + fp:close() + return by_iface +end + +local originators = parse_originators() + +-- ========================================================= +-- Wireless peers table (with Priority column) +-- ========================================================= +local function detect_nodes() + local data = {} + local fp = io.popen("batctl n 2>/dev/null | tail -n +2") + if not fp then return data end + for line in fp:lines() do + local iface, neighbor, lastseen = line:match("^%s*(%S+)%s+(%S+)%s+(%S+)") + if iface and neighbor and neighbor:match("^[0-9a-fA-F:]+$") then + if iface:match("^phy") or iface:match("mesh") then + table.insert(data, { IF = iface, Neighbor = neighbor, lastseen = lastseen }) + end + end + end + fp:close() + return data +end + +local wireless_count = 0 +local wired_count = 0 +for _, iface in ipairs(active_ifaces) do + if iface:match("^phy") or iface:match("mesh") then + wireless_count = wireless_count + 1 + elseif iface:match("^eth") or iface:match("^lan") then + wired_count = wired_count + 1 + end +end + +local total_links = wireless_count + wired_count +local status_line +if wired_count > 0 then + status_line = translate("Active mesh links") .. ": " .. total_links .. + " (" .. wireless_count .. " wireless + " .. wired_count .. " wired)" +else + status_line = translate("Active mesh links") .. ": " .. wireless_count .. " (all wireless)" +end + +local peers = detect_nodes() +for _, p in ipairs(peers) do + -- Skip ping if lastseen > 30s → no timeout cost for stale entries + local ls = tonumber((p.lastseen or ""):match("([%d%.]+)") or "9999") + if ls and ls < 30 then + p.Latency = batman_ping(p.Neighbor) + else + p.Latency = translate("stale") + end + p.Priority = read_override(p.IF) + local r = rates[p.IF] + if r then p.BW_RX = r.rx; p.BW_TX = r.tx + else p.BW_RX = "—"; p.BW_TX = "—" end +end + +local v = m:section(Table, peers, "", "" .. status_line .. "") +v:option(DummyValue, "IF", translate("Interface")) +v:option(DummyValue, "Neighbor", translate("Neighbor MAC")) +v:option(DummyValue, "lastseen", translate("Last seen")) +v:option(DummyValue, "Latency", translate("Latency")) +v:option(DummyValue, "BW_RX", translate("RX rate")) +v:option(DummyValue, "BW_TX", translate("TX rate")) +v:option(DummyValue, "Priority", translate("Priority weight")) + +-- ========================================================= +-- Wired backhaul details +-- ========================================================= +local function detect_wired_details() + local rows = {} + local fp = io.popen("batctl if 2>/dev/null") + if not fp then return rows end + for line in fp:lines() do + local iface, state = line:match("^([^:]+):%s*(%S+)") + if iface and state == "active" and + (iface:match("^eth%d+$") or iface:match("^lan%d+$")) then + + local carrier = "?" + local cf = io.open("/sys/class/net/" .. iface .. "/carrier", "r") + if cf then local val = cf:read("*l"); cf:close() + carrier = (val == "1") and "yes" or "no" end + + local speed = "?" + local sf = io.open("/sys/class/net/" .. iface .. "/speed", "r") + if sf then local val = sf:read("*l"); sf:close() + if val and val ~= "" and val ~= "-1" then speed = val .. " Mbps" end end + + local override = read_override(iface) + + local bridged = "no" + local bf = io.open("/sys/class/net/" .. iface .. "/brport/bridge", "r") + if bf then bf:close(); bridged = "yes" end + + local entries = originators[iface] or {} + local best = nil + for _, e in ipairs(entries) do + if e.starred then best = e; break end + end + best = best or entries[1] + local neighbor = best and best.mac or "—" + local lastseen = best and best.seen or "—" + + local latency = batman_ping(neighbor) + + local r = rates[iface] + table.insert(rows, { + IF = iface, State = state, Cable = carrier, Speed = speed, + Neighbor = neighbor, Lastseen = lastseen, Latency = latency, + BW_RX = r and r.rx or "—", BW_TX = r and r.tx or "—", + Override = override, Bridged = bridged, + }) + end + end + fp:close() + return rows +end + +local wired_rows = detect_wired_details() +if #wired_rows > 0 then + local wv = m:section(Table, wired_rows, + "" .. translate("Wired backhaul details") .. "") + wv:option(DummyValue, "IF", translate("Interface")) + wv:option(DummyValue, "State", translate("State")) + wv:option(DummyValue, "Cable", translate("Cable")) + wv:option(DummyValue, "Speed", translate("Link speed")) + wv:option(DummyValue, "Neighbor", translate("Neighbor MAC")) + wv:option(DummyValue, "Lastseen", translate("Last seen")) + wv:option(DummyValue, "Latency", translate("Latency")) + wv:option(DummyValue, "BW_RX", translate("RX rate")) + wv:option(DummyValue, "BW_TX", translate("TX rate")) + wv:option(DummyValue, "Override", translate("Priority weight")) + wv:option(DummyValue, "Bridged", translate("In LAN bridge")) +end + +-- ========================================================= +-- TOP: Preferred wired port +-- ========================================================= +local wp = m:section(TypedSection, "easymesh", translate("Wired backhaul port"), + translate("Convenience selector. Mirrors 'Wired backhaul interface' below. " .. + "Presets that use wired backhaul will remove this port from the LAN bridge. " .. + "Pick a port NOT used by your uplink or modem. " .. + "Leave empty to disable wired backhaul entirely.")) +wp.anonymous = true + +o = wp:option(ListValue, "preferred_wired_port", translate("Preferred wired port")) +o:value("", translate("(none — wired presets will skip)")) +for _, p in ipairs(list_eth_ports()) do o:value(p, port_label(p)) end +o.default = "" +o.rmempty = false + +-- ========================================================= +-- Boot-time reliability (cron toggle) +-- ========================================================= +local cf = m:section(TypedSection, "easymesh", + translate("Boot-time reliability"), + translate("The init script applies link priorities at boot and 90s later. " .. + "On some hardware, mesh interfaces appear even later, leaving " .. + "priority weight at 0. Enable cron fallback to reapply priority " .. + "every minute. Safe (idempotent) but runs in the background. " .. + "When disabled, the cron line is commented out in /etc/crontabs/root.")) +cf.anonymous = true + +o = cf:option(Flag, "cron_fallback", translate("Enable cron fallback")) +o.default = "0" +o.rmempty = false + +-- ========================================================= +-- Link priority weights (editable) +-- ========================================================= +local lp = m:section(TypedSection, "easymesh", + translate("Link priority weights"), + translate("Batman-adv throughput_override values in Kbit/s. " .. + "Higher = preferred. Set to 0 to let batman auto-decide per link. " .. + "Defaults: wired=1000000, 6G=900000, 5G=600000, 2.4G=30000. " .. + "Changes apply on next Save & Apply (or 'Reapply EasyMesh Settings').")) +lp.anonymous = true + +o = lp:option(Value, "priority_wired", translate("Wired (Kbit/s)")) +o.default = "1000000" +o.datatype = "uinteger" +o.rmempty = false + +o = lp:option(Value, "priority_6g", translate("6 GHz wireless (Kbit/s)")) +o.default = "900000" +o.datatype = "uinteger" +o.rmempty = false + +o = lp:option(Value, "priority_5g", translate("5 GHz wireless (Kbit/s)")) +o.default = "600000" +o.datatype = "uinteger" +o.rmempty = false + +o = lp:option(Value, "priority_24g", translate("2.4 GHz wireless (Kbit/s)")) +o.default = "30000" +o.datatype = "uinteger" +o.rmempty = false + +-- ========================================================= +-- Mesh VLANs (extend guest/IoT/hotspot over mesh) +-- ========================================================= +local mv = m:section(TypedSection, "easymesh", + translate("Mesh VLANs"), + translate("Extend guest / IoT / hotspot networks across the mesh. " .. + "Enter a comma-separated list of VLAN IDs (e.g. 10,20,30). " .. + "The init script creates bat0. as an 8021q device for each ID. " .. + "If a bridge named br-, br-lan, br-vlan, br-guest, " .. + "or br-iot already exists, it is auto-added to that bridge. " .. + "Otherwise, add the bat0. device manually in " .. + "Network → Interfaces → Devices. " .. + "Requires 802.1Q-capable switches on the physical wired backhaul.")) +mv.anonymous = true + +o = mv:option(Value, "mesh_vlans", + translate("VLAN IDs (comma-separated)"), + translate("Example: 10,20,30 — Leave empty to disable")) +o.default = "" +o.datatype = "maxlength(200)" +o.rmempty = false + +-- ========================================================= +-- QUICK SETUP — 19 presets +-- ========================================================= +local q = m:section(TypedSection, "easymesh", translate("Quick Setup"), + translate("Click a preset. A confirmation dialog will appear. " .. + "Current config is backed up automatically before applying. " .. + "Grouped by role: SERVER (has internet) → CLIENT (uses mesh) → NODE (relay only).")) +q.anonymous = true + +local presets = { + { id = "srv_5g", values = { enabled="1", role="server", band_mode="single_5g", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="" }, label = translate("1. Server — 5 GHz wireless only"), desc = translate("[SERVER] Gateway. Extenders join via 5 GHz mesh only.") }, + { id = "srv_2g", values = { enabled="1", role="server", band_mode="single_2g", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="" }, label = translate("2. Server — 2.4 GHz wireless only"), desc = translate("[SERVER] Extenders join via 2.4 GHz (longer range).") }, + { id = "srv_dual", values = { enabled="1", role="server", band_mode="dual", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="" }, label = translate("3. Server — Dual-band wireless only"), desc = translate("[SERVER] Both radios carry backhaul.") }, + { id = "srv_wired", values = { enabled="1", role="server", band_mode="none", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="AUTO_DETECT" }, label = translate("4. Server — Wired backhaul only"), desc = translate("[SERVER] No wireless mesh — extenders connect by cable.") }, + { id = "srv_5g_wired", values = { enabled="1", role="server", band_mode="single_5g_wired", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="AUTO_DETECT" }, label = translate("5. Server — 5 GHz + Wired"), desc = translate("[SERVER] 5 GHz mesh primary, Ethernet parallel.") }, + { id = "srv_2g_wired", values = { enabled="1", role="server", band_mode="single_2g_wired", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="AUTO_DETECT" }, label = translate("6. Server — 2.4 GHz + Wired"), desc = translate("[SERVER] 2.4 GHz mesh plus Ethernet.") }, + { id = "srv_dual_wired", values = { enabled="1", role="server", band_mode="dual_wired", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="AUTO_DETECT" }, label = translate("7. Server — Dual-band + Wired"), desc = translate("[SERVER] Maximum redundancy. Both radios + Ethernet.") }, + + { id = "cli_5g", values = { enabled="1", role="client", band_mode="single_5g", encryption="0", kvr="1", ap_mode="1", ip_mode="dhcp", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="" }, label = translate("8. Client — 5 GHz wireless only"), desc = translate("[CLIENT] Extender via 5 GHz. No cable.") }, + { id = "cli_2g", values = { enabled="1", role="client", band_mode="single_2g", encryption="0", kvr="1", ap_mode="1", ip_mode="dhcp", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="" }, label = translate("9. Client — 2.4 GHz wireless only"), desc = translate("[CLIENT] Extender via 2.4 GHz.") }, + { id = "cli_dual", values = { enabled="1", role="client", band_mode="dual", encryption="0", kvr="1", ap_mode="1", ip_mode="dhcp", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="" }, label = translate("10. Client — Dual-band wireless only"), desc = translate("[CLIENT] Extender via both radios.") }, + { id = "cli_wired", values = { enabled="1", role="client", band_mode="none", encryption="0", kvr="1", ap_mode="1", ip_mode="dhcp", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="AUTO_DETECT" }, label = translate("11. Client — Wired backhaul only"), desc = translate("[CLIENT] Cable only, no wireless mesh.") }, + { id = "cli_5g_wired", values = { enabled="1", role="client", band_mode="single_5g_wired", encryption="0", kvr="1", ap_mode="1", ip_mode="dhcp", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="AUTO_DETECT" }, label = translate("12. Client — 5 GHz + Wired fallback"), desc = translate("[CLIENT] 5 GHz primary, Ethernet fallback.") }, + { id = "cli_2g_wired", values = { enabled="1", role="client", band_mode="single_2g_wired", encryption="0", kvr="1", ap_mode="1", ip_mode="dhcp", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="AUTO_DETECT" }, label = translate("13. Client — 2.4 GHz + Wired fallback"), desc = translate("[CLIENT] 2.4 GHz primary, Ethernet fallback.") }, + { id = "cli_dual_wired", values = { enabled="1", role="client", band_mode="dual_wired", encryption="0", kvr="1", ap_mode="1", ip_mode="dhcp", ap_enabled_2g="1", ap_enabled_5g="1", ap_network="lan", wired_if="AUTO_DETECT" }, label = translate("14. Client — Dual-band + Wired fallback"), desc = translate("[CLIENT] Both radios + Ethernet. Maximum redundancy.") }, + + { id = "node_5g", values = { enabled="1", role="node", band_mode="single_5g", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="0", ap_enabled_5g="0", wired_if="" }, label = translate("15. Node — 5 GHz relay"), desc = translate("[NODE] Relay only, 5 GHz. No client SSID.") }, + { id = "node_dual", values = { enabled="1", role="node", band_mode="dual", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="0", ap_enabled_5g="0", wired_if="" }, label = translate("16. Node — Dual-band relay"), desc = translate("[NODE] Relay only, both radios.") }, + { id = "node_wired", values = { enabled="1", role="node", band_mode="none", encryption="0", kvr="1", ap_mode="0", ap_enabled_2g="0", ap_enabled_5g="0", wired_if="AUTO_DETECT" }, label = translate("17. Node — Wired relay only"), desc = translate("[NODE] Relay via cable only. No wireless.") }, + + { id = "revert_wired", values = {}, custom = "detach_wired", label = translate("18. Detach wired backhaul"), desc = translate("Removes Ethernet backhaul, restores port to bridge.") }, + { id = "disable", values = { enabled="0" }, label = translate("19. Disable mesh"), desc = translate("Turns off mesh. Radios stay enabled.") }, +} + +for _, p in ipairs(presets) do + local btn = q:option(Button, "_preset_" .. p.id, p.label) + btn.inputstyle = "apply" + btn.description = p.desc + + function btn.write(self, section) + luci.sys.call("/etc/init.d/easymesh backup >/tmp/easymesh-preset.log 2>&1") + + if p.custom == "detach_wired" then + luci.sys.call("/etc/init.d/easymesh detach_wired >>/tmp/easymesh-preset.log 2>&1") + else + local cur = luci.model.uci.cursor() + local needs_wired = false + for _, val in pairs(p.values) do + if val == "AUTO_DETECT" then needs_wired = true; break end + end + local wired_port = "" + if needs_wired then + wired_port = get_preferred_wired_port() + if wired_port == "" then + luci.sys.call("logger -t easymesh 'Preset needs wired but no port chosen — wired disabled'") + end + end + for k, val in pairs(p.values) do + if val ~= "AUTO_DETECT" then cur:set("easymesh", "config", k, val) end + end + cur:set("easymesh", "config", "wired_if", wired_port) + cur:set("easymesh", "config", "preferred_wired_port", wired_port) + if needs_wired and wired_port == "" then + local bm = p.values.band_mode or "single_5g" + bm = tostring(bm):gsub("_wired$", "") + cur:set("easymesh", "config", "band_mode", bm) + end + cur:commit("easymesh") + luci.sys.call("/etc/init.d/easymesh restart >>/tmp/easymesh-preset.log 2>&1") + end + luci.http.redirect(luci.dispatcher.build_url("admin", "network", "easymesh")) + end +end + +-- ========================================================= +-- MESH SETTINGS +-- ========================================================= +s = m:section(TypedSection, "easymesh", translate("Mesh settings")) +s.anonymous = true + +o = s:option(Flag, "enabled", translate("Enable EasyMesh")) +o.default = "0"; o.rmempty = false + +o = s:option(ListValue, "role", translate("Mesh role")) +o:value("off", translate("Off")) +o:value("server", translate("Server (gateway - has internet)")) +o:value("client", translate("Client (uses mesh internet)")) +o:value("node", translate("Node (relay only)")) +o.default = "off"; o.rmempty = false + +o = s:option(ListValue, "band_mode", translate("Mesh mode"), + translate("Combined wireless band + wired backhaul. Wired variants use the port selected above.")) +o:value("single_5g", translate("5 GHz wireless only")) +o:value("single_2g", translate("2.4 GHz wireless only")) +o:value("dual", translate("Dual-band wireless only")) +o:value("single_5g_wired", translate("5 GHz wireless + Wired backhaul")) +o:value("single_2g_wired", translate("2.4 GHz wireless + Wired backhaul")) +o:value("dual_wired", translate("Dual-band wireless + Wired backhaul")) +o:value("none", translate("Wired only (no wireless mesh)")) +o:value("custom", translate("Custom radio")) +o.default = "single_5g" +o:depends("enabled", "1") + +local apRadio = s:option(ListValue, "custom_radio", translate("Custom mesh radio")) +uci:foreach("wireless", "wifi-device", function(sect) + local name = sect['.name'] + local band = sect.band or "?" + local label = name .. " (" + if band == "2g" then label = label .. "2.4 GHz" + elseif band == "5g" then label = label .. "5 GHz" + elseif band == "6g" then label = label .. "6 GHz" + else label = label .. band end + apRadio:value(name, label .. ")") +end) +apRadio:depends("band_mode", "custom") + +o = s:option(Value, "mesh_id", translate("Mesh ID"), + translate("Must match on all nodes. Ignored in wired-only mode.")) +o.default = "EasyMesh_Dualband"; o.datatype = "maxlength(32)"; o:depends("enabled", "1") + +o = s:option(Flag, "encryption", translate("Require password (WPA3-SAE)")) +o.default = "0"; o.rmempty = false; o:depends("enabled", "1") + +o = s:option(Value, "key", translate("Mesh password"), + translate("Must match on all nodes. Only used when 'Require password' is enabled.")) +o.default = "changeme123"; o.password = true; o.rmempty = false; o:depends("encryption", "1") + +o = s:option(Flag, "kvr", translate("Enable 802.11k/v/r fast roaming")) +o.default = "1"; o.rmempty = false; o:depends("enabled", "1") + +o = s:option(Value, "mobility_domain", translate("Mobility domain")) +o.default = "4f57"; o.datatype = "and(hexstring,rangelength(4,4))"; o:depends("kvr", "1") + +-- ========================================================= +-- CLIENT WIFI — 2.4 GHz +-- ========================================================= +local t24 = m:section(TypedSection, "easymesh", translate("Client WiFi — 2.4 GHz"), + translate("Longer range, lower speed. Use a different SSID from 5 GHz.")) +t24.anonymous = true + +o = t24:option(Flag, "ap_enabled_2g", translate("Broadcast 2.4 GHz")) +o.default = "1"; o.rmempty = false; o:depends("enabled", "1") + +o = t24:option(Value, "ap_ssid_2g", translate("2.4 GHz SSID")) +o.default = "EasyMesh_WiFi"; o.datatype = "maxlength(32)"; o:depends("ap_enabled_2g", "1") + +o = t24:option(ListValue, "ap_encryption_2g", translate("2.4 GHz security")) +o:value("none", translate("None (open)")) +o:value("psk2+ccmp", translate("WPA2-PSK")) +o:value("sae", translate("WPA3-SAE")) +o:value("sae-mixed", translate("WPA2/WPA3 transition")) +o.default = "sae-mixed"; o:depends("ap_enabled_2g", "1") + +o = t24:option(Value, "ap_key_2g", translate("2.4 GHz password")) +o.default = "changeme123"; o.password = true +o:depends("ap_encryption_2g", "psk2+ccmp") +o:depends("ap_encryption_2g", "sae") +o:depends("ap_encryption_2g", "sae-mixed") + +-- ========================================================= +-- CLIENT WIFI — 5 GHz +-- ========================================================= +local t5 = m:section(TypedSection, "easymesh", translate("Client WiFi — 5 GHz"), + translate("Shorter range, much faster. Use a different SSID from 2.4 GHz.")) +t5.anonymous = true + +o = t5:option(Flag, "ap_enabled_5g", translate("Broadcast 5 GHz")) +o.default = "1"; o.rmempty = false; o:depends("enabled", "1") + +o = t5:option(Value, "ap_ssid_5g", translate("5 GHz SSID")) +o.default = "EasyMesh_WiFi_5G"; o.datatype = "maxlength(32)"; o:depends("ap_enabled_5g", "1") + +o = t5:option(ListValue, "ap_encryption_5g", translate("5 GHz security")) +o:value("none", translate("None (open)")) +o:value("psk2+ccmp", translate("WPA2-PSK")) +o:value("sae", translate("WPA3-SAE")) +o:value("sae-mixed", translate("WPA2/WPA3 transition")) +o.default = "sae-mixed"; o:depends("ap_enabled_5g", "1") + +o = t5:option(Value, "ap_key_5g", translate("5 GHz password")) +o.default = "changeme123"; o.password = true +o:depends("ap_encryption_5g", "psk2+ccmp") +o:depends("ap_encryption_5g", "sae") +o:depends("ap_encryption_5g", "sae-mixed") + +local tn = m:section(TypedSection, "easymesh", translate("Client WiFi — shared")) +tn.anonymous = true +o = tn:option(ListValue, "ap_network", translate("Attach client WiFi to network")) +o.default = "lan" +uci:foreach("network", "interface", function(sect) + local name = sect['.name'] + if name then o:value(name, name) end +end) +o:depends("enabled", "1") + +-- ========================================================= +-- AP MODE +-- ========================================================= +local u = m:section(TypedSection, "easymesh", translate("AP Mode (Dumb AP)")) +u.anonymous = true + +o = u:option(Flag, "ap_mode", translate("Enable AP Mode (Dumb AP)")) +o.default = "0"; o.rmempty = false +o:depends("role", "client"); o:depends("role", "node") + +o = u:option(ListValue, "ip_mode", translate("IP mode")) +o:value("dhcp", translate("DHCP (from Server)")) +o:value("static", translate("Static IP")) +o.default = "static"; o:depends("ap_mode", "1") + +o = u:option(Value, "ipaddr", translate("IP address")) +o.default = "192.168.10.10"; o.datatype = "ip4addr"; o:depends("ip_mode", "static") +o = u:option(Value, "netmask", translate("Netmask")) +o.default = "255.255.255.0"; o.datatype = "ip4addr"; o:depends("ip_mode", "static") +o = u:option(Value, "gateway", translate("Gateway")) +o.default = "192.168.10.1"; o.datatype = "ip4addr"; o:depends("ip_mode", "static") +o = u:option(Value, "dns", translate("DNS server")) +o.default = "192.168.10.1"; o.datatype = "ip4addr"; o:depends("ip_mode", "static") + +-- ========================================================= +-- BOTTOM: Wired backhaul (source of truth) +-- ========================================================= +local w = m:section(TypedSection, "easymesh", translate("Wired backhaul"), + translate("Optional. Adds a physical Ethernet port as a batman hardif. " .. + "Required on BOTH sides for a wired link. " .. + "The port is auto-removed from the LAN bridge on apply.")) +w.anonymous = true + +o = w:option(ListValue, "wired_if", translate("Wired backhaul interface")) +o:value("", translate("Disabled")) +for _, p in ipairs(list_eth_ports()) do o:value(p, port_label(p)) end + +-- ========================================================= +-- Safety and actions +-- ========================================================= +local a = m:section(TypedSection, "easymesh", translate("Safety and actions")) +a.anonymous = true + +o = a:option(Flag, "safe_apply", translate("Safe apply (60-second rollback)")) +o.default = "0"; o.rmempty = false + +local reapply = a:option(Button, "_reapply", translate("Reapply EasyMesh Settings")) +reapply.inputtitle = translate("Reapply EasyMesh Settings") +reapply.inputstyle = "apply" +function reapply.write(self, section) + luci.sys.call("/etc/init.d/easymesh restart >/tmp/easymesh-reapply.log 2>&1") + luci.http.redirect(luci.dispatcher.build_url("admin", "network", "easymesh")) +end + +local restore = a:option(Button, "_restore", translate("Restore from last backup")) +restore.inputtitle = translate("Restore from last backup") +restore.inputstyle = "reset" +function restore.write(self, section) + luci.sys.call("/etc/init.d/easymesh restore >/tmp/easymesh-restore.log 2>&1") + luci.http.redirect(luci.dispatcher.build_url("admin", "network", "easymesh")) +end + +local pairbtn = a:option(Button, "_pair", translate("Pair with another router")) +pairbtn.inputtitle = translate("Enter pairing mode (2 minutes)") +pairbtn.inputstyle = "apply" +pairbtn.description = translate("WPS-style pairing. Press the WPS button on both routers within 2 minutes. Auto-reverts on timeout.") +function pairbtn.write(self, section) + luci.sys.call("/etc/init.d/easymesh pair >/tmp/easymesh-pair.log 2>&1 &") + luci.http.redirect(luci.dispatcher.build_url("admin", "network", "easymesh")) +end + +local confirm_section = m:section(SimpleSection) +confirm_section.template = "easymesh/preset_confirm" + +return m diff --git a/luci-app-easymesh/meta-packages/mbedtls/Makefile b/luci-app-easymesh/meta-packages/mbedtls/Makefile new file mode 100644 index 000000000..a20010249 --- /dev/null +++ b/luci-app-easymesh/meta-packages/mbedtls/Makefile @@ -0,0 +1,28 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-easymesh-wpad-mbedtls +PKG_VERSION:=2.4.1 +PKG_RELEASE:=1 + +PKG_LICENSE:=GPL-2.0 +PKG_MAINTAINER:=Arafat Rahman Zami Mondol + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-easymesh-wpad-mbedtls + SECTION:=luci + CATEGORY:=LuCI + SUBMENU:=3. Applications + TITLE:=EasyMesh — install with wpad-mesh-mbedtls + DEPENDS:=+luci-app-easymesh +wpad-mesh-mbedtls + PKGARCH:=all +endef + +define Build/Compile +endef + +define Package/luci-app-easymesh-wpad-mbedtls/install + # Meta-package — no files. +endef + +$(eval $(call BuildPackage,luci-app-easymesh-wpad-mbedtls)) diff --git a/luci-app-easymesh/meta-packages/openssl/Makefile b/luci-app-easymesh/meta-packages/openssl/Makefile new file mode 100644 index 000000000..893f4ff14 --- /dev/null +++ b/luci-app-easymesh/meta-packages/openssl/Makefile @@ -0,0 +1,28 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-easymesh-wpad-openssl +PKG_VERSION:=2.4.1 +PKG_RELEASE:=1 + +PKG_LICENSE:=GPL-2.0 +PKG_MAINTAINER:=Arafat Rahman Zami Mondol + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-easymesh-wpad-openssl + SECTION:=luci + CATEGORY:=LuCI + SUBMENU:=3. Applications + TITLE:=EasyMesh — install with wpad-mesh-openssl + DEPENDS:=+luci-app-easymesh +wpad-mesh-openssl + PKGARCH:=all +endef + +define Build/Compile +endef + +define Package/luci-app-easymesh-wpad-openssl/install + # Meta-package — no files. +endef + +$(eval $(call BuildPackage,luci-app-easymesh-wpad-openssl)) diff --git a/luci-app-easymesh/meta-packages/wolfssl/Makefile b/luci-app-easymesh/meta-packages/wolfssl/Makefile new file mode 100644 index 000000000..f2c83cbbb --- /dev/null +++ b/luci-app-easymesh/meta-packages/wolfssl/Makefile @@ -0,0 +1,28 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-easymesh-wpad-wolfssl +PKG_VERSION:=2.4.1 +PKG_RELEASE:=1 + +PKG_LICENSE:=GPL-2.0 +PKG_MAINTAINER:=Arafat Rahman Zami Mondol + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-easymesh-wpad-wolfssl + SECTION:=luci + CATEGORY:=LuCI + SUBMENU:=3. Applications + TITLE:=EasyMesh — install with wpad-mesh-wolfssl + DEPENDS:=+luci-app-easymesh +wpad-mesh-wolfssl + PKGARCH:=all +endef + +define Build/Compile +endef + +define Package/luci-app-easymesh-wpad-wolfssl/install + # Meta-package — no files. Pulls in luci-app-easymesh + wpad-mesh-wolfssl. +endef + +$(eval $(call BuildPackage,luci-app-easymesh-wpad-wolfssl)) diff --git a/luci-app-easymesh/po/bn_BD/easymesh.po b/luci-app-easymesh/po/bn_BD/easymesh.po new file mode 100644 index 000000000..98a4f757b --- /dev/null +++ b/luci-app-easymesh/po/bn_BD/easymesh.po @@ -0,0 +1,467 @@ +msgid "" +msgstr "" +"Project-Id-Version: luci-app-easymesh 2.4.1-r1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-09-19 12:00+0600\n" +"PO-Revision-Date: 2026-09-19 12:00+0600\n" +"Last-Translator: Arafat Rahman Zami Mondol \n" +"Language-Team: Bengali \n" +"Language: bn\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +# ========================================================= +# Main app title & description +# ========================================================= +msgid "EasyMesh" +msgstr "EasyMesh" + +msgid "Configure a Batman-adv mesh network." +msgstr "একটি Batman-adv মেশ নেটওয়ার্ক কনফিগার করুন।" + +# ========================================================= +# Dashboard status +# ========================================================= +msgid "Active mesh links" +msgstr "সক্রিয় মেশ সংযোগ" + +msgid "Interface" +msgstr "ইন্টারফেস" + +msgid "Neighbor MAC" +msgstr "প্রতিবেশী MAC" + +msgid "Last seen" +msgstr "সর্বশেষ দেখা" + +msgid "Latency" +msgstr "লেটেন্সি" + +msgid "RX rate" +msgstr "RX হার" + +msgid "TX rate" +msgstr "TX হার" + +# ========================================================= +# Wired backhaul details +# ========================================================= +msgid "Wired backhaul details" +msgstr "তারযুক্ত ব্যাকহল বিস্তারিত" + +msgid "State" +msgstr "অবস্থা" + +msgid "Cable" +msgstr "কেবল" + +msgid "Link speed" +msgstr "সংযোগ গতি" + +msgid "Priority weight" +msgstr "অগ্রাধিকার ওজন" + +msgid "In LAN bridge" +msgstr "LAN ব্রিজে" + +# ========================================================= +# Wired port picker +# ========================================================= +msgid "Wired backhaul port" +msgstr "তারযুক্ত ব্যাকহল পোর্ট" + +msgid "Preferred wired port" +msgstr "পছন্দের তারযুক্ত পোর্ট" + +msgid "(none — wired presets will skip)" +msgstr "(কোনোটি নয় — তারযুক্ত প্রিসেট এড়িয়ে যাবে)" + +# ========================================================= +# Boot-time reliability / Cron +# ========================================================= +msgid "Boot-time reliability" +msgstr "বুট-সময় নির্ভরযোগ্যতা" + +msgid "Enable cron fallback" +msgstr "cron ফলব্যাক সক্রিয় করুন" + +# ========================================================= +# Link priority weights (new) +# ========================================================= +msgid "Link priority weights" +msgstr "সংযোগ অগ্রাধিকার ওজন" + +msgid "Wired (Kbit/s)" +msgstr "তারযুক্ত (Kbit/s)" + +msgid "6 GHz wireless (Kbit/s)" +msgstr "6 GHz বেতার (Kbit/s)" + +msgid "5 GHz wireless (Kbit/s)" +msgstr "5 GHz বেতার (Kbit/s)" + +msgid "2.4 GHz wireless (Kbit/s)" +msgstr "2.4 GHz বেতার (Kbit/s)" + +# ========================================================= +# Mesh VLANs (new) +# ========================================================= +msgid "Mesh VLANs" +msgstr "মেশ VLAN" + +msgid "VLAN IDs (comma-separated)" +msgstr "VLAN ID (কমা-বিভক্ত)" + +# ========================================================= +# Quick Setup +# ========================================================= +msgid "Quick Setup" +msgstr "দ্রুত সেটআপ" + +msgid "1. Server — 5 GHz wireless only" +msgstr "১. সার্ভার — শুধু 5 GHz বেতার" + +msgid "2. Server — 2.4 GHz wireless only" +msgstr "২. সার্ভার — শুধু 2.4 GHz বেতার" + +msgid "3. Server — Dual-band wireless only" +msgstr "৩. সার্ভার — শুধু দ্বি-ব্যান্ড বেতার" + +msgid "4. Server — Wired backhaul only" +msgstr "৪. সার্ভার — শুধু তারযুক্ত ব্যাকহল" + +msgid "5. Server — 5 GHz + Wired" +msgstr "৫. সার্ভার — 5 GHz + তারযুক্ত" + +msgid "6. Server — 2.4 GHz + Wired" +msgstr "৬. সার্ভার — 2.4 GHz + তারযুক্ত" + +msgid "7. Server — Dual-band + Wired" +msgstr "৭. সার্ভার — দ্বি-ব্যান্ড + তারযুক্ত" + +msgid "8. Client — 5 GHz wireless only" +msgstr "৮. ক্লায়েন্ট — শুধু 5 GHz বেতার" + +msgid "9. Client — 2.4 GHz wireless only" +msgstr "৯. ক্লায়েন্ট — শুধু 2.4 GHz বেতার" + +msgid "10. Client — Dual-band wireless only" +msgstr "১০. ক্লায়েন্ট — শুধু দ্বি-ব্যান্ড বেতার" + +msgid "11. Client — Wired backhaul only" +msgstr "১১. ক্লায়েন্ট — শুধু তারযুক্ত ব্যাকহল" + +msgid "12. Client — 5 GHz + Wired fallback" +msgstr "১২. ক্লায়েন্ট — 5 GHz + তারযুক্ত ফলব্যাক" + +msgid "13. Client — 2.4 GHz + Wired fallback" +msgstr "১৩. ক্লায়েন্ট — 2.4 GHz + তারযুক্ত ফলব্যাক" + +msgid "14. Client — Dual-band + Wired fallback" +msgstr "১৪. ক্লায়েন্ট — দ্বি-ব্যান্ড + তারযুক্ত ফলব্যাক" + +msgid "15. Node — 5 GHz relay" +msgstr "১৫. নোড — 5 GHz রিলে" + +msgid "16. Node — Dual-band relay" +msgstr "১৬. নোড — দ্বি-ব্যান্ড রিলে" + +msgid "17. Node — Wired relay only" +msgstr "১৭. নোড — শুধু তারযুক্ত রিলে" + +msgid "18. Detach wired backhaul" +msgstr "১৮. তারযুক্ত ব্যাকহল বিচ্ছিন্ন করুন" + +msgid "19. Disable mesh" +msgstr "১৯. মেশ বন্ধ করুন" + +# Preset descriptions +msgid "[SERVER] Gateway. Extenders join via 5 GHz mesh only." +msgstr "[সার্ভার] গেটওয়ে। এক্সটেন্ডারগুলি শুধু 5 GHz মেশ দিয়ে যুক্ত হবে।" + +msgid "[SERVER] Extenders join via 2.4 GHz (longer range)." +msgstr "[সার্ভার] এক্সটেন্ডারগুলি 2.4 GHz দিয়ে যুক্ত হবে (দীর্ঘ পরিসর)।" + +msgid "[SERVER] Both radios carry backhaul." +msgstr "[সার্ভার] উভয় রেডিও ব্যাকহল বহন করবে।" + +msgid "[SERVER] No wireless mesh — extenders connect by cable." +msgstr "[সার্ভার] কোনো বেতার মেশ নেই — এক্সটেন্ডারগুলি কেবল দিয়ে যুক্ত হবে।" + +msgid "[SERVER] 5 GHz mesh primary, Ethernet parallel." +msgstr "[সার্ভার] 5 GHz মেশ প্রাথমিক, ইথারনেট সমান্তরাল।" + +msgid "[SERVER] 2.4 GHz mesh plus Ethernet." +msgstr "[সার্ভার] 2.4 GHz মেশ এবং ইথারনেট।" + +msgid "[SERVER] Maximum redundancy. Both radios + Ethernet." +msgstr "[সার্ভার] সর্বোচ্চ রিডান্ডেন্সি। উভয় রেডিও + ইথারনেট।" + +msgid "[CLIENT] Extender via 5 GHz. No cable." +msgstr "[ক্লায়েন্ট] 5 GHz দিয়ে এক্সটেন্ডার। কেবল নেই।" + +msgid "[CLIENT] Extender via 2.4 GHz." +msgstr "[ক্লায়েন্ট] 2.4 GHz দিয়ে এক্সটেন্ডার।" + +msgid "[CLIENT] Extender via both radios." +msgstr "[ক্লায়েন্ট] উভয় রেডিও দিয়ে এক্সটেন্ডার।" + +msgid "[CLIENT] Cable only, no wireless mesh." +msgstr "[ক্লায়েন্ট] শুধু কেবল, কোনো বেতার মেশ নেই।" + +msgid "[CLIENT] 5 GHz primary, Ethernet fallback." +msgstr "[ক্লায়েন্ট] 5 GHz প্রাথমিক, ইথারনেট ফলব্যাক।" + +msgid "[CLIENT] 2.4 GHz primary, Ethernet fallback." +msgstr "[ক্লায়েন্ট] 2.4 GHz প্রাথমিক, ইথারনেট ফলব্যাক।" + +msgid "[CLIENT] Both radios + Ethernet. Maximum redundancy." +msgstr "[ক্লায়েন্ট] উভয় রেডিও + ইথারনেট। সর্বোচ্চ রিডান্ডেন্সি।" + +msgid "[NODE] Relay only, 5 GHz. No client SSID." +msgstr "[নোড] শুধু রিলে, 5 GHz। কোনো ক্লায়েন্ট SSID নেই।" + +msgid "[NODE] Relay only, both radios." +msgstr "[নোড] শুধু রিলে, উভয় রেডিও।" + +msgid "[NODE] Relay via cable only. No wireless." +msgstr "[নোড] শুধু কেবল দিয়ে রিলে। বেতার নেই।" + +msgid "Removes Ethernet backhaul, restores port to bridge." +msgstr "ইথারনেট ব্যাকহল সরায়, পোর্ট ব্রিজে ফিরিয়ে দেয়।" + +msgid "Turns off mesh. Radios stay enabled." +msgstr "মেশ বন্ধ করে। রেডিও চালু থাকে।" + +# ========================================================= +# Mesh settings +# ========================================================= +msgid "Mesh settings" +msgstr "মেশ সেটিংস" + +msgid "Enable EasyMesh" +msgstr "EasyMesh সক্রিয় করুন" + +msgid "Mesh role" +msgstr "মেশ ভূমিকা" + +msgid "Off" +msgstr "বন্ধ" + +msgid "Server (gateway - has internet)" +msgstr "সার্ভার (গেটওয়ে - ইন্টারনেট আছে)" + +msgid "Client (uses mesh internet)" +msgstr "ক্লায়েন্ট (মেশ ইন্টারনেট ব্যবহার করে)" + +msgid "Node (relay only)" +msgstr "নোড (শুধু রিলে)" + +msgid "Mesh mode" +msgstr "মেশ মোড" + +msgid "5 GHz wireless only" +msgstr "শুধু 5 GHz বেতার" + +msgid "2.4 GHz wireless only" +msgstr "শুধু 2.4 GHz বেতার" + +msgid "Dual-band wireless only" +msgstr "শুধু দ্বি-ব্যান্ড বেতার" + +msgid "5 GHz wireless + Wired backhaul" +msgstr "5 GHz বেতার + তারযুক্ত ব্যাকহল" + +msgid "2.4 GHz wireless + Wired backhaul" +msgstr "2.4 GHz বেতার + তারযুক্ত ব্যাকহল" + +msgid "Dual-band wireless + Wired backhaul" +msgstr "দ্বি-ব্যান্ড বেতার + তারযুক্ত ব্যাকহল" + +msgid "Wired only (no wireless mesh)" +msgstr "শুধু তারযুক্ত (কোনো বেতার মেশ নেই)" + +msgid "Custom radio" +msgstr "কাস্টম রেডিও" + +msgid "Custom mesh radio" +msgstr "কাস্টম মেশ রেডিও" + +msgid "Mesh ID" +msgstr "মেশ ID" + +msgid "Require password (WPA3-SAE)" +msgstr "পাসওয়ার্ড আবশ্যক (WPA3-SAE)" + +msgid "Mesh password" +msgstr "মেশ পাসওয়ার্ড" + +msgid "Enable 802.11k/v/r fast roaming" +msgstr "802.11k/v/r দ্রুত রোমিং সক্রিয় করুন" + +msgid "Mobility domain" +msgstr "মোবিলিটি ডোমেইন" + +# ========================================================= +# Client WiFi +# ========================================================= +msgid "Client WiFi — 2.4 GHz" +msgstr "ক্লায়েন্ট WiFi — 2.4 GHz" + +msgid "Broadcast 2.4 GHz" +msgstr "2.4 GHz সম্প্রচার করুন" + +msgid "2.4 GHz SSID" +msgstr "2.4 GHz SSID" + +msgid "2.4 GHz security" +msgstr "2.4 GHz নিরাপত্তা" + +msgid "None (open)" +msgstr "কোনোটি নয় (খোলা)" + +msgid "WPA2-PSK" +msgstr "WPA2-PSK" + +msgid "WPA3-SAE" +msgstr "WPA3-SAE" + +msgid "WPA2/WPA3 transition" +msgstr "WPA2/WPA3 ট্রানজিশন" + +msgid "2.4 GHz password" +msgstr "2.4 GHz পাসওয়ার্ড" + +msgid "Client WiFi — 5 GHz" +msgstr "ক্লায়েন্ট WiFi — 5 GHz" + +msgid "Broadcast 5 GHz" +msgstr "5 GHz সম্প্রচার করুন" + +msgid "5 GHz SSID" +msgstr "5 GHz SSID" + +msgid "5 GHz security" +msgstr "5 GHz নিরাপত্তা" + +msgid "5 GHz password" +msgstr "5 GHz পাসওয়ার্ড" + +msgid "Client WiFi — shared" +msgstr "ক্লায়েন্ট WiFi — শেয়ার্ড" + +msgid "Attach client WiFi to network" +msgstr "ক্লায়েন্ট WiFi নেটওয়ার্কে সংযুক্ত করুন" + +# ========================================================= +# AP Mode +# ========================================================= +msgid "AP Mode (Dumb AP)" +msgstr "AP মোড (Dumb AP)" + +msgid "Enable AP Mode (Dumb AP)" +msgstr "AP মোড সক্রিয় করুন (Dumb AP)" + +msgid "IP mode" +msgstr "IP মোড" + +msgid "DHCP (from Server)" +msgstr "DHCP (সার্ভার থেকে)" + +msgid "Static IP" +msgstr "স্থির IP" + +msgid "IP address" +msgstr "IP ঠিকানা" + +msgid "Netmask" +msgstr "নেটমাস্ক" + +msgid "Gateway" +msgstr "গেটওয়ে" + +msgid "DNS server" +msgstr "DNS সার্ভার" + +# ========================================================= +# Wired backhaul +# ========================================================= +msgid "Wired backhaul" +msgstr "তারযুক্ত ব্যাকহল" + +msgid "Wired backhaul interface" +msgstr "তারযুক্ত ব্যাকহল ইন্টারফেস" + +msgid "Disabled" +msgstr "নিষ্ক্রিয়" + +# ========================================================= +# Safety and actions +# ========================================================= +msgid "Safety and actions" +msgstr "নিরাপত্তা ও ক্রিয়া" + +msgid "Safe apply (60-second rollback)" +msgstr "নিরাপদ প্রয়োগ (৬০-সেকেন্ড রোলব্যাক)" + +msgid "Reapply EasyMesh Settings" +msgstr "EasyMesh সেটিংস পুনঃপ্রয়োগ করুন" + +msgid "Restore from last backup" +msgstr "সর্বশেষ ব্যাকআপ থেকে পুনরুদ্ধার করুন" + +msgid "Pair with another router" +msgstr "অন্য রাউটারের সাথে পেয়ার করুন" + +msgid "Enter pairing mode (2 minutes)" +msgstr "পেয়ারিং মোডে প্রবেশ করুন (২ মিনিট)" + +# ========================================================= +# Long descriptions +# ========================================================= +msgid "Optional. Adds a physical Ethernet port as a batman hardif. Required on BOTH sides for a wired link. The port is auto-removed from the LAN bridge on apply." +msgstr "ঐচ্ছিক। একটি ফিজিক্যাল ইথারনেট পোর্ট batman hardif হিসেবে যোগ করে। তারযুক্ত সংযোগের জন্য উভয় পাশে প্রয়োজন। প্রয়োগের সময় পোর্ট স্বয়ংক্রিয়ভাবে LAN ব্রিজ থেকে সরানো হয়।" + +msgid "WPS-style pairing. Press the WPS button on both routers within 2 minutes. Auto-reverts on timeout." +msgstr "WPS-স্টাইল পেয়ারিং। ২ মিনিটের মধ্যে উভয় রাউটারে WPS বোতাম চাপুন। সময় শেষ হলে স্বয়ংক্রিয়ভাবে ফিরে যায়।" + +msgid "Batman-adv throughput_override values in Kbit/s. Higher = preferred. Set to 0 to let batman auto-decide per link. Defaults: wired=1000000, 6G=900000, 5G=600000, 2.4G=30000. Changes apply on next Save & Apply (or 'Reapply EasyMesh Settings')." +msgstr "Batman-adv throughput_override মান Kbit/s-এ। উচ্চতর = পছন্দের। প্রতিটি লিংকে batman-কে স্বয়ংক্রিয় সিদ্ধান্ত নিতে দিতে 0 সেট করুন। ডিফল্ট: তারযুক্ত=1000000, 6G=900000, 5G=600000, 2.4G=30000। পরিবর্তনগুলি পরবর্তী Save & Apply-এ (বা 'EasyMesh সেটিংস পুনঃপ্রয়োগ করুন') কার্যকর হয়।" + +msgid "Extend guest / IoT / hotspot networks across the mesh. Enter a comma-separated list of VLAN IDs (e.g. 10,20,30). The init script creates bat0. as an 8021q device for each ID. If a bridge named br-, br-lan, br-vlan, br-guest, or br-iot already exists, it is auto-added to that bridge. Otherwise, add the bat0. device manually in Network → Interfaces → Devices. Requires 802.1Q-capable switches on the physical wired backhaul." +msgstr "মেশ জুড়ে অতিথি / IoT / হটস্পট নেটওয়ার্ক প্রসারিত করুন। কমা-বিভক্ত VLAN ID তালিকা লিখুন (যেমন 10,20,30)। init লিপি প্রতিটি ID-এর জন্য bat0. কে একটি 8021q ডিভাইস হিসেবে তৈরি করে। যদি br-, br-lan, br-vlan, br-guest, বা br-iot নামের একটি ব্রিজ ইতিমধ্যে বিদ্যমান থাকে, তবে সেটি স্বয়ংক্রিয়ভাবে সেই ব্রিজে যোগ করা হয়। অন্যথায়, Network → Interfaces → Devices-এ ম্যানুয়ালি bat0. ডিভাইস যোগ করুন। ফিজিক্যাল তারযুক্ত ব্যাকহলে 802.1Q-সক্ষম সুইচ প্রয়োজন।" + +msgid "The init script applies link priorities at boot and 90s later. On some hardware, mesh interfaces appear even later, leaving priority weight at 0. Enable cron fallback to reapply priority every minute. Safe (idempotent) but runs in the background. When disabled, the cron line is commented out in /etc/crontabs/root." +msgstr "init লিপি বুটে এবং 90 সেকেন্ড পরে সংযোগ অগ্রাধিকার প্রয়োগ করে। কিছু হার্ডওয়্যারে, মেশ ইন্টারফেস আরও পরে দেখা দেয়, ফলে অগ্রাধিকার ওজন 0-এ থাকে। প্রতি মিনিটে অগ্রাধিকার পুনঃপ্রয়োগ করতে cron ফলব্যাক সক্রিয় করুন। নিরাপদ (idempotent) কিন্তু ব্যাকগ্রাউন্ডে চলে। নিষ্ক্রিয় থাকলে, /etc/crontabs/root-এ cron লাইনটি মন্তব্য করা থাকে।" + +msgid "Convenience selector. Mirrors 'Wired backhaul interface' below. Presets that use wired backhaul will remove this port from the LAN bridge. Pick a port NOT used by your uplink or modem. Leave empty to disable wired backhaul entirely." +msgstr "সুবিধাজনক নির্বাচক। নিচের 'তারযুক্ত ব্যাকহল ইন্টারফেস' এর সাথে মিলে যায়। তারযুক্ত ব্যাকহল ব্যবহার করে এমন প্রিসেট এই পোর্টটি LAN ব্রিজ থেকে সরিয়ে দেবে। আপনার আপলিংক বা মডেম দ্বারা ব্যবহৃত নয় এমন একটি পোর্ট বাছুন। তারযুক্ত ব্যাকহল সম্পূর্ণভাবে নিষ্ক্রিয় করতে খালি রাখুন।" + +msgid "Must match on all nodes. Ignored in wired-only mode." +msgstr "সমস্ত নোডে অবশ্যই মিলতে হবে। শুধু তারযুক্ত মোডে উপেক্ষা করা হয়।" + +msgid "Must match on all nodes. Only used when 'Require password' is enabled." +msgstr "সমস্ত নোডে অবশ্যই মিলতে হবে। শুধু 'পাসওয়ার্ড আবশ্যক' সক্রিয় থাকলে ব্যবহৃত হয়।" + +msgid "Click a preset. A confirmation dialog will appear. Current config is backed up automatically before applying. Grouped by role: SERVER (has internet) → CLIENT (uses mesh) → NODE (relay only)." +msgstr "একটি প্রিসেট ক্লিক করুন। একটি নিশ্চিতকরণ ডায়ালগ আসবে। প্রয়োগের আগে বর্তমান কনফিগার স্বয়ংক্রিয়ভাবে ব্যাকআপ করা হয়। ভূমিকা অনুযায়ী গোষ্ঠীভুক্ত: সার্ভার (ইন্টারনেট আছে) → ক্লায়েন্ট (মেশ ব্যবহার করে) → নোড (শুধু রিলে)।" + +msgid "Longer range, lower speed. Use a different SSID from 5 GHz." +msgstr "দীর্ঘ পরিসর, কম গতি। 5 GHz থেকে একটি ভিন্ন SSID ব্যবহার করুন।" + +msgid "Shorter range, much faster. Use a different SSID from 2.4 GHz." +msgstr "ছোট পরিসর, অনেক দ্রুত। 2.4 GHz থেকে একটি ভিন্ন SSID ব্যবহার করুন।" + + + + +# ========================================================= +# Additional descriptions +# ========================================================= +msgid "Combined wireless band + wired backhaul. Wired variants use the port selected above." +msgstr "সম্মিলিত বেতার ব্যান্ড + তারযুক্ত ব্যাকহল। তারযুক্ত ভেরিয়েন্ট উপরে নির্বাচিত পোর্ট ব্যবহার করে।" + +msgid "Example: 10,20,30 — Leave empty to disable" +msgstr "উদাহরণ: 10,20,30 — নিষ্ক্রিয় করতে খালি রাখুন" diff --git a/luci-app-easymesh/po/zh-cn b/luci-app-easymesh/po/zh-cn new file mode 120000 index 000000000..8d69574dd --- /dev/null +++ b/luci-app-easymesh/po/zh-cn @@ -0,0 +1 @@ +zh_Hans \ No newline at end of file diff --git a/luci-app-easymesh/po/zh_Hans/easymesh.po b/luci-app-easymesh/po/zh_Hans/easymesh.po new file mode 100755 index 000000000..847181b94 --- /dev/null +++ b/luci-app-easymesh/po/zh_Hans/easymesh.po @@ -0,0 +1,95 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8\n" + +msgid "easymesh" +msgstr "easymesh" + +msgid "Active node" +msgstr "活动节点" + +msgid "IF" +msgstr "IF" + +msgid "Neighbor" +msgstr "节点邻居设备" + +msgid "lastseen" +msgstr "上次连接延时" + +msgid "EASY MESH" +msgstr "简单MESH" + +msgid "Settings" +msgstr "设置" + +msgid "General Settings" +msgstr "基本设置" + +msgid "Enable" +msgstr "启用" + +msgid "role" +msgstr "角色" + +msgid "off" +msgstr "关闭" + +msgid "host MESH" +msgstr "主MESH" + +msgid "son MESH" +msgstr "子MESH" + +msgid "MESH Radio device" +msgstr "MESH无线设备" + +msgid "The radio device which MESH use" +msgstr "使用MESH组网的无线设备" + +msgid "AUTO" +msgstr "自动" + +msgid "Enable or disable EASY MESH" +msgstr "启用或禁用简单MESH" + +msgid "Encryption" +msgstr "加密" + +msgid "Key" +msgstr "密码" + +msgid "inter-access point protocol" +msgstr "接入点内部协议" + +msgid "Wireless Access Points (APs) running on different vendors can communicate with each other" +msgstr "运行在不同供应商的无线访问点(AP)可以相互交流" + +msgid "Threshold for an good RSSI" +msgstr "快速漫游接入值" + +msgid "Threshold for an bad RSSI" +msgstr "快速漫游踢出值" + +msgid "Threshold for a good RSSI" +msgstr "优良RSSI信号阈值" + +msgid "Threshold for a bad RSSI" +msgstr "不良RSSI信号阈值" + +msgid "AP MODE Enable" +msgstr "启用AP模式" + +msgid "Enable or disable AP MODE" +msgstr "启用或禁用AP模式" + +msgid "IPv4-Address" +msgstr "IPv4 地址" + +msgid "IPv4 netmask" +msgstr "IPv4 子网掩码" + +msgid "IPv4 gateway" +msgstr "IPv4 网关" + +msgid "Use custom DNS servers" +msgstr "使用自定义的 DNS 服务器" diff --git a/luci-app-easymesh/releases/2.4.1-r1/easymesh-pkg-2.4.1-r1.zip b/luci-app-easymesh/releases/2.4.1-r1/easymesh-pkg-2.4.1-r1.zip new file mode 100644 index 000000000..1dec78918 Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/easymesh-pkg-2.4.1-r1.zip differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-2.4.1-r1-full.tar.gz b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-2.4.1-r1-full.tar.gz new file mode 100644 index 000000000..21563c7bc Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-2.4.1-r1-full.tar.gz differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-2.4.1-r1.apk b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-2.4.1-r1.apk new file mode 100644 index 000000000..86aec6c5f Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-2.4.1-r1.apk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-mbedtls-2.4.1-r1.apk b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-mbedtls-2.4.1-r1.apk new file mode 100644 index 000000000..f3aabbd0b Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-mbedtls-2.4.1-r1.apk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-mbedtls_2.4.1-r1_all.ipk b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-mbedtls_2.4.1-r1_all.ipk new file mode 100644 index 000000000..5fb27e1c1 Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-mbedtls_2.4.1-r1_all.ipk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-openssl-2.4.1-r1.apk b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-openssl-2.4.1-r1.apk new file mode 100644 index 000000000..d3480b836 Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-openssl-2.4.1-r1.apk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk new file mode 100644 index 000000000..a3a71e71f Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-openssl_2.4.1-r1_all.ipk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-wolfssl-2.4.1-r1.apk b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-wolfssl-2.4.1-r1.apk new file mode 100644 index 000000000..ff8f8a3a7 Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-wolfssl-2.4.1-r1.apk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-wolfssl_2.4.1-r1_all.ipk b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-wolfssl_2.4.1-r1_all.ipk new file mode 100644 index 000000000..33929ba74 Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh-wpad-wolfssl_2.4.1-r1_all.ipk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh_2.4.1-r1_all.ipk b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh_2.4.1-r1_all.ipk new file mode 100644 index 000000000..e8da0d9b5 Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-app-easymesh_2.4.1-r1_all.ipk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-bn-26.184.37259~7cad107.apk b/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-bn-26.184.37259~7cad107.apk new file mode 100644 index 000000000..30ec344f4 Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-bn-26.184.37259~7cad107.apk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-bn_26.184.37259~7cad107_all.ipk b/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-bn_26.184.37259~7cad107_all.ipk new file mode 100644 index 000000000..826b28885 Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-bn_26.184.37259~7cad107_all.ipk differ diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-zh-cn-26.184.37259~7cad107.apk b/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-zh-cn-26.184.37259~7cad107.apk new file mode 100644 index 000000000..980664d9d --- /dev/null +++ b/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-zh-cn-26.184.37259~7cad107.apk @@ -0,0 +1,7 @@ +ADBdåV{PTU?°ÄÚê2f–:0_%{ÁTŒ¤$aJÜvqÊ ñ²{aWwïÞî½[Xb«© ‰�i˜’Šƒµ’,å£6’f©e–f¥Ӌ©½w—²Éj2j*ûÎÞ#2 ã¿Ítf¾=ûûÞßw^73ë>£`]\²R�"CУÎaN�ՑìH�Ê's¬´ÄÅIöä'íÉV~ÈÄ)Æԩ“Œw¦Mœ|ײ4+kKMIËÎY^r²²ÃÍãb·ˆ#֬ ôãdò{ƒíÏMê åxœÅᜎbgÃ3ìž“¸񱼛­výLÓÉ�)“»ÛÅ1O².‡ËÍÛÜN¦'“"�Ãic$ÑÊô ”�)²ŬŒͬÝÅòø°Ƴ#æx‰ q;Ý#q¢däÝ"'8—K²ÝSd´º]÷L¶˲ ¥3ÌeÓ׺ì§£ȊP4ô0# +y÷SÐd€LsVê\4âj½Nfù%H³‘Á†Ì@ˆ[àø'D9]⬤ãĞê5€^ŒèvËð?èÇ(x‡iL2Žƒ'[ Ÿ`|HüÈØcM±úwJ +Žu˜šõý‰ï¾8Xq[µuâÑóÔ_C/±€ ä�:ÍÅŒï~<øfHI6®˜õ8e‰æ6􈏬h-æÀuÐ2’‡@yzÀ +,&µ7ÌÏý}GYiÑɴ¾hW]ýkܠ�B;žÈã<‘<7DkyΥyîOc1k]̖p—òü9õ÷æ´÷¬ßßn¬¬ðæŽþåâ¼óßéºÆԲh|¯`zgâ”ÆgÿmˆÖ3»RjÓ&I —UÖtªïÞq,ü/@Ú[õa‰“#¶ÑÉò%rÂEThå3Æ^óÉ{7&àyt94'P4‹´·�øgú¾ÌÕZj`®xu1ý/Gíß9¸—Ò8ê¾V¥Ò®\Zñ–ö۵¢Uy}UØ@i(G(¨Twׇ;¶ªëv*­o¨µo Õ)Ugg[fÁ;è?j÷#¤lkQëvkH©imð�ì•õá5{@¶joWmñÕ1Ršj"3ùQk·‡Z:´hÁö°#ÜPëÊPûs¡Í+{‹Q¸¼rÿjÅ ¡——ßÞé ¥r»Ҽææաíˡ>H{ÕN(KY½ªëŸRUöûÉûÝQ®_«Ôù‚�*¥fmðÀ¥eƒ²qu¯˜]µ~0—i¯T7[w†¶‚-ëƒ- êþåJǫ]Þmj۫j  ÅP¼m$~/~8°C)oÑøÁ¶Má5Íf‹%'ØQ¯T¿ٵ©""€ðWäk5fšT_½ÒVݯæAŽéñIX©{MyÉKÿ7Õ@ëÔu»"åk<`À ­¿¤‹þÍÁ·Ö@©8+ςպ*¥²^yÑ]{ô9m[DÙÉSf¶l/Ý&d º¡JÜBå° +è¦Ñé1˩œ´~ÐcÜ*'çq`^çl‘âs0ã‹«S)>ó`óÅ̑ÔÞ ¤_päé/)&ö7.<^6Œâé@±6¼ë0ÅlÏÿc4œ€ ¥‡âçSœOäëâbŠm¿”kø‰bÂOØ*FýEm‚9n۴—½T¾”|l;ûÍXŠÉg¡ÞwúÔ~Š%þ|¡¹ïºùµáò)ê�øºÿ¡¹f*ï&5\͙Tn"õ,¹¡‘ÊÉóžð¾anÅk�n=-ͣõDM%ú +>_DågH|uazŻ ¾x×_'©þۤÿðõBü¢ \ No newline at end of file diff --git a/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-zh-cn_26.184.37259~7cad107_all.ipk b/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-zh-cn_26.184.37259~7cad107_all.ipk new file mode 100644 index 000000000..046d70179 Binary files /dev/null and b/luci-app-easymesh/releases/2.4.1-r1/luci-i18n-easymesh-zh-cn_26.184.37259~7cad107_all.ipk differ diff --git a/luci-app-easymesh/root/etc/config/easymesh b/luci-app-easymesh/root/etc/config/easymesh new file mode 100755 index 000000000..3446d21c1 --- /dev/null +++ b/luci-app-easymesh/root/etc/config/easymesh @@ -0,0 +1,3 @@ + +config easymesh 'config' + option enabled '0' diff --git a/luci-app-easymesh/root/etc/hotplug.d/iface/30-easymesh-priority b/luci-app-easymesh/root/etc/hotplug.d/iface/30-easymesh-priority new file mode 100755 index 000000000..1b2dee301 --- /dev/null +++ b/luci-app-easymesh/root/etc/hotplug.d/iface/30-easymesh-priority @@ -0,0 +1,29 @@ +#!/bin/sh +# Reapply link priorities when a mesh or wired-backhaul interface comes up. +# Runs once per interface event. Idempotent — safe to call repeatedly. + +[ "$ACTION" = "ifup" ] || exit 0 + +case "$INTERFACE" in + nwi_mesh_*|nwi_mesh_wired) + LOCK="/tmp/easymesh-priority.lock" + + # Stale lock cleanup — if the lock is older than 5 minutes, remove it + if [ -d "$LOCK" ]; then + lock_age=$(($(date +%s) - $(date -r "$LOCK" +%s 2>/dev/null || echo 0))) + [ "$lock_age" -gt 300 ] && rmdir "$LOCK" 2>/dev/null + fi + + # Atomic lock acquisition — only one priority run at a time + if mkdir "$LOCK" 2>/dev/null; then + ( + sleep 5 + [ -x /etc/init.d/easymesh ] && \ + /etc/init.d/easymesh priority >/dev/null 2>&1 + rmdir "$LOCK" 2>/dev/null + ) /dev/null 2>&1 & + fi + ;; +esac + +exit 0 diff --git a/luci-app-easymesh/root/etc/init.d/easymesh b/luci-app-easymesh/root/etc/init.d/easymesh new file mode 100755 index 000000000..f1de6beea --- /dev/null +++ b/luci-app-easymesh/root/etc/init.d/easymesh @@ -0,0 +1,994 @@ +#!/bin/sh /etc/rc.common +# +# EasyMesh init script +# Copyright (C) 2021 dz +# Copyright (C) 2026 Arafat Rahman Zami Mondol +# +# Modified from mobing8/luci-app-easymesh-dawn. +# Additions: 90-second priority retry loop, second-pass catchup, +# optional cron fallback, hotplug integration, +# robust UCI handling (no escaped quotes), safe role sanitation. + +START=99 +STOP=70 +USE_PROCD=0 + +# --- Custom subcommands ------------------------------------------------- +EXTRA_COMMANDS="backup restore detach_wired pair rollback_start rollback_confirm rollback_cancel priority" +EXTRA_HELP=" backup Save current config + restore Restore from last backup + detach_wired Remove wired backhaul + pair Enter WPS pairing mode + rollback_start Start rollback watchdog + rollback_confirm Confirm pending changes + rollback_cancel Revert immediately" + +# --- Constants ---------------------------------------------------------- +LOG_TAG="easymesh" +BACKUP_DIR="/etc/easymesh-backups" +MAX_BACKUPS=10 +PAIR_TIMEOUT=120 +PAIRING_MESH_ID="EasyMesh_PAIRING" +RB_DIR="/tmp/easymesh-rollback" +SAVED_PORTS_FILE="$BACKUP_DIR/original-ports.txt" + +log() { logger -t "$LOG_TAG" "$@"; echo "[$LOG_TAG] $*" >&2; } + +# ========================================================= +# Helpers +# ========================================================= +list_radios() { + uci show wireless 2>/dev/null | \ + sed -n 's/^wireless\.\(radio[0-9]*\)=wifi-device$/\1/p' +} + +radio_band() { uci -q get "wireless.$1.band"; } + +find_radio_by_band() { + local r + for r in $(list_radios); do + [ "$(radio_band "$r")" = "$1" ] && { echo "$r"; return 0; } + done + return 1 +} + +check_prereqs() { + local ok=1 + if ! lsmod 2>/dev/null | grep -q '^batman_adv'; then + log "ERROR: batman_adv module not loaded" + ok=0 + fi + command -v batctl >/dev/null 2>&1 || { log "ERROR: batctl missing"; ok=0; } + [ "$ok" = "1" ] +} + +find_lan_bridge() { + local dev + dev=$(uci -q get network.@device[0].name) + [ -n "$dev" ] && { echo "$dev"; return; } + echo "br-lan" +} + +# ========================================================= +# Wait for mesh VIFs and attach to bat0 +# ========================================================= +wait_and_attach_mesh() { + # Wired-only: no VIFs to wait for + if [ "$BAND_MODE" = "none" ]; then + log "Wired-only mode — skipping mesh VIF wait" + ifup bat0 + sleep 3 + return 0 + fi + + # Determine expected VIF count from the base band (strip _wired) + local base=$(echo "$BAND_MODE" | sed 's/_wired$//') + local expected + case "$base" in + dual) expected=2 ;; + custom) expected=1 ;; + *) expected=1 ;; + esac + + log "Waiting for mesh VIFs (expect $expected)..." + local tries=0 + + while [ $tries -lt 30 ]; do + local mesh_count=$(iw dev 2>/dev/null | grep -c "type mesh point") + [ "$mesh_count" -ge "$expected" ] && break + sleep 2 + tries=$((tries + 1)) + done + log "Mesh VIFs present: $(iw dev 2>/dev/null | grep -c 'type mesh point')/$expected" + + ifup bat0 + sleep 3 + + # Attach any mesh VIFs netifd missed + local iface + for iface in $(iw dev 2>/dev/null | awk '/Interface/{print $2}' | grep mesh); do + if ! batctl if 2>/dev/null | grep -q "^${iface}:"; then + log "Manually attaching $iface to bat0" + batctl if add "$iface" 2>/dev/null + fi + done + sleep 2 +} + +# ========================================================= +# Backup / restore +# ========================================================= +backup_config() { + local ts=$(date +%Y%m%d-%H%M%S) + mkdir -p "$BACKUP_DIR" + + for cfg in network wireless easymesh dhcp; do + [ -f "/etc/config/$cfg" ] && cp "/etc/config/$cfg" "$BACKUP_DIR/$cfg.$ts" + done + echo "$ts" > "$BACKUP_DIR/LAST" + + ls -1t "$BACKUP_DIR"/network.* 2>/dev/null | tail -n +$((MAX_BACKUPS + 1)) | xargs -r rm -f + ls -1t "$BACKUP_DIR"/wireless.* 2>/dev/null | tail -n +$((MAX_BACKUPS + 1)) | xargs -r rm -f + ls -1t "$BACKUP_DIR"/easymesh.* 2>/dev/null | tail -n +$((MAX_BACKUPS + 1)) | xargs -r rm -f + ls -1t "$BACKUP_DIR"/dhcp.* 2>/dev/null | tail -n +$((MAX_BACKUPS + 1)) | xargs -r rm -f + + log "Backup saved: ts=$ts" +} + +restore_config() { + [ -f "$BACKUP_DIR/LAST" ] || { log "No backup found"; return 1; } + local ts=$(cat "$BACKUP_DIR/LAST") + for cfg in network wireless easymesh dhcp; do + [ -f "$BACKUP_DIR/$cfg.$ts" ] && cp "$BACKUP_DIR/$cfg.$ts" "/etc/config/$cfg" + done + uci commit + log "Restored from backup ts=$ts" +} + +# ========================================================= +# Original bridge ports save / restore +# ========================================================= +save_original_ports() { + [ -f "$SAVED_PORTS_FILE" ] && return 0 + mkdir -p "$(dirname "$SAVED_PORTS_FILE")" + + local idx=0 name ports + while name=$(uci -q get "network.@device[$idx].name"); do + ports=$(uci -q get "network.@device[$idx].ports") + [ -n "$ports" ] && echo "$name|$ports" >> "$SAVED_PORTS_FILE" + idx=$((idx + 1)) + done + log "Saved original bridge ports" +} + +restore_wired_port() { + uci -q delete network.nwi_mesh_wired 2>/dev/null + uci commit network + + [ -f "$SAVED_PORTS_FILE" ] || { + log "No saved ports file — nothing to restore" + return 0 + } + + while IFS='|' read -r bridge ports; do + [ -z "$bridge" ] && continue + local idx=0 name + while name=$(uci -q get "network.@device[$idx].name"); do + if [ "$name" = "$bridge" ]; then + for p in $ports; do + uci -q add_list "network.@device[$idx].ports=$p" + done + uci commit network + log "Restored $ports to bridge $bridge" + break + fi + idx=$((idx + 1)) + done + done < "$SAVED_PORTS_FILE" + + /etc/init.d/network reload +} + +# ========================================================= +# Loop prevention +# ========================================================= +prevent_wired_loop() { + local iface="$1" + [ -z "$iface" ] && return 0 + + local idx=0 name ports + while name=$(uci -q get "network.@device[$idx].name"); do + ports=$(uci -q get "network.@device[$idx].ports") + case " $ports " in + *" $iface "*) + log "PREVENT: $iface in bridge $name — removing to avoid loop" + uci -q del_list "network.@device[$idx].ports=$iface" + uci commit network + ;; + esac + idx=$((idx + 1)) + done +} + +# ========================================================= +# Role sanitation — server keeps wired_if (needed for wired backhaul) +# ========================================================= +sanitize_for_role() { + case "$ROLE" in + server) + uci -q delete easymesh.config.ap_mode 2>/dev/null + uci -q delete easymesh.config.ip_mode 2>/dev/null + uci -q delete easymesh.config.ipaddr 2>/dev/null + uci -q delete easymesh.config.netmask 2>/dev/null + uci -q delete easymesh.config.gateway 2>/dev/null + uci -q delete easymesh.config.dns 2>/dev/null + uci commit easymesh + log "Sanitized: Server — removed AP mode (wired backhaul allowed)" + ;; + off) + uci -q delete easymesh.config.ap_mode 2>/dev/null + uci -q delete easymesh.config.wired_if 2>/dev/null + uci commit easymesh + ;; + esac +} + +# ========================================================= +# Config loader +# ========================================================= +load_config() { + ENABLED=$(uci -q get easymesh.config.enabled) + ROLE=$(uci -q get easymesh.config.role) + BAND_MODE=$(uci -q get easymesh.config.band_mode) + CUSTOM_RADIO=$(uci -q get easymesh.config.custom_radio) + MESH_ID=$(uci -q get easymesh.config.mesh_id) + ENCRYPTION=$(uci -q get easymesh.config.encryption) + KEY=$(uci -q get easymesh.config.key) + KVR=$(uci -q get easymesh.config.kvr) + MOBILITY_DOMAIN=$(uci -q get easymesh.config.mobility_domain) + WIRED_IF=$(uci -q get easymesh.config.wired_if) + + AP_ENABLED_2G=$(uci -q get easymesh.config.ap_enabled_2g) + AP_SSID_2G=$(uci -q get easymesh.config.ap_ssid_2g) + AP_ENCRYPTION_2G=$(uci -q get easymesh.config.ap_encryption_2g) + AP_KEY_2G=$(uci -q get easymesh.config.ap_key_2g) + + AP_ENABLED_5G=$(uci -q get easymesh.config.ap_enabled_5g) + AP_SSID_5G=$(uci -q get easymesh.config.ap_ssid_5g) + AP_ENCRYPTION_5G=$(uci -q get easymesh.config.ap_encryption_5g) + AP_KEY_5G=$(uci -q get easymesh.config.ap_key_5g) + + AP_NETWORK=$(uci -q get easymesh.config.ap_network) + SAFE_APPLY=$(uci -q get easymesh.config.safe_apply) + + AP_MODE=$(uci -q get easymesh.config.ap_mode) + IP_MODE=$(uci -q get easymesh.config.ip_mode) + IPADDR=$(uci -q get easymesh.config.ipaddr) + NETMASK=$(uci -q get easymesh.config.netmask) + GATEWAY=$(uci -q get easymesh.config.gateway) + DNS=$(uci -q get easymesh.config.dns) + + # SAFETY: server never has AP mode + if [ "$ROLE" = "server" ] && [ "$AP_MODE" = "1" ]; then + log "SAFETY: role=server has ap_mode=1 — forcing off" + AP_MODE="0" + uci set easymesh.config.ap_mode=0 + uci commit easymesh + fi + + : "${MESH_ID:=EasyMesh_Dualband}" + : "${BAND_MODE:=single_5g}" + : "${MOBILITY_DOMAIN:=4f57}" + : "${AP_ENABLED_2G:=1}" + : "${AP_ENABLED_5G:=1}" + : "${AP_SSID_2G:=EasyMesh_WiFi}" + : "${AP_SSID_5G:=EasyMesh_WiFi_5G}" + : "${AP_ENCRYPTION_2G:=sae-mixed}" + : "${AP_ENCRYPTION_5G:=sae-mixed}" + : "${AP_NETWORK:=lan}" + + # Priority weights (Kbit/s) + mesh VLANs + PRIORITY_WIRED=$(uci -q get easymesh.config.priority_wired) + PRIORITY_6G=$(uci -q get easymesh.config.priority_6g) + PRIORITY_5G=$(uci -q get easymesh.config.priority_5g) + PRIORITY_24G=$(uci -q get easymesh.config.priority_24g) + MESH_VLANS=$(uci -q get easymesh.config.mesh_vlans) + + : "${PRIORITY_WIRED:=1000000}" + : "${PRIORITY_6G:=900000}" + : "${PRIORITY_5G:=600000}" + : "${PRIORITY_24G:=30000}" + : "${MESH_VLANS:=}" +} + +# ========================================================= +# bat0 +# ========================================================= +setup_bat0() { + local gw_mode=off + case "$ROLE" in + server) gw_mode=server ;; + client) gw_mode=client ;; + node) gw_mode=off ;; + esac + + uci set network.bat0=interface + uci set network.bat0.proto=batadv + uci set network.bat0.routing_algo=BATMAN_IV + uci set network.bat0.gw_mode=$gw_mode + uci set network.bat0.aggregated_ogms=1 + uci set network.bat0.bridge_loop_avoidance=1 + uci set network.bat0.distributed_arp_table=1 + uci set network.bat0.fragmentation=1 + uci set network.bat0.hop_penalty=30 + uci set network.bat0.orig_interval=500 + uci set network.bat0.bonding=1 + uci commit network + log "bat0 configured (gw_mode=$gw_mode, bonding=1)" +} + +attach_bat0_to_lan() { + local brlan=$(find_lan_bridge) + if [ "$brlan" = "br-lan" ]; then + uci -q get network.@device[0].ports | grep -q bat0 || { + uci add_list network.@device[0].ports=bat0 + uci commit network + log "Added bat0 to br-lan" + } + + # Reduce bridge MAC aging for faster failover + local cur_ageing=$(uci -q get network.@device[0].ageing_time) + if [ "$cur_ageing" != "30" ]; then + uci set network.@device[0].ageing_time=30 + uci commit network + log "Bridge ageing_time set to 30s (was ${cur_ageing:-300})" + fi + else + local lan=$(uci -q get network.lan.device) + case " $lan " in + *" bat0 "*) : ;; + *) uci set network.lan.device="$lan bat0"; uci commit network ;; + esac + fi +} + +detach_bat0_from_lan() { + local brlan=$(find_lan_bridge) + if [ "$brlan" = "br-lan" ]; then + uci -q del_list network.@device[0].ports=bat0 2>/dev/null + uci commit network + else + local lan=$(uci -q get network.lan.device) + lan=$(echo "$lan" | sed 's/ bat0//; s/bat0 //; s/^bat0$//') + uci set network.lan.device="$lan" + uci commit network + fi +} + +# ========================================================= +# Mesh iface (no quoted values — no escaped-quote bug) +# ========================================================= +setup_mesh_iface() { + local radio="$1" + local meshname="mesh_${radio}" + local netname="nwi_mesh_${radio}" + + uci -q delete "wireless.$meshname" 2>/dev/null + uci -q delete "network.$netname" 2>/dev/null + + uci set "network.$netname=interface" + uci set "network.$netname.proto=batadv_hardif" + uci set "network.$netname.master=bat0" + uci set "network.$netname.mtu=1500" + uci commit network + + uci set "wireless.$meshname=wifi-iface" + uci set "wireless.$meshname.device=$radio" + uci set "wireless.$meshname.network=$netname" + uci set "wireless.$meshname.mode=mesh" + uci set "wireless.$meshname.mesh_id=$MESH_ID" + uci set "wireless.$meshname.mesh_fwding=1" + uci set "wireless.$meshname.mesh_ttl=2" + uci set "wireless.$meshname.mcast_rate=24000" + uci set "wireless.$meshname.disabled=0" + + if [ "$ENCRYPTION" = "1" ] && [ -n "$KEY" ]; then + uci set "wireless.$meshname.encryption=sae" + uci set "wireless.$meshname.key=$KEY" + uci set "wireless.$meshname.ieee80211w=2" + uci set "wireless.$meshname.sae_pwe=1" + log "Mesh iface $radio: WPA3-SAE" + else + uci set "wireless.$meshname.encryption=none" + uci -q delete "wireless.$meshname.key" 2>/dev/null + uci -q delete "wireless.$meshname.ieee80211w" 2>/dev/null + uci -q delete "wireless.$meshname.sae_pwe" 2>/dev/null + uci -q delete "wireless.$meshname.sae_anti_clogging_threshold" 2>/dev/null + log "Mesh iface $radio: OPEN" + fi + + uci commit wireless +} + +# ========================================================= +# K/V/R +# ========================================================= +apply_kvr() { + [ "$KVR" != "1" ] && return 0 + local r changed=0 + + for r in $(list_radios); do + uci -q get "wireless.default_$r" >/dev/null || continue + uci set "wireless.default_$r.ieee80211k=1" + uci set "wireless.default_$r.rrm_neighbor_report=1" + uci set "wireless.default_$r.rrm_beacon_report=1" + uci set "wireless.default_$r.ieee80211v=1" + uci set "wireless.default_$r.bss_transition=1" + uci set "wireless.default_$r.ieee80211r=1" + uci set "wireless.default_$r.mobility_domain=$MOBILITY_DOMAIN" + uci set "wireless.default_$r.ft_over_ds=1" + uci set "wireless.default_$r.ft_psk_generate_local=1" + changed=1 + done + + [ "$changed" = "1" ] && uci commit wireless && log "K/V/R applied" +} + +# ========================================================= +# Client AP +# ========================================================= +apply_client_ap() { + local r band iface enabled ssid enc key + + for r in $(list_radios); do + iface="default_$r" + uci -q get "wireless.$iface" >/dev/null || continue + + band=$(radio_band "$r") + + case "$band" in + 2g) + enabled="$AP_ENABLED_2G" + ssid="$AP_SSID_2G" + enc="$AP_ENCRYPTION_2G" + key="$AP_KEY_2G" + ;; + 5g) + enabled="$AP_ENABLED_5G" + ssid="$AP_SSID_5G" + enc="$AP_ENCRYPTION_5G" + key="$AP_KEY_5G" + ;; + *) + continue + ;; + esac + + # Only disable if EXPLICITLY set to 0. Empty = keep enabled. + if [ "$enabled" = "0" ]; then + uci set "wireless.$iface.disabled=1" + log "Client AP on $band ($r): disabled" + continue + fi + + if [ -z "$ssid" ]; then + ssid=$(uci -q get "wireless.$iface.ssid") + log "Using existing SSID for $band: $ssid" + fi + [ -n "$ssid" ] && uci set "wireless.$iface.ssid=$ssid" + + uci set "wireless.$iface.mode=ap" + uci set "wireless.$iface.network=$AP_NETWORK" + uci set "wireless.$iface.disabled=0" + + case "$enc" in + none) + uci set "wireless.$iface.encryption=none" + uci -q delete "wireless.$iface.key" 2>/dev/null + uci -q delete "wireless.$iface.ieee80211w" 2>/dev/null + ;; + psk2+ccmp) + uci set "wireless.$iface.encryption=psk2+ccmp" + uci set "wireless.$iface.key=$key" + uci -q delete "wireless.$iface.ieee80211w" 2>/dev/null + ;; + sae) + uci set "wireless.$iface.encryption=sae" + uci set "wireless.$iface.key=$key" + uci set "wireless.$iface.ieee80211w=2" + ;; + sae-mixed|*) + uci set "wireless.$iface.encryption=sae-mixed" + uci set "wireless.$iface.key=$key" + uci set "wireless.$iface.ieee80211w=1" + ;; + esac + + log "Client AP on $band ($r): ssid=$ssid enc=$enc" + done + + uci commit wireless +} + +# ========================================================= +# AP mode (with hard role guard) +# ========================================================= +apply_ap_mode() { + if [ "$ROLE" = "server" ]; then + log "apply_ap_mode: skipped (role=server)" + return 0 + fi + + [ "$AP_MODE" = "1" ] || return 0 + log "AP mode: ip_mode=$IP_MODE" + + if [ "$IP_MODE" = "static" ] && [ -n "$IPADDR" ]; then + uci set network.lan.proto=static + uci set network.lan.ipaddr=$IPADDR + uci set network.lan.netmask=${NETMASK:-255.255.255.0} + [ -n "$GATEWAY" ] && uci set network.lan.gateway=$GATEWAY + uci -q delete network.lan.dns 2>/dev/null + [ -n "$DNS" ] && uci add_list network.lan.dns=$DNS + elif [ "$IP_MODE" = "dhcp" ]; then + uci set network.lan.proto=dhcp + uci -q delete network.lan.ipaddr 2>/dev/null + uci -q delete network.lan.netmask 2>/dev/null + uci -q delete network.lan.gateway 2>/dev/null + uci -q delete network.lan.dns 2>/dev/null + fi + + uci set dhcp.lan.ignore=1 + uci -q delete dhcp.lan.dhcpv6 2>/dev/null + uci -q delete dhcp.lan.ra 2>/dev/null + uci -q delete dhcp.lan.ra_management 2>/dev/null + uci commit network + uci commit dhcp +} + +# ========================================================= +# Wired backhaul +# ========================================================= +setup_wired() { + if [ -z "$WIRED_IF" ]; then + # Clean up any previously-configured wired hardif + if uci -q get network.nwi_mesh_wired >/dev/null 2>&1; then + log "Wired disabled — removing nwi_mesh_wired config" + uci -q delete network.nwi_mesh_wired + uci commit network + # Restore the port to its original bridge so LAN works again + restore_wired_port + fi + return 0 + fi + + prevent_wired_loop "$WIRED_IF" + save_original_ports + + uci -q delete network.nwi_mesh_wired 2>/dev/null + uci set network.nwi_mesh_wired=interface + uci set network.nwi_mesh_wired.proto=batadv_hardif + uci set network.nwi_mesh_wired.master=bat0 + uci set network.nwi_mesh_wired.mtu=1500 + uci set network.nwi_mesh_wired.device=$WIRED_IF + uci commit network + log "Wired backhaul: $WIRED_IF" +} + + +# ========================================================= +# Mesh VLANs — extend guest/IoT over mesh via 802.1Q +# ========================================================= +setup_mesh_vlans() { + # Remove any previous 8021q devices on bat0 + local idx=0 name typ ifn + while name=$(uci -q get "network.@device[$idx].name"); do + typ=$(uci -q get "network.@device[$idx].type") + ifn=$(uci -q get "network.@device[$idx].device") + if [ "$typ" = "8021q" ] && [ "$ifn" = "bat0" ]; then + uci -q delete "network.@device[$idx]" + continue + fi + idx=$((idx + 1)) + done + + if [ -z "$MESH_VLANS" ]; then + uci commit network + return 0 + fi + + local vlan br_name + for vlan in $(echo "$MESH_VLANS" | tr ',' ' '); do + vlan=$(echo "$vlan" | tr -d ' ') + case "$vlan" in + ''|*[!0-9]*) log "Skipping invalid VLAN: $vlan"; continue ;; + esac + + # Create bat0. as 802.1q device + uci add network device >/dev/null + uci set "network.@device[-1].name=bat0.$vlan" + uci set "network.@device[-1].type=8021q" + uci set "network.@device[-1].device=bat0" + uci set "network.@device[-1].vid=$vlan" + log "Mesh VLAN: bat0.$vlan created" + + # Auto-add to matching bridge if it exists + for br_name in "br-$vlan" "br-lan$vlan" "br-vlan$vlan" "br-guest$vlan" "br-iot$vlan"; do + local bi=0 bn + while bn=$(uci -q get "network.@device[$bi].name"); do + if [ "$bn" = "$br_name" ]; then + uci -q get "network.@device[$bi].ports" | grep -q "bat0.$vlan" || \ + uci add_list "network.@device[$bi].ports=bat0.$vlan" + log "Mesh VLAN $vlan: attached to $br_name" + bi=-1 + break + fi + bi=$((bi + 1)) + done + [ "$bi" = "-1" ] && break + done + done + + uci commit network +} + + +# ========================================================= +# Link priority (throughput_override in Kbit/s) +# ========================================================= +apply_link_priority() { + local tries=0 + local active=0 + while [ $tries -lt 45 ]; do + active=$(batctl if 2>/dev/null | grep -c "active") + [ "$active" -gt 0 ] && break + sleep 2 + tries=$((tries + 1)) + done + log "apply_link_priority: found $active active hardif(s) after $((tries*2))s" + + if [ "$active" = "0" ]; then + log "apply_link_priority: no hardifs — aborting" + return 1 + fi + + sleep 2 + + # Wired hardifs + local iface + for iface in $(batctl if 2>/dev/null | awk -F: '/active/ {print $1}'); do + case "$iface" in + eth*|lan*) + batctl hardif "$iface" throughput_override "$PRIORITY_WIRED" 2>/dev/null + log "Link priority: $iface = $PRIORITY_WIRED Kbit (wired)" + ;; + esac + done + + # Wireless mesh — by frequency + for iface in $(batctl if 2>/dev/null | awk -F: '/active/ {print $1}'); do + case "$iface" in + phy*-mesh*) + local freq=$(iw dev "$iface" info 2>/dev/null | \ + sed -n 's/.*(\([0-9]*\) MHz).*/\1/p') + [ -z "$freq" ] && continue + + if [ "$freq" -lt 2500 ]; then + batctl hardif "$iface" throughput_override "$PRIORITY_24G" 2>/dev/null + log "Link priority: $iface = $PRIORITY_24G Kbit (2.4 GHz, $freq MHz)" + elif [ "$freq" -lt 6000 ]; then + batctl hardif "$iface" throughput_override "$PRIORITY_5G" 2>/dev/null + log "Link priority: $iface = $PRIORITY_5G Kbit (5 GHz, $freq MHz)" + else + batctl hardif "$iface" throughput_override "$PRIORITY_6G" 2>/dev/null + log "Link priority: $iface = $PRIORITY_6G Kbit (6 GHz, $freq MHz)" + fi + ;; + esac + done +} + + +# ========================================================= +# Cron fallback toggle +# ========================================================= +apply_cron_setting() { + local cron_file="/etc/crontabs/root" + local enabled=$(uci -q get easymesh.config.cron_fallback) + + [ -f "$cron_file" ] || { + mkdir -p /etc/crontabs + touch "$cron_file" + } + + # Remove any existing easymesh priority lines (active or commented) + sed -i '\|easymesh priority|d' "$cron_file" + + if [ "$enabled" = "1" ]; then + echo "* * * * * /etc/init.d/easymesh priority >/dev/null 2>&1" >> "$cron_file" + log "Cron fallback: ENABLED (every minute)" + else + echo "#* * * * * /etc/init.d/easymesh priority >/dev/null 2>&1" >> "$cron_file" + log "Cron fallback: DISABLED (commented out)" + fi + + /etc/init.d/cron restart 2>/dev/null || true +} + + +# ========================================================= +# Enable / disable +# ========================================================= +enable_mesh() { + check_prereqs || { log "Prereq failed"; return 1; } + + sanitize_for_role + load_config + + log "Enabling mesh (role=$ROLE, band=$BAND_MODE)" + + local r + for r in $(list_radios); do + uci set "wireless.$r.disabled=0" + done + uci commit wireless + + setup_bat0 + attach_bat0_to_lan + + # Normalize band mode: strip the _wired suffix to select radio logic + local BASE_BAND=$(echo "$BAND_MODE" | sed 's/_wired$//') + + case "$BASE_BAND" in + none) + log "Wired-only mode — skipping wireless mesh setup" + ;; + dual) + local r5 r2 + r5=$(find_radio_by_band 5g) + r2=$(find_radio_by_band 2g) + [ -n "$r5" ] && setup_mesh_iface "$r5" + [ -n "$r2" ] && setup_mesh_iface "$r2" + ;; + single_2g) + local r2b; r2b=$(find_radio_by_band 2g) + [ -n "$r2b" ] && setup_mesh_iface "$r2b" + ;; + custom) + [ -n "$CUSTOM_RADIO" ] && setup_mesh_iface "$CUSTOM_RADIO" + ;; + single_5g|*) + local r5b; r5b=$(find_radio_by_band 5g) + [ -n "$r5b" ] && setup_mesh_iface "$r5b" + ;; + esac + + setup_wired + setup_mesh_vlans + apply_kvr + apply_client_ap + apply_ap_mode + + apply_cron_setting + log "UCI config complete" +} + +disable_mesh() { + log "Disabling mesh" + + local r + for r in $(list_radios); do + uci -q delete "wireless.mesh_$r" 2>/dev/null + uci -q delete "network.nwi_mesh_$r" 2>/dev/null + done + uci -q delete network.nwi_mesh_wired 2>/dev/null + uci -q delete network.bat0 2>/dev/null + uci commit wireless + uci commit network + + detach_bat0_from_lan +} + +# ========================================================= +# WPS pairing +# ========================================================= +pair() { + log "Pairing mode ($PAIR_TIMEOUT sec)" + + backup_config + + local orig_id=$(uci -q get easymesh.config.mesh_id) + local orig_enc=$(uci -q get easymesh.config.encryption) + local orig_key=$(uci -q get easymesh.config.key) + echo "$orig_id|$orig_enc|$orig_key" > /tmp/easymesh-pair-orig + + uci set easymesh.config.mesh_id=$PAIRING_MESH_ID + uci set easymesh.config.encryption=0 + uci -q delete easymesh.config.key 2>/dev/null + uci commit easymesh + + load_config + enable_mesh + wifi reload + + wait_and_attach_mesh + + local led=$(ls /sys/class/leds/ 2>/dev/null | grep -iE 'wlan|wifi|status' | head -1) + local start=$(date +%s) + local peer_found=0 + + while [ $(( $(date +%s) - start )) -lt $PAIR_TIMEOUT ]; do + if [ -d /sys/class/net/bat0 ]; then + local n=$(batctl n 2>/dev/null | tail -n +2 | \ + grep -cE '([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}') + if [ "$n" -gt 0 ]; then + peer_found=1 + break + fi + fi + + if [ -n "$led" ]; then + echo 1 > /sys/class/leds/$led/brightness 2>/dev/null + sleep 1 + echo 0 > /sys/class/leds/$led/brightness 2>/dev/null + fi + sleep 1 + done + + if [ "$peer_found" = "1" ]; then + log "Peer joined — pairing successful" + [ -n "$led" ] && echo 1 > /sys/class/leds/$led/brightness 2>/dev/null + else + log "Timed out — restoring" + IFS='|' read -r orig_id orig_enc orig_key < /tmp/easymesh-pair-orig + uci set easymesh.config.mesh_id=$orig_id + uci set easymesh.config.encryption=$orig_enc + [ -n "$orig_key" ] && uci set easymesh.config.key=$orig_key + uci commit easymesh + + load_config + enable_mesh + wifi reload + wait_and_attach_mesh + log "Restored" + fi +} + +# ========================================================= +# Rollback watchdog +# ========================================================= +rollback_start() { + local delay="${1:-60}" + mkdir -p "$RB_DIR" + rm -f "$RB_DIR/confirmed" "$RB_DIR/running" + touch "$RB_DIR/running" + + cp /etc/config/network "$RB_DIR/network.before" + cp /etc/config/wireless "$RB_DIR/wireless.before" + + log "Rollback watchdog: ${delay}s to confirm" + + ( + sleep "$delay" + [ -f "$RB_DIR/confirmed" ] && { + log "Rollback: confirmed" + rm -f "$RB_DIR/running" + exit 0 + } + log "Rollback: no confirmation — restoring" + [ -f "$RB_DIR/network.before" ] && cp "$RB_DIR/network.before" /etc/config/network + [ -f "$RB_DIR/wireless.before" ] && cp "$RB_DIR/wireless.before" /etc/config/wireless + uci commit network + uci commit wireless + wifi reload + /etc/init.d/network reload + rm -f "$RB_DIR/running" + ) & +} + +rollback_confirm() { + mkdir -p "$RB_DIR" + touch "$RB_DIR/confirmed" + rm -f "$RB_DIR/running" + log "Rollback: user confirmed" +} + +rollback_cancel() { + mkdir -p "$RB_DIR" + log "Rollback: user cancelled" + if [ -f "$RB_DIR/network.before" ]; then + cp "$RB_DIR/network.before" /etc/config/network + cp "$RB_DIR/wireless.before" /etc/config/wireless + uci commit network + uci commit wireless + wifi reload + /etc/init.d/network reload + fi + rm -f "$RB_DIR/running" "$RB_DIR/confirmed" +} + +# ========================================================= +# Service entry points +# ========================================================= +start() { + load_config + [ "$ENABLED" = "1" ] || { log "easymesh disabled"; return 0; } + enable_mesh + log "Reloading wireless..." + wifi reload + + wait_and_attach_mesh + + [ -d /sys/class/net/bat0 ] && log "bat0 UP" || log "bat0 missing" + apply_link_priority + apply_cron_setting + + # Second pass 90s after boot to catch late-arriving hardifs + ( + sleep 90 + log "Priority: second pass (post-boot)" + apply_link_priority + ) /dev/null 2>&1 & +} + +stop() { + log "Stopping mesh (config preserved)" + local r + for r in $(list_radios); do + ifdown "nwi_mesh_$r" 2>/dev/null + done + ifdown bat0 2>/dev/null +} + +restart() { + load_config + [ "$ENABLED" = "1" ] || { log "easymesh disabled"; return 0; } + enable_mesh + wifi reload + + wait_and_attach_mesh + + [ -d /sys/class/net/bat0 ] && log "bat0 UP" || log "bat0 missing" + apply_link_priority + apply_cron_setting +} + +reload() { restart; } + +backup() { backup_config; } + +restore() { + restore_config + wifi reload + /etc/init.d/network reload +} + +detach_wired() { + log "Detaching wired backhaul" + restore_wired_port + uci -q delete easymesh.config.wired_if 2>/dev/null + uci -q delete easymesh.config.preferred_wired_port 2>/dev/null + uci commit easymesh + log "Wired backhaul removed" +} + +status() { + if [ -d /sys/class/net/bat0 ]; then + local n=$(batctl n 2>/dev/null | tail -n +2 | wc -l) + local w=$(batctl if 2>/dev/null | grep -cE '^(eth|lan)') + echo "easymesh: running - bat0 up, $n wireless + $w wired link(s)" + return 0 + fi + echo "easymesh: not running" + return 1 +} + +priority() { + load_config + apply_link_priority +} + +service_triggers() { + procd_add_reload_trigger "easymesh" +} + +# --- File ends here. --- diff --git a/luci-app-easymesh/root/etc/rc.wps/easymesh-pair b/luci-app-easymesh/root/etc/rc.wps/easymesh-pair new file mode 100755 index 000000000..86f6cdd69 --- /dev/null +++ b/luci-app-easymesh/root/etc/rc.wps/easymesh-pair @@ -0,0 +1,13 @@ +#!/bin/sh +# EasyMesh pairing handler +# Invoked by /etc/rc.button/wps → /etc/rc.wps/* dispatcher +# On WPS press: switch to pairing mode for 2 minutes. + +[ "$(uci -q get easymesh.config.enabled)" = "1" ] || exit 1 + +logger -t easymesh-button "WPS pressed → pairing mode" + +/etc/init.d/easymesh pair >/tmp/easymesh-pair.log 2>&1 & + +# Return success so the dispatcher stops scanning further hooks +exit 0 diff --git a/luci-app-easymesh/root/etc/uci-defaults/luci-easymesh b/luci-app-easymesh/root/etc/uci-defaults/luci-easymesh new file mode 100755 index 000000000..11850ed3d --- /dev/null +++ b/luci-app-easymesh/root/etc/uci-defaults/luci-easymesh @@ -0,0 +1,14 @@ +#!/bin/sh +[ -n "${IPKG_INSTROOT}" ] && exit 0 + +# Add easymesh backup dir to sysupgrade backups +uci -q add_list system.@system[0].backup_path='/etc/easymesh-backups' +uci commit system + +# Ensure the backup dir exists +mkdir -p /etc/easymesh-backups + +# Enable the mesh service +/etc/init.d/easymesh enable 2>/dev/null + +exit 0 diff --git a/luci-app-easymesh/root/usr/share/luci/menu.d/luci-app-easymesh.json b/luci-app-easymesh/root/usr/share/luci/menu.d/luci-app-easymesh.json new file mode 100644 index 000000000..363967017 --- /dev/null +++ b/luci-app-easymesh/root/usr/share/luci/menu.d/luci-app-easymesh.json @@ -0,0 +1,14 @@ +{ + "admin/network/easymesh": { + "title": "EASY MESH", + "order": 60, + "action": { + "type": "cbi", + "path": "easymesh" + }, + "depends": { + "acl": [ "luci-app-easymesh" ], + "uci": { "easymesh": true } + } + } +} diff --git a/luci-app-easymesh/root/usr/share/rpcd/acl.d/luci-app-easymesh.json b/luci-app-easymesh/root/usr/share/rpcd/acl.d/luci-app-easymesh.json new file mode 100644 index 000000000..f109ad6a1 --- /dev/null +++ b/luci-app-easymesh/root/usr/share/rpcd/acl.d/luci-app-easymesh.json @@ -0,0 +1,11 @@ +{ + "luci-app-easymesh": { + "description": "Grant UCI access for luci-app-easymesh", + "read": { + "uci": [ "easymesh", "wireless", "network" ] + }, + "write": { + "uci": [ "easymesh" ] + } + } +} \ No newline at end of file