Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions modules/kamailio/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,33 @@ echo "listen=udp:${SERVICE_IP}:5060" >> /tmp/kamailio-local-additional.cfg
echo "listen=tcp:${SERVICE_IP}:5060" >> /tmp/kamailio-local-additional.cfg
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.
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
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.
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
Comment on lines +69 to +76
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.
fi

echo "Generated slot: ${SLOT_NAME} on port ${SLOT_PORT} (UDP/TCP), $((SLOT_PORT + 1000)) (TLS)"
done
fi
Comment on lines +56 to +81
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.


# rendering the template of kamailio-local.cfg and kamailio.cfg
envsubst < /etc/kamailio/template.kamailio-local.cfg > /tmp/kamailio-local.cfg
Expand Down
32 changes: 31 additions & 1 deletion modules/kamailio/config/kamailio.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,10 @@ modparam("dispatcher", "db_url", DBURL)
modparam("dispatcher", "ds_ping_from", "sip:dispatcher@nethvoice.it")
// 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("dispatcher", "ds_probing_mode", 2) # Value 2: if set to 2, only gateways in INACTIVE state with PROBING mode set are tested.
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_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.
modparam("htable", "htable", "userblocklist=>size=8;autoexpire=300;")
modparam("htable", "htable", "ipban=>size=20;autoexpire=300;")
modparam("htable", "htable", "apiban=>size=11;")
Expand Down Expand Up @@ -310,6 +311,8 @@ request_route {
route(REWRITE_CONTACT);
// add_contact_alias();
}
# Select trunk slot if configured
route(SELECT_TRUNK_SLOT);
route(RELAY);
exit;
} else {
Expand Down Expand Up @@ -1106,3 +1109,30 @@ route[SET_SOCKET] {
}
}
} # end of route[SET_SOCKET]

# -----------------------------------------------------------------------------
# route[SELECT_TRUNK_SLOT]
# Select appropriate trunk slot based on internal source if configured
# -----------------------------------------------------------------------------
route[SELECT_TRUNK_SLOT] {
// $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");
// }
Comment on lines +1118 to +1129
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.
if (is_present_hf("X-ProxySlot") && $hdr(X-ProxySlot) != $null && $hdr(X-ProxySlot) != "" && $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");
Comment on lines +1131 to +1132
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.
// remove the header X-ProxySlot
remove_hf("X-ProxySlot");
} else {
if ($shv(debug) == 1) xlog('L_INFO', "[SELECT_TRUNK_SLOT] - $ci $rm-$cs - No slot mapping for: $si:$sp\n");
}
}
Loading