|
|
@@ -1,183 +1,220 @@
|
|
|
# client2server
|
|
|
|
|
|
-> **Lightweight event forwarder for OpenWrt routers to central server**
|
|
|
-> Version 2.0 - Lua + WebSocket implementation
|
|
|
+> **Lightweight bi-directional event forwarder for OpenWrt routers**
|
|
|
|
|
|
-## Overview
|
|
|
-
|
|
|
-Lightweight Lua scripts that listen to OpenWrt events (WiFi, DHCP, network) and forward them to a central server via WebSocket with auto-reconnect and local buffering.
|
|
|
-
|
|
|
-## Why Lua + WebSocket?
|
|
|
-
|
|
|
-| Feature | Bash (v1) | Lua + WS (v2) |
|
|
|
-|---------|----------|--------------|
|
|
|
-| **Connection** | HTTP polling | WebSocket (persistent) |
|
|
|
-| **Auto-reconnect** | Manual | Built-in |
|
|
|
-| **Offline buffer** | File only | In-memory + file |
|
|
|
-| **Error handling** | Basic | Exception-safe |
|
|
|
-| **Size** | ~20KB | ~30KB inc. deps |
|
|
|
+[](LICENSE)
|
|
|
|
|
|
-## Architecture
|
|
|
+## Overview
|
|
|
|
|
|
```
|
|
|
-┌─────────────────┐ WebSocket ┌─────────────────┐
|
|
|
-│ OpenWrt │ ════════════════► │ Central Server │
|
|
|
-│ Router │ │ │
|
|
|
-│ │ │ - Receive │
|
|
|
-│ - DHCP │ │ - Store │
|
|
|
-│ - WiFi │ │ - Real-time │
|
|
|
-│ - Network │ │ - LuIS UI │
|
|
|
-│ │ │ │
|
|
|
-│ Buffer [offline]│─► sync when ──► │ │
|
|
|
-└─────────────────┘ online └─────────────────┘
|
|
|
+┌─────────────────────────────────────────────────────────────┐
|
|
|
+│ client2server Architecture │
|
|
|
+├─────────────────────────────────────────────────────────────┤
|
|
|
+│ │
|
|
|
+│ OpenWrt Routers │
|
|
|
+│ │ │
|
|
|
+│ ▼ WebSocket :3843 │
|
|
|
+│ ┌──────────────────────────────────────────┐ │
|
|
|
+│ │ Caddy Load Balancer │ │
|
|
|
+│ │ - Health checks │ │
|
|
|
+│ │ - Auto-failover │ │
|
|
|
+│ └──────────────────┬───────────────────┘ │
|
|
|
+│ │ │
|
|
|
+│ ┌───────────┴───────────┐ │
|
|
|
+│ ▼ ▼ │
|
|
|
+│ ┌─────────────┐ ┌─────────────┐ │
|
|
|
+│ │ server1 │ │ server2 │ │
|
|
|
+│ │ (Go) │ │ (Go) │ │
|
|
|
+│ └─────┬───────┘ └─────┬───────┘ │
|
|
|
+│ │ │ │
|
|
|
+│ └────────┬─────────┘ │
|
|
|
+│ │ │
|
|
|
+│ ▼ │
|
|
|
+│ ┌─────────────────┐ │
|
|
|
+│ │ Redpanda │ Events stored │
|
|
|
+│ │ (Kafka) │ Persisted │
|
|
|
+│ └───────────────┘ │
|
|
|
+│ │
|
|
|
+└─────────────────────────────────────────────────────────────┘
|
|
|
```
|
|
|
|
|
|
-## Installing
|
|
|
-
|
|
|
-### On OpenWrt Router
|
|
|
+## Features
|
|
|
|
|
|
-```bash
|
|
|
-# Install dependencies
|
|
|
-opkg update
|
|
|
-opkg install lua luasocket
|
|
|
+- ✅ **WebSocket connection** with auto-reconnect
|
|
|
+- ✅ **Local buffer** (store-and-forward while offline)
|
|
|
+- ✅ **DHCP lease events** (new/expire)
|
|
|
+- ✅ **WAN link state** (up/down monitoring)
|
|
|
+- ✅ **WAN IP changes** (new ISP detection)
|
|
|
+- ✅ **Bidirectional** (receive commands from server)
|
|
|
+- ✅ **Command executor** (uci_set, shell, reboot, status)
|
|
|
+- ✅ **Load balancer** ready (Caddy)
|
|
|
+- ✅ **Event persistence** (Redpanda)
|
|
|
|
|
|
-# Copy files
|
|
|
-scp -r etc root@router:/etc/
|
|
|
-scp -r usr root@router:/usr/
|
|
|
+## Quick Start
|
|
|
|
|
|
-# Make executable
|
|
|
-chmod +x /etc/init.d/event-forwarder
|
|
|
-chmod +x /usr/sbin/event-forwarder.lua
|
|
|
+### Option 1: Docker Compose (Recommended)
|
|
|
|
|
|
-# Configure
|
|
|
-uci set event-forwarder.server.url='wss://your-server.com/ws'
|
|
|
-uci set event-forwarder.server.token='your-secret-token'
|
|
|
-uci set event-forwarder.general.enabled='1'
|
|
|
-uci commit event-forwarder
|
|
|
+```bash
|
|
|
+# Clone and run
|
|
|
+git clone https://git3.techno-world.net/lrosales/client2server.git
|
|
|
+cd client2server
|
|
|
|
|
|
-# Enable and start
|
|
|
-/etc/init.d/event-forwarder enable
|
|
|
-/etc/init.d/event-forwarder start
|
|
|
+# Start all services
|
|
|
+TOKEN=your-secret-token docker-compose up -d
|
|
|
|
|
|
-# Check logs
|
|
|
-logread -f -e client2server
|
|
|
+# Check status
|
|
|
+docker-compose ps
|
|
|
```
|
|
|
|
|
|
-### On Central Server
|
|
|
+### Option 2: Manual (Development)
|
|
|
|
|
|
```bash
|
|
|
+# Go server
|
|
|
cd server
|
|
|
-npm install express ws
|
|
|
-node server.js
|
|
|
+go build -o server .
|
|
|
+REDPANDA_BROKERS=localhost:9092 TOKEN=*** ./server
|
|
|
+
|
|
|
+# On OpenWrt router (copy Lua script)
|
|
|
+scp package/src/client2server-unified.lua root@router:/usr/sbin/
|
|
|
+ssh root@router "chmod +x /usr/sbin/client2server-unified.lua"
|
|
|
```
|
|
|
|
|
|
-## Files
|
|
|
+## Ports
|
|
|
|
|
|
-| File | Purpose |
|
|
|
-|------|---------|
|
|
|
-| `usr/sbin/client2server.lua` | Main Lua forwarder |
|
|
|
-| `usr/sbin/event-forwarder.lua` | Original Lua prototype |
|
|
|
-| `etc/init.d/event-forwarder` | Init script |
|
|
|
-| `etc/config/event-forwarder` | UCI config |
|
|
|
-| `server/index.js` | Central Node.js receiver |
|
|
|
-| `README.md` | This file |
|
|
|
+| Service | Port | Protocol |
|
|
|
+|----------|------|----------|
|
|
|
+| WebSocket | 3843 | WS |
|
|
|
+| HTTP API | 3844 | HTTP |
|
|
|
+| Redpanda | 9092 | Kafka |
|
|
|
|
|
|
## Configuration
|
|
|
|
|
|
-### UCI Config
|
|
|
+### Environment Variables
|
|
|
+
|
|
|
+```bash
|
|
|
+TOKEN=*** # Authentication token
|
|
|
+REDPANDA_BROKERS=redpanda:9092 # Redpanda address
|
|
|
+PORT=3843 # Server port
|
|
|
+```
|
|
|
+
|
|
|
+### UCI Config (on Router)
|
|
|
|
|
|
```bash
|
|
|
-# /etc/config/event-forwarder
|
|
|
-config event-forwarder 'general'
|
|
|
+# /etc/config/client2server
|
|
|
+config client2server 'general'
|
|
|
option enabled '1'
|
|
|
|
|
|
config server
|
|
|
- option url 'wss://your-server.com/ws'
|
|
|
- option token 'CHANGE_ME_SECRET_TOKEN'
|
|
|
+ option url 'wss://your-server.com:3843'
|
|
|
+ option token '***'
|
|
|
|
|
|
-config router
|
|
|
- option id 'router-hostname'
|
|
|
+config wan
|
|
|
+ option interface 'wan'
|
|
|
+ option device 'eth0'
|
|
|
```
|
|
|
|
|
|
-### Environment Variables
|
|
|
+## Commands
|
|
|
+
|
|
|
+Send from server to router via WebSocket or HTTP API:
|
|
|
|
|
|
```bash
|
|
|
-export SERVER_URL="wss://your-server.com/ws"
|
|
|
-export SERVER_TOKEN="your-token"
|
|
|
-export ROUTER_ID="router-name"
|
|
|
+# UCI set
|
|
|
+curl -X POST http://localhost:3844/api/command \
|
|
|
+ -H "Authorization: Bearer TOKEN" \
|
|
|
+ -d '{"router_id":"router1","command":"uci_set","args":{"config":"network","section":"lan","option":"ipaddr","value":"192.168.1.1"}}'
|
|
|
+
|
|
|
+# Shell
|
|
|
+curl -X POST http://localhost:3844/api/command \
|
|
|
+ -H "Authorization: Bearer TOKEN" \
|
|
|
+ -d '{"router_id":"router1","command":"shell","args":{"command":"reboot"}}'
|
|
|
+
|
|
|
+# Reboot
|
|
|
+curl -X POST http://localhost:3844/api/command \
|
|
|
+ -H "Authorization: Bearer TOKEN" \
|
|
|
+ -d '{"router_id":"router1","command":"reboot"}'
|
|
|
```
|
|
|
|
|
|
-## Event Types
|
|
|
+### Available Commands
|
|
|
|
|
|
-| Event | Source | Payload |
|
|
|
-|-------|-------|---------|
|
|
|
-| `dhcp_lease` | dnsmasq | mac, ip, hostname, action |
|
|
|
-| `wifi_connect` | hostapd | mac, signal, ssid |
|
|
|
-| `wifi_disconnect` | hostapd | mac, reason |
|
|
|
-| `interface_up` | netifd | device |
|
|
|
-| `interface_down` | netifd | device |
|
|
|
-
|
|
|
-## API Specification
|
|
|
-
|
|
|
-### WebSocket Message Format
|
|
|
-
|
|
|
-```json
|
|
|
-{
|
|
|
- "router_id": "router-name",
|
|
|
- "hostname": "openwrt-device",
|
|
|
- "event_type": "dhcp_lease",
|
|
|
- "timestamp": "2026-06-04T18:00:00Z",
|
|
|
- "payload": {
|
|
|
- "mac": "AA:BB:CC:DD:EE:FF",
|
|
|
- "ip": "192.168.1.100",
|
|
|
- "hostname": "iphone",
|
|
|
- "action": "new"
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
+| Command | Description | Arguments |
|
|
|
+|---------|-------------|-----------|
|
|
|
+| `uci_set` | Set UCI config value | `config`, `section`, `option`, `value` |
|
|
|
+| `shell` | Run shell command | `command` |
|
|
|
+| `reboot` | Reboot router | - |
|
|
|
+| `wifi_restart` | Restart WiFi | - |
|
|
|
+| `status` | Get router status | - |
|
|
|
|
|
|
-### Server Endpoint
|
|
|
+## Events
|
|
|
|
|
|
-```javascript
|
|
|
-// Expects WebSocket connection
|
|
|
-wss://your-server.com/ws
|
|
|
+Router sends these events to server:
|
|
|
|
|
|
-// Or HTTP POST fallback
|
|
|
-POST /api/events
|
|
|
-Authorization: Bearer <token>
|
|
|
-Content-Type: application/json
|
|
|
+| Event | Source | Payload |
|
|
|
+|-------|--------|---------|
|
|
|
+| `dhcp_lease_new` | dnsmasq | `mac`, `ip`, `hostname` |
|
|
|
+| `dhcp_lease_expire` | dnsmasq | `mac`, `old_ip` |
|
|
|
+| `wan_link_up` | /sys/class/net/* | `device` |
|
|
|
+| `wan_link_down` | /sys/class/net/* | `device` |
|
|
|
+| `wan_dhcp_new` | ubus | `new_ip` |
|
|
|
+| `wan_dhcp_changed` | ubus | `old_ip`, `new_ip` |
|
|
|
+
|
|
|
+## Project Structure
|
|
|
+
|
|
|
+```
|
|
|
+client2server/
|
|
|
+├── ARCHITECTURE.md # Architecture docs
|
|
|
+├── README.md # This file
|
|
|
+├── Caddyfile # Caddy load balancer
|
|
|
+├── docker-compose.yml # Full stack
|
|
|
+├── package/
|
|
|
+│ ├── Makefile # IPK build
|
|
|
+│ ├── src/
|
|
|
+│ │ └── client2server-unified.lua # Router script
|
|
|
+│ └── files/
|
|
|
+│ ├── etc/init.d/
|
|
|
+│ └── etc/config/
|
|
|
+└── server/
|
|
|
+ ├── main.go
|
|
|
+ ├── go.mod
|
|
|
+ ├── Dockerfile
|
|
|
+ └── index.js # HTTP fallback
|
|
|
```
|
|
|
|
|
|
-## Offline Behavior
|
|
|
+## Building IPK
|
|
|
|
|
|
-1. **Internet disconnects** → Events saved to local buffer
|
|
|
-2. **Router reboots** → Buffer persists in `/tmp/event_buffer`
|
|
|
-3. **Internet restores** → Buffer flushed in order
|
|
|
-4. **Never lose events** ✓
|
|
|
+```bash
|
|
|
+# With OpenWrt SDK
|
|
|
+./scripts/feeds update -a
|
|
|
+./scripts/feeds install client2server-unified
|
|
|
+make package/client2server-unified/compile
|
|
|
+make package/client2server-unified/ipk
|
|
|
+
|
|
|
+# Output
|
|
|
+# bin/packages/*/client2server-unified_*.ipk
|
|
|
+```
|
|
|
|
|
|
-## Troubleshooting
|
|
|
+## Monitoring
|
|
|
|
|
|
```bash
|
|
|
-# Check if running
|
|
|
-pgrep -a client2server
|
|
|
+# Server logs
|
|
|
+docker-compose logs -f server1
|
|
|
|
|
|
-# View logs
|
|
|
+# Router logs (on OpenWrt)
|
|
|
logread -f -e client2server
|
|
|
|
|
|
-# Check buffer
|
|
|
-cat /tmp/event_buffer
|
|
|
-
|
|
|
-# Manual test
|
|
|
-lua /usr/sbin/client2server.lua
|
|
|
+# Redpanda
|
|
|
+docker-compose logs -f redpanda
|
|
|
|
|
|
-# Force stop
|
|
|
-killall -9 client2server
|
|
|
+# API
|
|
|
+curl http://localhost:3844/api/routers
|
|
|
+curl http://localhost:3844/api/events
|
|
|
```
|
|
|
|
|
|
-## Dependencies
|
|
|
+## Security
|
|
|
|
|
|
-- OpenWrt: `lua`, `luasocket`
|
|
|
-- Server: `express`, `ws` (npm)
|
|
|
+- Token-based auth on both WS and HTTP
|
|
|
+- Use TLS (wss://) in production
|
|
|
+- Firewalls: Only allow port 3843 from router network
|
|
|
+- Redpanda: Enable auth for production
|
|
|
|
|
|
## License
|
|
|
|