ASA

TCP Normalization on the ASA: The Silent Connection Killer

ASA show asp drop counters from the TCP normalizer
In: ASA, CCIE, Labs, Network Security

Some of the hardest firewall tickets sound like ghost stories. A connection that works fine between two hosts dies the moment it crosses the ASA, both endpoints swear nothing is wrong, and there is no deny in any ACL. Nine times out of ten the culprit is TCP normalization, the ASA's silent enforcer of TCP correctness, and the only evidence it leaves is a counter most people never look at. This article is part of our Cisco ASA series, and it turns that ghost into something you can see, using a real tcp-map and the real accelerated-security-path drop counters from an ASAv 9.24 in CML.

TCP normalization is a set of checks the ASA runs on every TCP segment to make sure the connection behaves like a proper, in-order, standards-compliant TCP flow. Packets that fail those checks are dropped. That is a good thing when the packets really are malformed or part of an attack, and a maddening thing when an overly strict normalizer drops traffic the endpoints considered perfectly valid. Either way, the box is doing exactly what it was told, and learning to read its drop counters is how you tell the difference.

What the normalizer checks

The ASA normalizes TCP by default, and you can tune it with a tcp-map. The map is a bundle of individual checks, each of which can drop, clear, or allow a particular kind of anomaly. A few of the common ones:

  • check-retransmission - verify that a retransmitted segment matches the original, catching retransmission-based evasion.
  • exceed-mss - decide what to do with segments larger than the negotiated maximum segment size, either allow or drop them.
  • urgent-flag - allow or clear the TCP URG pointer, which is frequently abused and rarely legitimately needed.
  • tcp-options - control which TCP options are permitted, cleared, or cause a drop.

You bundle these into a named tcp-map and then apply it through the Modular Policy Framework, exactly like every other ASA feature from our MPF walkthrough. The map is the action; MPF is how it reaches the traffic.

Building and applying a tcp-map

Here is a small normalizer that checks retransmissions and drops oversized segments, applied globally to all traffic.

tcp-map PLZ-TCP-NORM
  check-retransmission
  exceed-mss drop
class-map PLZ-TCP-CLASS
 match any
policy-map global_policy
 class PLZ-TCP-CLASS
  set connection advanced-options PLZ-TCP-NORM

The three layers are the ones you already know. The tcp-map defines the checks. The class-map with match any selects all traffic. The policy-map attaches the map to that class with set connection advanced-options, which is the specific action that plugs a tcp-map into MPF. Because we added it to global_policy, it now applies everywhere the global service-policy does.

Confirm it is live by reading the global service-policy and filtering to the connection settings.

FW1# show service-policy global | include Set connection|advanced
      Set connection policy:         drop 0  embryonic drop 0
      Set connection advanced-options: PLZ-TCP-NORM

The line Set connection advanced-options: PLZ-TCP-NORM is the confirmation that our tcp-map is bound and active on the global policy. If that line is missing, the map exists in the config but is not doing anything, the same "defined but not applied" trap that catches every MPF feature.

The star command: show asp drop

Here is the command that solves the ghost stories. The accelerated security path (ASP) is the ASA's fast-path forwarding engine, and it keeps a running tally of every packet it drops and why. TCP normalization drops land here, alongside a long list of other fast-path discard reasons. On our lab box, after ambient traffic, the counters looked like this:

FW1# show asp drop
  Invalid UDP Length (invalid-udp-length)          2
  First TCP packet not SYN (tcp-not-syn)           10
  TCP RST/FIN out of order (tcp-rstfin-ooo)         2

Every one of those lines is a packet the ASA dropped without any ACL involved, and each names its reason in the parentheses. This is the single most useful "why did my packet vanish" command on the box, because it is the one place the firewall admits to dropping traffic that no ACL touched. When a connection fails and the ACLs are clean, this is where you look next.

Why each counter fired

Read the reasons, because they teach you how the stateful engine thinks.

tcp-not-syn (10) is the headline. The ASA is stateful, which means it expects a TCP connection to begin with a SYN so it can build a state entry for the flow. If the very first packet the firewall sees for a flow is not a SYN, the ASA has no state for that connection and, by default, drops it as tcp-not-syn. This happens constantly in the real world: a connection that was established before the firewall booted, an asymmetric path where the return traffic arrives on the ASA but the initial SYN went another way, or a flow that failed over from another device mid-stream. All of them look identical to the ASA, a first packet with no SYN and no state, and all of them get dropped. Ten such packets is completely ordinary ambient noise on any segment.

tcp-rstfin-ooo (2) catches RST or FIN segments that arrive out of order, outside the valid sequence window for the connection. Out-of-order teardown packets can be a sign of an evasion attempt or simply of a messy network, and the normalizer discards them rather than letting them tear down a connection they should not.

invalid-udp-length (2) is not TCP at all; it is the ASP dropping UDP datagrams whose declared length does not match reality, a basic malformed-packet check. It is in the same output because show asp drop is the catch-all for fast-path discards, which is exactly why it is so useful: one command, every silent drop reason, in one place.

tcp-not-syn
Count: 10
First packet of a flow was not a SYN, so the stateful ASA had no state entry. Points at asymmetry or a pre-existing connection.
tcp-rstfin-ooo
Count: 2
RST or FIN arrived outside the valid sequence window. Guards against out-of-order teardown and evasion.
invalid-udp-length
Count: 2
UDP datagram whose declared length did not match the packet. A basic malformed-packet check, not TCP related.

Default normalizer versus a custom tcp-map

It is worth being clear that you get normalization whether or not you ever write a tcp-map. The ASA applies a default normalizer to every TCP connection, and that default is already responsible for the tcp-not-syn drops above; no configuration of ours caused them. What a custom tcp-map does is let you tighten or loosen specific checks beyond the defaults, for example adding check-retransmission or changing how oversized segments are handled.

This distinction matters when you are troubleshooting, because engineers often assume that with no tcp-map configured the ASA is not normalizing at all. It is. If you genuinely need the firewall to accept a mid-stream flow that begins with something other than a SYN, the answer is not to remove a tcp-map you never created; it is to configure TCP state bypass for that specific traffic, which tells the ASA to forward the flow without building or enforcing full TCP state. That is a deliberate, scoped exception, and it is the correct tool for asymmetric designs rather than weakening the normalizer for everything.

The silent connection killer

Now the part that makes this feature worth a whole article. Because normalization runs in the fast path and drops packets without generating a per-packet deny that shows up where people usually look, an over-aggressive tcp-map can quietly break connections that look completely healthy from both endpoints. The client sends, the server never sees it, and neither machine logs anything unusual because from their point of view the packet was simply lost. There is no deny message on a screen anyone is watching. The only evidence that the firewall did it is a number climbing in show asp drop.

This is why TCP normalization earns the nickname "silent connection killer". Turn on a strict option like dropping all segments that exceed MSS, or clearing an option some legitimate application actually depends on, and you can introduce failures that survive hours of endpoint troubleshooting because nobody is looking at the firewall's ASP counters. Even the default normalizer, before you configure a single custom option, drops tcp-not-syn packets, which is exactly why asymmetric-routing designs and firewall-insertion projects so often surface mysterious connection failures on day one.

The lesson is procedural. When a connection dies crossing the ASA and the ACLs are clean, run show asp drop before you do anything else. Note the counters, reproduce the failure, run it again, and see which counter moved. The counter that increments is your answer. If it is tcp-not-syn, you are looking at a state or asymmetry problem, not a policy problem, and the fix is on the routing or the TCP-state-bypass side, not in an ACL. If it is a normalization reason tied to a custom tcp-map option, you have found an over-strict setting, and you can relax that specific check rather than tearing the whole map out.

Tuning without breaking things

A tcp-map is a scalpel, so use it like one. Add checks one at a time and watch the corresponding show asp drop counter before and after, so you know exactly what each option is dropping in your environment. If a counter climbs faster than you expected, you have caught a legitimate flow in the net, and you can decide whether to loosen the option or fix the traffic that triggered it. The goal is a normalizer strict enough to catch genuinely malformed or evasive TCP, but not so strict that it drops the slightly-imperfect-but-harmless segments real applications routinely send.

Where this all sits in the ASA's processing order matters too, because normalization happens in the fast path after the connection is established, not at the ACL stage. That is precisely why an ACL check can pass while a packet still vanishes: the two live at different points in the flow. Our ASA packet flow article maps that ordering out, and it is the model that finally makes show asp drop feel predictable rather than mysterious.

Key Takeaways

  • TCP normalization silently drops malformed or out-of-state TCP. It is on by default and tunable with a tcp-map applied via set connection advanced-options.
  • show asp drop is the star command. It is the one place the ASA records fast-path drops that no ACL caused, the definitive "why did my packet vanish" tool.
  • Read the drop reasons. tcp-not-syn 10 means a first packet arrived with no SYN and no state (asymmetry or a pre-existing flow); tcp-rstfin-ooo 2 caught out-of-order teardown; invalid-udp-length 2 is a malformed UDP check.
  • The silent killer is real. An over-strict tcp-map drops packets that look fine to both endpoints, leaving no evidence except a climbing ASP counter.
  • Diagnose by watching the counter move. Reproduce the failure and see which show asp drop line increments; that reason is your root cause.

Once you know show asp drop exists, a whole category of "impossible" firewall problems becomes routine. Continue through the Cisco ASA series, and pair this with the ASA packet flow article to see exactly where in the processing order these silent drops happen.

Written by
More from Ping Labz
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Ping Labz.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.