ICMP Inspection on the ASA
Last Updated: [last-modified] (UTC)
Introduction
ICMP’s primary functions are error reporting at layer-3, and troubleshooting. In fact, two of the most useful networking tools, ping and traceroute, rely upon it. However, it is tightly bound to the IP stack at layer-3, so it’s no surprise that the ASA firewall treats it differently to other protocols like TCP or UDP.
When a packet encapsulating a TCP segment or UDP datagram reaches the ASA, it is categorised into a flow based on the protocol, source and destination IP, and port numbers. The ASA maintains a data structure to track these flows, which means that only the first packet in the flow is evaluated by the packet filter. If the first packet is allowed by the packet filter, the rest of the packets in the flow (an ‘existing connection’) are automatically allowed as well.
On the ASA, ICMP is handled differently than TCP or UDP. By default, the ASA does not track an ICMP session, making it stateless. Being stateless, a return ICMP packet (such as an echo-reply) is not automatically allowed through the ASA, and will be dropped unless an ACL specifically allows it.
Keep in mind though, that this is the default behaviour of the ASA, which can be changed. The ASA can track an ICMP session by inspecting ICMP packets. This results in an ICMP session being tracked, which allows response packets back through.
ACL Evaluation
Inspect ICMP
Consider the topology below. R1 and R2 are separated by an ASA with default security configuration. The inside interface has security-level 100, and the outside interface has a security level of 0. All devices have appropriate IP addressing and routes.
With this configuration, consider the traffic flow when R1 tries to ping R2:
- R1 creates an ICMP echo packet, and forwards it to the next-hop, the ASA
- The ASA determines that the inside interface is the ingress, and the outside interface is the egress
- As the inside inderface has a higher security level than the outside, the packet is allowed to pass
- R2 receives the echo packet, and creates an echo-reply, which it sends to the next-hop (the ASA)
- The ASA determines the ingress and egress interfaces
- The packet is trying to travel from a lower security-level interface to a higher one, so the packet is dropped
Now, turn on ICMP inspection:
policy-map global_policy class inspection_default inspect icmp
The process now behaves a little differently:
- R1 creates an ICMP echo packet, and forwards it to the next-hop, the ASA
- The ASA determines that the inside interface is the ingress, and the outside interface is the egress
- As the inside inderface has a higher security level than the outside, the packet is allowed to pass
- The ASA begins to track this ICMP session
- R2 receives the echo packet, and creates an echo-reply, which it sends to its next-hop (the ASA)
- The ASA determines that this is part of an existing session, and allows the echo-reply through
What if R2 tries to ping R1 now? When R2 sends the echo packet to the ASA, the ASA will still drop the packet (due to the security-level configuration), and will not begin to track the session.
If ICMP is required from the outside interface, an ACL needs to be configured to allow it through.
Best Practice
If ACLs are going to be configured to allow ICMP, it is highly recommended that ICMP inspection is also turned on.This allows the ASA to match ICMP payload and sequence numbers, to confirm that a reply does belong to a legitimate session, rather than being a forged packet.
Inspect ICMP Error
Think of this scenario; There is a device outside the ASA that needs to run a traceroute to something inside the ASA, as shown in the diagram below.
The ASA is configured with ICMP inspection, and allows traceroute (ICMP and UDP) from the outside. Additionally, the host that R2 wants to traceroute to (192.168.100.100) has an object NAT applied, translating its IP to 200.200.200.200
When R2 starts a traceroute to 200.200.200.200, the ouput looks like this:
R2#trace 200.200.200.200 Type escape sequence to abort. Tracing the route to 200.200.200.200 VRF info: (vrf in name/id, vrf out name/id) 1 10.10.20.10 4 msec * 4 msec 2 200.200.200.200 9 msec 8 msec 8 msec 3 200.200.200.200 9 msec 8 msec 10 msec 4 200.200.200.200 9 msec 8 msec 12 msec 5 200.200.200.200 9 msec * 12 msec
In this scenario, each hop along the path is showing as the translated IP of the end host. When traceroute runs, each hop along the path sends an ICMP time-exceeded error message back to the host that started the traceroute. This is how the original host builds a list of hops in the path.
But if this is the case, why doesn’t the real IP addresses of the hops show in the traceroute? After all, they don’t have NAT applied to them.
The answer is in the way the ASA handles the time-exceeded packets. When the ASA receives one of these packets, it looks into the ICMP payload. All ICMP error messages include part of the original packet that caused the error in the payload.
So when the ASA examines the ICMP packet, it gets the original destination from the payload. in this case it would see it as 192.168.100.100, as this is on the inside, where NAT is not applied. The ASA then changes the source IP of the time-exceeded packet to 192.168.100.100, which later gets translated to 200.200.200.200.
This happens for each time-exceeded message from all the hops in the path. The purpose behind this is to save resources by not assigning additional NAT resources to each packet.
This is the default ASA behaviour, and can be changed like this:
policy-map global_policy class inspection_default inspect icmp error
This enables the ASA to inspect ICMP error messages, such as time-exceeded. This means that the source IP is left as it is.
The result of this configuration is for the ASA to allocate NAT resources to each of the time-exceeded packets, which allows traceroute to show each hop correctly:
R2#traceroute 200.200.200.200 Type escape sequence to abort. Tracing the route to 200.200.200.200 VRF info: (vrf in name/id, vrf out name/id) 1 10.10.20.10 5 msec * 3 msec 2 10.10.10.10 8 msec 8 msec 7 msec 3 10.10.101.10 8 msec 11 msec 8 msec 4 10.10.100.10 10 msec 8 msec 13 msec 5 200.200.200.200 10 msec * 13 msec
The Exception
There is an exception to the behaviour above, and that is when port-overloading is used. Port-overloading is a dynamic twice-NAT, where multiple IP’s are ‘overloaded’ onto a single or pool of translated IP’s. This is often done to allow inside hosts with private RFC 1918 addressing to browse the internet, while not requiring a public IP for each one. This could be configured something like this:
object network Overload host 1.2.3.4 nat (inside,outside) after-auto source dynamic any Overload
When this happens, even if inspect icmp error is enabled, a traceroute will once again show every hop as the translated IP of the destination host.
Note, each hop shows as the destination host’s translated IP, not the IP in the port-overload rule. So in the example above, the hops show as 200.200.200.200, not 1.2.3.4.
Author’s Note
After logging this issue with Cisco, bug number CSCvc12093 has been created.This has been confirmed to be a limitation with inspection engine. Cisco may release an enhancement to a future version to resolve this.