Flexible Packet Matching is a new feature that allows for granular packet inspection in Cisco IOS routers. Using FPM you can match any string, byte or even bit at any position in the IP (or theoretically non-IP) packet. This may greatly aid in identifying and blocking network attacks using static patterns found in the attack traffic. This feature has some limitation though.

a) First, it is completely stateless, e.g. does not track the state/history of the packet flow. Thus, FPM cannot discover dynamic protocol ports such as use by H.323 or FTP nor cannot it detect patterns split across multiple packets. Essentially, you are allowed to apply inspection per-packet basis only.

b) Additionally, you cannot apply FPM to the control-plane traffic, as the feature is implemented purely in CEF switching layer. Fragmented traffic is not assembled for matching, and the only inspected packet is the initial fragment of the IP packet flow.

c) IP packets with IP options are not matched by FPM as well, because they are punted to the route processor.

d) Lastly, this feature inspects only unicast packets and does not apply to MPLS encapsulated packets.

Configuring an FPM filter consists of a few steps.

(1) Loading protocol headers.
(2) Defining a protocol stack.
(3) Defining a traffic filter.
(4) Applying the policy & Verifying

Let’s look at every of these steps in depth.

Loading a PHDF (optional). PHDF stands for Packet Header Definition file. Those files use XML syntax and define the structure of various packet headers, such as Ethernet, IP, TCP, UDP and so on. They are very helpful in making filtering more structured, as with the PHDFs loaded you may filter based on the header field names and their values, instead of matching fixed offsets in the unstructured packet body. You may load the files into the router’s memory using the command load protocol . The PDHF files could be manually created using the simple XML syntax or downloaded from CCO. Since IOS version 12.4(15)T, four basic PHDFs are included in the IOS code and located at the virtual path system:fpm/phdfs. You could load the files directly from there. Defining a custom PHDF requires understanding of protocol header formats and field values along with basic XML formatting, which is beyond the scope of this document.

Defining a protocol stack (optional). This step uses the PDHFs loaded previously and allows specifying the protocol headers found in the traffic you want to inspect. Using the protocol stack definition induces structure in the packets being inspected. This allows for filtering based on header field values and specifying offsets in the packet relative to the header fields. Additionally, you may define various protocol stacks (e.g. UDP in IP, UDP in GRE in IP) and reuse the same access control policy with various stacks.

You define the protocol stack using the command class-map type-stack match-all <NAME>. This class-map it is always of type “match-all” and consists of a series of the match entries. Every match entry should specify a protocol name defined in loaded PHDFs, a header field value and the next protocol name found in stack. Look at the sample below – it defines the series of headers: TCP in IPIP tunnel headed (protocol 4) encapsulated in IP and in the Ethernet.


The order of the match statements is important – it defines the actual header stacking along with the “layer x” keywords. The layers are counted bottom up starting at the stack bottom and “layer 1” is the default. The other important thing is that every next match statement should define the protocol specified in the next clause of the previous match statement. This is a basic consistency check illustrated using the arrows in the diagram. Notice that most of the times you could just use a single-protocol stack, as you commonly deal with TCP or UDP protocols encapsulated in IP headers.

Now a few words about the match operator. This command uses the syntax match field <PROTO> <header-field> <operator> <value> . It is important to stress that the protocol and header fields are learned dynamically from PHDFS and this allows for high configuration flexibility. The operator could be “eq”, “neq”, “range”, “string”, “regex”, “lt” and “gt”. The operator names are self-explanatory, but the “eq” operator also supports a special “mask” parameter. The mask is interpreted as a bitmap, where “1” means the corresponding bit in the value could be any of “0” or “1” and “0” means the respective bit is “locked”.

By default, the protocol stack is matched stating AFTER the data-link level header. That is, the first header defined in the stack is match against the header going after the L2 header. In real life, this is commonly the IP header. However, sometime you may want to match the layer2 header fields as well, e.g. Ethernet addresses. To make the protocol stack match starting at L2 level, use the command stack-start l2-start.

At this point, having just stack class-maps you may already apply traffic filtering to the packets matching the configured stack. To accomplish this, create a policy-map of type “access-control” and assign a stack class to the policy-map. For example:

class-map type stack TCP_IN_IP_IN_ETHER
 statck-start l2-start
 match field ETHER type eq 0x800 next IP
 match field layer 2 IP protocol eq 0x6 next TCP
policy-map type access-control DROP_TCP_IN_IP_IN_ETHER

There is basically just a a couple of actions available with the access-control policy-maps: drop and log. You may also use the send-response command under a class to make the router send an ICMP unreachable response.

Of course, using just stack class-maps might be inflexible, as you cannot match the packets payload. You may however use this for filtering based on addresses, protocol flags and so on.

Defining a traffic filter. Traffic filter is defined by means of special class-map of type “access-control” and configuring a respective policy-map of the same type. Using this type of class-maps you can match the protocol field values using the command match field <PROTO> if the respective protocol’s PHDF has been loaded. If you match the protocol fields, then the policy-map using the newly defined access-control class-map must be nested under the stack-type class-map defining this protocol. We’ll see an example later.

In addition to matching the protocol header fields, you can match the packet payload at a fixed offset against a pre-defined value, value range, string or regular expression. You may base the offset off the defined protocol header field (e.g. +10 bytes from TCP flags) using the command match start <PROTO> <FIELD-NAME> offset <OFFSET> size <SIZE>. For example:

match start TCP checksum offset 100 size 1 ...
match start IP version offset 0 size 1 ...

Of course, this type of offset “basing” is only possible if the respective protocol definition has been loaded and the containing stack-type class map has this protocol defined.

Irrespective of the protocols loaded/defined, you may base the offset from the L2 or L3 packet starts (absolute offsets). For example if the packet is IP in Ethernet, than L2-start is the first bit of the Ethernet header and L3-start is the first bit of the IP header. The command syntax is match start {l2-start|l3-start} offset <OFFSET-BYTES> size <SIZE-BYTES> <operator> <value>. The command specifies the offset in bytes from the selected start. If the size if less than or equal to 4 bytes, you can use the “eq, neq, lt, gt” operators in addition to “regex” and “string”. This allows for per-bit matching using the “eq” and “mask” operators combination. For example:

match start l3-start offset 0 size 1 eq 0x2 mask 0x3
match start l2-start offset 36 size 5 string ABCDE

Creating an access-control policy-map. You have two options here. You may create a simple access-control policy-map, which has just the basic access-control assigned, without nesting. For example:

class-map type access-control PASSWORD
 match start l3-start offset 0 size 100 regex ”.*[pP][aA][sS][wW].* ”
policy-map type access-control DROP_PASSWORD

When using this simple non-nested syntax, you may only use the absolute offsets with the command match {l2-start|l3-start} and cannot reference any protocol header fields.

You may use a more flexible approach, by nesting the filtering policy under a stack-type class-map configured in containing policy-map. This allows for using protocol headers in filtering policy or basing the offsets from the packet header fields. Notice that the nested filtering policy may only use the protocol headers defined in the containing stack class-map. Here is an example that looks for string “TEST” in TCP packets:

class-map type stack TCP_IN_IP
 match field IP protocol eq 0x6 next TCP

! Protocol fields matched in the filtering policy
! must be for the protocols defined in the stack
! class-map
class-map type access-control match-any FILTER_CLASS
 match start TCP payload offset 0 size 4 string TEST
policy-map type access-control FILTER_POLICY
policy-map type access-control STACK_POLICY
  class TCP_IN_IP
  service-policy FILTER_POLICY

Applying the traffic filtering policy. Finally, the access-control policy map should be applied to an interface either inbound or outbound using the interface-level command service-policy type access-control {input|output} <NAME>. This could be a simple non-nested policy using just stack-type class-maps or access-control class-maps or more advanced, stack and nested access-control policy-map.

And now, let’s look at a sample task. We would like to filter ICMP echo packets with the stings “AAAA” inside them, encapsulated inside IPIP tunnel. Here is the diagram for the testbed scenario:


And now, the configuration to be applied to R6′s Fa0/0:

load protocol system:fpm/phdf/ip.phdf
load protocol system:fpm/phdf/icmp.phdf
load protocol system:fpm/phdf/ether.phdf

! Three protocols in stack: Ether/IP/IP
class-map type stack match-all ICMP_IN_IPIP_IN_ETHER
 stack-start l2-start
 match field ETHER type eq 0x800 next IP
 match field layer 2 IP protocol eq 4 next IP
 match field layer 3 IP protocol eq 0 mask 0xFF next ICMP

! Access-Control Policy, matches on the ICMP payload
class-map type access-control match-all ICMP_ECHO_STRING_AAAA
 match field ICMP type eq 8
 match start ICMP payload-start offset 0 size 256 regex ".*[aA][aA][aA][aA].*"

! Access-Control Policy-Map
policy-map type access-control LOG_ICMP_ECHO

! Outer policy map, matches the protocol stack.
policy-map type access-control STACK_POLICY
  service-policy LOG_ICMP_ECHO

Next, a basic verification of our configuration. We send ICMP packets loaded with AAA characters (ASCII code 0×41) from SW1 to SW2 and see if they match the policy-map in R6:

Rack1SW1#ping data 4141          

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to, timeout is 2 seconds:
Packet has data pattern 0x4141
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/3/4 ms
[Resuming connection 6 to R6 ... ]

Rack1R6#show policy-map type access-control interface fastEthernet 0/0

  Service-policy access-control input: STACK_POLICY

    Class-map: ICMP_IN_IPIP_IN_ETHER (match-all)
       5 packets, 670 bytes
      5 minute offered rate 0 bps
      Match: field ETHER type eq 0x800 next IP
      Match: field layer 2 IP protocol eq 4 next IP
      Match: field layer 3 IP protocol eq 0 mask 0xFF next ICMP

      Service-policy access-control : LOG_ICMP_ECHO

        Class-map: ICMP_ECHO_STRING_AAAA (match-all)
           5 packets, 670 bytes
          5 minute offered rate 0 bps
          Match: field ICMP type eq 8
          Match: start ICMP payload-start offset 0 size 256 regex ".*[aA][aA][aA][aA].*"

        Class-map: class-default (match-any)
          0 packets, 0 bytes
          5 minute offered rate 0 bps, drop rate 0 bps
          Match: any 

    Class-map: class-default (match-any)
      0 packets, 0 bytes
      5 minute offered rate 0 bps, drop rate 0 bps
      Match: any

A quick final notice on use the logging statement with the access-control policy-maps. The rate-limiting mechanism for those is the same as used for the access-lists, and configured using the commands ip access-list log-update|logging.

Further Reading:

Flexible Packet Matching
FPM Deployment Guide

The configuration in this post have been implemented and tested using IOS 12.4(24)T on a 2811 ISR. You may encounter bugs and errant behavior in earlier IOS releases, and I suggest you always using the latest image for FPM implementation.

About Petr Lapukhov, 4xCCIE/CCDE:

Petr Lapukhov's career in IT begain in 1988 with a focus on computer programming, and progressed into networking with his first exposure to Novell NetWare in 1991. Initially involved with Kazan State University's campus network support and UNIX system administration, he went through the path of becoming a networking consultant, taking part in many network deployment projects. Petr currently has over 12 years of experience working in the Cisco networking field, and is the only person in the world to have obtained four CCIEs in under two years, passing each on his first attempt. Petr is an exceptional case in that he has been working with all of the technologies covered in his four CCIE tracks (R&S, Security, SP, and Voice) on a daily basis for many years. When not actively teaching classes, developing self-paced products, studying for the CCDE Practical & the CCIE Storage Lab Exam, and completing his PhD in Applied Mathematics.

Find all posts by Petr Lapukhov, 4xCCIE/CCDE | Visit Website

You can leave a response, or trackback from your own site.

26 Responses to “Understanding Flexible Packet Matching”

  1. Paul Stewart says:

    Once again, a great article and explanation. For the life of me, I don’t understand why Cisco cannot use some simple, real world examples to demonstrate the use of their new features. I have found this RSS feed invaluable in my Security studies. Thanks Petr.

  2. VirtualChris says:

    FYI, FPM is broken in the release 12.4(15)T. You must use 12.4(15)T9 on the ISR platform.

    The 6500 series uses different code on the Sup32/PISA modules.

    The documentation is confusing because the code is written for both the 6500 and ISR platforms although there are two different development teams. Some commands work on the 6500 series and others work on the ISR’s. I tried escalating to get two sets of docs written, but I had to get back to my real job…swimming upstream is really tiring… :-/

    We used identical configurations on our 6500′s as our ISR’s and that’s when we ran into the bug with the phdf’s on the ISR’s. Once 12.4(15)T9 came out, we could actually run FPM without the routers crashing, but we needed to use the ‘match start offset’ instead of using the phdf’s.

    We are using it for simple TTL filtering to keep rougue wireless off the network (port security keeps the layer 2 stuff of the network and TTL filtering keeps the layer 3 stuff off).

    I can’t wait until you start a technical conversation about Flexible NetFlow. These 2 technologies are the greatest thing since sliced bread-


  3. Piotr Kaluzny says:


    Can you expand this Rouge AP TTL filtering concept a little bit?


  4. Chris Cugno says:

    Routers by nature decrement the TTL by 1 each time the packet transits a routed interface. If you do TTL filtering at the access layer, you should see the native TTL in the packet sent by the client OS.

    WinXP, for example, sends with a default TTL of 128, while a Linux box will send with a default TTL of 64. If you are looking at these values at the access layer, you should just see a bunch of 64′s and 128′s whizzing by. If you see a 63 or 127 come through, you can be fairly certain that someone has stopped by the local electronics store and picked up a wireless router (which will be giving out DHCP to the client machines, NATting, and decrementing the TTL) and connected it to your corporate network.

    We use FPM to identify these packets and drop them.


  5. VirtualChrisFan says:


    Man that’s so sweet. I liked the TTL filtering! One wonders how big is your CCIE burger..

  6. Steve says:


    In your TTL Filtering scenario, how do you know that the TTL isn’t decremented legitimately by another attached router? Do you exclude source MACs of all of your known routers or something?

    Sorry.. it must be more simple than this, and I’m having a mental block.. I shouldn’t try to think about this so early in the morning!! :-)


  7. Chris Cugno says:


    I know that the TTL is not being decremented legitimately because it is my network and the packets go where I tell them ;-) .

    Seriously, I am using FPM at the access layer (layer 2) at our hub site (on 6500′s), not in the dist or core. Our remote office connect into our core (at the hub location) and we have all sorts of TTL’s floating around in there. They are quite noisy! And I’m still not good with a calculator. :-)

    I have been planning to push FPM to the remote WAN edge (on the ISR platform), but that is when I ran into the bug with 12.4(15)T. Cisco resolved the issue with FPM in 12.4(15)T9, but I haven’t had time to push it out yet…so you still have time to crack our network. Just drive out to one of our remote offices and park your car out front. (I’m starting to change the passwords now)…

    @VC fan-

    Thanks, but I am still a no number… yet… My first attempt is booked… So hopefully I can get in an attempt or two before 4.0.


  8. Paul Stewart says:

    I wanted to mention that I have had issues with the following on dot1q–

    match field ETHER type eq 0×800 next IP

    I think the ETHER type is mapped to offset 96 which is ether type only when there is no dot1q header. I need to test further to validate this, but it may be that it would match to the TPID value of 0×8100.

  9. [...] was a CLND thread which i saw today morning, regarding FPM (Flexible packet matching ). There was some problem regarding matching the dot1q encapsulated packets in a trunk [...]

  10. [...] if you’re new to FPM, check THIS out. It’s an article by Petr Lapukhov, who is hands-down, the best tutorial writer to have [...]

  11. AJN says:

    Thanks guys, great FPM explanation and very interresting discussion about application on TTL,

  12. Fernando Vargas says:


    this is a great explanation about FPM, but i have a couple of question:

    - What is the need of defining the layer keyword in a statement like the following, since it has a value from 0 to 255?

    match field layer 2 IP protocol eq 0×6 next TCP

    - What is the meaning of the next keyword in the following statement in the third line, since you already defined the stack of protocols at layers 2 and 3, why do u need to add “next ip” for that line?

    match field ETHER type eq 0×800 next IP
    match field IP protocol eq 0×6 next TCP
    match field TCP dest-port eq 22 next IP


  13. Darryl Akeung says:

    Is FPM part of the ccie R&S ?

  14. Phuc Le says:

    Dear Peter,

    Congratulations on the only man in the world to have 4 CCIEs under your belt. Awesome achievemt!!!

    Thank you for this article on FPM

    Phuc Le

  15. Elvin Arias says:

    Excellent article, thanks for this awesome resource.


  16. Braulio Santos says:

    Petr, cannot apply the FPM @ Control Plane? even if i defined Nested Policy-map.

    • I don’t believe any type of NBAR is supported on the control plane. Did you try it?

      • Braulio Santos says:

        Brian, I did it and works!!!

      • Braulio Santos says:

        Please see example:
        class-map type stack match-all IP_TCP
        match field IP protocol eq 6 next TCP

        class-map type access-control match-all TCP_22
        match field TCP dest-port eq 22

        policy-map type access-control TCP_SSH
        class TCP_22

        policy-map type access-control DENYSSH_IN
        class IP_TCP
        service-policy TCP_SSH

        service-policy type access-control input DENYSSH_IN

  17. [...] INE FPM Article - Great resource for showing off the power of FPM, by matching ICMP packets within an IP-IP tunnel [...]


Leave a Reply


CCIE Bloggers