Skip to content

Conversation

@paolovisintin
Copy link
Collaborator

request to merge in main if if covers requrements expressed

Paolo added 7 commits October 31, 2025 11:55
Added htable configuration for trunk slots to enable selection of appropriate trunk slot based on internal source if configured. This commit will trigger a release bumping a MINOR version.
Added functionality to generate trunk port slots for multi-trunk support if TRUNK_SLOTS set and greater than 0. This commit will trigger a release bumping a MINOR version.
Created a new table to map trunk source IP:port to slot names in Kamailio htable format. Includes index for faster lookups and added comments for documentation.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request implements multi-port binding support for Kamailio to enable multiple trunk configurations. The implementation allows dynamic generation of listening sockets on different ports, each identified by a slot name, supporting both NAT and direct network modes.

  • Adds dynamic trunk slot generation in bootstrap.sh using TRUNK_SLOTS environment variable
  • Implements slot selection mechanism via X-ProxySlot SIP header in kamailio.cfg
  • Configures htable module with database URL for potential future enhancements

Reviewed changes

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

File Description
modules/kamailio/config/kamailio.cfg Adds htable db_url parameter and new SELECT_TRUNK_SLOT route to select outbound socket based on X-ProxySlot header
modules/kamailio/bootstrap.sh Generates multiple listening sockets (slots) for UDP/TCP/TLS protocols with configurable port ranges and NAT support

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

Comment on lines +56 to +81
# Generate trunk port slots if TRUNK_SLOTS is set and > 0
if [ -n "${TRUNK_SLOTS}" ] && [ "${TRUNK_SLOTS}" -gt 0 ]; then
TRUNK_PORT_START=${TRUNK_PORT_START:-5071}

echo "# Trunk Port Slots for Multi-Trunk Support" >> /tmp/kamailio-local-additional.cfg
echo "Generating ${TRUNK_SLOTS} trunk slots starting at port ${TRUNK_PORT_START}"

for i in $(seq 1 ${TRUNK_SLOTS}); do
SLOT_NAME="slot${i}"
SLOT_PORT=$((TRUNK_PORT_START + i - 1))

if [ "${BEHIND_NAT}" == "true" ]; then
# NAT mode: bind to private IP, advertise public IP
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_PORT + 1000)) advertise ${PUBLIC_IP}:$((SLOT_PORT + 1000)) name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
else
# Direct mode: bind to public IP
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_PORT + 1000)) name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
fi

echo "Generated slot: ${SLOT_NAME} on port ${SLOT_PORT} (UDP/TCP), $((SLOT_PORT + 1000)) (TLS)"
done
fi
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

Missing documentation for new environment variables. The new TRUNK_SLOTS, TRUNK_PORT_START, BEHIND_NAT, and PRIVATE_IP environment variables are used in the bootstrap script but are not documented in the README.md file. These should be added to the Environment variables section to help users understand how to configure multi-trunk support.

Copilot uses AI. Check for mistakes.
# NAT mode: bind to private IP, advertise public IP
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_PORT + 1000)) advertise ${PUBLIC_IP}:$((SLOT_PORT + 1000)) name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

Potential port conflict risk. The TLS port calculation $((SLOT_PORT + 1000)) could lead to port conflicts if TRUNK_SLOTS is large enough. For example, with TRUNK_PORT_START=5071 and TRUNK_SLOTS=30, the last UDP/TCP slot would be on port 5100, while the first TLS slot is on 6071. However, if users configure a large number of slots or a high starting port, there's no validation to prevent overlap. Consider adding validation or documenting the maximum safe value for TRUNK_SLOTS given the port offset.

Copilot uses AI. Check for mistakes.
echo "listen=tls:${SERVICE_IP}:5061" >> /tmp/kamailio-local-additional.cfg

# Generate trunk port slots if TRUNK_SLOTS is set and > 0
if [ -n "${TRUNK_SLOTS}" ] && [ "${TRUNK_SLOTS}" -gt 0 ]; then
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

Missing input validation for TRUNK_SLOTS. The script checks if TRUNK_SLOTS is set and greater than 0, but doesn't validate that it's a valid integer. If a non-numeric value is provided, the comparison -gt 0 will fail with an error. Consider adding validation like [[ "${TRUNK_SLOTS}" =~ ^[0-9]+$ ]] before the numeric comparison.

Suggested change
if [ -n "${TRUNK_SLOTS}" ] && [ "${TRUNK_SLOTS}" -gt 0 ]; then
if [ -n "${TRUNK_SLOTS}" ] && [[ "${TRUNK_SLOTS}" =~ ^[0-9]+$ ]] && [ "${TRUNK_SLOTS}" -gt 0 ]; then

Copilot uses AI. Check for mistakes.
Comment on lines +1131 to +1132
$fsn = $hdr(X-ProxySlot);
if ($shv(debug) == 1) xlog('L_WARN', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - Using slot: $fsn from X-ProxySlot header\n");
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

Missing input validation for X-ProxySlot header value. The header value is directly assigned to $fsn (forced send socket name) without validating that it corresponds to an actual configured slot name (e.g., "slot1", "slot2"). An attacker could send arbitrary values in this header, potentially causing routing errors or unexpected behavior. Consider validating that the header value matches the expected pattern (e.g., slot[1-9][0-9]*) and corresponds to a configured slot.

Suggested change
$fsn = $hdr(X-ProxySlot);
if ($shv(debug) == 1) xlog('L_WARN', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - Using slot: $fsn from X-ProxySlot header\n");
if ($(hdr(X-ProxySlot){re.match,^slot[1-9][0-9]*$})) {
# Optionally check if slot exists in trunkslots hash table
if ($sht(trunkslots=>$hdr(X-ProxySlot)) != $null && $sht(trunkslots=>$hdr(X-ProxySlot)) != "" && $sht(trunkslots=>$hdr(X-ProxySlot)) != 0) {
$fsn = $hdr(X-ProxySlot);
if ($shv(debug) == 1) xlog('L_WARN', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - Using slot: $fsn from X-ProxySlot header\n");
} else {
if ($shv(debug) == 1) xlog('L_WARN', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - X-ProxySlot value '$hdr(X-ProxySlot)' does not match any configured slot\n");
}
} else {
if ($shv(debug) == 1) xlog('L_WARN', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - Invalid X-ProxySlot header value: '$hdr(X-ProxySlot)'\n");
}

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +76
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_PORT + 1000)) advertise ${PUBLIC_IP}:$((SLOT_PORT + 1000)) name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
else
# Direct mode: bind to public IP
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_PORT + 1000)) name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

Same slot name assigned to different protocols and ports. All three protocols (UDP, TCP, TLS) for a given slot use the same name (e.g., "slot1"), but bind to different ports. While this might be intentional for logical grouping, it could cause confusion when trying to select a specific protocol+port combination via $fsn. Kamailio's forced send socket selection using the same name for multiple listeners may lead to unexpected behavior. Consider using protocol-specific names (e.g., "slot1_udp", "slot1_tcp", "slot1_tls") or document the expected behavior when the same name is used across protocols.

Suggested change
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_PORT + 1000)) advertise ${PUBLIC_IP}:$((SLOT_PORT + 1000)) name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
else
# Direct mode: bind to public IP
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_PORT + 1000)) name \"${SLOT_NAME}\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=udp:${PRIVATE_IP}:${SLOT_PORT} advertise ${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}_udp\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tcp:${PRIVATE_IP}:${SLOT_PORT} advertise ${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}_tcp\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tls:${PRIVATE_IP}:$((SLOT_PORT + 1000)) advertise ${PUBLIC_IP}:$((SLOT_PORT + 1000)) name \"${SLOT_NAME}_tls\"" >> /tmp/kamailio-local-additional.cfg
else
# Direct mode: bind to public IP
echo "listen=udp:${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}_udp\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tcp:${PUBLIC_IP}:${SLOT_PORT} name \"${SLOT_NAME}_tcp\"" >> /tmp/kamailio-local-additional.cfg
echo "listen=tls:${PUBLIC_IP}:$((SLOT_PORT + 1000)) name \"${SLOT_NAME}_tls\"" >> /tmp/kamailio-local-additional.cfg

Copilot uses AI. Check for mistakes.
Comment on lines +1118 to +1129
// $var(lookup_key) = $si + ":" + $sp;
// if ($shv(debug) == 1) xlog('L_WARN', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - Looking up: $var(lookup_key)\n");

// $var(slot) = $sht(trunkslots=>$var(lookup_key));
// if ($shv(debug) == 1) xlog('L_WARN', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - Found slot: $var(slot)\n");

// if ($var(slot) != $null && $var(slot) != "" && $var(slot) != 0) {
// $fsn = $var(slot);
// if ($shv(debug) == 1) xlog('L_WARN', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - Matched source: $var(lookup_key) -> Using slot: $var(slot)\n");
// } else {
// if ($shv(debug) == 1) xlog('L_INFO', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - No slot mapping for: $var(lookup_key)\n");
// }
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

Large block of commented-out code should be removed or properly documented. This commented implementation references a hash table lookup approach ($sht(trunkslots=>$var(lookup_key))) that appears to be an alternative to the current X-ProxySlot header approach. If this code represents a planned future enhancement, it should be documented in a comment explaining why it's preserved. Otherwise, it should be removed to improve maintainability.

Copilot uses AI. Check for mistakes.
modparam("dispatcher", "ds_ping_interval", 30) #With this parameter you can defined the interval for sending a request to a gateway marked as inactive upon a failed request routing to it. This parameter is only used, when the TM-Module is loaded. If set to "0", the pinging of inactive gateway is disabled.
modparam("dispatcher", "ds_ping_reply_codes", "class=2;code=403;code=488;code=401;class=3") #This parameter defines the valid response codes, which are accepted as a valid reply to the PING-Method. It is a list separated by colons, whery you may define either a single code (e.g. "code=202" would accept 202 as an additional, valid response) or a class of responses, you want to accept (e.g. "class=2" would accept everything from 200 to 299 as valid response). This parameter can be modified via ser config framework.
modparam("dispatcher", "flags", 2) #If flag 2 is set, then failover support is enabled. The functions exported by the module will store the rest of addresses from the destination set in the AVP, and use these AVPs to contact next address if the current-tried destination fails.
modparam("htable", "db_url", DBURL)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

The htable module's db_url parameter is added but no corresponding hash table for trunk slots is configured. The commented code at lines 1118-1129 references $sht(trunkslots=>$var(lookup_key)), but there's no modparam("htable", "htable", "trunkslots=>...") configuration. If the db_url is intended for future use with the commented code, this should be documented. If it's not needed for the current X-ProxySlot header implementation, it should be removed.

Suggested change
modparam("htable", "db_url", DBURL)

Copilot uses AI. Check for mistakes.
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.

2 participants