IC-7610 Remote Access — System Architecture

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 Problem

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 Solution

Remote wfview user │ │ UDP 50001-50003 ▼ ┌──────────────────────┐ │ Oracle Cloud VM │ Public IP: 129.146.84.173 │ (Free Tier) │ Ubuntu 22.04 │ │ │ ens3 ──── wg0 │ WireGuard: 10.10.0.1/24 │ DNAT + NAT │ └──────────┬───────────┘ │ │ WireGuard tunnel (UDP 51820) │ Encrypted, initiated outbound from home │ ┌──────────┴───────────┐ │ EdgeRouter X │ LAN: 192.168.1.1 │ (Home Router) │ WireGuard: 10.10.0.2/24 │ │ │ eth4 ──── wg0 │ WAN: T-Mobile 5G (CGNAT) │ switch0 │ └──────────┬───────────┘ │ │ LAN (192.168.1.0/24) │ ┌──────────┴───────────┐ │ Icom IC-7610 │ 192.168.1.40 │ HF Transceiver │ UDP 50001 (control) │ │ UDP 50002 (CAT/CI-V) │ │ UDP 50003 (audio) └──────────────────────┘

How It Works

The WireGuard Tunnel

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.

ParameterOracle VMEdgeRouter
WireGuard IP10.10.0.1/2410.10.0.2/24
Listen PortUDP 51820UDP 51820
MTU13801380
RoleListens for connectionsInitiates connection outbound
AllowedIPs10.10.0.2/32, 192.168.1.0/2410.10.0.1/32
Keepalive25s (set on both)25s

Packet Flow (Remote User → Radio)

  1. Remote wfview client sends UDP packet to 129.146.84.173:50001
  2. OCI security list allows the packet through
  3. Oracle VM iptables PREROUTING DNAT rewrites the destination to 192.168.1.40:50001
  4. Oracle VM iptables POSTROUTING MASQUERADE on wg0 rewrites the source to 10.10.0.1
  5. Packet enters the WireGuard tunnel, arrives at the EdgeRouter (10.10.0.2)
  6. EdgeRouter routes to 192.168.1.40 via switch0 (LAN)
  7. IC-7610 receives the packet and processes the wfview command

Packet Flow (Radio → Remote User)

  1. IC-7610 sends UDP response to 10.10.0.1 (the masqueraded source)
  2. EdgeRouter routes to 10.10.0.1 via wg0 (WireGuard tunnel)
  3. Packet arrives at Oracle VM via the tunnel
  4. Oracle VM's conntrack reverses the MASQUERADE and DNAT, restoring the original remote user's IP and port
  5. Packet exits via ens3 to the internet, back to the remote user
Why MASQUERADE on wg0? Without it, the DNAT rewrites the destination but leaves the source as the remote user's public IP. The radio sends its reply to that public IP via its default gateway (the EdgeRouter's WAN), which goes out through T-Mobile — NOT back through the tunnel. The reply never reaches the Oracle VM and the connection fails. MASQUERADE on wg0 makes the radio see the source as 10.10.0.1, so it replies through the tunnel.

Oracle Cloud VM Configuration

OCI Infrastructure

OCI Security List (Firewall)

ProtocolPortSourcePurpose
TCP220.0.0.0/0SSH
TCP800.0.0.0/0HTTP (redirects to HTTPS)
TCP4430.0.0.0/0HTTPS (user guide)
UDP518200.0.0.0/0WireGuard tunnel
UDP50001-500030.0.0.0/0wfview (control, CAT, audio)
ICMP3, 40.0.0.0/0Fragmentation needed
Three firewall layers: OCI has three independent layers that ALL must allow traffic: (1) OCI security list, (2) VNIC source/destination check, (3) OS-level iptables. Missing any one causes silent packet drops with no error messages.

WireGuard Config (wg0.conf)

[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

Key iptables Rules Explained

ChainRulePurpose
PREROUTINGDNAT udp 50001-50003 → 192.168.1.40Redirect wfview traffic to the radio's LAN IP
POSTROUTINGMASQUERADE on ens3NAT for outbound internet traffic
POSTROUTINGMASQUERADE on wg0Rewrite source IP so radio replies through tunnel
FORWARDACCEPT in/out wg0Allow traffic to flow through the tunnel

System Services

ServiceCommandPurpose
WireGuardsystemctl status wg-quick@wg0VPN tunnel (auto-starts on boot)
nginxsystemctl status nginxWeb server for user guide
certbotcertbot renewHTTPS certificate auto-renewal

EdgeRouter Configuration

WireGuard (EdgeOS CLI)

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
        }
    }
}
Do NOT use route-allowed-ips true on EdgeOS. It conflicts with the default route. Use an explicit static route instead.

Boot Resilience

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.

MTU Considerations

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.

Symptom of MTU issues: Ping works fine (small packets), but wfview audio is garbled or choppy (large packets get fragmented). If you see this, lower the MTU.

wfview Audio Settings

The IC-7610's built-in LAN server only supports LPCM codec (not Opus). For best results over the tunnel:

SettingRecommended ValueWhy
Sample Rate16000 HzReduces bandwidth from ~768kbps to ~256kbps, smaller packets
RX Latency200-250msBuffers against tunnel jitter
CodecLPCM 1ch 16bitOnly option for IC-7610's built-in server

Pitfalls and Lessons Learned

  1. OCI's three firewalls — Security list, VNIC source/destination check, and iptables must all allow traffic. This is the #1 cause of silent failures on OCI.
  2. OCI default iptables — Oracle's Ubuntu image ships with REJECT rules in INPUT and FORWARD chains. WireGuard PostUp rules that append (-A) land after the REJECT and never match. Remove the REJECT rules before starting WireGuard.
  3. MASQUERADE on wg0 — Without this, DNAT changes the destination but the source remains the remote user's IP. The radio replies directly out the WAN instead of through the tunnel. This was the hardest bug to diagnose.
  4. MTU mismatch — Must be identical on both tunnel endpoints. A mismatch causes fragmentation visible as garbled audio but working pings.
  5. EdgeOS route-allowed-ips — This setting conflicts with the default route on EdgeRouter. Use an explicit static route instead.
  6. EdgeOS reboot fragility — WireGuard config can silently disappear after reboot. A scheduled check script is essential.
  7. Ephemeral IP — Oracle Free Tier VMs get an ephemeral public IP by default. Reserve it (free, limit 1) or it changes on reboot.
  8. wfview port numbers — DNAT must preserve port numbers (50001→50001). wfview embeds port numbers in its protocol.
  9. CGNAT keepalive — T-Mobile's CGNAT has an aggressive UDP timeout. 25-second persistent-keepalive works, but if the tunnel drops intermittently, try 15 seconds.

Step-by-Step Implementation Guide

This section walks through building the entire system from scratch, in the correct order. Each step must pass its verification before moving on.

Phase 1 — Oracle Cloud Setup (Web Console)

Step 1.1 — Create an Always Free VM

  1. Sign up at oracle.com/cloud/free
  2. Navigate to: Compute → Instances → Create Instance
  3. Choose image: Canonical Ubuntu 22.04
  4. Choose shape: VM.Standard.E2.1.Micro (Always Free eligible)
  5. Under Networking, let it create a new VCN and public subnet automatically
  6. Upload or paste your SSH public key
  7. Click Create and wait for the instance to reach "Running" state
  8. Note the public IP address assigned to the instance

Verify: ssh ubuntu@YOUR_PUBLIC_IP connects successfully.

Step 1.2 — Reserve the Public IP

  1. Navigate to: Compute → Instances → (your instance) → Networking → (Primary VNIC) → IP administration
  2. Click the three-dot menu (⋯) on the IP row
  3. Select Edit and change from "Ephemeral" to "Reserved"

Verify: IP lifetime shows "Reserved" in the console.

Step 1.3 — Disable VNIC Source/Destination Check

  1. Navigate to: Compute → Instances → (your instance) → Networking → Attached VNICs
  2. Click the three-dot menu on the VNIC row → Edit VNIC
  3. Check "Skip source/destination check"
  4. Save
This step is critical. Without it, all DNAT-forwarded packets are silently dropped by OCI with no error messages. This is the most commonly missed step.

Step 1.4 — Open Security List Ports

  1. Navigate to: Networking → Virtual Cloud Networks → (your VCN) → Security Lists → Default Security List
  2. Click Add Ingress Rules and add:
ProtocolPortSourceDescription
UDP518200.0.0.0/0WireGuard
UDP50001-500030.0.0.0/0wfview
TCP800.0.0.0/0HTTP (optional, for user guide)
TCP4430.0.0.0/0HTTPS (optional, for user guide)
Do not remove the existing TCP 22 (SSH) rule or you will lock yourself out.

Phase 2 — Oracle VM Configuration (SSH)

Step 2.1 — Identify the Network Interface

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

Step 2.2 — Enable IP Forwarding

# 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.

Step 2.3 — Fix Default iptables Rules

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.

Step 2.4 — Install WireGuard and Generate Keys

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

Step 2.5 — Write the WireGuard Config

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

Step 2.6 — Start WireGuard

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

Verify: sudo wg show shows the wg0 interface listening on port 51820.

Phase 3 — EdgeRouter Configuration (SSH)

Step 3.1 — Generate WireGuard Keys

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

Step 3.2 — Configure WireGuard via EdgeOS CLI

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
If commit fails, run 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.

Step 3.3 — Create Boot Resilience Script

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

Phase 4 — Verification

Run these tests in order. All must pass before proceeding.

Test 1: WireGuard Handshake

# On both Oracle VM and EdgeRouter:
sudo wg show
# Look for "latest handshake" within the last 60 seconds

Test 2: Tunnel Connectivity

# 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.

Test 3: DNAT Rules

# On Oracle VM:
sudo iptables -t nat -L PREROUTING -n -v
# Should show DNAT rules for ports 50001, 50002, 50003

Test 4: wfview Connection

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.

If wfview doesn't connect: Check that the radio is powered on, verify the DNAT rules have packet counts incrementing, and ensure the MASQUERADE rule on wg0 is present (this is the most commonly missed rule — without it, return traffic doesn't flow through the tunnel).

Phase 5 — Optional: Web Server for User Guide

# 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.

Cost

This entire system runs for free:

Tools Used

ComponentRole
WireGuardEncrypted tunnel through CGNAT
Oracle Cloud Free TierPublic relay VM
wfviewRemote radio control software
Ubiquiti EdgeRouter XHome router with WireGuard support
nginx + Let's EncryptWeb server for setup guide

Built by WW0R. System designed and implemented with assistance from Claude (Anthropic). Last updated: March 2026.