OpenBSD Handbook

    Theme
    • Part I. Install & Configure
      • Introduction
      • Installing OpenBSD
      • The X Window System
      • Networking
      • System Configuration
      • OpenBSD Basics
      • Managing Software: Packages and Ports
    • Part II. Daily Operations
      • Graphical Environments
      • Multimedia
      • Printing
      • Linux Compatibility
      • Windows Compatibility
      • Games
    • Part III. System Administration
      • Virtualization
      • Storage and File Systems
      • Updating and Upgrading
      • Localization
      • The OpenBSD Boot Process
      • Security
    • Part IV. Networking & Daemons
      • Services
        • Database
          • MariaDB
          • PostgreSQL
          • Redis
          • memcached
        • Directory
          • YP (NIS)
          • LDAP
        • File
          • NFS
          • Samba
        • FTP Services
          • ftpd
          • ProFTPD
          • vsftpd
          • Pure-FTPd
          • TFTP
        • Mail
          • smtpd
          • Dovecot
          • Postfix
          • Exim
          • Rspamd
        • Name
          • Unbound
          • NSD
          • BIND
        • Networking
          • OpenBGPD
          • rad
          • DHCP
          • slaacd
        • Web
          • Apache
          • nginx
          • httpd
          • relayd
        • Logging
          • syslogd
        • Monitoring
          • SNMP
        • Remote Access
          • Audit OpenSSH
          • sshd
        • File Synchronization
          • rsync
        • Messaging
          • RabbitMQ
        • Time
          • NTP
      • PF
        • pfctl cheat sheet
        • PF Anchors
        • PF Filter Rules
        • PF Forwarding
        • PF Lists and Macros
        • PF Load Balancing
        • PF Logging
        • PF NAT
        • PF Options
        • PF Policies
        • PF Shortcuts
        • PF Tables
      • Advanced Networking
        • High Availability and State Replication
        • Multi-WAN and Policy-Based Routing
        • VPN and Cryptographic Tunneling
        • Classic and Lightweight Tunnels
        • IPv6 at Scale
        • QoS and Traffic Shaping
        • MPLS and Label Distribution
        • Network Services at Scale
        • Virtualization and Host Networking
        • Large-Scale L2 and L3 Design
        • Telemetry, Logging, and Flow Export
        • Hardening and Operational Safety
        • Reference Architectures
        • Troubleshooting Playbooks
      • Serial Communication
    • Part V. Miscellaneous
      • Virtualization Cheat Sheet
      • OpenBSD Cheatsheet
      • How-to Guides
        • Complete the First 30 Minutes After Installation
        • Check Hardware Compatibility Before Installing
        • Bootstrap Wi-Fi Firmware Without Ethernet
        • Set Up OpenBSD as a Laptop or Workstation
        • Configure Laptop Power Management
        • Run OpenBSD as a Virtual Machine Guest
        • Install OpenBSD on a VPS or Cloud Server
        • Install OpenBSD with Full-Disk Encryption
        • Troubleshoot Web Browsers on OpenBSD
        • Create an Administrator Account with doas and SSH Keys
        • Establish a Conservative Security Baseline
        • Manage OpenBSD with Ansible
        • Recover Access and Reset the Root Password
        • Collect OpenBSD Diagnostic Evidence
        • Configure Secure Remote Access with OpenSSH
        • Set Up WordPress
        • Build Third-Party Software from Ports
        • Use FIDO Security Keys with OpenSSH
        • Build OpenBSD from Source
        • Configure Wi-Fi Roaming and Wired Failover
        • Dual-Boot OpenBSD on a UEFI System
        • Troubleshoot PF, NAT, and Routing
        • Automate OpenBSD Installation with Autoinstall and PXE
        • Route Multiple VLANs with PF
        • Configure a Road-Warrior WireGuard VPN
        • Troubleshoot Package Installation and Update Failures
        • Route IPv6 Networks without NAT
        • Schedule Recurring Maintenance
        • Plan a Docker Workload Migration to OpenBSD
        • Build a Mail Server with OpenSMTPD, Dovecot, and Rspamd
        • Build a Simple Router and Firewall
        • Monitor an OpenBSD System
        • Back Up and Restore an OpenBSD System
        • Configure softraid RAID1
        • Recover an OpenBSD System That Does Not Boot
        • Install Z shell (zsh)
      • OpenBSD for Linux Users
      • OpenBSD for FreeBSD Users
      • OpenBSD for macOS Users
    • OpenBSD FAQ
    • Package Search
      • Synopsis
      • Record the Topology and Symptom
      • Check Links and Addresses
      • Check Routes
      • Check Packet Forwarding
      • Validate the PF Configuration
      • Inspect States
      • Capture Each Hop
      • Confirm the Destination Service
      • Test from the Correct Location
      • Check DNS Separately
      • Apply and Verify One Change

      Troubleshoot PF, NAT, and Routing

      Synopsis #

      Routing and Packet Filter failures often involve several layers at once. A port forward can fail because the interface has no address, the route is wrong, forwarding is disabled, PF loaded a different rule, an old state remains, the service is not listening, or the test never crossed the external interface.

      This guide follows the packet path in order. It begins with read-only inspection and changes one variable at a time. It does not disable PF, flush every state, or replace the ruleset on an exposed remote host.

      Record the Topology and Symptom #

      Write down:

      • source and destination addresses;
      • source and destination ports;
      • the expected ingress and egress interfaces;
      • the router addresses on each network;
      • whether the failure affects routing, DNS, one protocol, or one direction;
      • the last known-good configuration.

      Test both an IP address and a hostname. If the IP works but the name fails, investigate the resolver before PF.

      Check Links and Addresses #

      Use ifconfig(8) :

      $ ifconfig
        # Confirm interface flags, link state, addresses, and groups
      $ ifconfig -A
        # Display aliases and all address families
      

      An interface expected to carry traffic should be UP and normally RUNNING, with the intended address and prefix.

      Check Routes #

      Use route(8) to inspect the selected path:

      $ route -n show
        # Inspect the complete routing table without DNS lookups
      $ route -n get 198.51.100.25
        # Show the route selected for one destination
      

      On a client behind the router, confirm that the default gateway is the OpenBSD system. On the OpenBSD router, confirm routes to both the source and destination networks.

      Check Packet Forwarding #

      For an IPv4 router, inspect the forwarding control with sysctl(8) :

      $ sysctl net.inet.ip.forwarding
      

      The value must be 1 for IPv4 forwarding. IPv6 routing uses a separate control:

      $ sysctl net.inet6.ip6.forwarding
      

      Enable only the address family that the router is configured to route, and persist the decision in /etc/sysctl.conf.

      Validate the PF Configuration #

      Parse the file without loading it:

      # pfctl -nf /etc/pf.conf
      

      Then inspect the running filter and translation rules with pfctl(8) :

      # pfctl -si
        # Confirm PF status and global counters
      # pfctl -vvsr
        # Show active filter rules, counters, and evaluations
      # pfctl -sn
        # Show active translation rules
      

      If the file and active rules differ, determine why before reloading. Rules later in the file can replace earlier decisions unless quick stops evaluation. Translation does not compensate for a missing pass decision.

      Inspect States #

      Display the state table:

      # pfctl -ss
      

      An established state can preserve behavior after a rule change. Avoid flushing the entire table on a production firewall. Reproduce the test with a new source port or remove only the relevant state after confirming the effect on active users.

      Capture Each Hop #

      tcpdump(8) shows whether the packet reaches and leaves each interface. For an inbound HTTPS forward from vio0 to server 192.168.10.20 on vio1:

      # tcpdump -n -e -ttt -i vio0 'tcp port 443'
        # Confirm that the external packet reaches the router
      # tcpdump -n -e -ttt -i vio1 'host 192.168.10.20 and tcp port 443'
        # Confirm that the translated packet leaves toward the server
      

      Run the captures in separate console sessions while generating one controlled test.

      Interpret the first missing observation:

      • absent on ingress: upstream routing, addressing, switch, provider, or client problem;
      • present on ingress but absent on egress: route, forwarding, PF, or translation problem;
      • present on both but no reply: service, host firewall, server route, or server application problem;
      • reply reaches the router but not the source: state, reverse route, or asymmetric-path problem.

      Confirm the Destination Service #

      On the destination host, use netstat(1) to confirm that the application listens on the intended address and port:

      $ netstat -na -f inet | grep '\.443 .*LISTEN'
      

      A service bound only to 127.0.0.1 cannot receive a forward to the host’s LAN address.

      Test from the Correct Location #

      Test an Internet-facing port forward from a genuinely external network. A client on the LAN that connects to the router’s external address does not traverse the same path. Use split-horizon DNS for internal clients or configure deliberate reflection rules as described in PF NAT .

      Check DNS Separately #

      Compare direct reachability and name resolution:

      $ ping -c 3 <known-reachable-ip>
        # Test routing without DNS
      $ host service.example.com
        # Test resolver behavior
      

      Replace <known-reachable-ip> with an address that is expected to answer in the deployment. Documentation prefixes such as 198.51.100.0/24 are not reachability targets.

      Inspect /etc/resolv.conf and current route proposals. Do not make the file immutable; resolvd(8) manages it and preserves user-edited lines.

      Apply and Verify One Change #

      After correcting /etc/pf.conf:

      # pfctl -nf /etc/pf.conf
        # Parse before mutation
      # pfctl -f /etc/pf.conf
        # Load the complete validated ruleset
      # pfctl -vvsr
        # Confirm the expected active rule and counters
      

      Repeat the same packet capture and client test. Record the change that resolved the failure so that the persistent configuration, monitoring, and recovery notes remain consistent.

      For the configuration itself, see PF NAT , Packet Filter , and Build a Simple Router and Firewall .

      Report a bug
      • Synopsis
      • Record the Topology and Symptom
      • Check Links and Addresses
      • Check Routes
      • Check Packet Forwarding
      • Validate the PF Configuration
      • Inspect States
      • Capture Each Hop
      • Confirm the Destination Service
      • Test from the Correct Location
      • Check DNS Separately
      • Apply and Verify One Change