OpenBSD Handbook

    • 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
      • Security
      • Virtualization
      • Storage and File Systems
      • Updating and Upgrading
      • Localization
      • The OpenBSD Boot Process
    • Part IV. Networking & Daemons
      • Services
        • Database
          • MariaDB
          • PostgreSQL
          • Redis
          • memcached
        • Directory
          • YP (NIS)
          • LDAP
        • File
          • NFS
          • Samba
        • FTP Services
          • ftpd
          • ProFTPD
          • vsftpd
          • TFTP
        • Mail
          • Dovecot
          • smtpd
          • Postfix
          • Exim
          • Rspamd
        • Name
          • Named
          • Unbound
          • NSD
        • Networking
          • OpenBGPD
          • rtadvd
          • 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
      • Howto
        • Install Z shell (zsh)
        • Set Up WordPress
        • Build a Simple Router and Firewall
      • OpenBSD for Linux Users
      • OpenBSD for FreeBSD Users
      • OpenBSD for macOS Users
    • Package Search
      PF Policies
      • Introduction
      • Assigning Tags to Packets
      • Checking for Applied Tags
      • Policy Filtering
      • Tagging Ethernet Frames

      PF Policies

      Introduction #

      Packet tagging is a way of marking packets with an internal identifier that can later be used in filter and translation rule criteria. With tagging, it’s possible to do such things as create “trusts” between interfaces and determine if packets have been processed by translation rules. It’s also possible to move away from rule-based filtering and to start doing policy-based filtering.

      Assigning Tags to Packets #

      To add a tag to a packet, use the ’tag’ keyword:

      pass in on $int_if all tag INTERNAL_NET
      

      The tag ‘INTERNAL_NET’ will be added to any packet which matches the above rule.

      A tag can also be assigned using a macro . For instance:

      name = "INTERNAL_NET"
      pass in on $int_if all tag $name
      

      There are a set of predefined macros which can also be used.

      • ‘$if’ - The interface
      • ‘$srcaddr’ - Source IP address
      • ‘$dstaddr’ - Destination IP address
      • ‘$srcport’ - The source port specification
      • ‘$dstport’ - The destination port specification
      • ‘$proto’ - The protocol
      • ‘$nr’ - The rule number

      These macros are expanded at ruleset load time and NOT at runtime.

      Tagging follows these rules:

      • Tags are “sticky.” Once a tag is applied to a packet by a matching rule, it is never removed. It can, however, be replaced with a different tag.
      • Because of a tag’s “stickiness,” a packet can have a tag even if the last matching rule doesn’t use the ’tag’ keyword.
      • A packet is only ever assigned a maximum of one tag at a time.
      • Tags are internal identifiers. Tags are not sent out over the wire.
      • Tag names can be up to 63 characters long.

      Take the following ruleset as an example.

      pass in on $int_if tag INT_NET
      pass in quick on $int_if proto tcp to port 80 tag INT_NET_HTTP
      pass in quick on $int_if from 192.168.1.5
      
      • Packets coming in on ‘$int_if’ will be assigned a tag of ‘INT_NET’ by rule #1.
      • TCP packets coming in on ‘$int_if’ and destined for port 80 will first be assigned a tag of ‘INT_NET’ by rule #1. That tag will then be replaced with the ‘INT_NET_HTTP’ tag by rule #2.
      • Packets coming in on ‘$int_if’ from 192.168.1.5 will be tagged one of two ways. If the packet is destined for TCP port 80 it will match rule #2 and be tagged with ‘INT_NET_HTTP’. Otherwise, the packet will match rule #3 but will be tagged with ‘INT_NET’. Because the packet matches rule #1, the ‘INT_NET’ tag is applied and is not removed unless a subsequently matching rule specifies a tag (this is the “stickiness” of a tag).

      Checking for Applied Tags #

      To check for previously applied tags, use the ’tagged’ keyword:

      pass out on egress tagged INT_NET
      

      Outgoing packets on the external interface must be tagged with the ‘INT_NET’ tag in order to match the above rule. Inverse matching can also be done by using the ‘!’ operator:

      pass out on egress ! tagged WIFI_NET
      

      Policy Filtering #

      Policy filtering takes a different approach to writing a filter ruleset. A policy is defined which sets the rules for what types of traffic is passed and what types are blocked. Packets are then classified into the policy based on the traditional criteria of source/destination IP address/port, protocol, etc. For example, examine the following firewall policy:

      • Traffic from the internal LAN to the Internet is permitted (LAN_INET) and must be translated (LAN_INET_NAT).
      • Traffic from the internal LAN to the DMZ is permitted (LAN_DMZ).
      • Traffic from the Internet to servers in the DMZ is permitted (INET_DMZ).
      • Traffic from the Internet that’s being redirected to spamd(8) is permitted (SPAMD).
      • All other traffic is blocked.

      Note how the policy covers all traffic that will be passing through the firewall. The item in parenthesis indicates the tag that will be used for that policy item.

      Rules now need to be written to classify packets into the policy.

      block all
      pass out on egress inet tag LAN_INET_NAT tagged LAN_INET nat-to ($ext_if)
      pass in  on $int_if from $int_net tag LAN_INET
      pass in  on $int_if from $int_net to $dmz_net tag LAN_DMZ
      pass in  on egress proto tcp to $www_server port 80 tag INET_DMZ
      pass in  on egress proto tcp from <spamd> to port smtp tag SPAMD rdr-to 127.0.0.1 port 8025
      

      Now the rules that define the policy are set.

      pass in  quick on egress tagged SPAMD
      pass out quick on egress tagged LAN_INET_NAT
      pass out quick on $dmz_if tagged LAN_DMZ
      pass out quick on $dmz_if tagged INET_DMZ
      

      Now that the whole ruleset is setup, changes are a matter of modifying the classification rules. For example, if a POP3/SMTP server is added to the DMZ, it will be necessary to add classification rules for POP3 and SMTP traffic, like so:

      mail_server = "192.168.0.10"
      [...]
      pass in on egress proto tcp to $mail_server port { smtp, pop3 } tag INET_DMZ
      

      Email traffic will now be passed as part of the INET_DMZ policy entry.

      The complete ruleset:

      int_if      = "dc0"
      dmz_if      = "dc1"
      int_net     = "10.0.0.0/24"
      dmz_net     = "192.168.0.0/24"
      www_server  = "192.168.0.5"
      mail_server = "192.168.0.10"
      
      table <spamd> persist file "/etc/spammers"
      # classification -- classify packets based on the defined firewall policy.
      block all
      pass out on egress inet tag LAN_INET_NAT tagged LAN_INET nat-to (egress)
      pass in on $int_if from $int_net tag LAN_INET
      pass in on $int_if from $int_net to $dmz_net tag LAN_DMZ
      pass in on egress proto tcp to $www_server port 80 tag INET_DMZ
      pass in on egress proto tcp from <spamd> to port smtp tag SPAMD rdr-to 127.0.0.1 port 8025
      
      # policy enforcement -- pass/block based on the defined firewall policy.
      pass in  quick on egress tagged SPAMD
      pass out quick on egress tagged LAN_INET_NAT
      pass out quick on $dmz_if tagged LAN_DMZ
      pass out quick on $dmz_if tagged INET_DMZ
      

      Tagging Ethernet Frames #

      Tagging can be performed at the ethernet level if the machine doing the tagging/filtering is also acting as a bridge. By creating bridge filter rules that use the ’tag’ keyword, PF can be made to filter based on the source or destination MAC address. Bridge(4) rules are created using the ifconfig command. Example:

      # ifconfig bridge0 rule pass in on fxp0 src 0:de:ad:be:ef:0 tag USER1
      

      And then in ‘pf.conf’:

      pass in on fxp0 tagged USER1
      
      Report a bug
      • Introduction
      • Assigning Tags to Packets
      • Checking for Applied Tags
      • Policy Filtering
      • Tagging Ethernet Frames