Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

macOS Network Tools

Inspecting and configuring networking on macOS with networksetup, scutil, ifconfig, pfctl, and the unified diagnostics tools.

macOS Network Tools

Inspecting and configuring networking on macOS with networksetup, scutil, ifconfig, pfctl, and the unified diagnostics tools.

Overview

macOS networking is two layers stacked on top of one another:

  • A BSD core inherited from FreeBSD — ifconfig, route, netstat, arp, ndp, and the pf packet filter. These behave broadly as they do on FreeBSD.
  • Apple's System Configuration framework — a daemon (configd) backed by a key/value "dynamic store", plus the user-facing CLIs networksetup and scutil. This is what the GUI in System Settings drives, and it is the source of truth for persistent configuration.

The headline trap for anyone arriving from Linux or generic BSD: the BSD tools are for inspection, not durable configuration. ifconfig/route changes apply to the running kernel but are not persistent and configd will revert them — sometimes within seconds, certainly across a link flap, DHCP renewal, or reboot. For changes that stick, use networksetup, scutil, or the GUI.

Apple System Configuration layer (source of truth)applies + enforcesinspect onlyBSD core (runtime state, not persistent)ifconfigroutepf / pfctlSystem Settings GUInetworksetupscutilconfigddynamic storeYouApple System Configuration layer (source of truth)applies + enforcesinspect onlyBSD core (runtime state, not persistent)ifconfigroutepf / pfctlSystem Settings GUInetworksetupscutilconfigddynamic storeYou

Interface naming

macOS does not use predictable eth0/ens33-style names. Names are assigned by the kernel in probe order and vary by hardware:

Interface Typical role
en0 First Ethernet or Wi-Fi — not guaranteed to be Ethernet
en1, en2, … Further Ethernet/Wi-Fi/Thunderbolt/USB adapters
lo0 Loopback
bridge0 Bridge (Internet Sharing, virtualisation)
utun0, utun1, … Tunnels — VPNs, Tailscale, WireGuard, Back to My Mac
awdl0 Apple Wireless Direct Link (AirDrop, AirPlay, Continuity)
llw0 Low-latency WLAN (Apple peer-to-peer)
gif0, stf0 Generic/6-to-4 tunnel pseudo-interfaces
anpi0, ap1 Apple-internal management/AP interfaces on some Macs

On a laptop en0 is usually Wi-Fi; on a Mac with built-in Ethernet en0 is usually Ethernet and Wi-Fi lands on en1. Never hard-code the mapping — resolve it with networksetup -listallhardwareports (see below).

Services vs interfaces

A crucial Apple-specific distinction:

  • A hardware port / interface is the BSD device (en0).
  • A Network Service is a named configuration layered on top of a port — "Wi-Fi", "Ethernet", "USB 10/100/1000 LAN", "Thunderbolt Bridge". A service carries the IP config, DNS, proxies, and service order (priority). Multiple services can map to the same port, and services live inside a Location (a named set of services you can switch between).

networksetup operates on services (by their human-readable name), whereas ifconfig/route operate on interfaces. This is why networksetup -setdnsservers "Wi-Fi" … works but there is no networksetup command that takes en0 directly for DNS — you map the port to a service name first.

The Big Gotcha: runtime vs persistent

You run ifconfig en0192.0.2.10Kernel accepts itimmediatelyconfigdreconciliation eventDHCP renew / linkflap / rebootconfigd reappliestheservice config fromthe dynamic storeYour manual changeis goneYou run networksetup-setmanual Wi-Fi ...Written to dynamicstoreconfigd applies ANDre-applies itChange persistsYou run ifconfig en0192.0.2.10Kernel accepts itimmediatelyconfigdreconciliation eventDHCP renew / linkflap / rebootconfigd reappliestheservice config fromthe dynamic storeYour manual changeis goneYou run networksetup-setmanual Wi-Fi ...Written to dynamicstoreconfigd applies ANDre-applies itChange persists

Rules of thumb:

  • Inspect with ifconfig, netstat, route get, arp, ndp — these are accurate snapshots of live kernel state.
  • Configure with networksetup, scutil, or the GUI — these write to the dynamic store that configd enforces.
  • A manual ifconfig/route change is a temporary override. It is genuinely useful for testing (e.g. add a throwaway route to verify reachability) but treat it as ephemeral.
  • Many operations need sudo (anything that mutates config) and some need to be in the admin group.
  • SIP (System Integrity Protection) protects system daemons and many /System paths; you cannot replace configd, mDNSResponder, or the firewall binaries, and some diagnostic tools (e.g. wdutil) gate output behind sudo. SIP does not stop normal config via networksetup/scutil.
  • /etc/resolv.conf is generated by the system and is incomplete — editing it does essentially nothing useful (see DNS section).

networksetup

The primary Apple CLI for persistent configuration. Lives at /usr/sbin/networksetup. Most read operations work unprivileged; writes need sudo. Run networksetup -help for the full (long) list — option names are verbose and case-sensitive, and service names with spaces must be quoted.

Discovery

# List network services in priority order (the service-order list)
networksetup -listallnetworkservices
# An asterisk (*) prefix marks a DISABLED service.

# Map services to hardware ports and BSD device names — the canonical
# way to find out which en* is Wi-Fi vs Ethernet on THIS machine
networksetup -listallhardwareports

# Show the device name for a given port
networksetup -listallhardwareports | grep -A2 "Wi-Fi"

# Full info for a service (IP, mask, router, MAC)
networksetup -getinfo "Wi-Fi"

# Hardware (MAC) address for a port
networksetup -getmacaddress "Wi-Fi"
networksetup -getmacaddress en0

IPv4 configuration

# Switch a service to DHCP
sudo networksetup -setdhcp "Wi-Fi"

# Set a static (manual) IPv4 config: service IP mask router
sudo networksetup -setmanual "Ethernet" 192.0.2.50 255.255.255.0 192.0.2.1

# DHCP but with a manual address handed back to you (BOOTP)
sudo networksetup -setbootp "Ethernet"

# Set/clear a DHCP client ID
sudo networksetup -setdhcp "Wi-Fi" MyClientID

# Read back the current IPv4 method and addressing
networksetup -getinfo "Ethernet"

DNS and search domains

DNS set here is per-service and is what configd publishes to the resolver. This is the correct, persistent way to set DNS — not editing /etc/resolv.conf.

# Show DNS servers for a service
networksetup -getdnsservers "Wi-Fi"
# Prints "There aren't any DNS Servers set on Wi-Fi." when the service
# inherits DNS from DHCP — that is normal, not an error.

# Set DNS servers (replaces the list; space-separated)
sudo networksetup -setdnsservers "Wi-Fi" 1.1.1.1 9.9.9.9

# Revert to DHCP-provided DNS — the literal word "Empty" clears the override
sudo networksetup -setdnsservers "Wi-Fi" Empty

# Search domains
networksetup -getsearchdomains "Wi-Fi"
sudo networksetup -setsearchdomains "Wi-Fi" example.com internal.example.com
sudo networksetup -setsearchdomains "Wi-Fi" Empty

Wi-Fi (AirPort) control

The Wi-Fi port name is usually en0 or en1 — confirm with -listallhardwareports. These commands take the device name, not the service name. The en0 below is illustrative: it holds on most laptops, but on desktops and a Mac mini with built-in Ethernet en0 is Ethernet and Wi-Fi is typically en1, so substitute the device your own -listallhardwareports reports.

# Power the radio on/off
sudo networksetup -setairportpower en0 off
sudo networksetup -setairportpower en0 on

# Current power state
networksetup -getairportpower en0

# Join a network (prompts add it to the keychain/preferred list)
sudo networksetup -setairportnetwork en0 "MySSID" "wifi-password"

# Currently associated SSID
networksetup -getairportnetwork en0

# Preferred networks (the remembered list, in join-priority order)
networksetup -listpreferredwirelessnetworks en0
sudo networksetup -addpreferredwirelessnetworkatindex en0 "MySSID" 0 WPA2 "password"
sudo networksetup -removepreferredwirelessnetwork en0 "MySSID"
sudo networksetup -removeallpreferredwirelessnetworks en0

Service order and IPv6

# Reorder services — first service is preferred for the default route.
# Pass the service names in the desired priority order.
sudo networksetup -ordernetworkservices "Ethernet" "Wi-Fi" "Thunderbolt Bridge"

# Enable/disable a whole service
sudo networksetup -setnetworkserviceenabled "Wi-Fi" off

# IPv6: automatic (default), link-local only, manual, or fully off
networksetup -getinfo "Wi-Fi"          # shows IPv6 method
sudo networksetup -setv6automatic "Wi-Fi"
sudo networksetup -setv6linklocal "Wi-Fi"
sudo networksetup -setv6off "Wi-Fi"    # disable IPv6 on the service
sudo networksetup -setv6manual "Wi-Fi" 2001:db8::10 64 2001:db8::1

Proxies (briefly)

# Web proxy (HTTP)
sudo networksetup -setwebproxy "Wi-Fi" proxy.example.com 8080
networksetup -getwebproxy "Wi-Fi"

# Auto-proxy (PAC) URL
sudo networksetup -setautoproxyurl "Wi-Fi" "http://proxy.example.com/proxy.pac"

# Turn a proxy off without forgetting its settings
sudo networksetup -setwebproxystate "Wi-Fi" off

scutil

scutil is the System Configuration utility — a thin CLI over the dynamic store plus a set of high-level query subcommands. Use it to read the effective resolver and reachability state and to manage the three host names.

High-level queries

# Network information / reachability: which interfaces have a usable
# default route, in priority order, for IPv4 and IPv6
scutil --nwi

# The effective resolver configuration — THE authoritative view of DNS
# on macOS. Shows scoped and multicast resolvers, not just one list.
scutil --dns

# Effective proxy configuration (resolved from service + PAC)
scutil --proxy

scutil --dns is the one to internalise. macOS DNS is scoped/split: different resolvers apply to different interfaces and different domains. The output is grouped into "resolver #1, #2, …", each with nameserver[], an optional domain/search, and an if_index/flags showing which interface it is bound to. A VPN's utun resolver and your Wi-Fi resolver coexist; a query for corp.internal may go to the VPN resolver while everything else goes to Wi-Fi. /etc/resolv.conf only ever reflects the primary resolver and omits all the scoped ones — which is why a tool that reads resolv.conf (or dig with no scoping) can disagree with what apps actually resolve.

Browsing the dynamic store

scutil with no arguments drops into an interactive session over the key/value store. Keys are path-like (State:/… for live state, Setup:/… for configured intent).

scutil
> list                                   # list all keys
> list State:/Network/.*                 # keys matching a regex
> show State:/Network/Global/IPv4        # primary service, router, interface
> show State:/Network/Global/DNS         # effective global DNS
> show State:/Network/Interface/en0/IPv4
> show Setup:/Network/Service/<id>/DNS   # configured (persistent) intent
> quit

One-liners without entering the REPL:

# Primary interface + router in one shot
echo 'show State:/Network/Global/IPv4' | scutil

# Watch a key for changes (notification on update)
scutil -n   # then 'open', 'n.add <key>', 'n.watch'

The three host names

macOS has three distinct name fields. Confusing them is a common cause of "why is my hostname wrong" tickets.

Name scutil key What it is Example
ComputerName ComputerName Friendly UI name (Sharing pane, AirDrop). May contain spaces/Unicode. Mike's MacBook Pro
HostName HostName The "real" DNS/Unix hostname. Often unset, in which case it falls back to reverse-DNS or LocalHostName. mbp.example.com
LocalHostName LocalHostName The Bonjour/mDNS name, advertised as <name>.local. ASCII, no spaces. Mikes-MacBook-Pro
# Read
scutil --get ComputerName
scutil --get HostName        # errors "HostName: not set" if unset — normal
scutil --get LocalHostName

# Set (needs sudo)
sudo scutil --set ComputerName "Mike's MacBook Pro"
sudo scutil --set HostName mbp.example.com
sudo scutil --set LocalHostName Mikes-MacBook-Pro

hostname (the BSD command) reflects HostName if set, otherwise a transient value; setting it with sudo hostname foo is not persistent — use scutil --set HostName.

DNS on macOS

The system resolver is mDNSResponder (the discoveryd experiment of 10.10 was reverted; mDNSResponder has been the resolver since and remains so through current macOS). It handles unicast DNS, multicast DNS (Bonjour), caching, and the per-interface/per-domain scoping described above.

getaddrinfoscoped lookupscoped lookup.localbypass scoping,talk to a serverdirectlyAppmDNSResponderWi-Fi resolverutun / VPN resolverMulticast DNSdig / host /nslookupgetaddrinfoscoped lookupscoped lookup.localbypass scoping,talk to a serverdirectlyAppmDNSResponderWi-Fi resolverutun / VPN resolverMulticast DNSdig / host /nslookup

Querying the way apps see it

dig, host, and nslookup talk to a DNS server directly and do not honour macOS scoping or the search/resolver split. To reproduce what an application actually gets, query through the system resolver:

# Resolve via the system resolver (respects scoping, search domains, /etc/hosts)
dscacheutil -q host -a name example.com

# Bonjour / mDNS browsing and resolution
dns-sd -B _http._tcp           # browse for HTTP services on the LAN
dns-sd -B _ssh._tcp local      # browse SSH services
dns-sd -L "My Mac" _ssh._tcp   # look up a specific instance
dns-sd -G v4v6 host.local      # resolve a .local name to addresses
dns-sd -q example.com          # unicast query through the resolver

# dig/host still useful for authoritative debugging (which server, raw records)
dig @1.1.1.1 example.com A +short

Rule: if dig succeeds but the app cannot resolve (or vice versa), trust dscacheutil/dns-sd for "what the app sees" and scutil --dns to explain why (wrong scoped resolver, missing search domain, VPN ordering).

Flushing the DNS cache

The current incantation (macOS 10.10.4+ and all modern releases) is both halves together:

sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

dscacheutil -flushcache flushes the Directory Service cache; the killall -HUP mDNSResponder is what actually flushes the resolver/mDNS cache. Run both.

BSD inspection tools

These work as on FreeBSD and are the right tools for looking. Config changes made with them do not persist (see The Big Gotcha).

# Interface state, addresses, flags, media
ifconfig                 # all interfaces
ifconfig en0             # one interface
ifconfig -a              # include down interfaces

# Routing table (numeric) and the active default route
netstat -rn
netstat -rn -f inet      # IPv4 only
netstat -rn -f inet6     # IPv6 only
route -n get default     # the resolved default route + its interface
route get 8.8.8.8        # which route/interface a given dst would use

# ARP / NDP neighbour tables
arp -a                   # IPv4 neighbour (ARP) cache
arp -d 192.0.2.1         # delete an entry (sudo)
ndp -an                  # IPv6 neighbour cache (the IPv6 'arp')
ndp -rn                  # IPv6 default routers

# Sockets / connections (no 'ss' on macOS — see below)
netstat -an              # all sockets, numeric
netstat -an -p tcp       # TCP only
netstat -i               # per-interface packet counts / errors / drops
netstat -s               # protocol statistics

route get/route -n get default is the single most useful idiom for "which interface and gateway will traffic to X actually use" — it accounts for scoped routes, VPNs, and service order.

No ss command. macOS ships no iproute2, so there is no ss and no ip command. Use netstat, lsof -i, nettop, or arp/ndp/route instead. (Some people install iproute2mac via Homebrew for muscle-memory ip, but it only wraps a subset.)

Sockets and listening ports

The ss -tlnp idiom from Linux has no direct equivalent. The macOS way is lsof (or netstat -an for a raw socket list).

# Everything with an open network socket, numeric, no DNS/port name lookups
sudo lsof -i -P -n

# The "what is listening, and which process" idiom (the macOS ss replacement)
sudo lsof -nP -iTCP -sTCP:LISTEN

# Who is using a specific port
sudo lsof -nP -iTCP:443
sudo lsof -nP -i :8080

# UDP sockets
sudo lsof -nP -iUDP

# Live per-process network bandwidth (interactive; q to quit)
nettop
nettop -P                # per-process aggregate
nettop -m tcp            # TCP only

# GUI equivalent: Activity Monitor → Network tab (per-process throughput),
# and the 'Open Files and Ports' inspector for a process.

Run lsof under sudo to see other users' and system sockets; without it you only see your own.

pf firewall vs the Application Firewall

macOS ships two distinct firewalls. Confusing them is why "my pf rules don't seem to do anything" tickets exist.

pf (packet filter) Application Firewall (ALF)
Layer Packet/port (network) Per-application (socket)
Origin OpenBSD pf, via FreeBSD Apple's own (socketfilterfw)
Controlled by pfctl, /etc/pf.conf System Settings → Network → Firewall, or socketfilterfw
Default state Disabled out of the box User-toggled; off by default
Use case NAT, port filtering, scripted rules, Internet Sharing "Allow/block incoming connections to this app"

The GUI firewall in System Settings drives the ALF, not pf. pf is enabled and configured separately on the command line.

pf (pfctl)

# Enable / disable the packet filter
sudo pfctl -e            # enable
sudo pfctl -d            # disable

# Load a ruleset
sudo pfctl -f /etc/pf.conf

# Show loaded rules / state / info
sudo pfctl -sr           # show rules
sudo pfctl -s rules      # (same)
sudo pfctl -si           # show info / status (incl. whether Enabled)
sudo pfctl -ss           # show state table
sudo pfctl -sn           # show NAT rules

# Anchors (named sub-rulesets)
sudo pfctl -s Anchors    # list anchors
sudo pfctl -a com.apple/250.ApplicationFirewall -sr

Config lives in /etc/pf.conf (the main file, which anchors in others) and /etc/pf.anchors/. Apple uses anchors heavily — Internet Sharing installs NAT rules into a pf anchor (com.apple.internet-sharing), and macOS keeps the base pf.conf minimal so your edits don't fight Apple's. For persistent custom rules across reboots, the robust approach is your own anchor plus a LaunchDaemon that runs pfctl -e -f, since the default pf.conf is not guaranteed to load your additions on boot.

Application Firewall (socketfilterfw)

The ALF binary is /usr/libexec/ApplicationFirewall/socketfilterfw.

ALF=/usr/libexec/ApplicationFirewall/socketfilterfw

# Global state
sudo "$ALF" --getglobalstate
sudo "$ALF" --setglobalstate on

# Stealth mode (don't respond to probes like ICMP/closed-port)
sudo "$ALF" --getstealthmode
sudo "$ALF" --setstealthmode on

# Block all incoming except essential services
sudo "$ALF" --setblockall on

# Per-app rules
sudo "$ALF" --add /Applications/Some.app
sudo "$ALF" --setallowsigned on
sudo "$ALF" --listapps

Wi-Fi diagnostics

The airport CLI is gone. The old workhorse — /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport — was deprecated and neutered in macOS Sonoma 14.4: it stopped returning RSSI/channel/scan data and printed only a deprecation notice ("WARNING: The airport command line tool is deprecated…"). In later releases the binary has been removed outright — it is absent on macOS 15 (Sequoia) and macOS 26, so the path above no longer exists. Do not rely on it. Use the replacements below.

# Current Apple-blessed Wi-Fi diagnostics dump (RSSI, channel, PHY, BSSID,
# security, tx rate, country code). Needs sudo for the full detail.
sudo wdutil info

# Wi-Fi hardware + current association via system_profiler (no sudo;
# verbose but reliable for SSID, channel, PHY mode, RSSI, noise)
system_profiler SPAirPortDataType

# Currently associated SSID (works without the airport tool)
networksetup -getairportnetwork en0   # confirm the device with -listallhardwareports

# Radio power
networksetup -getairportpower en0

For scanning nearby networks, there is no fully-supported CLI replacement post-Sonoma; the Wireless Diagnostics app (hold Option, click the Wi-Fi menu → "Open Wireless Diagnostics", then Window → Scan) is Apple's intended path. wdutil info and system_profiler SPAirPortDataType cover the current association details that airport -I used to give.

Diagnostics and captures

Most of the familiar tools are present and BSD-flavoured.

# Reachability
ping example.com
ping -c 4 example.com
ping6 2001:db8::1            # or: ping -6

# Path / latency per hop
traceroute example.com
traceroute6 example.com
# (no mtr by default — install via Homebrew if wanted)

# Packet capture is built in (BSD tcpdump). See the tcpdump/Wireshark sheet.
sudo tcpdump -i en0 -nn 'port 443'
sudo tcpdump -i utun3 -nn   # capture on a VPN/Tailscale tunnel

# Apple's built-in network quality / responsiveness test (macOS 12+).
# Measures throughput AND latency-under-load (RPM, "responsiveness").
networkQuality
networkQuality -v           # verbose, separate up/down
networkQuality -s           # sequential (down then up) rather than parallel

# Direct DNS debugging (bypasses macOS scoping — see DNS section)
dig example.com +short
host example.com

Reminder: dig/host/nslookup query a server directly and bypass macOS resolver scoping. For "what the app sees", use dscacheutil -q host -a name <host> and dns-sd.

VPN, utun, and Tailscale

VPN and mesh-VPN tunnels surface as utun* interfaces. Tailscale (utunN), WireGuard, and the built-in IKEv2/IPsec client all allocate one.

# List tunnel interfaces and their addresses
ifconfig | grep -A4 utun

# Which interface/route a destination resolves to — does it go down the tunnel?
route -n get default
route get 100.64.0.1        # a Tailscale CGNAT address, say

# Is the VPN's DNS resolver scoped in? (look for a utun if_index)
scutil --dns | grep -A6 utun

# Capture on the tunnel
sudo tcpdump -i utun3 -nn

The two questions a utun interface usually raises — "is my traffic actually going through it?" and "is split DNS pointing the right names at it?" — are answered by route -n get default / route get <dst> and scutil --dns respectively, not by ifconfig alone.

Quick Reference

Tool mapping (Linux → macOS)

Task Linux macOS
Show interfaces/addresses ip addr ifconfig
Show routes ip route netstat -rn, route -n get default
Add a (temporary) route ip route add route add (not persistent)
Listening sockets ss -tlnp sudo lsof -nP -iTCP -sTCP:LISTEN
All sockets ss -an netstat -an
Neighbour cache (ARP) ip neigh arp -a, ndp -an (v6)
Set static IP (persistent) nmcli / netplan networksetup -setmanual
Set DNS (persistent) nmcli / resolvectl networksetup -setdnsservers
Effective resolver view resolvectl status scutil --dns
Flush DNS cache resolvectl flush-caches dscacheutil -flushcache; killall -HUP mDNSResponder
Per-process bandwidth nethogs nettop
Firewall nft/iptables pfctl (packet) / socketfilterfw (per-app)

Most-used commands

Command Purpose
networksetup -listallhardwareports Map en* → Wi-Fi/Ethernet on this Mac
networksetup -getinfo "Wi-Fi" IP/mask/router/MAC for a service
sudo networksetup -setmanual "Ethernet" IP MASK ROUTER Persistent static IP
sudo networksetup -setdnsservers "Wi-Fi" 1.1.1.1 Persistent DNS
scutil --nwi Reachability / primary interfaces
scutil --dns The authoritative (scoped) resolver view
scutil --get LocalHostName The .local Bonjour name
dscacheutil -q host -a name HOST Resolve as the app would (scoped)
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder Flush DNS cache
route -n get default Resolve the active default route/interface
sudo lsof -nP -iTCP -sTCP:LISTEN What is listening (no ss)
nettop Live per-process bandwidth
sudo pfctl -si / -sr pf status / rules
sudo wdutil info Current Wi-Fi RSSI/channel/PHY
networkQuality Throughput + responsiveness test

Common Issues and Solutions

Issue Cause Solution
ifconfig/route change reverted after a few seconds or a reboot configd re-applies the service config from the dynamic store; manual BSD changes are runtime-only Make the change persistent with networksetup/scutil (or the GUI). Use ifconfig/route only for throwaway tests.
dig resolves but the app/browser can't (or resolves differently) macOS DNS is scoped/split; dig bypasses scoping and talks to a server directly Inspect with scutil --dns, reproduce the app's view with dscacheutil -q host -a name HOST and dns-sd. Check VPN/utun resolver ordering and search domains.
Editing /etc/resolv.conf has no effect It is generated and only reflects the primary resolver Set DNS via sudo networksetup -setdnsservers "<service>" …; verify with scutil --dns.
Stale DNS after a server/zone change mDNSResponder cache sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder (run both halves).
airport command prints a deprecation warning / no data, or the binary is missing entirely Neutered in macOS Sonoma 14.4; removed outright in macOS 15 (Sequoia)+ Use sudo wdutil info and system_profiler SPAirPortDataType; use Wireless Diagnostics for scans.
ss: command not found No iproute2 on macOS Use sudo lsof -nP -iTCP -sTCP:LISTEN, netstat -an, or nettop.
pf rules "don't apply" pf is disabled by default, or you're editing the wrong firewall sudo pfctl -si to check Enabled; sudo pfctl -e -f /etc/pf.conf. Remember the GUI firewall is the ALF (socketfilterfw), not pf.
GUI firewall on but ports still open The ALF is per-app, not per-port; it doesn't block outbound or non-app listeners the way pf does Use pf for packet/port filtering; use the ALF for "allow/block this app's incoming connections".
Can't set a static IP that survives reboot Used ifconfig/route sudo networksetup -setmanual "<service>" IP MASK ROUTER, or System Settings → Network → service → Details → TCP/IP → Manually.
Wrong interface assumed (en0 isn't Ethernet) Interface names are probe-order, not role-based Resolve with networksetup -listallhardwareports; never hard-code en0 = Ethernet.
sudo hostname foo doesn't persist BSD hostname sets a transient value sudo scutil --set HostName foo (and consider ComputerName/LocalHostName).
VPN up but traffic not using it Routing/service-order or scoped DNS mismatch route -n get default and route get <dst> for routing; scutil --dns for which resolver a name uses.
Permission denied / empty lsof output Need elevated privileges to see other users' sockets Run with sudo.

Related Topics

  • tcpdump/Wireshark — packet capture on en*/utun* interfaces (built-in tcpdump)
  • Tailscale / WireGuard — the utun tunnels you'll be inspecting with route get and scutil --dns
  • DNS fundamentals — scoped resolvers, search domains, and what scutil --dns is actually showing
  • pf firewall — deeper pfctl/anchor configuration shared with FreeBSD/OpenBSD
  • Linux network tools (ip/ss/nmcli) — the counterpart sheet this one maps against
  • Bonjour / mDNS / zeroconf — dns-sd, service discovery, and .local naming