tl;dr: use a transparent proxy when VoWiFi is blocked.

Background

Wi-Fi calling (VoWiFi) allows making calls and SMS over IP networks. This is particularly useful when one is roaming, as VoWiFi does not incur roaming costs. However VoWiFi might be blocked by one’s ISP. Symptoms:

  1. Wi-Fi Calling is enabled in settings
  2. The phone shows no service or the roaming carrier but no Wi-Fi calling

Setup

Setup the following topology:

Router 192.168.1.1/24
| Gateway 192.168.1.2 (default route via 192.168.1.1)
| iPhone 192.168.1.3

Gateway

Assuming the gateway connects to the LAN eth0, run the following as root:

# enable IPv4 forwarding
sysctl -w net.ipv4.ip_forward=1

# configure NAT
cat <<EOF > /etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet my_nat {
  chain my_masquerade {
    type nat hook postrouting priority srcnat;
    oifname "eth0" masquerade
  }
}
EOF

# apply firewall changes
nft -f /etc/nftables.conf

iOS client setup

In Settings -> Wi-Fi -> [Your SSID] -> IPV4 ADDRESS -> Configure IP, select Manual and set the following

Key Value
IP Address 192.168.1.3
Subnet Mask 255.255.255.0
Router 192.168.1.2

Important make sure IPv6 is disabled, otherwise some traffic might leak through IPv6. Currently the only way I’m aware of is disabling at the router side.

Checking VoWiFi traffic

Now we can inspect the traffic from iPhone to the router. Launch wireshark and use the following filter, reference

(udp.port == 500 || udp.port == 4500 || udp.port == 5996) && (ip.src == 192.168.1.3)

You should see some traffic that originates from your iPhone and is sent to your carrier. If not, try re-enabling WiFi calling on your iPhone. The ASN of the destination IP should match your carrier.

If everything is okay, you should see the traffic between your iPhone and your carrier. However your ISP might block your carrier server.

Bypassing blockage

It’s easiest to use a VPN to bypass. However, the OS is likely to tunnel these ports separately according to this post

Wi-Fi calling mostly uses IKE UDP 500 and once a call is established it switches to UDP 4500. Both of these ports are treated as critical on mobile devices using modern iOS and Android operating systems. The phone is therefore sending that traffic outside the VPN tunnel, regardless of the tunnel settings.

We can use the gateway as a transparent proxy:

Gateway setup

Assuming you have setup eth1 to some ISP that allows VoWiFi traffic, then

# configure NAT
cat <<EOF > /etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet my_nat {
  chain my_masquerade {
    type nat hook postrouting priority srcnat;
    # oifname "eth0" masquerade
    oifname "eth1" masquerade
  }
}
EOF

# apply firewall changes
nft -f /etc/nftables.conf

# confirm default gateway is through eth1, this should output something like
#  default via ... dev eth1
ip route

Now you should see the traffic between your iPhone and your carrier.

Bonus points: Avoid roaming charges

To avoid roaming being accidentally activated, use either of the following:

  • Have flight mode enabled. You can still use WiFi calling.
  • Disable automatic network selection and select a (local) carrier that does not partner with your carrier (no signal).