A firewall doesn't "block" anything wholesale: it reads headers, layer by layer, and each layer allows for a different type of filtering. Here's an overview from Layer 2 to Layer 7, using pfSense as an example.
> ### The Essentials in 30 Seconds > > - A firewall is, first and foremost, a header reader. The deeper it decapsulates, the more granular the filtering—and the higher the CPU overhead. > - Layer 2: MAC/VLAN/EtherType filtering. Marginal for routing, essential for transparent bridging. > - Layer 3: the historic building block. Source/destination IP, protocol, ICMP, NAT. This is packet filtering. > - Layer 4: TCP/UDP ports and, most importantly, the state table. This is stateful inspection, the true foundation of modern firewalls. > - Layers 5–6: where TLS complicates everything. The SNI and the certificate are the last elements that can be read without decryption. > - Layer 7: application filtering, IDS/IPS, URL and DNS filtering. Requires DPI, and often breaking TLS. > - The pitfall: a firewall does not process packets in order from 1 to 7. The actual pipeline is more convoluted, and this explains 80% of the “why isn’t my rule matching” questions.
Why Bring Up OSI Again When Discussing Firewalls
Following the articles on RJ45 cabling (Layer 1), MAC addresses (Layer 2), IP and routing (Layer 3), TCP and ports (Layer 4), and finally the application (Layer 7), the question that keeps coming up from readers preparing for the TSSR is always the same: “OK, but in practical terms, which layer does my firewall operate on?”
The answer can be summed up in one word: all. And that’s exactly why this topic deserves an article.
A firewall, whatever it may be—a pfSense VM set up in ten minutes for a lab, or an appliance in production—always does the same thing: it receives a frame, decapsulates it layer by layer, and with each layer peeled away, it gains the ability to make a more granular decision. The OSI model isn’t just a textbook diagram here—it’s literally a map of the device’s decision points.
Where it gets interesting is that each layer has a cost. Filtering by IP address involves a 4-byte comparison. Filtering to determine “whether this HTTPS traffic is from Dropbox or OneDrive” involves decrypting TLS, reconstructing the traffic, and running it through a signature engine. Between the two, there’s a three-order-of-magnitude difference in CPU usage. The whole art lies in applying the right filter at the right layer.
This article uses pfSense as a textbook example—because it’s free, can be set up in ten minutes on a personal network, and its transparency (it’s OpenBSD’s pf, Suricata, and Squid—open-source building blocks that can be inspected) makes it an excellent tool for understanding what’s happening, whereas a proprietary appliance hides the inner workings.
The Chart: One Layer, One Filtering Type
| OSI Layer | What the Firewall Reads | Filtering Type | In pfSense |
|---|---|---|---|
| 2 — Link | Source/destination MAC, 802.1Q tag, EtherType | MAC filtering, VLAN isolation, anti-ARP spoofing | VLAN interfaces, filtering bridge, Ethernet Rules (pfSense Plus only) |
| 3 — Network | Source/destination IP, protocol, TTL, fragmentation flags | Packet filtering, ACL, NAT, anti-spoofing | pf rules, aliases, Outbound NAT, pfBlockerNG (GeoIP/lists) |
| 4 — Transport | TCP/UDP ports, TCP flags, sequence | Stateful inspection, state table, rate limiting | State table (pfctl -s state), keep state and synproxy options, limiters |
| 5-6 — Session / Presentation | TLS handshake, SNI, certificate, ALPN | Domain name filtering without decryption, encryption checking | SNI filtering via HAProxy / Suricata, pfBlockerNG DNSBL |
| 7 — Application | Full payload, URL, HTTP method, DNS request, file | DPI, IDS/IPS, URL filtering, DNS filtering | Suricata/Snort, Squid + SquidGuard, pfBlockerNG DNSBL |
Most importantly, remember the dividing line: Up to Layer 4, the firewall decides on its own, in the kernel, at full speed. Starting at Layer 5, it needs an inspection engine, context, and often certificates.
Layer 2: The Filtering We Tend to Forget
In classic router mode, Layer 2 is mainly used to determine which interface the packet arrived on. A pfSense rule always starts with an interface (LAN, WAN, a VLAN): this is already Layer 2 in disguise, since the interface defines the broadcast domain.
VLANs work the same way: an 802.1Q tag in the Ethernet header, which the firewall reads to assign the packet to a logical subinterface. When separating a guest network from a server network, the most robust security decision is made before even looking at an IP address: the two VLANs simply aren’t allowed to communicate with each other—period.
The scenario where Layer 2 becomes a true filtering mechanism is the transparent bridge: the firewall is placed in the middle of an L2 segment, without an IP address, without NAT, and filters traffic between two ports just as a switch with rules would. pfSense can do this—a bridge groups multiple interfaces into a single broadcast domain, and the difference from a hardware switch is precisely that you can apply pf rules between the members.
Note the pfSense nuance: explicit filtering based on Ethernet header attributes (Ethernet Rules) is a pfSense Plus feature starting with version 23.05, disabled by default, and must be enabled in System > Advanced. In pfSense CE, you don’t have that tab. It’s the kind of detail that can waste an entire evening when you’re following a tutorial without checking the version.
And to tell the truth: MAC filtering as a security measure is just for show. It only takes two commands to change a MAC address. As I wrote in the Layer 2 article, the MAC address identifies—it does not authenticate. True Layer 2 security is called 802.1X, DHCP snooping, and Dynamic ARP Inspection—and it happens on the switch, not on the firewall.
Layer 3: Packet Filtering, the Still-Living Ancestor
This is the layer of the “traditional” firewall: it reads the source IP, the destination IP, the protocol (TCP/UDP/ICMP/GRE/ESP…), and makes a decision. A purely Layer 3 firewall is stateless: each packet is evaluated in isolation, with no memory of what came before.
No one does this anymore for general filtering—but Layer 3 remains ubiquitous:
- Aliases. In pfSense, an alias designates a set of IP addresses (or ports, or networks) so you don’t have to write 40 rules. It’s the basic building block for organizing a readable set of rules.
- GeoIP and reputation lists. pfBlockerNG blocks entire countries or ASNs without ever looking beyond Layer 3. It’s blunt, it’s effective, and it costs next to nothing.
- Anti-spoofing. A packet claiming to come from 192.168.10.5 but arriving via the WAN port is inconsistent with the routing table: it’s discarded.
pfdoes this via theantispoofoption—this is uRPF as discussed in the Layer 3 article. - NAT. Rewriting L3 headers (and L4 headers for PAT) is pure Layer 3 manipulation.
- ICMP. The most mistreated protocol in the history of firewalling. Blocking all outbound ICMP breaks Path MTU Discovery and results in TLS sessions freezing halfway through without any logs. Allow
type 3 code 4—details are covered in the Layer 3 article.
This is also where much of the basic anti-DoS protection takes place: L3/L4 counters (X packets per second from a source, and we drop them). Simple, fast—and a formidable source of false positives if the threshold is poorly calibrated, but that’s another story.
Layer 4: State—the true heart of the system
If you had to remember just one layer, this is the one.
A stateful firewall doesn’t just check ports. It maintains a state table: for each flow, it stores the quintuple (source IP, source port, destination IP, destination port, protocol), the connection state, and a timer. The implication is huge:
> You only write rules in one direction. Return traffic is automatically allowed because it matches an existing state. A packet that arrives without a matching state and without a rule allowing it is dropped.
That’s what makes a firewall usable. Without state tracking, you’d have to explicitly allow all high-numbered ports on the incoming side so your users could browse the web—in other words, you’d have to filter nothing. This is a direct extension of what we saw in the Layer 4 article on TCP states.
In practice:
# pfSense — view the state table
pfctl -s state | grep 10.0.0.50
pfctl -s info # number of states, searches, inserts, mismatches
pf shows you who is talking to whom, in what state, and for how much longer.
The pitfall that trips everyone up: when you modify a rule, already established connections are not re-evaluated. You block a connection, but it keeps going through, and you spend an hour trying to figure out why. The correct response: clear the relevant states (pfctl -k with a filter) before concluding that the rule is faulty.
UDP deserves a mention: there’s no state in UDP. The firewall invents one by creating a pseudo-state with a short timer as soon as it sees an outgoing packet. This is fragile, and that’s why asymmetric UDP protocols (VoIP, gaming, certain VPNs) cause so many headaches—the link to UDP’s “connectionless” nature, as described in Layer 4, is direct.
Layers 5 and 6: The TLS Wall
This is where the OSI model ceases to be a pedagogical abstraction and becomes a very concrete operational problem. And this is the exact extension of what we said about the phantom layers: TLS performs the work of the presentation layer without truly belonging to any layer.
Today, the overwhelming majority of traffic is encrypted. Above Layer 4, the firewall sees… noise. Except in three places, in the ClientHello of the TLS handshake, which is in clear text:
- SNI (Server Name Indication)—the requested domain name.
- The server certificate—its CN, SAN, issuer, and validity.
- The TLS version and the negotiated cipher suites, plus the ALPN.
This leads to two strategies:
Inspection by SNI/certificate. The firewall reads the SNI and the certificate, makes a decision, and leaves the tunnel intact. It’s low-cost, doesn’t break anything, and requires no CA deployed on endpoints. It allows you to block facebook.com or categorize a URL at the domain level. But it doesn’t see the full URL, the content, or the downloaded file. On pfSense, this is achieved through a combination of tools: HAProxy for SNI filtering, or Suricata’s SNI rules.
Deep inspection (SSL/TLS interception). The firewall impersonates the server to the client and the client to the server. It decrypts, inspects, and re-encrypts. It sees everything. The trade-offs:
- You must deploy the firewall’s CA on all devices (GPO, MDM, etc.).
- Certificate pinning for modern applications (Chrome updates, mobile apps, cloud agents) will break. Exemptions will be required.
- CPU usage skyrockets. It becomes the number one cost driver as soon as it’s enabled.
- And legally, in a corporate setting, this can’t be improvised: users must be notified, and categories such as healthcare, banking, and HR must be excluded.
On pfSense, there’s no “out-of-the-box” TLS interception: you have to piece it together, using Squid as an explicit proxy and SSL bump. It works, but you have to put it together yourself—which is both the strength (you understand every component) and the limitation (you’re the integrator) of the open-source approach.
ESNI/ECH Note: The encryption of the ClientHello (Encrypted Client Hello) is gradually eliminating plaintext SNI. Once it becomes widespread, SNI-based filtering will become obsolete, leaving only DNS and full decryption. Keep a close eye on this.
Layer 7: Where the Real Work Happens
Filtering at Layer 7 means examining the content once everything else has been parsed (and decrypted, if applicable).
This allows for capabilities that no lower layer can provide:
- URL filtering: block
example.com/malware/but allowexample.com/support/. On pfSense, this is handled by Squid + SquidGuard. - IDS/IPS: detect an exploit attempt within the payload. This is Suricata (or Snort) in inline or legacy mode, using ETOpen + Snort GPLv2 rules as a free foundation.
- DNS filtering: intercept the request and respond on behalf of the server. This is the principle behind pfBlockerNG DNSBL—and it’s the most cost-effective of all L7 filtering methods: virtually free in terms of CPU usage, yet highly effective, because it blocks the traffic before a TCP session even exists. This is exactly the mechanism described in the Layer 7 article on DNS: we act at the name resolution level, at the very top of the stack, to prevent the rest from ever establishing a connection.
A classic and robust setup in pfSense involves stacking pfBlockerNG first (GeoIP at Layer 3, DNSBL at Layer 7), Suricata next (payload inspection), and pf rules last (the stateful backbone). Each layer filters what concerns it, at its own cost.
The real pipeline isn’t 1 → 7
This is the point I’ve been trying to make from the start, and the one that distinguishes an academic understanding from an operational one.
Intuitively, we imagine: the packet arrives, we parse Layer 2, then 3, then 4, then 7, and at each step we can say no. That’s wrong.
Consider application-level filtering via an IDS. For Suricata to recognize that a flow is BitTorrent, it must have already let the first packets of the session through—otherwise, it has nothing to analyze. The decision to block therefore comes after the fact, on a session that has already begun. You don’t block before identifying—you interrupt once it’s been identified.
This is where the confusion arises: “My Suricata rule blocks BitTorrent, but I still see traffic in the logs.” Of course: the first packets were necessary to determine that it was BitTorrent.
Direct and counterintuitive consequence:
> Layer 7 information (the application identity) is required to apply the decision—but it’s only available AFTER traffic has been allowed through. The firewall therefore makes its decision based on a session that’s already begun, not on the first packet.
Another, more insidious pitfall: the order of the rules. pf evaluates using last-match by default (the last rule that matches wins), whereas many other engines use first-match. A rule that’s too broad and placed in the wrong spot undermines your intended behavior. This is a classic pitfall—and an endless source of “my rule isn’t matching.”
Common Mistakes
Filtering too high up what can be filtered lower down. Blocking a country via application inspection when a Layer 3 geolocation rule (pfBlockerNG GeoIP) does the job for 1% of the CPU. Always filter at the lowest layer necessary.
Believing that the port defines the service. Anything goes on port 443 these days. A “allow TCP/443” rule does not allow “the web”: it allows anything someone has managed to wrap in TLS. That is precisely the purpose of application inspection.
Block all ICMP. You’re breaking PMTUD. Symptoms: small packets are OK, large packets disappear, sessions freeze. Diagnosis is impossible without understanding the mechanism—see the Layer 3 article.
Forgetting that state information isn’t re-evaluated. You modify a rule, nothing changes, and you conclude that the rule is wrong. Clear the state (pfctl -k).
Enabling deep inspection everywhere at once. Your CPU goes through the roof, pinning breaks a dozen applications, and you spend the week writing exemptions. Deploy it by network segment, gradually, with exemptions prepared in advance.
Confusing pfSense CE and pfSense Plus. Ethernet Rules, function order, certain packages: tutorials don’t always specify these differences. Check the edition before looking for a menu that doesn’t exist.
Believing that a firewall eliminates the need for a network architecture. Even the best firewall in the world can’t see traffic that doesn’t pass through it. If your L3 switch routes between VLANs, your firewall is blind to that traffic. Segmentation comes before filtering.
Key Takeaways
When it comes to firewalls, the OSI model isn’t a study guide—it’s a cost matrix. Each additional layer examined adds precision but comes at the cost of CPU usage, latency, and operational complexity. A good set of rules is one where every decision is made at the lowest possible layer.
The second lesson, which is less comfortable: the actual pipeline of a modern firewall blithely violates the order of the layers. It consults a state table (L4) before checking a rule; it lets packets through to identify an application (L7) before blocking it. The OSI model describes what the device reads; it does not describe the order in which it makes decisions. Confusing the two is the source of half of all “my rule isn’t working” support tickets.
And the third, which wraps up the OSI series: each layer of the stack we’ve covered—from the cable to the application—corresponds to a possible decision point in a firewall. Understanding the OSI model isn’t about studying for an exam—it’s about knowing, when faced with a traffic flow to block, at which layer to place the filter. That’s the entire series condensed into a single skill.
Sources and References
- pfSense — official documentation (Netgate): docs.netgate.com/pfsense
pf— OpenBSD packet filter (the reference engine used in pfSense): openbsd.org/faq/pf- Suricata — IDS/IPS engine: docs.suricata.io
- pfBlockerNG (GeoIP, DNSBL): docs.netgate.com — pfBlockerNG
- RFC 8446 — TLS 1.3 (SNI, ClientHello): datatracker.ietf.org/doc/rfc8446
- RFC 9293 — TCP (states, stateful): datatracker.ietf.org/doc/rfc9293
- Encrypted Client Hello (the future of SNI): datatracker.ietf.org — TLS ECH
- To understand the context of each layer: the complete OSI series on Aperture Zone.
This article concludes the series on the OSI model: after building the stack layer by layer, we saw how a single device traverses the entire stack to make a decision.