Skip to content

NethVoice: Review UDP/TCP Port Assignment #7498

Closed
1 of 1 issue completed
Closed

Description

@andrea-marchionni

As more NethVoice instances are deployed on a single NethServer 8 (NS8) node, it becomes increasingly important to ensure that:

  • UDP port usage does not conflict across applications (check all service RTP, Janus, etc.)
  • TCP port usage does not conflict across applications (check all service LDAP phonebook, etc.)
  1. Review UDP Port Assignments:

    • Perform a detailed audit of how UDP ports are allocated for RTP/media and SIP signaling for Asterisk and Janus in each NethVoice instance.
    • Determine the maximum number of coexisting NethVoice applications that can be safely hosted on a single NS8 node without port overlap.
  2. Review TCP Port Assignments:

    • Perform a detailed audit of how TCP ports are allocated for LDAP service for example in each NethVoice instance.
    • Determine the maximum number of coexisting NethVoice applications that can be safely hosted on a single NS8 node without port overlap.

Activity

  1. changed the title [-]NethVoice: Review UDP Port Assignment and Limit Concurrent Calls[/-] [+]NethVoice: Review UDP/TCP Port Assignment and Limit Concurrent Calls[/+] on Jun 10, 2025
  2. tommaso-ascani commented on May 8, 2026

    @tommaso-ascani

    Test Cases — Managed RTP/UDP ports (PR nethesis/ns8-nethvoice#817)

    Replaces the previous test cases (they targeted an older version of the PR).

    This PR requests a 2003-port UDP block + 36 TCP ports from ns8-core and
    derives every consumer from it. SIP UDP is distinct from SIP TCP.

    UDP offset Service
    +0 … +999 Asterisk RTP
    +1000 … +1999 Janus RTP
    +2000 Satellite RTP
    +2001 Asterisk IAX
    +2002 Asterisk SIP UDP

    Update baselines — a temporary fix (1.6.8, commit 4202d5ad) made Satellite
    reuse SATELLITE_HTTP_PORT and unset the dedicated SATELLITE_RTP_PORT; this PR
    re-introduces it at +2000. Updates must migrate correctly from both origins:

    From Satellite RTP before update
    ≤ 1.6.7 dedicated SATELLITE_RTP_PORT (old model)
    1.6.8 → 1.7.4 (current stable) SATELLITE_HTTP_PORT reuse; SATELLITE_RTP_PORT unset

    Environment: ns8-core cluster, 2 nodes for clone/move; one user domain +
    one NethVoice Proxy per node; root SSH.
    Images: <IMG> = PR image; update baselines 1.7.4 and 1.6.7.
    Placeholders: <ID> = module id (e.g. nethvoice1) · <NODE> = node id.


    Automated tests (Robot Framework — tests/)

    Already covered by the automated suites; no manual run needed.

    ID Test Expected
    A2 Port allocation 2003 UDP + 36 TCP allocated
    A3 Deterministic layout RTP/Janus/Satellite/IAX/SIP-UDP at expected offsets
    A4 SIP separation ASTERISK_SIP_UDP_PORT ≠ ASTERISK_SIP_PORT
    A5 configure-module rc=0, no abort
    A6 DB ports after FreePBX boot iax/sip/kvstore rows = allocated values
    A7 Live Asterisk sockets SIP UDP, SIP TCP, IAX, RTP listening
    A8 Live Janus/Satellite sockets Janus RTP range; Satellite RTP
    A9 get-ports-list API values/protocols = env
    A10 Firewall public ports open with correct protocols
    A11 Service discovery SIP UDP advertised; phonebook present

    Manual tests

    Each test is self-contained: do the step, run the command, check the result.

    A1 — Clean install

    1. Install: add-module <IMG> <NODE> → note <ID>. → rc=0.
    2. Configure:
      api-cli run module/<ID>/configure-module --data '{"nethvoice_host":"pbx.<domain>","nethcti_ui_host":"cti.<domain>","user_domain":"<domain>","timezone":"Europe/Rome","reports_international_prefix":"39","nethvoice_adm_username":"admin","nethvoice_adm_password":"<password>"}'
      → rc=0.
    3. Check services: runagent -m <ID> systemctl --user --failed → no failed units.
    4. Open the PBX and CTI URLs, log in as admin → both work.

    A13 — Ports are correct after install

    1. runagent -m <ID> env | grep -E 'ASTERISK_SIP_PORT|ASTERISK_SIP_UDP_PORT|ASTERISK_IAX_PORT|SATELLITE_RTP_PORT|ASTERISK_RTPSTART'
      → all set; ASTERISK_SIP_UDP_PORT ≠ ASTERISK_SIP_PORT.
    2. runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'
      → the UDP transport is bound to ASTERISK_SIP_UDP_PORT.
    3. ss -lun | grep <ASTERISK_SIP_UDP_PORT> → the SIP UDP port is listening.

    B1 — Two instances on the same node don't collide

    1. With one instance already installed, add a second on the same node: add-module <IMG> <NODE> → <ID2>; configure it.
    2. Compare the ranges:
      runagent -m <ID1> env | grep UDP_PORTS_RANGE
      runagent -m <ID2> env | grep UDP_PORTS_RANGE
      → the two ranges do not overlap.
    3. journalctl _UID=$(id -u <ID2>) | grep -i "address already in use" → no matches (includes DUNDI 4520 and Janus demo 5002/5102).

    B2 — Removing an instance frees its ports

    1. remove-module <ID2>
    2. sqlite3 /var/lib/nethserver/node/state/ports.sqlite "SELECT * FROM UDP_PORTS;" → no rows left for <ID2>.

    Update tests

    Before each update, set a marker to check persistence: create a test extension in the PBX and note the admin password.

    C1 — Update from 1.7.4 (current stable)

    1. Install and configure a 1.7.4 instance.
    2. Confirm the old state: runagent -m <ID> env | grep SATELLITE_RTP_PORT → empty (unset).
    3. Update:
      api-cli run update-module --data '{"module_url":"<IMG>","instances":["<ID>"],"force":true}'
      → rc=0.
    4. runagent -m <ID> env | grep SATELLITE_RTP_PORT → now set (Satellite has its own port again).
    5. runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports' → UDP transport on the new ASTERISK_SIP_UDP_PORT, no manual restart needed.
    6. Test extension still present, admin login still works → data preserved.
    7. Run the same update command again → rc=0, nothing changes (idempotent).

    C2 — Update from 1.6.7 (before the temporary fix)

    1. Install and configure a 1.6.7 instance.
    2. Confirm the old state: runagent -m <ID> env | grep SATELLITE_RTP_PORT → set (old dedicated port).
    3. Update to <IMG> (same command as C1 step 3). → rc=0.
    4. runagent -m <ID> env | grep -E 'SATELLITE_RTP_PORT|UDP_PORTS_RANGE' → Satellite port is inside the new UDP block; no stale old value.
    5. Test extension still present, admin login still works → data preserved.

    C3 — Update next to an existing instance (collision)

    1. On one node: one PR instance + one legacy instance (1.7.4 or 1.6.7) with overlapping old ranges.
    2. Update the legacy instance to <IMG>.
    3. Compare UDP_PORTS_RANGE of the two instances → non-overlapping.
    4. journalctl _UID=$(id -u <ID>) | grep -i "address already in use" → no matches.

    C4 — Update to a wrong image fails cleanly

    1. api-cli run update-module with a non-existent module_url. → fails; the instance keeps working on the current image.

    D1 — Clone to another node

    1. Clone: api-cli run clone-module --data '{"module_id":"<ID>","node":2,"replace":false}' → rc=0. Note <ID_DST> on node 2; configure it.
    2. Source still works (check its UI/services).
    3. On the destination: runagent -m <ID_DST> podman exec freepbx asterisk -rx 'pjsip show transports'
      → UDP transport on the destination ASTERISK_SIP_UDP_PORT, TCP transport on the destination ASTERISK_SIP_PORT — UDP ≠ TCP.
    4. runagent -m <ID_DST> env | grep UDP_PORTS_RANGE → different range from the source.

    D2 — Clone with FreePBX offline (config rewrite)

    1. Run the clone while the destination FreePBX is not running (offline path). → completes with no "sed not found" / exit 127.

    E1 — Move to another node

    1. Move: api-cli run clone-module --data '{"module_id":"<ID>","node":2,"replace":true}' → rc=0.
    2. runagent -m <ID_DST> env | grep MODULE_UUID and the source → no duplicate UUID; source removed.
    3. Destination ports correct: runagent -m <ID_DST> podman exec freepbx asterisk -rx 'pjsip show transports' → on the destination SIP UDP/TCP ports.

    F1 — Backup and restore

    1. Set a marker (test extension), then back up: api-cli run cluster/run-backup --data '{"id":<repo>}' → rc=0.
    2. Restore: api-cli run restore-module --data '{"repository":"<uuid>","path":"<path>","snapshot":"","node":<NODE>,"replace":false}' → rc=0 (does not abort).
    3. Marker present and admin login works → data restored.
    4. runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports' → SIP UDP/TCP on the restored instance's ports.
    5. Satellite is running: runagent -m <ID> systemctl --user status satellite → active (running) (Satellite PostgreSQL restored).

    F2 — Restore in place (replace=true)

    1. Restore over an existing instance with "replace":true. → rc=0; instance works; ports correct.

    G1 — FreePBX restart keeps ports

    1. runagent -m <ID> systemctl --user restart freepbx
    2. runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports' → SIP UDP still on ASTERISK_SIP_UDP_PORT.

    G2 — Reconfigure doesn't drop calls

    1. Run configure-module again changing a non-port setting (e.g. an integration).
    2. runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports' → ports unchanged; Asterisk not restarted unnecessarily.

    H1 — Remove frees everything

    1. remove-module <ID>
    2. sqlite3 /var/lib/nethserver/node/state/ports.sqlite "SELECT * FROM UDP_PORTS;" and TCP_PORTS → no rows for <ID>.

    J1 — Real call (final gate)

    1. Register two extensions on a fresh PR instance and place a call between them. → SIP registers on the allocated ports; two-way audio works.

    Notes

    • Clone/move atomicity (no partial destination, no duplicate live UUID) and the
      allocator capacity limit depend on ns8-core — verify at integration level.
    • The real call (J1) is the final gate before merge.
  3. moved this from Todo to In Progress in NethVoiceon May 8, 2026
  4. changed the title [-]NethVoice: Review UDP/TCP Port Assignment and Limit Concurrent Calls[/-] [+]NethVoice: Review UDP/TCP Port Assignment[/+] on May 8, 2026
  5. deleted a comment from margit86 on Jul 13, 2026
  6. self-assigned this
    on Jul 20, 2026
  7. 22 remaining items

  8. added theissue type on Oct 6, 2026
  9. removed
    testingPackages are available from testing repositories
    on Oct 9, 2026
  10. added
    testingPackages are available from testing repositories
    and removed
    verifiedAll test cases were verified successfully
    on Oct 9, 2026
  11. edospadoni commented on Oct 12, 2026

    @edospadoni
    Member
  12. removed
    testingPackages are available from testing repositories
    on Oct 12, 2026
  13. moved this from Testing to Verified in NethVoiceon Oct 12, 2026
  14. Amygos commented on Oct 12, 2026

    @Amygos
    Member

    Release nethesis/ns8-nethvoice 1.7.10

  15. moved this from Verified to Done in NethVoiceon Oct 12, 2026
  16. removed
    verifiedAll test cases were verified successfully
    on Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    nethvoiceBug or features releted to the NethVoice project

    Type

    Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions