Skip to content

feat: Dynamic port slot binding for multi-trunk support (Vianova)#158

Open
paolovisintin wants to merge 18 commits into
mainfrom
147-vianova
Open

feat: Dynamic port slot binding for multi-trunk support (Vianova)#158
paolovisintin wants to merge 18 commits into
mainfrom
147-vianova

Conversation

@paolovisintin

@paolovisintin paolovisintin commented Mar 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Implements dynamic port slot binding to support multiple Vianova trunks from a single proxy IP
  • When Kamailio processes an outbound REGISTER to a "special" domain (e.g., voip.vianova.it), it automatically assigns a unique port slot to each (username, domain) tuple
  • The Contact header is rewritten with the slot port (e.g., 88.88.88.1:5071), and all subsequent SIP traffic (INVITE, BYE, etc.) uses the same slot
  • Slot assignments are persisted in PostgreSQL and managed with a 3-hour TTL, reset on every REGISTER refresh

Changes

  • modules/kamailio/config/kamailio.cfg — New routes: SLOT_REGISTER, SLOT_INVITE, SLOT_REWRITE_CONTACT; SET_SOCKET guard for $fsn; htable definitions (slotdomains, slotassign, slotpool); expired event route for automatic slot release
  • modules/kamailio/bootstrap.sh — Dynamic generation of named listen sockets (slots) based on TRUNK_SLOTS and TRUNK_PORT_START env vars
  • modules/kamailio/config/template.kamailio-local.cfg — Added TRUNK_SLOTS and TRUNK_PORT_START defines
  • modules/postgres/migrations/05_slot_tables.sql — New tables slot_domains and slot_assignments for htable persistence
  • imageroot/actions/create-module/10env — Default env vars (TRUNK_SLOTS=0, TRUNK_PORT_START=5071)

How it works

  1. Admin adds a domain to slot_domains table and reloads with kamcmd htable.reload slotdomains
  2. Each outbound REGISTER to that domain gets a unique slot (port) per trunk username
  3. INVITE and other methods look up the slot by $fU::$rd (From-User + Request-Domain)
  4. $fsn forces the outbound socket; SET_SOCKET is guarded to not override it
  5. Slots are released automatically after 3h TTL expiry or explicit un-register (Expires: 0)

Tested on GNS3

  • Two trunks (040989898 → slot1/5071, 040888888 → slot2/5072) registering simultaneously
  • REGISTER, INVITE, BYE all using correct slot ports
  • Slot persistence across Kamailio restart
  • No race conditions with concurrent registrations

Related

Test plan

  • Verify REGISTER to non-special domain uses legacy behavior (no slot)
  • Verify first REGISTER to special domain assigns slot1
  • Verify second trunk to same domain assigns slot2
  • Verify REGISTER refresh keeps same slot, resets TTL
  • Verify INVITE uses correct slot port
  • Verify slot release on Expires: 0
  • Verify 503 when all slots exhausted
  • Verify persistence after Kamailio restart
  • Verify firewall ports are opened (pending 20firewall update)

Paolo added 7 commits March 23, 2026 15:19
Added functionality to generate trunk port slots for multi-trunk support if TRUNK_SLOTS set and greater than 0. This will allow for easier configuration and management of trunk ports.
 TRUNK_SLOTS and TR_START agent configuration.
…slot-based port binding forunk supportail configuration. the initialization of slot pools, automatic slot release on TTL expiration, assignment of slots for outbound REGISTER requests to special domains, and slot binding for outbound INVITE and other methods. Contact is rewritten slot port for special domains. This management and enhance multi-trunk support in Kamailio.
Added new tables for slot-based port binding to support multi-trunk functionality. This commit follows the Kamailio htable schema for compatibility with dbmode=1.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds dynamic “slot” (named socket/port) binding in the Kamailio module to support multiple Vianova trunks behind a single proxy IP, persisting slot assignments in PostgreSQL and generating per-slot listeners at bootstrap time.

Changes:

  • Introduces new Kamailio htables and routes to assign/apply/release per-trunk slot bindings and rewrite Contact accordingly.
  • Generates additional named listen sockets (“slotN”) from TRUNK_SLOTS/TRUNK_PORT_START environment variables.
  • Adds PostgreSQL tables/migration for htable-backed persistence and wires new env defaults/defines.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
modules/postgres/migrations/05_slot_tables.sql Adds slot_domains and slot_assignments tables/sequences/indexes for htable persistence.
modules/kamailio/config/template.kamailio-local.cfg Adds TRUNK_SLOTS and TRUNK_PORT_START preprocessor defines.
modules/kamailio/config/kamailio.cfg Adds htable configuration + slot allocation/apply/rewrite routes and expiry hook.
modules/kamailio/bootstrap.sh Generates named listen sockets for slot ports based on environment variables.
imageroot/actions/create-module/10env Sets default values for TRUNK_SLOTS and TRUNK_PORT_START.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1184 to +1194
# Pre-initialize all slot pool entries to 0 (free)
$sht(slotpool=>slot1) = 0;
$sht(slotpool=>slot2) = 0;
$sht(slotpool=>slot3) = 0;
$sht(slotpool=>slot4) = 0;
$sht(slotpool=>slot5) = 0;
$sht(slotpool=>slot6) = 0;
$sht(slotpool=>slot7) = 0;
$sht(slotpool=>slot8) = 0;
$sht(slotpool=>slot9) = 0;
$sht(slotpool=>slot10) = 0;

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

event_route[htable:mod-init] initializes slotpool only for slot1..slot10, but TRUNK_SLOTS is configurable and can exceed 10. This will leave higher-numbered slots uninitialized (or unclear) and makes the configuration harder to maintain. Consider initializing slotpool in a loop up to TRUNK_SLOTS (and/or relying on initval) rather than hardcoding 10 entries.

Suggested change
# Pre-initialize all slot pool entries to 0 (free)
$sht(slotpool=>slot1) = 0;
$sht(slotpool=>slot2) = 0;
$sht(slotpool=>slot3) = 0;
$sht(slotpool=>slot4) = 0;
$sht(slotpool=>slot5) = 0;
$sht(slotpool=>slot6) = 0;
$sht(slotpool=>slot7) = 0;
$sht(slotpool=>slot8) = 0;
$sht(slotpool=>slot9) = 0;
$sht(slotpool=>slot10) = 0;
# Pre-initialize all slot pool entries to 0 (free), up to TRUNK_SLOTS
#!for $i 1 $TRUNK_SLOTS
$sht(slotpool=>slot$i) = 0;
#!next

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proposed #!for directive doesn't exist in Kamailio's preprocessor. Kamailio only supports #!define, #!ifdef, #!ifndef, #!else, #!endif, #!subst, #!substdef, #!include, and a few others — there is no loop construct at the preprocessor level.

A runtime loop would work instead:

$var(i) = 1;
while ($var(i) <= TRUNK_SLOTS) {
$sht(slotpool=>"slot" + $var(i)) = 0;
$var(i) = $var(i) + 1;
}
That said, the issue is already mitigated by the htable definition:

modparam("htable", "htable", "slotpool=>size=4;autoexpire=0;initval=0;")
The initval=0 means any key not explicitly set in slotpool already returns 0 (free) by default. So even if TRUNK_SLOTS exceeds 10, uninitialized slots would still behave correctly. The explicit initialization is redundant but improves readability.

We can replace the hardcoded block with the runtime loop for clarity and consistency with TRUNK_SLOTS.

Comment on lines +1196 to +1202
# Rebuild occupancy: slotassign is loaded from DB (dbmode=2) before mod-init.
# Mark occupied slots by checking each slot name against slotassign values.
# We check each pool slot against all known assignments via a simple scan.
# Since slotassign has dbmode=2, entries are already loaded at this point.
# We rely on SLOT_REGISTER to re-mark slots on next REGISTER cycle.

xlog("L_INFO", "[SLOT_INIT] Slot pool initialized\n");

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says this route rebuilds slot occupancy from persisted assignments, but the implementation currently sets every slotpool entry to free and never marks slots that are already present in slotassign (loaded from DB). After a restart, new REGISTERs can reuse an already-assigned slot until the old trunk refreshes, causing collisions. Consider iterating existing slotassign entries at startup (e.g., via htable iteration or a DB query) and marking the corresponding slotpool entries as occupied.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. The current implementation does have a collision window after a restart: slotassign is restored from DB (dbmode=2), but slotpool is reset to all zeros. If a new trunk sends a REGISTER before the old trunk refreshes, it could claim a slot that's already assigned in slotassign.

The comment acknowledges this by saying "We rely on SLOT_REGISTER to re-mark slots on next REGISTER cycle", but we can do better by rebuilding occupancy at startup using sht_iterator on the already-loaded slotassign:

sht_iterator_start("iter", "slotassign");
while (sht_iterator_next("iter")) {
$var(slot_val) = $shtitval(iter);
if ($var(slot_val) != $null && $var(slot_val) != "") {
$sht(slotpool=>$var(slot_val)) = 1;
xlog("L_INFO", "[SLOT_INIT] Restored: $shtitkey(iter) -> $var(slot_val)\n");
}
}
sht_iterator_end("iter");
This iterates slotassign (read-only) and writes to slotpool (a different htable), so no locking issues. Will include this in the next update.

Comment on lines +1230 to +1236
# 3. Handle un-register (Expires: 0)
$var(expires) = 0;
if (is_present_hf("Expires")) {
$var(expires) = $hdr(Expires);
}

if ($var(expires) == 0) {

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Un-register detection only checks the Expires header. REGISTER unregistration often uses Contact header parameters (e.g., "Contact: ...;expires=0") without an Expires header, in which case the slot will not be released. Consider also parsing Contact expires (and potentially multiple Contacts) to reliably detect unregistration.

Suggested change
# 3. Handle un-register (Expires: 0)
$var(expires) = 0;
if (is_present_hf("Expires")) {
$var(expires) = $hdr(Expires);
}
if ($var(expires) == 0) {
# 3. Handle un-register (Expires: 0 or Contact expires=0)
$var(expires) = 0;
$var(is_unreg) = 0;
if (is_present_hf("Expires")) {
$var(expires) = $hdr(Expires);
if ($var(expires) == 0) {
$var(is_unreg) = 1;
}
}
# If there is no explicit Expires: 0, also check Contact header parameters
if ($var(is_unreg) == 0 && is_present_hf("Contact")) {
# Treat as un-register if any Contact has an expires=0 parameter
if ($hdr(Contact) =~ "(^|[; ,])expires=0([^0-9]|$)") {
$var(is_unreg) = 1;
}
}
if ($var(is_unreg) == 1) {

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The observation is technically valid in the general SIP case, but in this specific context the risk is minimal:

This route handles outbound trunk registrations (Asterisk → SIP provider), not UA client registrations. Trunk REGISTER unsubscriptions use the Expires: 0 header — the Contact: ...;expires=0 pattern is typical of UA registrations, which are not handled here.

The suggested regex is fragile — parsing Contact header parameters with regex can break on whitespace variations, quoted strings, or multiple Contact entries.

There's already a safety net: slotassign entries have a 10800-second TTL (3 hours). Even if an unregister is somehow missed, the slot is automatically freed by event_route[htable:expired:slotassign].

Given these factors, we don't think adding Contact header parsing complexity is warranted here.

Comment on lines +1258 to +1272
# 5. Find first available slot
$var(found) = 0;
$var(i) = 1;
while ($var(i) <= TRUNK_SLOTS && $var(found) == 0) {
$var(slot_name) = "slot" + $var(i);
$var(pool_val) = $sht(slotpool=>$var(slot_name));
if ($var(pool_val) == 0) {
# Slot is free — claim it
$sht(slotpool=>$var(slot_name)) = 1;
$sht(slotassign=>$var(slot_key)) = $var(slot_name);
$shtex(slotassign=>$var(slot_key)) = 10800;
$fsn = $var(slot_name);
$var(found) = 1;
xlog("L_WARN", "[SLOT_REGISTER] - $ci $rm-$cs - ASSIGNED: $var(slot_key) -> $fsn (TTL 3h)\n");
}

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slot allocation scans slotpool and then writes slotpool/slotassign in multiple steps without any locking/atomicity. With multiple Kamailio children, concurrent REGISTERs can observe the same free slot and both claim it, leading to duplicate port assignments. Consider protecting the allocate/claim section with an htable lock (or moving allocation to an atomic DB operation with uniqueness on slot value).

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically correct — the read-then-write on slotpool is not atomic across Kamailio workers. However, the practical risk is negligible:

Trunk registrations are infrequent events — each trunk registers every few minutes. The probability of two new trunks sending their first REGISTER in the same microsecond window is extremely low.
The race window is minimal — microseconds between the slotpool read and write.
It's self-correcting — on the next REGISTER refresh (step 4), the trunk finds its existing assignment in slotassign and renews it. Even if a collision occurred, one trunk would get reassigned a different slot on the next cycle.
The slot count is small — typically single digits, so the scan is near-instantaneous.
A lock()/unlock() (cfgutils module) around the allocation section could eliminate the theoretical race, but it would add complexity for a scenario that is virtually unreachable in production. We'll keep this in mind but don't consider it a priority.

Comment on lines +70 to +79
SLOT_TLS_PORT=$((SLOT_PORT + 1000))

if [ "${BEHIND_NAT}" == "true" ]; then
echo "listen=udp:${PRIVATE_IP}:${SLOT_PORT} advertise ${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tcp:${PRIVATE_IP}:${SLOT_PORT} advertise ${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tls:${PRIVATE_IP}:${SLOT_TLS_PORT} advertise ${PUBLIC_IP}:${SLOT_TLS_PORT} name \"${SLOT_NAME}_tls\"" >> /tmp/kamailio-local-additional.cfg
else
echo "listen=udp:${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tcp:${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tls:${PUBLIC_IP}:${SLOT_TLS_PORT} name \"${SLOT_NAME}_tls\"" >> /tmp/kamailio-local-additional.cfg

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bootstrap.sh generates TLS slot listeners on SLOT_TLS_PORT (slot UDP/TCP port + 1000) and names them "slotN_tls", but the Kamailio logic assigns $fsn="slotN" and rewrites Contact using the UDP/TCP slot port even when transport is TLS. This combination will mis-advertise TLS contacts and can select the wrong outbound socket for TLS traffic. Consider aligning TLS port/name selection with the $fsn and Contact rewrite logic (or omit TLS slot listeners if unsupported).

Suggested change
SLOT_TLS_PORT=$((SLOT_PORT + 1000))
if [ "${BEHIND_NAT}" == "true" ]; then
echo "listen=udp:${PRIVATE_IP}:${SLOT_PORT} advertise ${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tcp:${PRIVATE_IP}:${SLOT_PORT} advertise ${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tls:${PRIVATE_IP}:${SLOT_TLS_PORT} advertise ${PUBLIC_IP}:${SLOT_TLS_PORT} name \"${SLOT_NAME}_tls\"" >> /tmp/kamailio-local-additional.cfg
else
echo "listen=udp:${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tcp:${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tls:${PUBLIC_IP}:${SLOT_TLS_PORT} name \"${SLOT_NAME}_tls\"" >> /tmp/kamailio-local-additional.cfg
# Use the same port for TLS as for UDP/TCP to align with Kamailio $fsn and Contact logic
SLOT_TLS_PORT=${SLOT_PORT}
if [ "${BEHIND_NAT}" == "true" ]; then
echo "listen=udp:${PRIVATE_IP}:${SLOT_PORT} advertise ${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tcp:${PRIVATE_IP}:${SLOT_PORT} advertise ${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tls:${PRIVATE_IP}:${SLOT_TLS_PORT} advertise ${PUBLIC_IP}:${SLOT_TLS_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
else
echo "listen=udp:${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tcp:${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tls:${PUBLIC_IP}:${SLOT_TLS_PORT} name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The observation partially identifies a real issue, but the suggested fix (using the same port for UDP and TLS) is incorrect — you cannot bind UDP and TLS on the same port in Kamailio.

Here's how $fsn actually works internally: when $fsn = "slot1" is set, Kamailio resolves it to the first matching listener (UDP, since it's checked first). At send time, if the outbound transport differs (e.g., TLS), Kamailio tries to find a listener on the same IP:port with the correct protocol. Since our TLS listeners are on a different port (SLOT_PORT + 1000), find_si() won't find them and falls back to default socket selection — effectively losing the slot binding for TLS.

That said, in practice this is a non-issue for our use case: trunk registrations to SIP providers (Vianova, etc.) use UDP or TCP, not TLS. The TLS slot listeners are provisioned for future-proofing but are not actively used by any current trunk configuration.

If TLS trunk support becomes a requirement in the future, the fix would be to detect the outbound transport in SLOT_REGISTER/SLOT_INVITE and set $fsn = "slotN_tls" accordingly, plus adjust SLOT_REWRITE_CONTACT to use the TLS port. We'll address that when needed.

Comment thread modules/postgres/migrations/05_slot_tables.sql Outdated
Comment on lines +1315 to +1335
# Calculate port from slot name: "slotN" -> TRUNK_PORT_START + N - 1
$var(slot_num) = $(fsn{s.substr,4,0});
$var(slot_port) = TRUNK_PORT_START + $var(slot_num) - 1;

# Determine transport from original Contact
$var(ctProto) = $(ct{nameaddr.uri}{uri.transport});
if ($var(ctProto) == $null || $var(ctProto) == "" || $var(ctProto) == 0) {
$var(ctProto) = "UDP";
}

# Preserve Contact URI parameters if present
$var(contactParams) = "";
if ($(ct{nameaddr.uri}{uri.params}) != $null && $(ct{nameaddr.uri}{uri.params}) != "" && $(ct{nameaddr.uri}{uri.params}) != 0) {
$var(contactParams) = ";" + $(ct{nameaddr.uri}{uri.params});
}

remove_hf("Contact");
$var(default_contact) = DEFAULT_CONTACT;
# Strip any existing port from DEFAULT_CONTACT before adding slot port
$var(contact_host) = $(var(default_contact){s.select,0,:});
append_hf("Contact: <sip:$fU@$var(contact_host):$var(slot_port);transport=$(var(ctProto){s.tolower})$var(contactParams)>\r\n", "Call-ID");

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SLOT_REWRITE_CONTACT always uses the calculated UDP/TCP slot port, even when the original Contact transport is TLS. However bootstrap.sh creates TLS listeners on a different port (slot_port+1000) and with a different socket name (slotN_tls), so TLS registrations/calls will advertise an incorrect port and $fsn will not select the TLS slot. Consider mapping TLS contacts to the TLS slot port and setting/using the corresponding named socket for TLS traffic.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same concern raised on the bootstrap.sh TLS listeners — see the response there. In short: trunk registrations in our deployment use UDP/TCP, not TLS. The TLS slot listeners are provisioned for future use. If TLS trunk support becomes a requirement, we'll adjust both $fsn selection and SLOT_REWRITE_CONTACT port calculation to handle the TLS case. Not a priority for now.

paolovisintin and others added 11 commits April 7, 2026 11:58
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Pass TRUNK_* into the container so bootstrap.sh can render TRUNK_SLOTS
and TRUNK_PORT_START into kamailio.cfg.
Compute firewall service ports from TRUNK_SLOTS and
TRUNK_PORT_START so create-module and configure-module keep
the slot range aligned.

Move the shared firewall helpers into imageroot/pypkg so the
same NAT rich-rule logic is reused during destroy-module
cleanup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Seed TRUNK_SLOTS and TRUNK_PORT_START during module updates
so upgraded instances keep the new slot-based trunk settings
available.

Reuse the shared firewall helper constant so the default trunk
port start stays aligned with the firewall integration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the firewall-focused branch testable by preserving the
configure-module regression that checks slot ports survive
repeated reconfiguration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the firewall port calculation to bootstrap.sh so each
slot opens the UDP/TCP listener range and the corresponding
TLS range at slot_port + 1000.

Extend the firewall regression test to assert the TLS slot
ports are kept across reconfiguration as well.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the add-slot-domain action so operators can register a
special domain in slot_domains and immediately reload the
slotdomains htable in Kamailio.

The implementation follows the PR logic directly: create or
update the persisted domain entry, then refresh the runtime
htable view.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- SLOT_INVITE keyed the slot lookup on $fU (CallerID) but the slot is
  assigned from $tU on REGISTER. With providers like Vianova, where the
  auth username differs from the number placed in From, the keys didn't
  match and the INVITE fell back to the default 5060 socket, causing a
  403 from the provider. Use the Contact URI user — Asterisk puts the
  trunk auth username there.

- SLOT_REGISTER released the slot immediately on un-REGISTER (Expires: 0).
  The 401/407 auth retry then found no slot, $fsn couldn't be set, and
  the retry went out from 5060. Keep the assignment: auth retries reuse
  the same slot, a re-REGISTER refreshes it, and an abandoned trunk is
  eventually freed by TTL expiry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nethvoice: Vianova trunk

3 participants