udphole

Basic UDP wormhole proxy
git clone git://git.finwo.net/app/udphole
Log | Files | Refs | README | LICENSE

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.