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
      • 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.

      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) :

      # 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) :

      # 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
      
      match out on $wan from $local_nets nat-to ($wan)
      
      pass out on $wan from $local_nets
      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.

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

      # pfctl -nf /etc/pf.conf
        # Reject syntax errors without changing the active ruleset
      # 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:

      # tcpdump -eni em1 vlan
      

      Then inspect each logical interface:

      # tcpdump -ni vlan10
        # Observe untagged packets after VLAN 10 decapsulation
      # 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
      # pfctl -vvsr
        # Inspect rule counters
      # 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