# NetPenetrate **Repository Path**: zli/NetPenetrate ## Basic Information - **Project Name**: NetPenetrate - **Description**: 两台内网的PC通过p2p穿透互联 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-29 - **Last Updated**: 2026-07-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # NetPenetrate NetPenetrate is a small one-to-one P2P IP tunnel prototype. It uses a public UDP pairing server only to match two clients with the same UUID and exchange their observed UDP endpoints. Application traffic is never relayed by the server. Clients create a virtual tunnel interface and exchange raw IPv4 packets over a UDP P2P channel. ## Defaults - Pairing server: UDP `7000` - Virtual network: `10.66.0.0/30` - First client in a UUID room: `10.66.0.1` - Second client in a UUID room: `10.66.0.2` - Payload: IPv4 packets carried over UDP - GUI: tkinter ## Files - `server.py` - UDP pairing server - `client.py` - tkinter client - `netpenetrate/protocol.py` - control and data packet helpers - `netpenetrate/fec.py` - negotiated XOR loss-recovery encoder and decoder - `netpenetrate/server_core.py` - in-memory UUID room logic - `netpenetrate/client_core.py` - client runtime, punching, and forwarding - `netpenetrate/tun.py` - Linux TUN and Windows Wintun adapter code ## Run the server Linux or Windows: ```bash python server.py --host 0.0.0.0 --port 7000 ``` The server does not need administrator privileges when using port `7000`. ## Run the client on Linux Linux clients need root privileges and `/dev/net/tun`. If `pkexec` is available in a graphical session, the client tries to request elevation automatically. ```bash python3 client.py ``` Enter the public server address, UDP port `7000`, and the same UUID on both clients. The first client receives `10.66.0.1`; the second receives `10.66.0.2`. ## Run the client on Windows Windows clients require Wintun. If the client is not already elevated, it requests Administrator privileges through UAC at startup. 1. Install Wintun or place `wintun.dll` beside `client.py` or in `PATH`. 2. Run: ```powershell python client.py ``` The client creates or opens a Wintun adapter named `NetPenetrate`. ## Saved client settings The client saves settings when you click **Start** and restores them on the next launch. The saved values include: - server host - server UDP port - UUID - persistent client ID - preferred virtual IP role - integrity key - advanced timing parameters The persistent client ID lets the pairing server recognize the same client after a restart, which avoids stale entries filling a UUID room. The preferred virtual IP role is learned after the first successful assignment and sent back to the server on future starts. The server keeps the same virtual IP when that role is available, so restarting clients in a different order is less likely to swap `10.66.0.1` and `10.66.0.2`. Configuration is stored in: - Windows: `%APPDATA%\NetPenetrate\config.json` - Linux: `~/.config/netpenetrate/config.json`, or `$XDG_CONFIG_HOME/netpenetrate/config.json` ## Advanced timing The client exposes several timing parameters in the GUI: - `服务端心跳` - how often the client refreshes its endpoint with the pairing server. Default: `5s`. - `对端保活` - how often a connected client sends UDP punch keepalives to the peer. Default: `2s`. - `断线判定` - how long without inbound peer control packets or tunnel data before the client marks the P2P path stale. Default: `30s`. - `重试打洞` - after the first immediate reconnect request, how often a stale client asks the server again to coordinate a fresh punch. Default: `10s`. - `换UDP端口` - how long a stale client waits before rebuilding the local UDP socket to get a fresh local port and NAT mapping. Default: `60s`. Set it equal to `断线判定` to renew the UDP port immediately when the path is marked stale. Before `Socket Reset` is reached, reconnect requests do not ask clients to refresh their UDP sockets. If this retry-only punch succeeds, the log prints: ```text P2P reconnected before UDP socket reset; retry-only punch worked ``` For unstable NATs, try `对端保活=1`, `断线判定=45`, `换UDP端口=90`. For quieter logs and fewer reconnects, increase `断线判定` and `换UDP端口`. ## UDP loss protection Version 0.5 adds an optional **FEC 丢包保护** mode for lossy direct paths. Each client advertises the `fec-xor-v1` capability in direct punch messages. FEC is activated only after both clients advertise support; pairing servers and older clients can still be used, and a mixed-version pair automatically keeps the legacy packet format. The default FEC group contains eight IPv4 packets plus one XOR parity packet, for about 12.5% parity overhead. Original packets are delivered immediately. When exactly one packet from a group is lost, the receiver reconstructs it from the remaining packets and parity instead of waiting for the inner TCP connection to retransmit. Multiple losses in one group are left to the inner transport, avoiding a reliable-tunnel head-of-line queue. The diagnostics panel reports whether FEC is `active` or `legacy`, data frame counters, recovered packets, and packets that could not be recovered. Disable FEC when bandwidth is more constrained than packet loss, or when comparing baseline performance. ## Diagnostics and HMAC The client shows a diagnostics panel with: - local UDP socket - peer endpoint - seconds since the last peer packet - HMAC status - tunnel packet and byte counters - peer control packet counters - HMAC failures, dropped packets, UDP errors, socket resets, and reconnects - stale peer control packets ignored during reconnect - current punch interval and mode (`Punch Int`) - negotiated FEC mode, FEC frame counts, and recovered/unrecoverable packets Punch send/receive events are retained as `DEBUG` messages. Use `Punch Int` and `Ctrl Tx/Rx` in diagnostics to verify whether the configured peer keepalive interval is taking effect. The log level selector supports `DEBUG`, `INFO`, `WARNING`, and `ERROR`. `INFO` is the default, so repetitive punch traffic stays hidden; selecting `DEBUG` reveals it without changing how punch packets are sent or processed. Changing the level filters retained GUI log history as well as new messages. ## P2P link test The **P2P 链路测试** button performs a separate 12-second direct-UDP probe after the tunnel is connected. It sends a small control datagram every 250 ms on the active path and, when UDP hot standby is available, on the standby path too. The log reports each path's public endpoint, the local public IP:port observed by the peer, sent/received count, loss rate, minimum/average/p95/maximum RTT, and average RTT jitter. Probe packets do not refresh peer liveness, change the selected public endpoint, promote a standby path, request reconnection, or reset a UDP socket. They are therefore safe to run while an FTP transfer is active. Both peers need version 0.5.1 or later: an older peer does not advertise `path-probe-v1`, and the button reports that the peer is not ready while retaining the existing tunnel behavior. The **完整链路测试** button runs a roughly 94-second active test. It first measures a low-rate baseline, then compares 512, 800, 1000, 1200, and 1300-byte datagrams, followed by 1300-byte bidirectional echo stages at 0.25, 0.5, 1, 2, 4, 8, 12, 16, 20, 24, and 32 Mbps. Stages from 8 Mbps upward run for eight seconds to make the capacity knee less sensitive to short loss bursts. The test sends one logical service stream on the current active path. Normal hot-standby creation and scheduled rotation remain enabled; after a rotation, the stream follows the new active path and uses the same brief old/new overlap as tunnel traffic. Results are aggregated across path generations rather than treating a retired half-path as a separate low-bandwidth link. Each stage logs target/offered/echo throughput, loss, average and p95 RTT, jitter, and the maximum response gap. The final summary separately reports clean stable bandwidth (at most 2.5% loss), peak effective echo bandwidth, the capacity-knee interval, rotation count, and the longest response gap under the configured automatic-rotation policy. A `>=` result means the service reached the 32 Mbps test ceiling, so the real maximum may be higher. The result also identifies a sender-limited test instead of mistaking it for a path limit. The full test intentionally creates network load and can slow an FTP transfer while it runs. It still does not refresh liveness, select or promote a path, change an endpoint, reconnect, or reset a socket. Both peers need version 0.6.0 or later for the negotiated `full-link-test-v1` capability. A 0.6.0 client can continue using the lightweight test with a 0.5.1 peer. Version 0.6.1 adds a monotonically increasing active-path generation. The pairing server and both clients must be updated to 0.6.1 for stale, reordered path announcements to be rejected. During a confirmed endpoint switch, tunnel traffic is briefly sent to both the old and new public endpoints to reduce the visible interruption. Version 0.6.3 changes the full test from independent physical-path sampling to effective service-path measurement. Automatic rotation is deliberately left running, and one logical stream follows the active generation while transition duplicates are counted only once. Version 0.6.4 expands the high-load sweep and separates the conservative clean bandwidth result from peak effective throughput and the estimated capacity knee, avoiding the misleading interpretation that the clean threshold is the absolute link maximum. Version 0.6.5 keeps scheduled hot-standby rotation preventative: standby validation is moved into a preflight window before the configured deadline, and the client still switches immediately when the deadline arrives. A stale candidate is rebuilt and punched while the old path remains active. During the brief old/new overlap, traffic on the old path triggers a fast rollback if the new path carries no return traffic, instead of waiting for the normal lost-link timeout. Conflicting same-generation server snapshots are identified separately from genuinely older generation updates and never replace the directly confirmed path. Version 0.6.6 separates local UDP path identity from the peer's independently rotating path identity. A local `main`/`standby` switch no longer overwrites the remembered peer path with the same local name, and direct peer control can correct a same-generation server snapshot. This prevents the conflict/reconnect loop that could begin after the two clients rotated at different times. The `Integrity Key` field is optional. When both clients use the same non-empty key, tunnel data packets are sent as: ```text NPDH1 + nonce + IPv4 packet + HMAC-SHA256 ``` When the key is empty, the client uses the legacy unauthenticated format: ```text NPDT1 + IPv4 packet ``` Peer-to-peer control messages are also HMAC-signed while the key is enabled, including punch and punch_ack packets. Server control messages remain unsigned because the pairing server does not know the integrity key. Instead, clients send the server a short key ID derived from the key; rooms are isolated by both key ID and UUID, so two groups can reuse the same UUID with different integrity keys without being paired together. HMAC detects modified or mismatched peer packets, but it does not encrypt the traffic. ## Usage After both clients show `connected`, client A can access services on client B by connecting to B's virtual IP, for example: ```bash curl http://10.66.0.2:8080/ ``` Likewise, client B can connect to client A: ```bash curl http://10.66.0.1:8080/ ``` ## SOCKS5 exit and LAN sharing The client can optionally expose a SOCKS5 proxy without changing the existing P2P tunnel behavior. To let client A use client B's network: 1. On B, enable **SOCKS5 Proxy -> Exit**. 2. Select the outbound source IP. Use `Auto` to let the OS choose the default route, or select a specific local IPv4 address to force that network interface. 3. Keep the exit port at `1080`, or set another port. 4. Start both clients and wait for the tunnel assignment. 5. On A, configure the application or browser to use SOCKS5: ```text Host: B virtual IP, for example 10.66.0.1 Port: 1080 DNS: remote / proxy DNS ``` To let a LAN device C use B's network through A: 1. On B, enable **Exit** as above. 2. On A, enable **LAN Share**. 3. Select A's LAN IP, for example `192.168.1.20`. 4. Set the LAN Share port, for example `1080`. 5. Make sure A's **Exit Port** field matches B's exit port; this is the upstream SOCKS5 port used by the LAN share forwarder. 6. On C, configure SOCKS5: ```text Host: A LAN IP, for example 192.168.1.20 Port: A LAN Share port, for example 1080 DNS: remote / proxy DNS ``` The GUI shows `Open: host:port` next to each proxy mode after the listener is started. The listeners are off by default and stop together with the client. ## Important limits - This is P2P only. If NAT punching fails, the connection fails. There is no relay fallback. - If an established P2P path goes stale, clients ask the pairing server to coordinate a fresh punch burst. The server still relays only control messages, not tunnel traffic. - When a P2P path goes stale, the reconnect request asks both clients to refresh their local UDP socket, get fresh public endpoints through the pairing server, and punch again. - UDP is used for punching and carrying tunnel packets. - IPv4 packets are forwarded generically. TCP, UDP, and ICMP may work if the operating system, firewall, and service binding allow them. - There is no encryption and no authentication by design. Use it only in trusted and authorized environments. - SOCKS5 proxy mode is unauthenticated. Bind LAN Share only to the intended LAN address and make sure the local firewall policy matches who should be allowed to use it. - Services must listen on `0.0.0.0` or on the assigned tunnel IP to be reachable. Services bound only to `127.0.0.1` or only to another interface IP may not accept connections through the tunnel. - The Windows Wintun path is implemented with `ctypes`, but it still depends on a compatible installed `wintun.dll` and elevated network configuration rights. ## Development checks ```bash python -m unittest discover -s tests python -m py_compile server.py client.py netpenetrate/*.py ```