Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No audio after UPDATE packages behind NAT #37

Merged
merged 8 commits into from
Aug 28, 2024
Merged
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ run-rtpengine-dev:
podman rm rtpengine || exit 0
podman run -d --name rtpengine --env-file ~/.config/state/environment --network=host \
-v ./modules/rtpengine/files/rtpengine.conf.template:/src/rtpengine.conf.template:z \
-v ./modules/rtpengine/bootstrap.sh:/bootstrap.sh:z \
ghcr.io/nethesis/nethvoice-proxy-rtpengine:$(TAG)

log:
Expand Down
35 changes: 35 additions & 0 deletions imageroot/actions/configure-module/10validate_local_networks
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env python3

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

import json
import sys
import ipaddress
import os
import agent

agent.set_weight(os.path.basename(__file__), 0) # Validation step, no task progress at all

def is_cidr_format(ip_str):
try:
ipaddress.IPv4Network(ip_str)
return True
except ValueError:
return False

# Try to parse the stdin as JSON.
# If parsing fails, output everything to stderr
data = json.load(sys.stdin)

if "local_networks" in data:
local_network = ""
try:
for local_network in data["local_networks"]:
ipaddress.IPv4Network(local_network)
except ValueError:
agent.set_status('validation-failed')
json.dump([{'field':'service_networks','value': local_network,'error':'local_network_netmask_is_not_valid'}], fp=sys.stdout)
sys.exit(3)
13 changes: 13 additions & 0 deletions imageroot/actions/configure-module/20configure
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import os

data = json.load(sys.stdin)

local_networks = set()

if "addresses" in data:

address = data["addresses"]
Expand All @@ -23,19 +25,30 @@ if "addresses" in data:
agent.set_env("PUBLIC_IP", address["address"])
agent.set_env("PRIVATE_IP", "")
agent.set_env("BEHIND_NAT", "false")
agent.unset_env("LOCALNETWORKS")
else:
# if there's a public address, the proxy is behind NAT
agent.set_env("PUBLIC_IP", address["public_address"])
agent.set_env("PRIVATE_IP", address["address"])
agent.set_env("BEHIND_NAT", "true")

# Get local network from routing table (excluding default route)
local_networks.add(os.popen("ip route | grep -v default | grep 'src " + address["address"] + "' | awk '{print $1}'").read().strip())
agent.set_env("LOCALNETWORKS", list(local_networks)[0])

agent.set_env("DEFAULT_CONTACT", address["address"] + ":5060" if "public_address" not in address else address["public_address"] + ":5060")

if "service_net" in data:
service = data["service_net"]
agent.set_env("SERVICE_IP", service["address"])
agent.set_env("SERVICE_NET", service["netmask"])

#check if local_networks is not empty and local_networks filed is present in data
if local_networks and "local_networks" in data:
# Add extra local networks
local_networks.update(set(data["local_networks"]))
agent.set_env("LOCALNETWORKS", ",".join(local_networks))

if "fqdn" in data:
# Check if previous fqdn is different from new one
if os.environ["KML_DEFAULT_FQDN"] != data["fqdn"]:
Expand Down
8 changes: 8 additions & 0 deletions imageroot/actions/configure-module/validate-input.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@
"type": "string"
}
}
},
"local_networks": {
"title": "Local networks",
"type": "array",
"items": {
"title": "Netmask of the local network",
"type": "string"
}
}
}
}
2 changes: 2 additions & 0 deletions imageroot/actions/get-configuration/20read
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ config["fqdn"] = os.environ["KML_DEFAULT_FQDN"]

config["service_network"] = service_network

config["local_networks"] = os.environ["LOCALNETWORKS"].split(",") if os.getenv("LOCALNETWORKS") else []

json.dump(config, fp=sys.stdout)
8 changes: 8 additions & 0 deletions imageroot/actions/get-configuration/validate-output.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@
"type": "string"
}
}
},
"local_networks": {
"title": "Local networks",
"type": "array",
"items": {
"title": "Netmask of the local network",
"type": "string"
}
}
}
}
1 change: 1 addition & 0 deletions imageroot/systemd/user/kamailio.service
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ ExecStart=/usr/bin/podman run \
--env=SERVICE_IP \
--env=SERVICE_NET \
--env=BEHIND_NAT \
--env=LOCALNETWORKS \
--detach \
--conmon-pidfile=%t/kmalio.pid \
--cidfile=%t/kmalio.ctr-id \
Expand Down
57 changes: 52 additions & 5 deletions modules/kamailio/config/kamailio.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,7 @@ request_route {
route(HEALTHCHECK);
# manage dialog module
dlg_manage();



if (!is_method("UPDATE")){
if (is_in_subnet($si, INTERNAL_NETWORK) || $si == "127.0.0.1" ) {
$avp(direction) = "out";
Expand Down Expand Up @@ -513,12 +512,12 @@ route[NATMANAGE] {
xlog('L_WARN', "[DEV] - $ci $rm-$cs - direction: $avp(direction) / $dlg_var(direction) ($rs) \n");
if ((($avp(direction) == 'in' || $dlg_var(direction) == 'in') && is_request()) || (($avp(direction) == 'out' || $dlg_var(direction) == 'out') && is_reply()) ) {
if (has_body("application/sdp")) {
$var(rtpengine_conf) = "external internal " + $var(rtpengine_conf);
$var(rtpengine_conf) = "direction=external direction=internal " + $var(rtpengine_conf);
if($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - external - internal (rs: $rs)\n");
}
} else {
if (has_body("application/sdp")) {
$var(rtpengine_conf) = "internal external " + $var(rtpengine_conf);
$var(rtpengine_conf) = "direction=internal direction=external " + $var(rtpengine_conf);
if($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - internal - external (rs: $rs)\n");
}
}
Expand Down Expand Up @@ -589,6 +588,7 @@ route[NATMANAGE] {
}

if ($shv(rtpengine) == 't') {
route(SET_SOCKET);
# Adding loog protection and ICE removing
$var(rtpengine_conf) = "" + $var(rtpengine_conf) + " ICE=remove";
if (nat_uac_test("8")) {
Expand Down Expand Up @@ -930,4 +930,51 @@ route[HANDLE_ALIAS] {
route(RELAY);
}

} # end of route[HANDLE_ALIAS]
} # end of route[HANDLE_ALIAS]

# -----------------------------------------------------------------------------
# route[SET_FROM_SOCKET]
# this route set the from socket accoding also to LOCALNETWORKS
# -----------------------------------------------------------------------------
route[SET_SOCKET] {
if( $shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - in SET_SOCKET route \n");
if(is_request()) {
# print $rd is an ip and is in LOCALNETWORKS then print it
$var(destination_ip)= $null; # reinitialize the variable
$var(destination_ip) = $(ru{uri.host}); # fetch the destination ip from ru
if ($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - destination ip: $var(destination_ip) \n");
if ($var(destination_ip) != $null && $var(destination_ip) != 0 && $var(destination_ip) != "") {
if (is_in_subnet($var(destination_ip), LOCALNETWORKS)) {
if ($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - destination ip is in LOCALNETWORKS \n");
$var(from_socket) = PRIVATE_IP;
set_advertised_address(PRIVATE_IP);
# logging I've set the PRIVATE IP
if ($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - from_socket: $var(from_socket) \n");
# handling rtpengine manage
if ($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - rtpengine_conf: $var(rtpengine_conf) \n");
# replace external in $var(rtpengine_conf) with local
$var(rtpengine_conf) = $(var(rtpengine_conf){s.replace,external,local});
# logging rtpengine_conf
if ($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - rtpengine_conf: $var(rtpengine_conf) \n");
}
}
} else {
if(is_reply()){
$var(destination_ip)= $null; # reinitialize the variable
# logging $dlg_var(source_ip)
if ($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - Original source ip: $dlg_var(source_ip) \n");
# fetching destination ip from contact
$var(destination_ip) = $dlg_var(source_ip);
if ($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - destination ip: $var(destination_ip) \n");
if ($var(destination_ip) != $null && $var(destination_ip) != 0 && $var(destination_ip) != "") {
if (is_in_subnet($var(destination_ip), LOCALNETWORKS)) {
if ($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - destination ip is in LOCALNETWORKS \n");
# replace external in $var(rtpengine_conf) with local
$var(rtpengine_conf) = $(var(rtpengine_conf){s.replace,external,local});
# logging rtpengine_conf
if ($shv(debug) == 1) xlog('L_WARN', "[DEV] - $ci $rm-$cs - rtpengine_conf: $var(rtpengine_conf) \n");
}
}
}
}
}
2 changes: 2 additions & 0 deletions modules/kamailio/config/template.kamailio-local.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
#!define KML_UA_HEADER "${KML_UA_HEADER}"
#!define DEFAULT_CONTACT "${DEFAULT_CONTACT}"
#!define WITH_TLS
#!define LOCALNETWORKS "${LOCALNETWORKS}"
#!define PRIVATE_IP "${PRIVATE_IP}"
##!define WITH_SIPTRACE

server_header="Server: ${KML_SERVER_HEADER}"
Expand Down
2 changes: 1 addition & 1 deletion modules/rtpengine/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# generating ${INTERFACE_SECTION} depending on the value of ${BEHIND_NAT}
if [ "${BEHIND_NAT}" == "true" ]; then
export INTERFACE_SECTION="interface=internal/${SERVICE_IP};external/${PRIVATE_IP}!${PUBLIC_IP}"
export INTERFACE_SECTION="interface=local/${PRIVATE_IP};internal/${SERVICE_IP};external/${PRIVATE_IP}!${PUBLIC_IP}"
else
export INTERFACE_SECTION="interface=internal/${SERVICE_IP};external/${PUBLIC_IP}"
fi
Expand Down
2 changes: 2 additions & 0 deletions tests/00_nethvoice-proxy_add-module.robot
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ Gather system information

FOR ${interface} IN @{response['data']}
${address}= Set Variable ${interface['addresses'][0]['address']}
${network}= Set Variable ${interface['addresses'][0]['network']}
EXIT FOR LOOP IF "${interface['name']}" == "eth0"
END
Set Global Variable ${local_ip} ${address}
Set Global Variable ${local_network} ${network}

Check if nethvoice-proxy is configured wih the correctly default values
${response} = Run task module/${module_id}/list-service-providers
Expand Down
4 changes: 4 additions & 0 deletions tests/10_actions/00_configure_module_validate.robot
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ Service network's address and netmask must be valid
... {"addresses": {"address": "127.0.0.1"}, "service_network": {"address": "A", "netmask": "10.5.4.0/24"}} rc_expected=10 decode_json=False
${response} = Run task module/${module_id}/configure-module
... {"addresses": {"address": "127.0.0.1"}, "service_network": {"address": "10.5.4.1", "netmask": "A"}} rc_expected=2 decode_json=False

Networks in local networks list must be valid
${response} = Run task module/${module_id}/configure-module
... {"local_networks": ["A"]} rc_expected=3 decode_json=False
8 changes: 8 additions & 0 deletions tests/10_actions/00_get_configuration_validate.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*** Settings ***
Library SSHLibrary
Resource ../api.resource

*** Test Cases ***
Get default configuration
${response} = Run task module/${module_id}/get-configuration
... {} rc_expected=0
5 changes: 4 additions & 1 deletion tests/10_actions/10_configure_integrations.robot
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Set an address
${response} = Run task module/${module_id}/get-configuration
... {}
Should Be Equal ${response["addresses"]} ${{ {"address": "${local_ip}"} }}
Should Be Empty ${response['local_networks']}
${response} = Run task module/${module_id}/list-service-providers
... {"service": "sip", "transport": "tcp", "filter": {"module_id": "${module_id}"} }
Should Be Equal ${response[0]['address']} ${local_ip}
Expand All @@ -20,10 +21,12 @@ Set an address

Set an address behind NAT
Run task module/${module_id}/configure-module
... {"addresses": {"address": "${local_ip}", "public_address": "1.2.3.4"}}
... {"addresses": {"address": "${local_ip}", "public_address": "1.2.3.4"}, "local_networks": ["10.20.30.0/24"]}
${response} = Run task module/${module_id}/get-configuration
... {}
Should Be Equal ${response["addresses"]} ${{ {"address": "${local_ip}", "public_address": "1.2.3.4"} }}
Should Contain Match ${response['local_networks']} ${local_network}
Should Contain Match ${response['local_networks']} 10.20.30.0/24
${response} = Run task module/${module_id}/list-service-providers
... {"service": "sip", "transport": "tcp", "filter": {"module_id": "${module_id}"} }
Should Be Equal ${response[0]['address']} ${local_ip}
Expand Down
Loading