This document describes how we built a system to remotely operate an Icom IC-7610 HF transceiver over the internet using wfview, bypassing carrier-grade NAT (CGNAT) with a WireGuard tunnel through an Oracle Cloud free-tier VM.
This is a real, working system — not a theoretical design. Everything here has been tested and verified.
The IC-7610 sits on a home LAN behind a T-Mobile 5G gateway, which uses CGNAT. This means:
We need remote operators to connect to the radio using wfview, which requires UDP access to three ports on the radio. The remote operators should not need to install any VPN software.
The key insight is that the EdgeRouter initiates the tunnel outbound to the Oracle VM. Outbound UDP connections work fine through CGNAT — it's only inbound that's blocked. WireGuard's persistent-keepalive (set to 25 seconds) keeps the NAT mapping alive so the Oracle VM can send packets back through the tunnel at any time.
| Parameter | Oracle VM | EdgeRouter |
|---|---|---|
| WireGuard IP | 10.10.0.1/24 | 10.10.0.2/24 |
| Listen Port | UDP 51820 | UDP 51820 |
| MTU | 1380 | 1380 |
| Role | Listens for connections | Initiates connection outbound |
| AllowedIPs | 10.10.0.2/32, 192.168.1.0/24 | 10.10.0.1/32 |
| Keepalive | 25s (set on both) | 25s |
129.146.84.173:50001192.168.1.40:5000110.10.0.1192.168.1.40 via switch0 (LAN)10.10.0.1 (the masqueraded source)| Protocol | Port | Source | Purpose |
|---|---|---|---|
| TCP | 22 | 0.0.0.0/0 | SSH |
| TCP | 80 | 0.0.0.0/0 | HTTP (redirects to HTTPS) |
| TCP | 443 | 0.0.0.0/0 | HTTPS (user guide) |
| UDP | 51820 | 0.0.0.0/0 | WireGuard tunnel |
| UDP | 50001-50003 | 0.0.0.0/0 | wfview (control, CAT, audio) |
| ICMP | 3, 4 | 0.0.0.0/0 | Fragmentation needed |
[Interface] Address = 10.10.0.1/24 ListenPort = 51820 MTU = 1380 PrivateKey = (hidden) # NAT for traffic leaving to the internet PostUp = iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE # NAT for traffic entering the tunnel (critical for return path) PostUp = iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE # Allow forwarding through the tunnel PostUp = iptables -A FORWARD -i wg0 -j ACCEPT PostUp = iptables -A FORWARD -o wg0 -j ACCEPT # DNAT: forward wfview ports to the radio PostUp = iptables -t nat -A PREROUTING -i ens3 -p udp --dport 50001 -j DNAT --to-destination 192.168.1.40:50001 PostUp = iptables -t nat -A PREROUTING -i ens3 -p udp --dport 50002 -j DNAT --to-destination 192.168.1.40:50002 PostUp = iptables -t nat -A PREROUTING -i ens3 -p udp --dport 50003 -j DNAT --to-destination 192.168.1.40:50003 # (matching PostDown rules to clean up) [Peer] PublicKey = (EdgeRouter's public key) AllowedIPs = 10.10.0.2/32, 192.168.1.0/24 PersistentKeepalive = 25
| Chain | Rule | Purpose |
|---|---|---|
| PREROUTING | DNAT udp 50001-50003 → 192.168.1.40 | Redirect wfview traffic to the radio's LAN IP |
| POSTROUTING | MASQUERADE on ens3 | NAT for outbound internet traffic |
| POSTROUTING | MASQUERADE on wg0 | Rewrite source IP so radio replies through tunnel |
| FORWARD | ACCEPT in/out wg0 | Allow traffic to flow through the tunnel |
| Service | Command | Purpose |
|---|---|---|
| WireGuard | systemctl status wg-quick@wg0 | VPN tunnel (auto-starts on boot) |
| nginx | systemctl status nginx | Web server for user guide |
| certbot | certbot renew | HTTPS certificate auto-renewal |
interfaces {
wireguard wg0 {
address 10.10.0.2/24
listen-port 51820
mtu 1380
private-key (hidden)
peer (Oracle VM's public key) {
allowed-ips 10.10.0.1/32
endpoint 129.146.84.173:51820
persistent-keepalive 25
}
}
}
protocols {
static {
route 10.10.0.0/24 {
next-hop-interface wg0
}
}
}
route-allowed-ips true on EdgeOS. It conflicts with the default route. Use an explicit static route instead.
EdgeRouter's WireGuard interface and static route can silently disappear after reboot if the WAN interface isn't ready during initialization. A task-scheduler script runs every 5 minutes to verify and re-apply if needed:
system {
task-scheduler {
task check-wg {
executable {
path /config/scripts/check-wg.sh
}
interval 5m
}
}
}
The script checks if wg0 exists and the static route is present, re-creating them if missing.
WireGuard adds ~60 bytes of overhead per packet. With OCI's networking layer adding its own encapsulation, the default MTU of 1420 caused audio packet fragmentation and garbled audio in wfview.
Setting MTU = 1380 on both ends of the tunnel resolved this. The MTU must match — a mismatch between the two ends causes fragmentation on one side.
The IC-7610's built-in LAN server only supports LPCM codec (not Opus). For best results over the tunnel:
| Setting | Recommended Value | Why |
|---|---|---|
| Sample Rate | 16000 Hz | Reduces bandwidth from ~768kbps to ~256kbps, smaller packets |
| RX Latency | 200-250ms | Buffers against tunnel jitter |
| Codec | LPCM 1ch 16bit | Only option for IC-7610's built-in server |
This section walks through building the entire system from scratch, in the correct order. Each step must pass its verification before moving on.
Verify: ssh ubuntu@YOUR_PUBLIC_IP connects successfully.
Verify: IP lifetime shows "Reserved" in the console.
| Protocol | Port | Source | Description |
|---|---|---|---|
| UDP | 51820 | 0.0.0.0/0 | WireGuard |
| UDP | 50001-50003 | 0.0.0.0/0 | wfview |
| TCP | 80 | 0.0.0.0/0 | HTTP (optional, for user guide) |
| TCP | 443 | 0.0.0.0/0 | HTTPS (optional, for user guide) |
ip link show | grep -E "^[0-9]+:" | grep -v lo # Note the name — typically ens3 or enp0s3 # You'll use this in the WireGuard config below
# Enable now sudo sysctl -w net.ipv4.ip_forward=1 # Persist across reboots echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf sudo sysctl -p /etc/sysctl.d/99-wireguard.conf
Verify: cat /proc/sys/net/ipv4/ip_forward returns 1.
Oracle's Ubuntu image ships with REJECT rules that block forwarding. Remove them:
# Check for REJECT rules sudo iptables -L FORWARD -n --line-numbers # If you see a REJECT rule, delete it: sudo iptables -D FORWARD -j REJECT --reject-with icmp-host-prohibited # Persist the change sudo netfilter-persistent save
Verify: sudo iptables -L FORWARD -n shows no REJECT rules.
sudo apt update && sudo apt install -y wireguard # Generate server keys wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key sudo chmod 600 /etc/wireguard/server_private.key # Display the public key — you'll need this for the EdgeRouter config sudo cat /etc/wireguard/server_public.key
Replace INTERFACE_NAME with your actual interface from Step 2.1, SERVER_PRIVATE_KEY with the contents of server_private.key, and EDGEROUTER_PUBLIC_KEY with the EdgeRouter's public key (generated in Phase 3).
sudo tee /etc/wireguard/wg0.conf > /dev/null << 'EOF' [Interface] Address = 10.10.0.1/24 ListenPort = 51820 MTU = 1380 PrivateKey = SERVER_PRIVATE_KEY PostUp = iptables -t nat -A POSTROUTING -o INTERFACE_NAME -j MASQUERADE PostUp = iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE PostUp = iptables -A FORWARD -i wg0 -j ACCEPT PostUp = iptables -A FORWARD -o wg0 -j ACCEPT PostUp = iptables -t nat -A PREROUTING -i INTERFACE_NAME -p udp --dport 50001 -j DNAT --to-destination RADIO_IP:50001 PostUp = iptables -t nat -A PREROUTING -i INTERFACE_NAME -p udp --dport 50002 -j DNAT --to-destination RADIO_IP:50002 PostUp = iptables -t nat -A PREROUTING -i INTERFACE_NAME -p udp --dport 50003 -j DNAT --to-destination RADIO_IP:50003 PostDown = iptables -t nat -D POSTROUTING -o INTERFACE_NAME -j MASQUERADE PostDown = iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -j ACCEPT PostDown = iptables -D FORWARD -o wg0 -j ACCEPT PostDown = iptables -t nat -D PREROUTING -i INTERFACE_NAME -p udp --dport 50001 -j DNAT --to-destination RADIO_IP:50001 PostDown = iptables -t nat -D PREROUTING -i INTERFACE_NAME -p udp --dport 50002 -j DNAT --to-destination RADIO_IP:50002 PostDown = iptables -t nat -D PREROUTING -i INTERFACE_NAME -p udp --dport 50003 -j DNAT --to-destination RADIO_IP:50003 [Peer] PublicKey = EDGEROUTER_PUBLIC_KEY AllowedIPs = 10.10.0.2/32, 192.168.1.0/24 PersistentKeepalive = 25 EOF sudo chmod 600 /etc/wireguard/wg0.conf
sudo systemctl enable wg-quick@wg0 sudo systemctl start wg-quick@wg0
Verify: sudo wg show shows the wg0 interface listening on port 51820.
wg genkey | tee /tmp/edge_private.key | wg pubkey # The output is the public key — give this to the Oracle VM config # Save the private key from /tmp/edge_private.key — you'll need it below
EdgeOS uses a configure / set / commit / save workflow (Vyatta-based). All changes happen in a session and only take effect on commit.
configure set interfaces wireguard wg0 address 10.10.0.2/24 set interfaces wireguard wg0 listen-port 51820 set interfaces wireguard wg0 mtu 1380 set interfaces wireguard wg0 private-key EDGEROUTER_PRIVATE_KEY set interfaces wireguard wg0 peer ORACLE_VM_PUBLIC_KEY allowed-ips 10.10.0.1/32 set interfaces wireguard wg0 peer ORACLE_VM_PUBLIC_KEY endpoint ORACLE_VM_PUBLIC_IP:51820 set interfaces wireguard wg0 peer ORACLE_VM_PUBLIC_KEY persistent-keepalive 25 set protocols static route 10.10.0.0/24 next-hop-interface wg0 commit save exit
exit discard to abandon the session before starting a new configure. Uncommitted changes accumulate across sessions and cause confusion.
Verify: sudo wg show shows a handshake timestamp within the last 60 seconds.
sudo tee /config/scripts/check-wg.sh > /dev/null << 'SCRIPT'
#!/bin/bash
if ! ip link show wg0 > /dev/null 2>&1; then
logger "check-wg: wg0 interface missing, triggering config reload"
/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper begin
/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper end
sleep 5
fi
if ! ip route show 10.10.0.0/24 | grep -q wg0; then
logger "check-wg: static route to wg0 missing, re-adding"
ip route add 10.10.0.0/24 dev wg0
fi
SCRIPT
sudo chmod +x /config/scripts/check-wg.sh
Register it with the task scheduler:
configure set system task-scheduler task check-wg interval 5m set system task-scheduler task check-wg executable path /config/scripts/check-wg.sh commit save exit
Run these tests in order. All must pass before proceeding.
# On both Oracle VM and EdgeRouter: sudo wg show # Look for "latest handshake" within the last 60 seconds
# From Oracle VM: ping -c 3 10.10.0.2 # EdgeRouter WireGuard address ping -c 3 192.168.1.1 # EdgeRouter LAN address ping -c 3 192.168.1.40 # Radio (replace with your radio's IP)
All three must succeed. If the first works but the others don't, check the EdgeRouter's routing table and ensure the static route to 10.10.0.0/24 exists.
# On Oracle VM: sudo iptables -t nat -L PREROUTING -n -v # Should show DNAT rules for ports 50001, 50002, 50003
From any computer with wfview installed, connect using the Oracle VM's public IP as the hostname with ports 50001/50002/50003. If the radio is powered on and the tunnel is working, wfview should connect within 5-10 seconds.
# Install nginx sudo apt install -y nginx sudo systemctl enable nginx # Open ports in iptables sudo iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT sudo iptables -I INPUT 2 -p tcp --dport 443 -j ACCEPT sudo netfilter-persistent save # Install Let's Encrypt (replace YOUR_DOMAIN and YOUR_EMAIL) sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d YOUR_DOMAIN --non-interactive --agree-tos --email YOUR_EMAIL --redirect
Verify: https://YOUR_DOMAIN loads with a valid certificate.
This entire system runs for free:
| Component | Role |
|---|---|
| WireGuard | Encrypted tunnel through CGNAT |
| Oracle Cloud Free Tier | Public relay VM |
| wfview | Remote radio control software |
| Ubiquiti EdgeRouter X | Home router with WireGuard support |
| nginx + Let's Encrypt | Web server for setup guide |
Built by WW0R. System designed and implemented with assistance from Claude (Anthropic). Last updated: March 2026.