Option 1: Use Mac's built-in Wireless Diagnostics tool
Apple provides a built-in tool called Wireless Diagnostics which automatically solves many networking issues. You can run it by searching for the tool name in Spotlight or going to /System/Library/CoreServices/Applications/ where the app is located. Alternatively, you can start it from the terminal, where it will also print a lot of useful debugging information, even if it fails to fix the issue:
sudo wdutil diagnose -q # -q flag is used to skip the initial Yes/No prompt
Option 2: Use a Python script to save and later restore working network settings
If the Wireless Diagnostics / wdutil solution fails to work, use these two Python scripts that I wrote. This may seem like a bit of overkill but based on my extensive testing, nothing else (short of a reboot) can fix tethering. Unfortunately, ~5% of the time even the script solution fails and I'm forced to reboot OSX to fix connectivity, but it does save time the other 95% of the time.
Script 1: Export working network settings while tethering works
Reboot the machine, connect to Personal Hotspot, and then persist the valid network configuration using the following Python script. The script will try to save the configuration for both Wi-Fi and USB tethering by default. It will generate a .json
file containing the interface name and the network name, so you could potentially use this to store the network information for non-hotspot networks as well.
AFAIK all modern iPhones use 192.0.0.1 and 172.0.0.1 as their router address, however each phone uses a different IPv6 router address, so running this script is unavoidable.
import subprocess
import re
import json
DEVICE_NAME_WIFI = "Wi-Fi" # Tethering via Wi-Fi
DEVICE_NAME_USB = "iPhone USB" # Tethering via USB cable
def remove_non_alphanumeric(string):
if string is None:
return ""
return "".join(re.findall("[A-Za-z0-9]", string))
def get_config_filename(device_name, interface):
filename = f"network_settings_{remove_non_alphanumeric(device_name)}"
if device_name == DEVICE_NAME_WIFI:
try:
network_info = subprocess.check_output(
["networksetup", "-getairportnetwork", interface], text=True
)
network_name = remove_non_alphanumeric(network_info.split(": ")[1].strip())
network_name = network_name[:15] # Avoid saving overly long network names
suffix = f"_{network_name}"
except Exception as e:
suffix = "_unknown"
return f"{filename}{suffix}.json"
def get_network_info(device_name):
print(f"Saving {device_name} settings...")
try:
hardware_ports = subprocess.check_output(
["networksetup", "-listallhardwareports"], text=True
)
device_match = re.search(
f"Hardware Port: {device_name}\nDevice: (\\w+)", hardware_ports
)
if not device_match:
print(f"Failed: No {device_name} interface found")
if device_name == DEVICE_NAME_USB:
print(
"Ensure your iPhone is plugged in and Personal Hotspot is enabled to save USB tethering settings."
)
return
interface = device_match.group(1)
ifconfig_output = (
subprocess.check_output(f"ifconfig {interface}", shell=True)
.decode()
.splitlines()
)
ipv4_address = None
ipv6_addresses = []
for line in ifconfig_output:
if "inet " in line:
ipv4_address = line.split()[1]
elif "inet6 " in line:
ipv6_addresses.append(line.split()[1].split("%")[0])
netstat_output = (
subprocess.check_output(
f"netstat -nr | grep default | grep {interface}", shell=True
)
.decode()
.splitlines()
)
ipv4_router = ipv6_router = None
for line in netstat_output:
parts = line.split()
if len(parts) > 1:
if ":" not in parts[1]:
ipv4_router = parts[1]
else:
ipv6_router = parts[1].split("%")[0]
config = {
"network_service_name": device_name,
"network_interface_id": interface,
"ipv4_address": ipv4_address or "Unknown",
"ipv4_subnet_mask": "255.255.255.0",
"ipv4_router": ipv4_router or "Unknown",
"ipv6_primary_address": ipv6_addresses[0] if ipv6_addresses else "Unknown",
"ipv6_temporary_address": (
ipv6_addresses[1] if ipv6_addresses[1:] else "Unknown"
),
"ipv6_clat46_address": (
ipv6_addresses[2] if ipv6_addresses[2:] else "Unknown"
),
"ipv6_prefix_length": "64",
"ipv6_router": ipv6_router or "Unknown",
}
config_filename = get_config_filename(device_name, interface)
with open(config_filename, "w") as f:
json.dump(config, f, indent=2)
print(
f"Success: Saved config for {device_name} ({interface}) to {config_filename}"
)
except Exception as e:
print(f"Failed: Error saving config for {device_name}: {str(e)}")
if __name__ == "__main__":
get_network_info(DEVICE_NAME_WIFI)
print("\n")
get_network_info(DEVICE_NAME_USB)
The Python script will generate a JSON file with working IPv4 and IPv6 settings within the same folder.
Script 2: Enable manual TCP/IP configuration → restore settings from Script 1 → re-enable DHCP again
Create another Python script. Note that running it requires sudo access, because changing most network settings on Mac cannot be done without sudo
privileges.
import subprocess
import json
import time
import sys
def run_cmd(cmd, ignore_errors=False):
try:
subprocess.run(cmd, check=True)
print(f"Success: {' '.join(cmd)}")
return True
except subprocess.CalledProcessError as e:
if not ignore_errors:
print(f"Failed: {' '.join(cmd)}: {e}")
return False
def enable_network_autoconfiguration(config):
print("Resetting to automatic configuration...")
# Remove all configured IP addresses
output = subprocess.check_output(
f"ifconfig {config['network_interface_id']}",
shell=True
).decode().splitlines()
for line in output:
if "inet " in line:
run_cmd([
"sudo", "ifconfig", config["network_interface_id"],
"inet", line.split()[1], "remove"
])
elif "inet6 " in line:
run_cmd([
"sudo", "ifconfig", config["network_interface_id"],
"inet6", "del", line.split()[1]
])
# Reset to automatic configuration
run_cmd(["sudo", "networksetup", "-setdhcp", config["network_service_name"]])
run_cmd(["sudo", "networksetup", "-setv6automatic", config["network_service_name"]])
def load_network_settings(config):
print("\nSetting manual IP configuration...")
# Configure IPv4 settings
run_cmd([
"sudo", "networksetup", "-setmanual",
config["network_service_name"],
config["ipv4_address"],
config["ipv4_subnet_mask"],
config["ipv4_router"]
])
# Update IPv4 routing
run_cmd(["sudo", "route", "delete", "default"])
run_cmd(["sudo", "route", "add", "default", config["ipv4_router"]])
# Configure IPv6 settings
run_cmd([
"sudo", "networksetup", "-setv6manual",
config["network_service_name"],
config["ipv6_primary_address"],
config["ipv6_prefix_length"],
config["ipv6_router"]
])
# Add additional IPv6 addresses
run_cmd([
"sudo", "ifconfig", config["network_interface_id"],
"inet6", "add", config["ipv6_temporary_address"],
"prefixlen", config["ipv6_prefix_length"]
])
run_cmd([
"sudo", "ifconfig", config["network_interface_id"],
"inet6", "add", config["ipv6_clat46_address"],
"prefixlen", config["ipv6_prefix_length"]
])
# Update IPv6 routing
run_cmd(["sudo", "route", "delete", "-inet6", "default"], ignore_errors=True)
run_cmd([
"sudo", "route", "add", "-inet6", "default",
f"{config['ipv6_router']}%{config['network_interface_id']}"
])
if __name__ == "__main__":
with open(sys.argv[1]) as f:
config = json.load(f)
load_network_settings(config)
print("\nWaiting for 1 second before resetting back to auto/DHCP network configuration...\n")
time.sleep(1)
enable_network_autoconfiguration(config)
When tethering stops working, execute the script, passing the generated config file name into it:
python3 load_working_network_settings.py network_settings_en0.json
Voila! Your internet access should (hopefully) be restored now.
Option 3: Reboot your Mac
If the first two options fail, rebooting the Mac is the only option. If that doesn't work, try running the Wireless Diagnostics tool again after the reboot.