README.md (6436B)
1 # UDPHOLE 2 3 A generic session-based UDP wormhole proxy: forward UDP packets between sockets using a simple RESP2 API 4 5 --- 6 7 ## Building 8 9 Requirements: [dep](https://github.com/finwo/dep), C compiler. 10 11 ```bash 12 make 13 ``` 14 15 The binary is `udphole`. 16 17 --- 18 19 ## Global options 20 21 These options apply to all commands and must appear **before** the command name. 22 23 | Option | Short | Description | 24 |--------|-------|-------------| 25 | `--config <path>` | `-f` | Config file path. If omitted, the following are tried in order: `$HOME/.config/udphole.conf`, `$HOME/.udphole.conf`, `/etc/udphole/udphole.conf`, `/etc/udphole.conf`. | 26 | `--verbosity <level>` | `-v` | Log verbosity: fatal, error, warn, info, debug, trace (default: info). | 27 | `--log <path>` | | Also write log to file (SIGHUP reopens for logrotate). | 28 29 --- 30 31 ## Running the daemon 32 33 The main entry point is the **daemon** command. 34 35 ```bash 36 # Foreground (default, config auto-detected) 37 ./udphole daemon 38 39 # Explicit config file 40 ./udphole -f /etc/udphole.conf daemon 41 42 # Background (daemonize) 43 ./udphole -f /etc/udphole.conf daemon -d 44 45 # Force foreground even if config has daemonize=1 46 ./udphole -f /etc/udphole.conf daemon -D 47 ``` 48 49 | Option | Short | Description | 50 |--------|--------|--------------| 51 | `--daemonize` | `-d` | Run in background (double fork, detach from terminal). | 52 | `--no-daemonize` | `-D` | Force foreground; overrides `daemonize=1` in config. | 53 54 Daemonize behaviour: 55 56 - By default the daemon runs in the **foreground**. 57 - It goes to the **background** only if `daemonize=1` is set in `[udphole]` **or** you pass `-d`/`--daemonize`. 58 - `-D`/`--no-daemonize` always forces foreground. 59 60 After starting, the daemon loads config, starts the UDP proxy manager, binds the API socket, and handles session/socket/forward management via RESP2 protocol. Logging goes to stderr (and optionally to a file if you use global `--log`). 61 62 --- 63 64 ## How it works 65 66 UDP hole is a simple UDP packet forwarder. It creates "sessions" that contain "sockets" and "forwards": 67 68 1. **Session**: A container for sockets and forwards. Has an idle timeout. 69 2. **Socket**: A UDP socket that can either: 70 - **Listen**: Binds to a port and learns the remote address from the first packet received (NAT traversal) 71 - **Connect**: Binds to a port and connects to a fixed remote address 72 3. **Forward**: Routes packets from a source socket to a destination socket 73 74 ### Use cases 75 76 - **NAT traversal**: A socket in listen mode learns the remote address from the first incoming packet, enabling symmetric NAT traversal 77 - **Fixed forwarding**: Connect sockets to fixed IP:port for simple relay 78 - **Session management**: Idle sessions expire automatically, useful for temporary forwards 79 80 --- 81 82 ## Example: simple UDP echo 83 84 ```bash 85 # Create a session with 60 second idle timeout 86 session.create mysession 60 87 88 # Create a listen socket (will learn remote from first packet) 89 session.socket.create.listen mysession socket1 90 91 # Get the port assigned to socket1 (response: [port, advertise_addr]) 92 # The port will be in range 7000-7999 93 94 # From a remote client, send UDP packet to that port 95 # The socket now "knows" the remote address 96 97 # Create another socket to forward to 98 session.socket.create.connect mysession socket2 8.8.8.8 53 99 100 # Create forward: packets from socket1 -> socket2 101 session.forward.create mysession socket1 socket2 102 103 # Now packets received on socket1 are forwarded to 8.8.8.8:53 104 # And responses are sent back to the original sender 105 ``` 106 107 --- 108 109 ## Example: symmetric NAT traversal 110 111 ```bash 112 # Create session 113 session.create nat-traversal 300 114 115 # Client A: create listen socket, gets port 7012 116 session.socket.create.listen nat-traversal client-a 117 118 # Client B: connect to client A's port 119 session.socket.create.connect nat-traversal client-b <client-a-ip> 7012 120 121 # Client B now receives packets from client A 122 session.forward.create nat-traversal client-a client-b 123 124 # Bidirectional 125 session.forward.create nat-traversal client-b client-a 126 ``` 127 128 --- 129 130 ## Configuration 131 132 ```ini 133 [udphole] 134 ports = 7000-7999 135 listen = :12345 136 advertise = 203.0.113.1 137 138 [user:admin] 139 secret = adminpass 140 permit = * 141 142 [user:*] 143 permit = ping 144 ``` 145 146 ### `[udphole]` 147 148 | Option | Description | 149 |--------|-------------| 150 | `ports` | Port range for UDP sockets, as `low-high` (e.g. `7000-7999`). Default 7000–7999. | 151 | `listen` | API server listen address. If not set, API server is disabled. | 152 | `advertise` | Optional. IP address to advertise in API responses instead of the port number. Useful when behind NAT. | 153 154 ### `[user:<name>]` 155 156 | Option | Description | 157 |--------|-------------| 158 | `secret` | Password for authentication. | 159 | `permit` | Space-separated list of command patterns the user can execute. Use `*` for all commands, `session.*` for all session commands, etc. | 160 161 --- 162 163 ## API commands 164 165 The API uses the RESP2 (Redis) protocol. Connect with `redis-cli` or any Redis client library. 166 167 | Command | Response | 168 |---------|----------| 169 | `auth username password` | `+OK` or `-ERR invalid credentials` | 170 | `ping` | `+PONG` | 171 | `quit` | `+OK`, closes connection | 172 | `command` | List of commands accessible to the current user | 173 174 ### Session commands 175 176 | Command | Response | 177 |---------|----------| 178 | `session.create <id> [idle_expiry]` | `+OK` - creates session, optional idle expiry in seconds (default 60) | 179 | `session.list` | Array of session IDs | 180 | `session.info <id>` | Map with: session_id, created, last_activity, idle_expiry, sockets, forwards, fd_count, marked_for_deletion | 181 | `session.destroy <id>` | `+OK` - destroys session and all its sockets/forwards | 182 183 ### Socket commands 184 185 | Command | Response | 186 |---------|----------| 187 | `session.socket.create.listen <session_id> <socket_id>` | Array: `[port, advertise_addr]` | 188 | `session.socket.create.connect <session_id> <socket_id> <ip> <port>` | Array: `[port, advertise_addr]` | 189 | `session.socket.destroy <session_id> <socket_id>` | `+OK` | 190 191 ### Forward commands 192 193 | Command | Response | 194 |---------|----------| 195 | `session.forward.list <session_id>` | Array of `[src_socket_id, dst_socket_id]` pairs | 196 | `session.forward.create <session_id> <src_socket_id> <dst_socket_id>` | `+OK` | 197 | `session.forward.destroy <session_id> <src_socket_id> <dst_socket_id>` | `+OK` | 198 199 ### System commands 200 201 | Command | Response | 202 |---------|----------| 203 | `system.load` | Array: `[1min, 5min, 15min]` load averages | 204 | `session.count` | Integer: number of active sessions | 205 206 --- 207 208 A generic session-based UDP wormhole proxy.