2026-05-18 11:43:28 +04:00
2026-05-14 12:23:10 +04:00
2026-05-14 16:40:10 +04:00
2026-05-14 16:40:10 +04:00
2026-05-18 11:43:28 +04:00
2026-05-14 16:40:10 +04:00
2026-05-14 12:23:10 +04:00
2026-05-14 16:40:10 +04:00
2026-05-14 16:40:10 +04:00
2026-05-14 16:40:10 +04:00
2026-05-18 11:43:28 +04:00
2026-05-14 12:23:10 +04:00
2026-05-14 12:23:10 +04:00
2026-05-18 11:43:28 +04:00

nim-libplum

Nim binding for 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

git submodule update --init
nimble install

Usage

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

  destroyMapping(res.id)
  discard cleanup()

waitFor main()

See examples/port_mapping.nim for a complete example that pauses between add and remove so you can verify the mapping on your router:

nim c -r examples/port_mapping.nim

Timeouts

Pass timeout options to init to control how long discovery and mapping wait:

discard init(discoverTimeout = 5000, mappingTimeout = 10000)

Pass a timeout to createMapping to control the overall wait:

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:

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

if hasMapping(id):
  echo "mapping is still active"

API

See api.md for the full API reference.

Testing

Basic tests run without a router:

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:

nimble testIntegration

This builds the image and runs two containers: one for PCP and one for UPnP. 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. To see the miniupnpd logs and the resolved external addresses, pass TEST_VERBOSE=1:

TEST_VERBOSE=1 nimble testIntegration

License

Licensed and distributed under either of

at your option.

Description
Nim bindings for libplum
Readme
Languages
Nim 62.2%
C 22%
Shell 8.1%
Dockerfile 7.7%