Fix VPN mobile connectivity and WiFi-to-cellular audio handoff

- Add local_net entries to PJSIP transports in LAN/VPN mode when
  VLAN/VPN subnets are configured, so Asterisk recognizes VPN traffic
  as local and mobile devices no longer appear offline on VPN
- Auto-detect VPN interfaces (Tailscale, WireGuard, OpenVPN) and
  suggest their subnets in the VLAN/VPN configuration menu
- Add rtp_keepalive=15, rtp_timeout=120, and rtp_timeout_hold=120
  to mobile device endpoints to maintain NAT mappings during
  WiFi-to-cellular transitions (fixes one-way audio)
- Reduce qualify_frequency from 60s to 30s for faster re-registration
  detection when mobile devices change networks
- Update README with VPN troubleshooting and one-way audio guidance
  including client-side fixes for Sipnetic and Linphone

https://claude.ai/code/session_01Vm6NLaQuzM4VosAotqS1q8
This commit is contained in:
Claude
2026-02-21 12:31:49 +00:00
parent 18b38bff26
commit cd5e989d2f
3 changed files with 215 additions and 15 deletions
+87 -15
View File
@@ -11,12 +11,15 @@
# - FIXED: Extension renaming now preserves AA tags correctly
# - FIXED: LAN/VPN devices now explicitly use UDP transport (prevents TLS fallback)
# - FIXED: LAN devices now have media_encryption=no to prevent SRTP issues
# - FIXED: VPN subnets now included as local_net in LAN mode (fixes VPN mobile offline)
# - FIXED: One-way audio on WiFi-to-mobile-data handoff (rtp_keepalive + timers)
# - ADDED: Web Admin interface for browser-based client management
# - View device status (online/offline) in real-time
# - Add/delete devices via web interface
# - View rooms and categories
# - HTTP Basic authentication with SHA256 password hashing
# - Access at http://server:8080/clients
# - ADDED: VPN subnet auto-detection (Tailscale, WireGuard, OpenVPN)
# - IMPROVED: Device deletion uses awk for reliable multi-section removal
# - IMPROVED: Device renaming uses awk to handle all edge cases
#
@@ -649,6 +652,15 @@ add_device_menu() {
backup_config "/etc/asterisk/pjsip.conf"
# Mobile devices benefit from keepalive to maintain NAT mappings
# during WiFi/mobile data transitions
local keepalive_block=""
if [[ "$cat_id" == "mobile" ]]; then
keepalive_block="rtp_keepalive=15
rtp_timeout=120
rtp_timeout_hold=120"
fi
cat >> /etc/asterisk/pjsip.conf << EOF
; === Device: $name ($cat_id) $override_tag ===
@@ -666,6 +678,7 @@ direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes
${keepalive_block}
${ice_block}
auth=${ext}
aors=${ext}
@@ -681,7 +694,7 @@ password=${pass}
type=aor
max_contacts=5
remove_existing=yes
qualify_frequency=60
qualify_frequency=30
EOF
chown -R asterisk:asterisk /etc/asterisk 2>/dev/null || true
@@ -1494,35 +1507,81 @@ verify_cidr_config() {
}
configure_vlan_subnets() {
print_header "VLAN Configuration"
print_header "VLAN / VPN Subnet Configuration"
load_config
echo "VLAN Support for Asterisk Easy"
echo "Additional Subnet Support for Easy Asterisk"
echo "================================================"
echo ""
echo "If your network uses VLANs (Virtual LANs), you need to"
echo "tell Asterisk about all the local subnets to prevent"
echo "calls from dropping after 30 seconds."
echo "If your network uses VLANs or VPNs, you need to tell"
echo "Asterisk about all the local subnets so that:"
echo " - Calls don't drop after 30 seconds (VLAN issue)"
echo " - VPN-connected mobile devices can register"
echo " - Audio works correctly for VPN users"
echo ""
echo "Example subnets:"
echo " 192.168.1.0/24 - Main network"
echo " 192.168.10.0/24 - IoT VLAN"
echo " 192.168.20.0/24 - Guest VLAN"
echo " 10.0.0.0/8 - Large private network"
echo " 100.64.0.0/10 - Tailscale VPN"
echo " 10.0.0.0/8 - WireGuard/OpenVPN"
echo ""
read -p "Does your network use VLANs? (y/n) [${HAS_VLANS}]: " has_vlans
# Auto-detect VPN interfaces and their subnets
local detected_vpn_subnets=""
local vpn_info=""
while IFS= read -r line; do
local iface=$(echo "$line" | awk '{print $2}' | tr -d ':')
local addr=$(echo "$line" | awk '{print $4}')
if [[ -n "$addr" && -n "$iface" ]]; then
case "$iface" in
tailscale*|ts*)
vpn_info="${vpn_info} Detected: ${iface} -> ${addr} (Tailscale)\n"
detected_vpn_subnets="${detected_vpn_subnets} 100.64.0.0/10"
;;
wg*)
vpn_info="${vpn_info} Detected: ${iface} -> ${addr} (WireGuard)\n"
detected_vpn_subnets="${detected_vpn_subnets} ${addr}"
;;
tun*|tap*)
vpn_info="${vpn_info} Detected: ${iface} -> ${addr} (OpenVPN/VPN tunnel)\n"
detected_vpn_subnets="${detected_vpn_subnets} ${addr}"
;;
nordlynx*|proton*)
vpn_info="${vpn_info} Detected: ${iface} -> ${addr} (VPN)\n"
detected_vpn_subnets="${detected_vpn_subnets} ${addr}"
;;
esac
fi
done < <(ip -o -f inet addr show 2>/dev/null | grep -vE 'lo |docker|br-|veth')
detected_vpn_subnets=$(echo "$detected_vpn_subnets" | xargs -n1 2>/dev/null | sort -u | xargs 2>/dev/null)
if [[ -n "$vpn_info" ]]; then
echo -e "${GREEN}VPN interfaces detected on this server:${NC}"
echo -e "$vpn_info"
echo " Suggested VPN subnets: ${detected_vpn_subnets}"
echo ""
echo " NOTE: If mobile devices connect via VPN (e.g., Tailscale on phones),"
echo " you MUST add the VPN subnet here for them to reach Asterisk."
echo ""
fi
read -p "Does your network use VLANs or VPNs? (y/n) [${HAS_VLANS}]: " has_vlans
has_vlans=${has_vlans:-$HAS_VLANS}
if [[ "$has_vlans" =~ ^[Yy] ]]; then
HAS_VLANS="y"
echo ""
echo "Current VLAN Subnets: ${VLAN_SUBNETS:-none}"
echo "Current Subnets: ${VLAN_SUBNETS:-none}"
if [[ -n "$detected_vpn_subnets" ]]; then
echo "Detected VPN Subnets: ${detected_vpn_subnets}"
fi
echo ""
echo "Enter VLAN subnets in CIDR notation, separated by spaces."
echo "Example: 192.168.1.0/24 192.168.10.0/24 192.168.20.0/24"
echo "Enter ALL additional subnets (VLAN + VPN) in CIDR notation, separated by spaces."
echo "Example: 192.168.10.0/24 100.64.0.0/10"
echo ""
read -p "VLAN Subnets: " vlan_input
local default_subnets="${VLAN_SUBNETS:-$detected_vpn_subnets}"
read -p "Subnets [${default_subnets}]: " vlan_input
vlan_input="${vlan_input:-$default_subnets}"
if [[ -n "$vlan_input" ]]; then
VLAN_SUBNETS="$vlan_input"
@@ -2841,10 +2900,17 @@ local_net=${vlan_subnet}"
local nat_settings=""
if [[ -n "$public_ip" && -n "$DOMAIN_NAME" ]]; then
# FQDN mode: full NAT settings with external addresses
nat_settings="external_media_address=$public_ip
external_signaling_address=$public_ip
${all_local_nets}"
print_info "NAT: Public IP=$public_ip, Server IP=$server_ip"
elif [[ "$HAS_VLANS" == "y" && -n "$VLAN_SUBNETS" ]]; then
# LAN/VPN mode with VLAN/VPN subnets: include local_net entries
# so Asterisk recognizes VPN traffic as local (prevents VPN devices
# appearing offline and fixes media routing for VPN-connected mobiles)
nat_settings="${all_local_nets}"
print_info "LAN mode with additional subnets: $VLAN_SUBNETS"
fi
cat > "$conf_file" << EOF
@@ -4312,6 +4378,11 @@ def add_device(name, category, extension, conn_type='lan', auto_answer=None):
elif auto_answer == 'no':
aa_tag = '[AA:no] '
# Mobile devices get keepalive settings for NAT traversal
keepalive = ''
if category == 'mobile':
keepalive = 'rtp_keepalive=15\nrtp_timeout=120\nrtp_timeout_hold=120'
device_config = f'''
; === Device: {name} ({category}) {aa_tag}===
[{extension}]
@@ -4328,6 +4399,7 @@ direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes
{keepalive}
{ice}
auth={extension}
aors={extension}
@@ -4343,7 +4415,7 @@ password={password}
type=aor
max_contacts=5
remove_existing=yes
qualify_frequency=60
qualify_frequency=30
'''
with open(PJSIP_CONF, 'a') as f:
@@ -5878,7 +5950,7 @@ submenu_server() {
echo " 6) Verify CIDR/NAT config"
echo " 7) Watch Live Logs"
echo " 8) Router Doctor"
echo " 9) Configure VLAN Subnets"
echo " 9) Configure VLAN/VPN Subnets"
echo " 10) Provisioning Manager"
echo " 11) Web Admin (Client Management)"
echo " 0) Back"