Firewall filters containing match conditions with Layer 4 header elements, such as TCP/UDP ports, may unintentionally drop IP packets when they are fragmented.
Firewall filters containing match conditions with Layer 3 header elements only, such as source/destination IP address, CoS, protocol field or any other field contained in the IP header, are not impacted by fragmentation.
This article shows some typical use cases and describes possible mitigation techniques.
Time of Log: 2021-03-05 11:10:11 GMT, Filter: pfe, Filter action: discard, Name of interface: ae55.751, Name of protocol: UDP, Packet Length: 1448, Source address: 10.238.14.250, Destination address: 10.151.121.24
Fragmentation is applied on the complete IP payload, which includes Layer 4 headers as well. Since both TCP and UDP source and destination ports occupy the first 4 bytes of the Layer 4 header, they will always fall within the first IP fragment (0) only, while other fragments won't have that information. Therefore, if a fragmented IP packet is handled by a firewall filter matching on TCP or UDP ports, the filter will actually affect the first fragment only. Any successive fragments will skip the filter term where TCP/UDP ports are handled. For instance, if the firewall filter below is used as input filter on a router interface:
[edit firewall family inet] filter protect-intranet { term intranet-8080 { from { source-address { 198.51.100.8/29; 198.51.100.16/29; } protocol tcp; destination-port 8080; } then accept; } term drop-the-rest { then discard; } }
TCP packets sourced from 198.51.100.9 , using 8080/tcp as destination port entering that router interface will pass the term intranet-8080 , if they are not fragmented. Otherwise, if fragmentation occurs only the first fragment, containing TCP source/destination port information will pass the term intranet-8080 , while all successive fragments not having port information will simply be dropped, because they will not pass the term intranet-8080 and they will match the next one ( drop-the-rest ), having discard as action. Or even funnier - when the payload of the frame contains the number 8080 (hex: 0x1f90 ) in the 3rd and 4th byte of the payload (e.g. if the payload of the fragment begins with: 11 23 1F 90 ) the packet will be allowed, since the filter will recognize this as TCP packet with destination port 8080.
198.51.100.9
8080/tcp
intranet-8080
drop-the-rest
discard
0x1f90
11 23 1F 90
For example, Cisco Access Control Lists containing TCP/UDP ports, treat fragmented IP packets differently. Instead of dropping fragments bluntly, they let the fragments through as long as the ACL rule matches on Layer 3 headers (i.e. if the source/destination IP address and protocol field within the IP packet matches the ACL rule). See article Access Control Lists and IP Fragments on the Cisco website for more details. For instance, the Junos Firewall Filter protect-intranet shown above translates to Cisco ACL shown below:
protect-intranet
ip access-list extended protect-intranet permit tcp 198.51.100.8 0.0.0.7 any eq 8080 permit 198.51.100.16 0.0.0.7 any eq 8080 deny ip any any
If a fragmented TCP packet sourced from 198.51.100.9 , using 8080/tcp as destination port comes into the router interface where the ACL above is defined as input filter, the first fragment will pass the first rule of the ACL, while any subsequent fragments, not having any TCP port information, will still match the first rule, because all those fragments will still have 198.51.100.9 as source IP address and Cisco ACL will let them through.
IP Fragmentation and Reassembly is a mechanism that is more than 40-years old. Originally standardized by RFC791 , it was intended for use by the networks of that time, which was not capable of handling large packets. The prescribed minimum MTU of 576 bytes required by IP was required back then as well. Networks have evolved since. Today, networks can easily handle packets as large as 9100 bytes. Some Juniper Networks routers can even handle packets up to 16,000 bytes in length.
On the other hand, to avoid fragmentation, many mechanisms have been developed, such as:
tcp-mss
Finally, the operational experience of many large operators revealed that IP fragmentation introduces severe fragility and security risks to various network applications. One of the oldest vulnerabilities of fragmentation are described in RFC1858 , which documents some fragmentation-related attacks. Aside from security issues, fragmentation impacts other network mechanisms, such as Equal Cost Multi Path (ECMP), Link Aggregation Groups (IEEE 802.3ad LAGs), load balancing, stateless firewalls, etc. Most of those problems have recently been documented in RFC8900 . Therefore, the networks should ideally be designed to avoid fragmentation at all costs. MTU sizes in the network should be set to the same, consistent value throughout the network. The core layer of the network should make use of jumbo frames if possible. PMTUD or TCP MSS clamping should be in place for all end-to-end applications. When IP filters or firewalls are in place within the network, care must be taken not to block ICMP traffic; and if there is a need to filter ICMP traffic, ensure that ICMP Destination Unreachable / Fragmentation Needed packets (ICMP Type=3, Code=4) are allowed to flow unblocked through the network.
If your network applications strongly rely on IP fragmentation or you are dealing with legacy networks not capable of handling PMTUD properly, IP fragmentation will occur on a regular basis. In this case, firewall filters containing Layer 4 headers (e.g. TCP/UDP ports) should be designed to ensure proper handling of fragments not containing TCP/UDP port information. For example, for firewall filter protect-intranet shown in the example above, a quick fix would be to allow any non-zero fragments having the same source/destination IP addresses, regardless of TCP/UDP port information. This leads to the following configuration:
[edit firewall family inet] filter protect-intranet { term intranet-8080 { from { source-address { 198.51.100.8/29; 198.51.100.16/29; } protocol tcp; destination-port 8080; } then accept; } term non-zero-fragments { from { source-address { 198.51.100.8/29; 198.51.100.16/29; } protocol tcp; is-fragment; # Matches fragments with non-zero offset
} then accept; } term drop-the-rest { then discard; } }
While the filter above provides a viable solution, it may still expose the networks to the Tiny Fragment Attack, described in RFC1858 , where two mitigation options are proposed. The so-called indirect method, still in use by many vendors today, blocks any fragments having offset value of 1 and IP protocol field of TCP. RFC1858 provides a reasoning for this as follows:
Having the above in mind, this leads to the following configuration (Note: RFC791 defines Fragment Offset as a 12-bit field, representing the offset in the payload where the fragment starts, multiplied by 8; hence, the maximum value is 8191, allowing addressing offsets up to the maximum length of the IP packet, being 65535 bytes):
[edit firewall family inet] filter protect-intranet { term intranet-8080 { # Matches non-fragmented packets and fragments with FO=0 from { source-address { 198.51.100.8/29; 198.51.100.16/29; } protocol tcp; destination-port 8080; # This information is contained in fragment zero, so we are fine } then accept; } term non-zero-fragments { from { source-address { 198.51.100.8/29; 198.51.100.16/29; } protocol tcp; fragment-offset 2-8191; # FO=1 is dropped, only FO>1 and up are accepted
RFC3128 provides an additional mitigation measure for Tiny Fragment Attacks, originally described in RFC1858 . If the first fragment doesn't contain the full TCP header the packet should be dropped as well. The minimum size of the TCP header is 20 bytes. Added to the IP header length of 20 bytes, this gives the total minimum length of a TCP packet being 40 bytes. Any TCP packet having smaller size is apparently illegal. This gives the filter as follows:
[edit firewall family inet] filter protect-intranet { term deny-tiny-fragments { from { packet-length [ 0-39 ]; first-fragment; protocol tcp; } then discard; } term intranet-8080 { # Matches non-fragmented packets and fragments with FO=0 from { source-address { 198.51.100.8/29; 198.51.100.16/29; } protocol tcp; destination-port 8080; # This information is contained in fragment zero, so we are fine } then accept; } term non-zero-fragments { from { source-address { 198.51.100.8/29; 198.51.100.16/29; } protocol tcp; fragment-offset 2-8191; # FO=1 is dropped, only FO>1 and up are accepted } then accept; } term drop-the-rest { then discard; } }
2021-06-14: Article rewritten to provide more clarity and reference to the latest best operational practices.