# INP2P 自研 P2P 隧道组网系统。两个二进制,零依赖部署。 ``` inp2ps — 信令服务器(STUN + WebSocket + REST API) inp2pc — 客户端(NAT 探测 + 打洞 + 中继 + 端口转发) ``` ## 为什么造这个 试过 OpenP2P、FRP、ZeroTier,各有各的问题。OpenP2P 最接近需求,但踩了一堆坑: - `MsgReportBasic` 不回响应 → 客户端反复断连 - Push 消息白名单太窄 → 操作被拒绝 - TOTP relay 认证链过于复杂 → Access Denied - `peerNatType=314` 跳过所有打洞 → 只走 relay - 中继节点选择单一,没有分层概念 INP2P 从协议层重新设计,解决以上所有问题。 ## 特性 - **连接优先级**:UDP 直连 → UDP 打洞 → TCP 直连 → TCP 打洞 → 私有中继 → 超级中继 → 服务器中继 - **流多路复用**:一条 P2P 连接承载多个隧道,7 字节帧头(StreamID + Flags + Length) - **分层中继**:任意客户端可选开启 `--relay` / `--super`,自动参与中继 - **TOTP 认证**:CRC64 token + 时间窗口验证,跨用户场景用 HMAC-SHA256 一次性令牌 - **NAT 探测**:内置 STUN 服务端(UDP×2 + TCP×2),客户端自动探测 NAT 类型 - **断线重连**:5 秒退避自动重连,节点上线广播触发隧道重建 - **静态编译**:零 CGO 依赖,单二进制部署 ## 快速开始 ### 编译 ```bash git clone https://gitea.king.nyc.mn/openclaw/inp2p.git cd inp2p go build -o bin/inp2ps ./cmd/inp2ps/ go build -o bin/inp2pc ./cmd/inp2pc/ ``` 生产编译(更小体积): ```bash CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/inp2ps ./cmd/inp2ps/ CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/inp2pc ./cmd/inp2pc/ ``` ### 启动服务器 ```bash # 生成 token # token 是 CRC64(user + password) 的结果,客户端和服务端必须一致 ./bin/inp2ps -token 12345678 \ -ws-port 27183 \ -stun-udp1 27182 -stun-udp2 27183 \ -stun-tcp1 27180 -stun-tcp2 27181 ``` 健康检查: ```bash curl http://localhost:27183/api/v1/health # {"status":"ok","version":"0.1.0","nodes":0} ``` ### 启动客户端 节点 A(开启中继能力): ```bash ./bin/inp2pc \ -serverhost your-server.com \ -serverport 27183 \ -node nodeA \ -token 12345678 \ -relay \ -config config.json \ -newconfig ``` 节点 B: ```bash ./bin/inp2pc \ -serverhost your-server.com \ -serverport 27183 \ -node nodeB \ -token 12345678 \ -config config.json \ -newconfig ``` ### 参数说明 #### inp2ps | 参数 | 默认值 | 说明 | |------|--------|------| | `-token` | 必填 | 认证 token(uint64) | | `-ws-port` | 27183 | WebSocket 信令端口 | | `-stun-udp1` | 27182 | UDP STUN 端口 1 | | `-stun-udp2` | 27183 | UDP STUN 端口 2 | | `-stun-tcp1` | 27180 | TCP STUN 端口 1 | | `-stun-tcp2` | 27181 | TCP STUN 端口 2 | #### inp2pc | 参数 | 默认值 | 说明 | |------|--------|------| | `-serverhost` | 必填 | 服务器地址 | | `-serverport` | 27183 | 服务器端口 | | `-node` | 必填 | 节点名称(唯一标识) | | `-token` | 必填 | 认证 token(与服务器一致) | | `-insecure` | false | 跳过 TLS 验证(用 ws:// 代替 wss://) | | `-relay` | false | 启用中继节点功能 | | `-super` | false | 启用超级中继(对所有用户开放) | | `-relay-port` | 27185 | 中继监听端口 | | `-bw` | 10 | 共享带宽上限(Mbps) | | `-config` | config.json | 配置文件路径 | | `-newconfig` | false | 忽略已有配置,使用命令行参数 | | `-stun-udp1/2` | 同服务器 | STUN 端口(通常与服务器一致) | | `-stun-tcp1/2` | 同服务器 | STUN 端口(通常与服务器一致) | ## 架构 ``` ┌─────────────────────────────────────────────┐ │ inp2ps │ │ │ │ ┌─────────┐ ┌──────┐ ┌───────────────┐ │ │ │ STUN │ │ WSS │ │ Coordinator │ │ │ │ UDP × 2 │ │Server│ │ (punch sync) │ │ │ │ TCP × 2 │ │ │ │ │ │ │ └─────────┘ └──────┘ └───────────────┘ │ │ │ │ │ ┌───────┴───────┐ │ │ │ Node Manager │ │ │ │ (online/hb) │ │ │ └───────────────┘ │ └─────────────────────────────────────────────┘ ▲ WSS ▲ WSS │ │ ┌─────────┴──┐ ┌─────┴────────┐ │ inp2pc │◄──P2P──►│ inp2pc │ │ nodeA │ punch │ nodeB │ │ │ /relay │ │ │ ┌────────┐ │ │ ┌──────────┐ │ │ │ Tunnel │ │ │ │ Tunnel │ │ │ │ Mux │ │ │ │ Mux │ │ │ └────────┘ │ │ └──────────┘ │ └────────────┘ └──────────────┘ ``` ### 连接建立流程 ``` nodeA inp2ps nodeB │ │ │ │── ConnectReq ─────────►│ │ │ │── PunchStart ─────────►│ │◄── PunchStart ─────────│ │ │ │ │ │◄═══════ UDP/TCP Punch ═══════════════════════►│ │ │ │ │ (if punch fails, fallback to relay) │ │ │ │ │── RelayNodeReq ───────►│ │ │◄── RelayNodeRsp ───────│── PushRelayOffer ────►│ │ │ │ │──── RelayHandshake ──►[R]◄── RelayHandshake ───│ │ [R] bridges A ↔ B │ │◄═══════════ Mux Stream ═══════════════════════►│ ``` ### 多路复用帧格式 ``` 0 4 5 7 ├───────┼────┼───────┤ │Stream │Flag│Length │ Data... │ ID │ s │ │ └───────┴────┴───────┘ 4B 1B 2B 0~65535B Flags: SYN=0x01 FIN=0x02 DATA=0x04 PING=0x08 PONG=0x10 RST=0x20 StreamID: 客户端奇数(1,3,5...) 服务端偶数(2,4,6...) ``` ## 项目结构 ``` inp2p/ ├── cmd/ │ ├── inp2ps/main.go # 服务器入口 │ └── inp2pc/main.go # 客户端入口 ├── internal/ │ ├── server/ │ │ ├── server.go # WSS 主循环、登录、心跳、节点管理 │ │ └── coordinator.go # 打洞协调、App 推送 │ └── client/ │ └── client.go # NAT 探测、登录、打洞、中继、重连 ├── pkg/ │ ├── protocol/ # 消息定义、编解码、NAT 类型枚举 │ ├── config/ # 配置结构体、默认值、环境变量 │ ├── auth/ # CRC64 token、TOTP、HMAC-SHA256 令牌 │ ├── nat/ # UDP/TCP STUN 客户端和服务端 │ ├── signal/ # WebSocket 封装、handler、同步请求 │ ├── punch/ # UDP/TCP 打洞 + 优先级链 │ ├── mux/ # 流多路复用(7B 帧头) │ ├── tunnel/ # 基于 mux 的端口转发 │ └── relay/ # 中继管理、TOTP 握手、会话桥接 ├── go.mod ├── go.sum ├── TASKS.md # 任务拆分与进度 └── .gitignore ``` ## 测试 ```bash go test ./... -v ``` ``` internal/client PASS 8.3s — NAT + WSS + Login + Report 完整链路 internal/server PASS 0.8s — 双客户端登录 + Relay 节点发现 pkg/mux PASS 0.2s — 7 测试:并发/大载荷/FIN/session pkg/tunnel PASS 0.16s — 端到端转发 + 5 并发 + 统计 pkg/relay PASS 0.3s — 双向桥接 + 1MB 中继 + 认证拒绝 ``` ## 防火墙 服务器需要开放以下端口: | 端口 | 协议 | 用途 | |------|------|------| | 27183 | TCP | WebSocket 信令 | | 27182, 27183 | UDP | STUN NAT 探测 | | 27180, 27181 | TCP | STUN TCP 回退 | 客户端(如果开启 `--relay`): | 端口 | 协议 | 用途 | |------|------|------| | 27185 | TCP | 中继服务 | ## Systemd 部署示例 ### 服务器 ```ini [Unit] Description=INP2P Signaling Server After=network.target [Service] ExecStart=/usr/local/bin/inp2ps -token YOUR_TOKEN Restart=always RestartSec=5 [Install] WantedBy=multi-user.target ``` ### 客户端 ```ini [Unit] Description=INP2P Client After=network.target [Service] WorkingDirectory=/usr/local/inp2p ExecStart=/usr/local/bin/inp2pc -serverhost your-server.com -node mynode -token YOUR_TOKEN -insecure Restart=always RestartSec=5 [Install] WantedBy=multi-user.target ``` ## Roadmap - [x] 信令连接 + 认证 - [x] NAT 探测(UDP/TCP STUN) - [x] UDP/TCP 打洞 + 双端协调 - [x] 流多路复用隧道 - [x] 中继握手 + TOTP 认证 - [ ] SDWAN 虚拟组网(TUN 网卡 + 虚拟 IP) - [ ] Web 控制台 - [ ] UDP 端口转发 - [ ] 自动升级 - [ ] Docker 镜像 ## License MIT