OpenBSD Handbook

    Theme
    • Part I. Install & Configure
      • Introduction to OpenBSD
      • Installing OpenBSD
      • The X Window System
      • Networking on OpenBSD
      • System Configuration
      • OpenBSD Basics
      • Managing Software: Packages and Ports
    • Part II. Daily Operations
      • OpenBSD Desktop 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
          • OpenSMTPD Mail Server
          • Dovecot IMAP and POP3 Server
          • Postfix
          • Exim
          • Rspamd
        • Name
          • Unbound DNS Resolver
          • NSD Authoritative DNS Server
          • BIND DNS Server
        • Networking
          • OpenBGPD
          • rad
          • DHCP on OpenBSD
          • slaacd
        • Web
          • Apache HTTP Server
          • nginx Web Server
          • OpenBSD httpd Web Server
          • relayd Reverse Proxy
        • Logging
          • syslogd
        • Monitoring
          • SNMP
        • Remote Access
          • Audit OpenSSH
          • OpenSSH Server
        • File Synchronization
          • rsync
        • Messaging
          • RabbitMQ Message Broker
        • Time
          • NTP Time Synchronization
        • Load Balancing
          • Build Redundant Load-Balanced Gateways
          • Load Balance Services with NAT or DSR
          • Build an HA HTTPS Load Balancer
      • 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
        • Installation and First Boot
          • Complete the First 30 Minutes After Installation
          • Check Hardware Compatibility Before Installing
          • Bootstrap Wi-Fi Firmware Without Ethernet
          • Run OpenBSD as a Virtual Machine Guest
          • Install OpenBSD on a VPS or Cloud Server
          • Install OpenBSD with Full-Disk Encryption
          • Dual-Boot OpenBSD on a UEFI System
          • Automate OpenBSD Installation with Autoinstall and PXE
        • Desktops and Laptops
          • Set Up OpenBSD as a Laptop or Workstation
          • Configure Laptop Power Management
          • Troubleshoot Web Browsers on OpenBSD
          • Use Plan 9 Tools on OpenBSD
          • Build a Minimal Desktop with suckless Tools
          • Install Z shell (zsh)
        • Networking and Remote Access
          • Create an Administrator Account with doas and SSH Keys
          • Configure Secure Remote Access with OpenSSH
          • Use FIDO Security Keys with OpenSSH
          • Configure Wi-Fi Roaming and Wired Failover
          • Troubleshoot PF, NAT, and Routing
          • Route Multiple VLANs with PF
          • Configure a Road-Warrior WireGuard VPN
          • Route IPv6 Networks without NAT
          • Test OpenBGPD with Two Providers
          • Unlock an Internal Service with an SSH Key
          • Build a Simple Router and Firewall
        • Servers and Services
          • Set Up WordPress on OpenBSD
          • Build a Mail Server with OpenSMTPD, Dovecot, and Rspamd
        • Security, Maintenance and Recovery
          • Establish a Conservative Security Baseline
          • Recover Access and Reset the Root Password
          • Collect OpenBSD Diagnostic Evidence
          • Troubleshoot Package Installation and Update Failures
          • Schedule Recurring Maintenance
          • Monitor an OpenBSD System
          • Back Up and Restore an OpenBSD System
          • Configure softraid RAID1
          • Recover an OpenBSD System That Does Not Boot
        • Building, Automation and Virtual Machines
          • Manage OpenBSD with Ansible
          • Build Third-Party Software from Ports
          • Build OpenBSD from Source
          • Plan a Docker Workload Migration to OpenBSD
      • OpenBSD for Linux Users
      • OpenBSD for FreeBSD Users
      • OpenBSD for macOS Users
    • OpenBSD FAQ
    • OpenBSD Architectures
      • OpenBSD Package Search
        • Synopsis
        • Example Topology
        • Configure the Trunk and VLAN Interfaces
        • Enable IPv4 Forwarding
        • Add a PF Policy
        • Verify Tagged Traffic and Routing
        • Operational Notes

        Route Multiple VLANs with PF

        Synopsis #

        A virtual LAN (VLAN) separates one Ethernet link into tagged Layer 2 networks. OpenBSD represents each tagged network with a vlan(4) interface. This guide configures two VLANs on one trunk link, enables IPv4 routing, applies an explicit pf.conf(5) policy, and verifies traffic at each layer.

        The connected switch must already carry the selected VLAN identifiers as tagged traffic. Management access must not depend on the rules being changed. Keep a console or an established recovery session available until a new administrative session succeeds.

        Example Topology #

        The example uses:

        RoleInterface or network
        Internet uplinkem0, configured separately
        VLAN trunkem1
        Administrative VLANVLAN 10, 10.10.10.0/24
        Service VLANVLAN 20, 10.20.20.0/24
        Router addresses10.10.10.1 and 10.20.20.1

        Replace every interface, VLAN identifier, address, and policy with values from the actual network plan.

        One trunk, two routed VLANsA switch has untagged access ports for the administration VLAN 10 and service VLAN 20. Its tagged trunk carries both VLANs to em1 on the OpenBSD router. Logical interfaces vlan10 at 10.10.10.1 and vlan20 at 10.20.20.1 use parent em1. PF permits connections initiated from the administrative network to services, and blocks connections initiated in the reverse direction. Replies to permitted connections use PF state. Both VLANs have Internet access through em0 with IPv4 NAT.One trunk, two routed VLANsAdministrationVLAN 10 · 10.10.10.0/24ServicesVLAN 20 · 10.20.20.0/24Untagged access portsSwitchTagged trunkVLANs 10 and 20Parent: em1 (no IP)OpenBSD routervlan1010.10.10.1/24vlan2010.20.20.1/24PF permits administration → services connections.Services → administration: new connections blocked.PF state allows replies to permitted connections.em0 · IPv4 NATInternet access for both VLANs
        The switch carries both VLANs over one tagged trunk. OpenBSD routes between the logical interfaces, while PF controls which connections may cross between them.

        Configure the Trunk and VLAN Interfaces #

        Leave the physical trunk without an IP address. Create /etc/hostname.em1:

        up
        

        Create /etc/hostname.vlan10:

        parent em1
        vnetid 10
        inet 10.10.10.1 255.255.255.0
        description "administration"
        up
        

        Create /etc/hostname.vlan20:

        parent em1
        vnetid 20
        inet 10.20.20.1 255.255.255.0
        description "services"
        up
        

        The file names do not have to match the VLAN identifiers, but matching names make the configuration easier to audit. Apply the three interfaces with netstart(8) :

        doas sh /etc/netstart em1 vlan10 vlan20
        

        Confirm the parent, tag, addresses, and link state with ifconfig(8) :

        ifconfig em1
          # Confirm that the physical trunk is active
        ifconfig vlan10
          # Confirm parent em1, vnetid 10, and 10.10.10.1/24
        ifconfig vlan20
          # Confirm parent em1, vnetid 20, and 10.20.20.1/24
        

        Enable IPv4 Forwarding #

        Add the following line to /etc/sysctl.conf:

        net.inet.ip.forwarding=1
        

        Apply it immediately with sysctl(8) :

        doas sysctl net.inet.ip.forwarding=1
        

        Forwarding only permits the kernel to route packets. PF still determines which traffic may cross between networks.

        Add a PF Policy #

        Integrate the following policy into the complete /etc/pf.conf; do not replace an established ruleset without reviewing its existing macros, tables, anchors, and management rules:

        wan = "em0"
        admin_if = "vlan10"
        service_if = "vlan20"
        
        admin_net = "10.10.10.0/24"
        service_net = "10.20.20.0/24"
        local_nets = "{ 10.10.10.0/24, 10.20.20.0/24 }"
        
        set skip on lo
        block all
        
        pass out on $wan inet from $local_nets nat-to ($wan)
        pass out on $service_if from $admin_net to $service_net
        pass in quick on $admin_if inet proto icmp from $admin_net to ($admin_if) icmp-type echoreq
        pass in quick on $admin_if proto { tcp udp } from $admin_net to ($admin_if) port domain
        pass in quick on $admin_if proto tcp from $admin_net to ($admin_if) port ssh
        block in quick on $admin_if from $admin_net to self
        pass in on $admin_if from $admin_net to any
        pass in quick on $service_if inet proto icmp from $service_net to ($service_if) icmp-type echoreq
        block in quick on $service_if from $service_net to self
        block in quick on $service_if from $service_net to $admin_net
        pass in on $service_if from $service_net to any
        

        This example permits both VLANs to test their own router address, permits the administrative VLAN to use SSH and an optional local DNS resolver, permits administrators to reach the service network, prevents the service network from initiating other connections to the router or administrative network, and provides outbound Internet access through IPv4 network address translation. It does not configure DHCP or DNS service; a permitted port has no effect unless a daemon is bound to that address.

        Traffic from the administration VLAN to the service VLAN must pass PF rules on both interfaces. The outgoing rule allows this path. Connections started from the service VLAN to the administration VLAN remain blocked.

        Parse the complete ruleset before loading it with pfctl(8) :

        doas pfctl -nf /etc/pf.conf
          # Reject syntax errors without changing the active ruleset
        doas pfctl -f /etc/pf.conf
          # Load the rules only after parsing succeeds
        

        Keep the previous administrative session open. Test a new SSH connection from the administrative VLAN before ending the recovery session.

        Verify Tagged Traffic and Routing #

        Use tcpdump(8) on the physical trunk to confirm that tags arrive from the switch:

        doas tcpdump -eni em1 vlan
        

        Then inspect each logical interface:

        doas tcpdump -ni vlan10
          # Observe untagged packets after VLAN 10 decapsulation
        doas tcpdump -ni vlan20
          # Observe untagged packets after VLAN 20 decapsulation
        

        From a host in each VLAN, verify its address, default gateway, DNS configuration, and reachability to the router address. Then test only the cross-VLAN and Internet paths that the PF policy permits.

        Display routes, PF rules, and states when a test fails:

        route -n show
          # Confirm connected routes for both VLAN networks
        doas pfctl -vvsr
          # Inspect rule counters
        doas pfctl -ss
          # Inspect active states
        

        No tagged packets on em1 indicates a switch, cabling, parent-interface, or VLAN-identifier problem. Packets on a VLAN interface but no forwarding indicate an address, route, forwarding, or PF problem.

        Operational Notes #

        • A switch access port normally carries one untagged network and does not replace the tagged trunk used here.
        • Native or untagged traffic on the trunk requires a separate, explicit design. Do not assume a switch’s native VLAN behavior.
        • DHCP service requires one subnet declaration per served VLAN and explicit daemon interface flags.
        • IPv6 requires routed prefixes, router advertisements, ICMPv6, and a separate PF policy. Route IPv6 Networks without NAT covers that design.
        • VLAN separation does not itself enforce policy. PF, switch policy, and service binding must agree.

        See Networking for the concise VLAN interface reference and Troubleshoot PF, NAT, and Routing for a layer-by-layer diagnostic procedure.

        Report a bug
        • Synopsis
        • Example Topology
        • Configure the Trunk and VLAN Interfaces
        • Enable IPv4 Forwarding
        • Add a PF Policy
        • Verify Tagged Traffic and Routing
        • Operational Notes