mirror of
https://github.com/logos-storage/nim-libplum.git
synced 2026-06-07 17:49:25 +00:00
130 lines
3.3 KiB
Markdown
130 lines
3.3 KiB
Markdown
# nim-libplum
|
|
|
|
Nim binding for [libplum](https://github.com/paullouisageneau/libplum), a portable C library for NAT port mapping via PCP, NAT-PMP, and UPnP-IGD.
|
|
|
|
libplum tries each protocol in order (PCP → NAT-PMP → UPnP-IGD) and falls back automatically. If the local address is already public, it uses it directly.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
git submodule update --init
|
|
nimble install
|
|
```
|
|
|
|
## Usage
|
|
|
|
```nim
|
|
import chronos
|
|
import libplum/plum
|
|
|
|
proc main() {.async.} =
|
|
let initRes = init()
|
|
if initRes.isErr():
|
|
echo "init failed: ", initRes.error
|
|
return
|
|
|
|
let r = await createMapping(TCP, 8080)
|
|
if r.isErr():
|
|
echo "failed: ", r.error
|
|
discard cleanup()
|
|
return
|
|
|
|
let res = r.value
|
|
echo "external: ", res.mapping.externalHost, ":", res.mapping.externalPort
|
|
echo "protocol: ", res.mapping.mappingProtocol # PCP, NatPmp, UPnP, or Direct
|
|
|
|
destroyMapping(res.id)
|
|
discard cleanup()
|
|
|
|
waitFor main()
|
|
```
|
|
|
|
See [examples/port_mapping.nim](examples/port_mapping.nim) for a complete example that pauses between add and remove so you can verify the mapping on your router:
|
|
|
|
```bash
|
|
nim c -r examples/port_mapping.nim
|
|
```
|
|
|
|
### Timeouts
|
|
|
|
Pass timeout options to `init` to control how long discovery and mapping wait:
|
|
|
|
```nim
|
|
discard init(discoverTimeout = 5000, mappingTimeout = 10000)
|
|
```
|
|
|
|
Pass a `timeout` to `createMapping` to control the overall wait:
|
|
|
|
```nim
|
|
let r = await createMapping(TCP, 8080, timeout = seconds(15))
|
|
```
|
|
|
|
### Ongoing state changes
|
|
|
|
Pass an `onStateChange` callback to `createMapping` to be notified when the mapping is renewed or lost:
|
|
|
|
```nim
|
|
proc onStateChange(state: PlumState, mapping: PlumMapping) {.cdecl, raises: [], gcsafe.} =
|
|
echo "state changed: ", state, " external: ", mapping.externalHost, ":", mapping.externalPort
|
|
|
|
let r = await createMapping(TCP, 8080, onStateChange = onStateChange)
|
|
```
|
|
|
|
### Checking mapping state
|
|
|
|
```nim
|
|
if hasMapping(id):
|
|
echo "mapping is still active"
|
|
```
|
|
|
|
## API
|
|
|
|
See [api.md](api.md) for the full API reference.
|
|
|
|
## Testing
|
|
|
|
Basic tests run without a router:
|
|
|
|
```bash
|
|
nimble test
|
|
```
|
|
|
|
Integration tests run miniupnpd inside a Docker/Podman container and exercise the PCP and UPnP-IGD flows.
|
|
Podman or Docker as fallback will be used for testing with `NET_ADMIN` capability:
|
|
|
|
```bash
|
|
nimble testIntegration
|
|
```
|
|
|
|
This builds the image and runs three containers: PCP, UPnP, and a NAT-PMP fallback scenario
|
|
(miniupnpd compiled without PCP so libplum must fall back from PCP timeout to NAT-PMP).
|
|
Each protocol is tested under both `orc` and `refc` memory managers.
|
|
miniupnpd is built with a stub firewall backend (`tests/miniupnpd_stub_rdr.c`) so it accepts mapping requests without requiring iptables or nftables in the container.
|
|
|
|
Three env vars control verbosity:
|
|
|
|
- `TEST_VERBOSE=1`: print resolved external addresses
|
|
- `MINIUPNPD_VERBOSE=1`: print miniupnpd logs
|
|
- `LIBPLUM_VERBOSE=1`: enable verbose libplum internal logs
|
|
|
|
```bash
|
|
TEST_VERBOSE=1 MINIUPNPD_VERBOSE=1 LIBPLUM_VERBOSE=1 nimble testIntegration
|
|
```
|
|
|
|
## Development
|
|
|
|
Format the code with [nph](https://github.com/arnetheduck/nph):
|
|
|
|
```bash
|
|
nimble format
|
|
```
|
|
|
|
## License
|
|
|
|
Licensed and distributed under either of
|
|
|
|
* MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
|
|
* Apache License, Version 2.0: [LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
at your option.
|