writing a parser from wire to window

43
Writing a Parser from Wire to Window A Beginner’s Guide to Parser Development By: Michael A. Hawker Copyright 2008 Microsoft

Upload: jonah-h-harris

Post on 28-Nov-2016

24 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window A Beginner’s Guide to Parser Development

By: Michael A. Hawker

Copyright 2008 Microsoft

Page 2: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 2

Table of Contents Introduction .................................................................................................................................................. 3

Network Monitor Parsing Language Introduction ........................................................................................ 3

What is NPL? ............................................................................................................................................. 3

How does NPL work? ................................................................................................................................ 3

How Network Data Becomes Readable Text ............................................................................................ 3

Researching your Protocol ............................................................................................................................ 4

Parsers on CodePlex .................................................................................................................................. 4

RFC Documents ......................................................................................................................................... 4

The Bit/Byte Breakdown ........................................................................................................................... 4

Getting Started Writing a Parser ................................................................................................................... 5

Network Monitor as a Tool ....................................................................................................................... 5

Download Network Monitor 3.2 ............................................................................................................... 6

Setting up Network Monitor for your Parser Development ..................................................................... 6

Saving your Parser for the First Time .................................................................................................... 6

How Network Monitor Loads Parsers ................................................................................................... 7

Loading your Parser into Network Monitor .......................................................................................... 7

Making sure Everything Works ............................................................................................................. 7

Writing a Parser from the Ground Up....................................................................................................... 9

Creating a Lookup Table........................................................................................................................ 9

The Switch Statement ........................................................................................................................... 9

Creating Your Protocol ........................................................................................................................ 10

Registering Your Protocol with RegisterAfter or RegisterBefore ........................................................ 11

Breaking down the Bytes .................................................................................................................... 13

Using the FormatString Plugin ............................................................................................................ 13

Looking up a Value in a Table ............................................................................................................. 14

Fancy Bit Display ................................................................................................................................. 15

Structures ............................................................................................................................................ 17

Custom Data Types – AsciiString ......................................................................................................... 21

Simple Comparison – Faking an “If” Statement .................................................................................. 21

Cleaning Things Up: Creating a Useful Display Summary with Type Casting and Look Ahead ........... 22

Appendix A: Complete Example .................................................................................................................. 25

Appendix B: HSRP RFC #2281 ...................................................................................................................... 27

Page 3: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 3

Introduction Writing a parser from scratch isn’t as tricky of a task as it may sound. We can thank NPL for that. It makes it pretty simple and straightforward to write a parser to decode your favorite network protocol. First let’s talk a bit more about what NPL is and how it works. Then we’ll jump right in, pick a protocol and write up an NPL for it line by line.

Network Monitor Parsing Language Introduction

What is NPL? NPL stands for Network Monitor Parsing Language. It was introduced in Network Monitor v3.0, and it is the meat and bones of how protocols are decoded into human readable text. However, while it may look like C++, don’t be fooled. NPL works differently than your traditional programming language, but once you understand the basic principles behind how NPL works, it’s a piece of cake.

How does NPL work? While an NPL file looks much like a bit of C++ code from a syntactical point-of-view, it works in a completely different manner semantically. Traditional programmers may get confused by this, but if you can push past the look and dive into the background, NPL can make a lot of sense. From a technical standpoint NPL works in a similar manner to Java or Python, your NPL code file is compiled to an underlying byte-code which is then used by Network Monitor for decoding and filtering. However, we normally don’t have to worry about this.

How Network Data Becomes Readable Text The best way to think about NPL is as if it were a line-by-line script. The premise is we have a huge glob of network data. It’s just a bunch of bytes in a buffer somewhere. Our job is to take those bytes and decode them into something we understand and can read. When your protocol is defined, the NPL will be evaluated line-by-line. Most of these lines are going to be defining a data type which will consume a set amount of data from the buffer with some named identifier. E.g. “UINT32 DataField;” would create a field called DataField which consumes a 32-bit Unsigned Integer. The DataField could then be used in expressions to branch out and consume other fields or move deeper into other protocols. Initially, all of this network data (organized in units called “frames”) is passed into the Frame.npl file. This file is the practical root and starts the data parsing process. The Frame NPL file figures out from the first few bytes of network data which media type the packet came from (such as Ethernet, TokenRing, WiFi, etc…). As soon as it does, it’s finished its job and passes on responsibility to one of many other parsers. For those C/C++ users out there, this is almost like main() and using the arguments to decide the path your program takes. Ethernet is probably the most common type of packet, and it will determine if the packet is IPv4 or IPv6 or some other high-level protocol. And this process continues until we’ve reached the end of our parsing tree or run out of data for that frame. In the end, we’ll have enumerated a lot of different data fields and be able to figure out a lot more about that piece of network data then we did before.

Page 4: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 4

Researching your Protocol

Parsers on CodePlex Obviously, you’ve found out about the parsers existing here on CodePlex. The parsers which ship with Network Monitor and which are available here on CodePlex are a combination of public RFC based protocols and Microsoft protocols which have recently been published on MSDN through the Open Protocol Initiative: http://www.microsoft.com/protocols/.

RFC Documents Another necessity besides the basics of NPL we’ve described is, of course, a good resource for the protocol parser you wish to write. A lot of common public protocols as we mentioned already have an RFC document which is freely available. RFC stands for “Requests for Comments” and sometimes these documents don’t contain protocols at all. However, a lot of protocols used today have a set of corresponding RFC documents which describes in detail the byte breakdown and other functionality of the protocol. More information can be found on the main RFC website here: http://www.rfc-editor.org/index.html

The Bit/Byte Breakdown It helps quite a bit to have a clear diagram displaying how bytes are laid out in a network frame. The RFC Sourcebook website (http://www.networksorcery.com/enp/) usually is a good resource for this visualization. It also points directly to the original RFC as well. (For convenience, we’ve included a copy of the RFC from the RFC database (http://www.rfc-editor.org/rfcsearch.html) in Appendix B.) For our example today, we’re going to use the HSRP protocol. It’s a very simple protocol which has a couple of curveballs in it which makes it ideal for an introductory protocol. Just like most Internet protocols, the protocol name is an abbreviation. HSRP really stands for “Hot Standby Router Protocol” and was developed by Cisco. Thanks to the RFC Sourcebook, we have a great diagram of the protocol here:

Figure 1 - HSRP Protocol Breakdown

As we can see from the diagram above, the HSRP protocol is 20 bytes long. We have 5 lines above each containing 4 bytes of data. The top-level numbers represent the bit positions of each field. Fortunately for us these are aligned nicely to the byte, and usually are for most protocols.

Page 5: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 5

Getting Started Writing a Parser

Network Monitor as a Tool The great thing about Network Monitor is it’s also a tool for viewing and developing protocols right out of the box! In case you haven’t investigated it before, checkout the “Parsers” tab from the Network Monitor UI:

Underneath the Object View, you’ll see a lot of options. These help breakdown protocols into more basic units. On the highest level you have Parser Files these are the actual .npl files installed with Network Monitor or the separate Parser Packages available on CodePlex. These files usually contain one protocol each, but sometimes are more complex or just contain data descriptions. Tables are a basic unit used in almost every protocol written in NPL. They provide a simple lookup mechanism which takes in a value and returns some other value. We’ll get to more on this later. But this view provides

access to every table defined throughout each NPL file. Protocols are the main point of coming to this page. Here every protocol that Network Monitor can parse is listed. Click on one in the list and you’ll see it appear on the right-hand side of your window with the exact section of the NPL highlighted. Note how over 300 protocols are already supported; more are being created every day. Data Types displays the basic units used in protocols. This list shows the building blocks we’ll use later to construct our protocol. Many of the simple types are defined in the basicdatatypes.npl file. Structs can almost be thought of as a mini-protocol. They are a specific grouping of fields which usually occur more than once. In this manner we can define a struct to use these groupings over and over again without wasting a lot of code to parse it twice in multiple locations. They’re activated just like Data Types and have all the same power and flexibility as Protocols do. We’ll talk more about these later. Custom Data Types are special Data Types which are built into Network Monitor. They provide functionality which NPL isn’t capable of handling on its own. They usually work just like regular Data Types, but can sometimes also take special parameters to effect how they work. You can click on these items for more documentation about how to use these types. Plugins provide various miscellaneous functions to accomplish tasks not possible in NPL as well. Again, you can click on these items for more documentation about how to use these functions. A lot more information about NPL itself is included in the reference guide under available from the Help Menu under “NPL and API Documentation” in Network Monitor.

Figure 2 - Parser Object View

Page 6: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 6

Download Network Monitor 3.2 Now, that we have a lot of information about the protocol we want to write, and we know how Network Monitor can help us see all different protocols and how they interact, we can get started. As we mentioned before, we’ll start writing the HSRP protocol. It was recently included in the latest set of parsers on CodePlex, so don’t cheat. Make sure you have the original 3.2 parser set before starting this exercise. You can download it with Network Monitor 3.2 here: http://www.microsoft.com/downloads/details.aspx?FamilyID=f4db40af-1e08-4a21-a26b-ec2f4dc4190d&DisplayLang=en

Setting up Network Monitor for your Parser Development The first steps into parser writing involving getting a place to write your protocol. We’re going to open up Network Monitor and click on the Parsers tab. From there we’re going to click on the “New Parser” Button. This should create a blank space for us to fill with our protocol. Let’s start by quickly inserting a comment about our protocol:

//# ** Public Protocol Parser **

//# (c) 2008 Microsoft Corporation

//#

//# Title: Hot Standby Router Protocol

//#

//# Details:

//#

//# References: http://www.networksorcery.com/enp/protocol/hsrp.htm

//# http://www.javvin.com/protocol/rfc2281.pdf

//#

//# Comments:

//#

//####

As you can see, we’ve added some details about who made the parser, what it’s called, and where we found out information about it. We’ve also left a section for comments in the future. The “//” characters are used to define the comment. Everything after them on the line is ignored by NPL. But you probably already knew that, eh?

Saving your Parser for the First Time Now that we have some text, let’s save our file. When we click on the “Save” Button on the toolbar, Network Monitor will popup our standard Save dialog. The default location should be in your Documents folder under “Network Monitor 3\Parsers”. You should already see a my_sparser.npl file listed there (if not we’ll create one), we’ll get to that in a moment. Name your file something meaningful, usually the name of the protocol with the .npl extension. In this case we’ll use hsrp.npl.

Page 7: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 7

How Network Monitor Loads Parsers Before we dive into writing the parser, there’s one more step we have to perform. By default Network Monitor doesn’t load all the .npl files in a directory. It uses a special file called sparser.npl which specifies which other NPL files should be loaded. This file is automatically generated by Network Monitor based on your Parser set configuration in the options dialog (Tools->Options, Parser tab). It can also be thought of like a header file which tells Network Monitor what to include. Sparser.npl includes two other files, one for all the built-in protocol parsers, and the other is my_sparser.npl. The my_sparser.npl file we saw before was your custom parser list. Any new protocols you want Network Monitor to know about need to be included here. To open that file you can click on the “Open Parser” button or navigate to my_sparser.npl under the “Parser Files” branch in the navigation window to the left of your workspace. If one doesn’t exist, just create a new file and save it as my_sparser.npl and place it in the same place we did for hsrp.npl. The other thing to be aware of is once Network Monitor has the list of files is where it goes looking for them. If you looked at the Microsoft_Parsers_sparser.npl file, you’ll have noticed all the entries do not contain paths. Network Monitor obtains the paths from the list in the Parser tab in the Options dialog. It starts looking for files in the path starting at the top of the list and progressing downwards. Therefore, if you wanted to write your own version of TCP.npl to override the default one, you could move your “my documents” based folder to the top of the list, copy the regular tcp.npl file and place it there and make your changes. Network Monitor would then load your copy of tcp.npl instead of the regular one because it will find it first based on this path hierarchy.

Loading your Parser into Network Monitor Once you’ve opened your my_sparser.npl file, we just need to add a single line to the end, it should now look like this:

// Personal NPL Files

include "hsrp.npl"

This will tell Network Monitor to look at your file when it loads the parser set.

Making sure Everything Works Make sure you’ve saved all your files, and let’s click on the “Reload Parsers” button now. It will take a few seconds, but you’ll see Network Monitor loading your parsers. In the “Message View” window at the bottom of the Parsers tab you should see two blue icons with a “Successfully loaded…” and a “Successfully serialized NPL parser…” message. Let’s check to see if you have everything setup correctly now. Click on the “+” next to “Parser Files” in your navigation window. Now double check that you can see the new hsrp.npl file in this list. If you do, then you’ve done everything right. Otherwise, make sure you saved your hsrp.npl file in the same directory where the my_sparser.npl file was, and you included the line in your my_sparser.npl file just as displayed in the previous section. If you double-click on your hsrp.npl file in the tree, it should be displayed with your comment on the right.

Page 8: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 8

It’s important to note that whenever you make a change to an NPL file, you’ll need to hit the “Reload Parsers” button after saving your file in order to compile those changes and see an effect when you process network traffic. The parsers should reload automatically when you launch Network Monitor if any changes have been made since it last loaded the parser set. It is also important to note that conversations may not be rebuilt when you reload the parsers, so you may need to re-open your capture file if you’re currently viewing it. However, for our simple example, this won’t be a problem. It might be a good idea to continue saving and reloading the parser set as you progress through each section of this tutorial to ensure everything’s working right. I won’t leave you in a broken mess in-between the steps, so it’ll be a good way to gauge your progress.

Page 9: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 9

Writing a Parser from the Ground Up Protocols can be pretty complicated. Network Monitor looks at the data byte by byte and this works in a lot of cases, fortunately for us HSRP is one of them. There are other times when protocols need to jump ahead to figure out what’s going on, but we’re going to focus on the basics first and save that for another time. You can follow along step-by-step here and recreate the HSRP protocol parser.

Creating a Lookup Table We have a pretty nice description of HSRP based upon the RFC document. Our byte breakdown helps us to visualize our end result and the list of fields which should be displayed in the Frame Summary view of Network Monitor when an HSRP packet is parsed. But for a simple introduction to NPL, we’ll start at the lower-level first and create our first table. Tables allow us to correlate values to other values, usually strings. For instance, the first field in HSRP is an Opcode. We want to match this Opcode to one of three values “Hello”, “Coup” or “Resign”. We can use a simple table for this. First, we start with the keyword Table which tells the NPL we want to define a table. Immediately following the Table keyword is the name of the Table. Usually we write the name of the protocol first as Tables are listed globally in our navigation tree as well. We’ll name this table: HSRPOpcodeTable. We’ll then put a set of curly braces to enclose our Table. You should have:

Table HSRPOpcodeTable

{

}

Now that we have a Table, we need to populate it with values. For this we need to use a more complex command, the switch statement.

The Switch Statement The switch statement allows us to branch our parsing based on a certain value. In this case for our HSRP table, we’re going to use this to return different string values. We start with the switch keyword followed by (value) in parenthesis. The “value” word here acts as an argument (it’s the default parameter name for tables). It is possible to have multiple arguments to a table, but we’re not going there in this tutorial. Arguments for a table aside, normally, you’d pass in a field or property if you were using a switch within a structure or protocol (we’ll see this later too). We then open another set of curly braces. Now within our braces we can define a number of different outcomes for whatever value we may receive. We’ll use the case keyword for this followed by the number we want our value to match, a colon, the string we want to return, and finally a terminating semi-colon. We can also use the default keyword instead of case and omit the number to provide a exception route. If NPL can’t match any value, it’ll just use the default case instead.

Page 10: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 10

We’re going to create three cases for HSRP. The RFC states our Opcode values can be 0, 1, or 2 each assigned correspondingly to “Hello”, “Coup”, and “Resign”. We’ll end with a default case for “Unknown Code”. You should now have something that looks like this:

Table HSRPOpcodeTable

{

switch(value)

{

case 0x00 : "Hello";

case 0x01 : "Coup";

case 0x02 : "Resign";

default : "Unknown Code";

}

}

If we wanted to, we could add comments with “//” after each of the semi-colons and define what HSRP is doing in each of these cases, but we’ll save space here. HSRP also has another Table for State based information:

Table HSRPStateTable

{

switch(value)

{

case 0x00 : "Initial";

case 0x01 : "Learn";

case 0x02 : "Listen";

case 0x04 : "Speak";

case 0x08 : "Standby";

case 0x10 : "Active";

default : "Unknown Code";

}

}

Notice how in this table the values aren’t sequential. You can use any values you want in switch statements in any order. Just remember that they’re evaluated from the top down.

Creating Your Protocol The next step is to actually create the body of our protocol. This can probably be one of the most complex bits of coding required for your protocol. This mainly stems from being able to display useful information about the protocol ahead of time. But if you wanted to be vague about it, it’s pretty simple. We’ll start with simple and rework this section later once we’ve learned more. So, to start we’ll add a new line with the Protocol keyword followed by a unique programmatic name of the protocol. This name must adhere to typical programming standards as it is used to refer to other values within the protocol and from other protocols. Therefore it needs to be globally unique. Usually just using the abbreviated protocol name like HSRP is sufficient.

Page 11: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 11

Following the protocol name is usually an equal sign followed by a String value. This string value will be displayed in the Frame Summary view of Network Monitor under the Description column next to every frame which is determined to be your end-level protocol. It is also automatically appended to the protocol name defined in the previous step as well as a colon. Therefore if our string was “Hello, World!” the string as it appears in the Network Monitor UI would be “HSRP:Hello, World!”. It’s usually a good idea to make this string short but informative, as it can help someone looking at a trace determine if they need to dive further into your protocol. For now, we’re going to leave things blank and get back to this once we know a few more tricks. You should have:

Protocol HSRP

{

}

Registering Your Protocol with RegisterAfter or RegisterBefore While in the previous chapter we showed you how to get Network Monitor to load your parser file, we didn’t explain how to get your parser called and registered from another. This is a critical step needed to have your parser be of any use and to actually start seeing results and being able to test your protocol. Looking at the documentation for HSRP, we see it’s carried on top of the UDP protocol on port 1985. If we look at udp.npl, we’ll see the following lines in a switch statement on port numbers:

case 1900:

HTTP http;

case 2049:

RPC Rpc;

Of course now we have HSRP which comes in on port 1985. We’d like to put it in this switch statement so Network Monitor will go through UDP and call upon us to process the data correctly. We basically want our end result to look like:

case 1900:

HTTP http;

case 1985: // If you’re about to copy and paste this, STOP!!!

HSRP Hsrp;

case 2049:

RPC Rpc;

If we modify udp.npl directly, we have a number of drawbacks. We create a dependency between UDP and HSRP, need to distribute two files for our protocol to work, and prevent others from modifying UDP in combination with using our new parser as they could have already modified UDP to fix a bug… This is where the RegisterAfter/RegisterBefore constructs combat this problem. They are the preferred way to register your protocol using a special directive. Directives are a language feature which allows the execution of certain plugins or directives, or the creation of special property values before parts of the NPL are parsed.

Page 12: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 12

By using the RegisterAfter/RegisterBefore constructs any dependency on other files is removed, as normally you’d have to open the parent NPL protocol file and manually add your own protocol to be parsed (as we demonstrated above). To use this feature, we need to know three things: where we need to insert it, what we need to insert, and in what cases we insert it. Of course the second part is easy, because we know we want to insert HSRP. Luckily for us, we know that HSRP packets come in across the UDP protocol on port 1985. The RegisterAfter and RegisterBefore constructs behave in basically the same manner. They allow us to insert new options into a switch statement in memory without the need to manually modify the parent protocol file. Therefore, by using this directive we can create the situation we showed above without needing to touch udp.npl! We’ve already found the best place to insert HSRP (as shown above). The Register constructs take three parameters. The first is the location of the insertion, the second is the name for what is being added, and the third is the optional conditional clause for when it appears (usually used when inserting into a switch). Which Register you use depends upon where you’d like to insert your protocol. In our demonstration we wanted to add our protocol After HTTP, but we could have just as easily said we’d insert it Before RPC. We’ll call our new case “Hsrp”, and we know it’s coming in on port 1985. Therefore our final expression will look like:

[ RegisterAfter (UDP.http, Hsrp, 1985) ]

Protocol HSRP

{

}

Or as mentioned we could have just as easily used:

[ RegisterBefore (UDP.Rpc, Hsrp, 1985) ]

Protocol HSRP

{

}

There are a few more examples with more information about RegisterAfter/RegisterBefore in the Network Monitor SDK available from the Help Menu under “NPL and API Documentation” in Network Monitor. You can grab some HSRP sample captures from here: http://wiki.wireshark.org/SampleCaptures. If you load one of those files into Network Monitor (making sure to reload your parsers first), you should now see packets labeled as HSRP with the Description “HSRP”.

Page 13: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 13

Breaking down the Bytes Now that we have some lookup tables to match values to functions and have registered HSRP with its parent protocol, we’re ready to create the bulk of our Protocol and tell Network Monitor how to interpret the binary information in an HSRP packet. Up until now things have been pretty straight-forward. HSRP is a pretty straight-forward protocol too, so this section is pretty simple and can be fun. However, be warned many larger protocols are very complex, and successfully engineering a parser requires not only programming talent and network knowledge but comprehensive reading and analysis skills to convert the documentation of a protocol into something that can read the bytes on the wire. But, we’ll assume if you’re reading this you’ve got passion, motivation, and plenty of time. So we’ll move on. The basic building block for consuming network data in NPL and displaying it is really very simple. It will simply consist of a data type representing the amount of data you want to consume and how it should be interpreted and a name for that field followed by a semi-colon. The name you give your field should be unique to the curly braced section it’s in. Looking at our HSRP byte breakdown, we’ll see we have an 8 bit field which represents the version number of the protocol. To interpret this in Network Monitor we have a UINT8 data type. This will read 8 bits of data and interpret it as an unsigned integer value. Our corresponding line in NPL will look like this:

Protocol HSRP

{

UINT8 Version;

}

If we reload our parsers again, and look at our open capture file we won’t notice any difference at first. However, if we select an HSRP packet and go to the Frame Details window, we’ll see there’s now a + next to HSRP we can expand.

Using the FormatString Plugin We’ve covered the elementary steps of consuming data from our binary blob and getting it to display in the Network Monitor UI. The next step is to format data more appropriately and do some basic interpretation of the bits on the wire. This will turn basic information about your protocol into a more powerful diagnostic tool. Usually the FormatString plugin is used to accomplish this task and provide more robust information related to the specific field or frame being parsed. The FormatString plugin is similar to printf for those C++ users out there. The first parameter is a string which can contain control codes. Different control

Page 14: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 14

codes represent different data, for instance using “%d” represents a signed decimal integer and “%s” is a string. For each control code used, you need to provide another parameter to FormatString which will be used to represent that value. More information on FormatString can be obtained by clicking on it in the Plugin list in your navigation tree. We’re going to add the next field to HSRP. According to the byte breakdown, it’s the Opcode field. It’s another 8 bit/1 byte field which contains a special flag. We’re going to use the FormatString plug-in to change how it’s displayed in the UI. To add the special format to the field, after it’s declaration before the semi-colon, we simply add an equal sign. Afterwards, we add “FormatString” to call the FormatString plug-in and pass in a string argument as the first parameter. This will be the format string and has many options. For each control code we use, we need to be sure to add an additional parameter for its value just like if we were using printf. In our case we’re going to use the control code %u for an unsigned decimal integer. The second parameter is going to be “this” which represents the data the field is consuming. You should have a line like the one below:

UINT8 Version;

UINT8 Opcode = FormatString("%u", this);

Now, if you reload your parsers with the change and look at an HSRP field in your capture, you’ll notice information like the example below:

Notice how the Opcode field doesn’t have the “(0x0)” after it. This is because the default formatting for UINT8 is “%u (0x%X)” which displays the unsigned integer like we had and the uppercase hexadecimal representation. This format string is defined in the basicdatatypes.npl file. When we overrode the default by using our own FormatString command, the simple unsigned decimal notation we requested is used instead.

Looking up a Value in a Table Of course, having the value presented as a number isn’t too helpful if you don’t know what the number means or represents. This is where after all the hard work, in the beginning of this lesson, of creating our tables pays off. We want to help provide a little more detailed information about what the protocol is actually doing when it receives certain types of packets. From our protocol documentation we have some basic insights based upon some of the lookup tables. The Opcode Table for instance shows how the router sending these packets is behaving, whether it is announcing itself to the network or shutting down. In order to display this in the UI, we’re just going to pass in the value we’re getting for our field and look it up in our table. For instance in our example, our value was zero, therefore we should display “Hello” next to the Opcode field to let anyone looking at the data know that it’s an announcement message. Looking up values in a table is easy. You can almost think of them as simple functions, they take a value in and return a new value. We’re going to add this table lookup into our FormatString from the last

Page 15: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 15

example at the same time too. We’ll add a %s control code to represent that we’ll be inserting a string (which is the return value from our table). Then we simply add HSRPOpcodeTable(this) as an argument to obtain the string from our table. It’s that simple!

UINT8 Version;

UINT8 Opcode = FormatString("%s (%u)", HSRPOpcodeTable(this), this);

Now, if we load up the HSRP frame again in the UI, we’ll see the following result:

Fancy Bit Display Sometimes a flag will have many values or it could have multiple bits set at once to indicate different things. In these cases we can use some special syntax to create “Bit Fields”. These special fields will breakdown our UINT8 data type (or any other data type) into smaller bit-sized chunks. It will also help display the fancy structure you’ve probably seen in some other protocols which will breakdown the bits and their meaning. I think it’ll become a little clearer as we move on. The first step is actually the same as we’ve just been doing for our last two fields. We declare a new field with the total size of the field we’re interested in (we can even throw in some formatting and our table lookup too):

UINT8 State = FormatString("%s (%d)", HSRPStateTable(this), this)

Notice how I left out the semi-colon though? We’ve got a bit more to add this time if we want to use the Bit Field approach. We need to create some curly braces for the next part:

UINT8 State = FormatString("%s (%d)", HSRPStateTable(this), this)

{

}

The next step we’re going to take is to create the Bit Fields themselves. We’re going to create one for every set of bits we want to breakdown. It starts out like a regular field, except we’re going to use the parent field’s data type. In this case it’s UINT8 again. We’re going to give it a name too just as we’ve been doing, but then we’re going to throw in a colon. This will flag that we’re only interested in a certain number of bits. We then follow the colon by the number of bits we’re interested in. Be aware that you’re breaking down the parsing into smaller chunks. Anything in the braces will refer to the parent data field only; you’ll be breaking down the same data that it just parsed. If we look at the State field in our protocol definition, we’ll see the first 3 bits are reserved. We’ll add a field for this first:

UINT8 State = FormatString("%s (%d)", HSRPStateTable(this), this)

{

UINT8 Bit0_2:3 = FormatString("(%s) Reserved", this.ToBitString);

};

Page 16: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 16

You’ll see that we’re creating a field called Bit0_2, and that it’ll display a string control code. You’ll also notice something else: this.ToBitString. What this does is display the relevant bytes in relation to the total parent data type’s length in zeros and ones. In this case since the data should always be zero, you’ll see the following:

Notice how the Bit0_2 was placed underneath the parent’s data type. This is because the data is related. If you didn’t have a parent data type and just placed the Bit0_2 field, it would look like this:

That’s the other nice thing with Bit Fields is you can use them just by themselves too. This gives you a lot of flexibility for your protocols and their display. Let’s do one more field together. The next field actually has a meaning. When flagged it means the router is forwarding packets to the group’s virtual MAC address. This could be important information for someone interested in viewing these packets. Let’s try and highlight this out more:

UINT8 Bit3:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Active" : "");

Now, we have another Bit Field called Bit3 which will consume 1 bit. Its format string starts with a couple of spaces, this is because our first field name had two extra characters and we want things to line up in the UI. More importantly, we not only have the ToBitString again as the first additional parameter, but we have some new syntax as well. For those C users, this should be familiar, an inline conditional. This will let us evaluate our bit as 0 or 1 and based on the outcome display a message. If it’s true, we’ll display “Active” otherwise, nothing. Therefore if our HSRP packet’s state is Active we’ll see the following in the UI:

Page 17: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 17

And if it’s a zero instead:

In this manner we’ve provided a quick and easy way for our users to glance at the Bit Fields and see which ones are active. We’ll just repeat the process to get the rest of the fields below:

UINT8 State = FormatString("%s (%d)", HSRPStateTable(this), this)

{

UINT8 Bit0_2:3 = FormatString("(%s) Reserved", this.ToBitString);

UINT8 Bit3:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Active" : "");

UINT8 Bit4:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Standby" : "");

UINT8 Bit5:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Speak" : "");

UINT8 Bit6:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Listen" : "");

UINT8 Bit7:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Learn" : "");

};

Structures We’ve just seen two new ways to display and organize data within the Network Monitor UI while we parser our protocol. There are many other ways as well. These types of mechanisms help to aid a user of Network Monitor in quickly finding the information they need by providing different levels of detail. For instance in our HSRP example, we had a field for the state, but also provided a more detail breakdown of the flags. This data only appears if the user opens up the Frame Details tree in the UI. In addition to the Bit Fields we’ve just seen with their parent fields, another grouping mechanism exists as well. These are called Structures. Structures allow you to arbitrarily group a set of fields within your protocol. Structures can also help you create more compact protocol parsers as you can create a new “data type” which represents a similar group of recurring fields. You can then place the structure as a multi-field consumer instead of copying and pasting all the same fields over and over again for the same data groupings. Structures exist in two flavors public and private. Public structures are just like we mentioned in the above paragraph, they exist outside your protocol definition and act as a data type. They are also accessible to other parsers and form a collapsing tree element for its elements in the UI. Private structures are defined inline with your structure or protocol definition and are not accessible outside of the local scope. However, unlike public structures, they cannot act as a new data type. There are many examples of structures in the NPL help documentation.

Page 18: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 18

Structures in some ways act as mini-protocols; they can contain other structures and switches. They can also be formatted just like every other type of construct we’ve mentioned. In addition, they can be named. This not only puts the top-level label on a public structure for its tree node, but also allows access to the structure in filter expressions and from other parsers. As we can see from the HSRP definition, the byte breakdown shows two more byte length fields in the protocol. However the Hellotime and Holdtime fields are only meaningful in Hello type messages (refer to the Opcode table). Therefore, we’re going to make them not appear as fields when we don’t have a Hello message. For this case we’d just need a private structure, but let’s do some experimentation first so you can see the difference between the two types of structures. We start a structure by starting with the struct keyword. We follow that by a name and an optional format string (we’re going to do something fancy here this time). We’ll then open up a set of curly braces too. Then just like our Bit Fields or the Protocol itself, we can add fields which will appear as child nodes in the tree. In the end we’ll have something like this:

};

struct HelloData = FormatString("Hellotime: %d", Hellotime) {

UINT8 Hellotime;

UINT8 Holdtime;

}

}

Notice how in our Format String we can reference the inner field. This is useful for providing a summary in the parent node for what may be contained in the children. We’ll dive into this in a little more detail later. For now, reload your parsers and look at a frame:

You’ll also notice if you try and type a filter, the private structure gives you the same layout there too:

Page 19: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 19

Now, let’s rewrite this to be a hidden structure. All we need to do for that is to add a “_” character before our struct keyword. A hidden structure is a special type of private structure. With a hidden struct, the parent node is removed and the fields appear to be flat as if inline with the rest of the protocol. In this case we can remove our Format String as it won’t appear in the UI:

_struct HelloData {

UINT8 Hellotime;

UINT8 Holdtime;

}

And in the UI, we’ll just see the two inner fields without the expandable node:

But you’ll notice if you try and use a filter, it’ll give you the same result as we saw before:

This is because we gave our private hidden structure a name after the “_struct”. If we remove the name from the structure entirely:

};

struct {

UINT8 Hellotime;

UINT8 Holdtime;

}

We’ll get the same exact look in the Frame Details view, however when we try and filter:

We’ll now see the fields just as if they were part of the protocol. Why would we want to use a struct then? Let’s take our case in HSRP. We only want to display the Hellotime and Holdtime fields in the

Page 20: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 20

case we have a Hello message. We’re going to add a switch statement to determine what type of message we have. In the case of a Hello message we’ll display the fields, otherwise we’ll just state the data is unimportant. Switch statements in your protocol work similarly to how they did in our tables. They’ll take a value in and based on its value do something else. The difference is a table switch case returns a single value, whereas a switch case in a protocol or structure needs to return a field for a single data type. This is why we need a private structure, they count as a single data type. In this manner, we can “return” two fields at the same time. In this case we’ll be switching on the Opcode field we read in earlier in the protocol. If it’s “0” for the Hello message, then we’ll use our private structure to read in the two bytes into two separate fields: Hellotime and Holdtime. Otherwise, we’re going to use the Blob type to read in the two bytes and move on. In the end it’ll look like this:

switch(Opcode) {

case 0:

struct {

UINT8 Hellotime;

UINT8 Holdtime;

}

default:

BLOB(2) ImplementationSpecificData;

}

Notice the “(2)” after the BLOB data type. Some data types will take in a parameter to specify their length. This is a special case and the parameters can depend on the data type. However the BLOB type is simple, it just takes the length (in bytes) of the amount of data you want to consume, in this case two bytes. Now, if we find a Hello message, we’ll have our nice flat structure:

And if we don’t have a Hello message, we’ll see this:

Page 21: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 21

This helps anyone reading these frames to not get confused with Hello message only fields. We can also filter on these fields directly with “HSRP.Hellotime” or “HSRP.ImplementationSpecificData”. If you just filter on the field itself without anything else, it’ll return frames to you which contain that field. Therefore, we’ve also provided a handy way to access only Hello messages or other types of messages.

Custom Data Types – AsciiString Before we jump into the next section, we have an easy task to accomplish first. We just need to add three more fields (as defined in the HSRP RFC) to our parser:

UINT8 Priority;

UINT8 Group;

UINT8 Reserved;

Alright, now we’re ready. The next thing we’re going to use is a Custom Data Type. These types are more complex than normal data types and need special extensions to be processed. However they work just like normal data types in terms of coding. We need to use the AsciiString custom data type to retrieve the plain text authentication string HSRP uses (it defaults to “cisco”).

AsciiString(8) AuthenticationData;

We can see in the above line something similar to how we used the BLOB type before. We’re passing in an argument to our data type. In this case it’s simple again and we’re just passing in the length of the field that we wish to retrieve. This will take the next 8 bytes of data and give us back an ASCII string version of the data. It’s that simple!

Simple Comparison – Faking an “If” Statement The last field we have to deal with in HSRP is the Virtual IP Address. However we want to make sure we still have enough data in our buffer to consume for this final field. We can check a couple of special properties to help us with this: FrameOffset and FrameLength. FrameOffset represents the number of bytes from the beginning of the network data frame to the current location in the network data frame. And FrameLength represents the total number of bytes in the current network data frame. However, NPL has no notion of a direct “If” statement, which may be a strange thing for programmers. We’ve seen the inline conditional in the previous section, but this is only good for returning different values and not taking alternative execution paths. However, a simple switch statement can be used instead for this purpose.

Page 22: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 22

Instead of passing a value into the switch statement, we can leave things blank and do an evaluation within the case itself, just as if we were making an if statement. We can then process our final field:

switch

{

case FrameOffset + 4 <= FrameLength:

IPv4Address VirtualIPAddress;

}

}

You’ll notice instead of using a BLOB of the 4 bytes for the IP Address, we’ve used a special IPv4Address data type. This data type will consume the 4 bytes of data and display it in the nice ###.###.###.### formatting.

Cleaning Things Up: Creating a Useful Display Summary with Type Casting and Look Ahead We’ve covered a lot of ground so far! We’re almost done. We just have one last bit to do to go back and put the icing on the cake. We want to be able to provide some useful information in the Frame Summary View’s Description column so that a user can more easily find the packet they’re looking for. For our protocol, we’ll keep things simple and simply look up two values in our table and display them. These values are of course our only two tables for the Opcode and the State. In order to display something useful, as we mentioned as we were creating our protocol, we just need to put an equal sign and a string value at the end of our protocol definition line:

[ RegisterAfter (UDP.http, Hsrp, 1985) ]

Protocol HSRP = "Hello, World!"

{

Of course we’ve since learned a lot and are able to create something much more meaningful than a static string. Just like every other field, we can use FormatString here as well to aid us in our quest for more human friendly network data. We’ll simply take our two fields for Opcode and State which describe a lot about the type of message being sent and display them as our overall description. This will look a lot like a combination of the work we did for the two fields earlier:

[ RegisterAfter (UDP.http, Hsrp, 1985) ]

Protocol HSRP = FormatString("Opcode = %s, State = %s",

HSRPOpcodeTable(Opcode),

HSRPStateTable(State)

)

{

Page 23: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 23

And Voila, we have more useful information about what’s going on at a glance:

Notice how again (as we had mentioned earlier) it is possible to reference the child fields in the parent’s description. However, I can’t let you go just yet. I have one more thing to teach you. Suppose we have a protocol that is really large and we want to be able to make a stub for it in the future (that’ll have to be a future lesson). Stub parsers provide the basic shell of a parser to let you know when you encounter that protocol, but no other detailed information about it. It has been shown that the more fields your protocol has, the longer the time it takes to parse, so by using stubs we can narrow down information more quickly. Anyway, you’d want to be able to easily create this stub from your current parser, right? Just basically remove everything in the curly brace section of your protocol and leave it at that (though normally stubs will say nothing but “load full parsers”). Or suppose you want to rename the Opcode field or discovered a typo? By using a special technique called “Type Casting” we can limit the risk for multiple changes in the parser later on by removing the reference to the named field. Type Casting is used to look ahead of or behind our data stream and pick out specific pieces of information. By looking at our Byte Breakdown we’ll see the two values which correspond to the Opcode and State fields are stored in the 2nd and 3rd bytes of our protocol. To retrieve these values before we parse them we need to do our look ahead. The first thing we need to know is the type of data we’re retrieving. In this case they’re both UINT8. And of course the second piece is where they are in our frame, but we know that too. So, to combine these pieces of information to get the data, we simply start with our Data Type and put some parenthesis as if we were calling a function. We can then use two special variables: FrameData (which contains the binary data of the packet) and Offset (which contains the current index of where we’re consuming the binary data). We’re going to pass the FrameData in as the first parameter to specify the chunk of data we’re looking in, and then the Offset as the second for where we want to look. For the Opcode though we have to add 1 to get to the 2nd byte of data, and the State needs an additional 2 to get to the 3rd. In the end, we’ll have something that looks like this:

Protocol HSRP = FormatString("Opcode = %s, State = %s",

HSRPOpcodeTable(UINT8(FrameData, Offset + 1)),

HSRPStateTable(UINT8(FrameData, Offset + 2))

)

Now, we can rename our Opcode and State fields to something else in the future or rip them out and still have a completely useful parser. This is also useful if your protocol needs additional information outside of itself, for instance you know your parent protocol’s last byte determines how you behave.

Page 24: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 24

We’re not saying that your protocols should do such odd things, but if they already exist and do, then you have the power to do it with NPL. Thank you for your time!

Page 25: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 25

Appendix A: Complete Example

//# ** Public Protocol Parser **

//# (c) 2008 Microsoft Corporation

//#

//# Title: Hot Standby Router Protocol

//#

//# Details:

//#

//# References: http://www.networksorcery.com/enp/protocol/hsrp.htm

//# http://www.javvin.com/protocol/rfc2281.pdf

//#

//# Comments:

//#

//####

Table HSRPOpcodeTable

{

switch(value)

{

case 0x00 : "Hello";

case 0x01 : "Coup";

case 0x02 : "Resign";

default : "Unknown Code";

}

}

Table HSRPStateTable

{

switch(value)

{

case 0x00 : "Initial";

case 0x01 : "Learn";

case 0x02 : "Listen";

case 0x04 : "Speak";

case 0x08 : "Standby";

case 0x10 : "Active";

default : "Unknown Code";

}

}

[ RegisterAfter (UDP.http, Hsrp, 1985) ]

Protocol HSRP = FormatString("Opcode = %s, State = %s",

HSRPOpcodeTable(UINT8(FrameData, Offset + 1)),

HSRPStateTable(UINT8(FrameData, Offset + 2))

)

{

UINT8 Version;

UINT8 Opcode = FormatString("%s (%u)", HSRPOpcodeTable(this), this);

UINT8 State = FormatString("%s (%d)", HSRPStateTable(this), this)

Page 26: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 26

{

UINT8 Bit0_2:3 = FormatString("(%s) Reserved", this.ToBitString);

UINT8 Bit3:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Active" : "");

UINT8 Bit4:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Standby" : "");

UINT8 Bit5:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Speak" : "");

UINT8 Bit6:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Listen" : "");

UINT8 Bit7:1 = FormatString(" (%s) %s", this.ToBitString,

this ? "Learn" : "");

};

switch(Opcode) {

case 0:

struct {

UINT8 Hellotime;

UINT8 Holdtime;

}

default:

BLOB(2) ImplementationSpecificData;

}

UINT8 Priority;

UINT8 Group;

UINT8 Reserved;

AsciiString(8) AuthenticationData;

switch

{

case FrameOffset + 4 <= FrameLength:

IPv4Address VirtualIPAddress;

}

}

Page 27: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 27

Appendix B: HSRP RFC #2281

Network Working Group T. Li

Request for Comments: 2281 Juniper Networks

Category: Informational B. Cole

Juniper Networks

P. Morton

Cisco Systems

D. Li

Cisco Systems

March 1998

Cisco Hot Standby Router Protocol (HSRP)

Status of this Memo

This memo provides information for the Internet community. It does

not specify an Internet standard of any kind. Distribution of this

memo is unlimited.

Copyright Notice

Copyright (C) The Internet Society (1998). All Rights Reserved.

IESG Note

This document reflects an existing deployed protocol. The IETF does

have a working group which is in the process of producing a standards

track protocol to address the same issues.

Abstract

The memo specifies the Hot Standby Router Protocol (HSRP). The goal

of the protocol is to allow hosts to appear to use a single router

and to maintain connectivity even if the actual first hop router they

are using fails. Multiple routers participate in this protocol and

in concert create the illusion of a single virtual router. The

protocol insures that one and only one of the routers is forwarding

packets on behalf of the virtual router. End hosts forward their

packets to the virtual router.

The router forwarding packets is known as the active router. A

standby router is selected to replace the active router should it

fail. The protocol provides a mechanism for determining active and

standby routers, using the IP addresses on the participating routers.

If an active router fails a standby router can take over without a

major interruption in the host's connectivity. This memo also

discusses the ARP, MAC address, and security issues with this

protocol.

Li, et. al. Informational [Page 1]

RFC 2281 Cisco HSRP March 1998

Page 28: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 28

TABLE OF CONTENTS

1 Introduction .............................................. 2

2 Conditions of Use ......................................... 3

3 Scope ..................................................... 4

3.1 Terminology ............................................... 4

4 Definitions ............................................... 4

5 Protocol .................................................. 4

5.1 Packet formats ............................................ 4

5.2 Operational parameters .................................... 7

5.3 States .................................................... 8

5.4 Timers .................................................... 9

5.5 Events .................................................... 9

5.6 Actions ................................................... 10

5.7 State Transitions.......................................... 11

6 MAC address considerations ................................ 13

6.1 General ................................................... 13

6.2 Address Filter ............................................ 14

6.3 ICMP Redirect ............................................. 14

6.4 Proxy ARP ................................................. 15

7 Security Considerations ................................... 15

8 References ................................................ 15

9 Authors' Addresses ........................................ 16

10 Full Copyright Statement .................................. 17

1. Introduction

The Hot Standby Router Protocol, HSRP, provides a mechanism which is

designed to support non-disruptive failover of IP traffic in certain

circumstances. In particular, the protocol protects against the

failure of the first hop router when the source host cannot learn the

IP address of the first hop router dynamically. The protocol is

designed for use over multi-access, multicast or broadcast capable

LANs (e.g., Ethernet). HSRP is not intended as a replacement for

existing dynamic router discovery mechanisms and those protocols

should be used instead whenever possible [1]. A large class of

legacy host implementations that do not support dynamic discovery are

capable of configuring a default router. HSRP provides failover

services to those hosts.

All of the routers participating in HSRP are assumed to be running

appropriate IP routing protocols and have a consistent set of routes.

The discussion of which protocols are appropriate and whether routing

is consistent in any given situation is beyond the scope of this

specification.

Li, et. al. Informational [Page 2]

RFC 2281 Cisco HSRP March 1998

Page 29: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 29

Using HSRP, a set of routers work in concert to present the illusion

of a single virtual router to the hosts on the LAN. This set is

known as an HSRP group or a standby group. A single router elected

from the group is responsible for forwarding the packets that hosts

send to the virtual router. This router is known as the active

router. Another router is elected as the standby router. In the

event that the active router fails, the standby assumes the packet

forwarding duties of the active router. Although an arbitrary number

of routers may run HSRP, only the active router forwards the packets

sent to the virtual router.

To minimize network traffic, only the active and the standby routers

send periodic HSRP messages once the protocol has completed the

election process. If the active router fails, the standby router

takes over as the active router. If the standby router fails or

becomes the active router, another router is elected as the standby

router.

On a particular LAN, multiple hot standby groups may coexist and

overlap. Each standby group emulates a single virtual router. For

each standby group, a single well-known MAC address is allocated to

the group, as well as an IP address. The IP address SHOULD belong to

the primary subnet in use on the LAN, but MUST differ from the

addresses allocated as interface addresses on all routers and hosts

on the LAN, including virtual IP addresses assigned to other HSRP

groups.

If multiple groups are used on a single LAN, load splitting can be

achieved by distributing hosts among different standby groups.

The remainder of this specification discusses the operation of a

single standby group. In the case of multiple groups, each group

operates independently of other groups on the LAN and according to

this specification. Note that individual routers may participate in

multiple groups. In this case, the router maintains separate state

and timers for each group.

2 Conditions of Use

US Patent number 5,473,599 [2], assigned to Cisco Systems, Inc. may

be applicable to HSRP. If an implementation requires the use of any

claims of patent no. 5,473,599, Cisco will license such claims on

reasonable, nondiscriminatory terms for use in practicing the

standard. More specifically, such license will be available for a

one-time, paid up fee.

Li, et. al. Informational [Page 3]

RFC 2281 Cisco HSRP March 1998

Page 30: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 30

3 Scope

This document describes the packets, messages, states, and events

used to implement the protocol. It does not discuss network

management or internal implementation issues.

3.1 Terminology

The language conventions of RFC 2119 [3] are used in this document.

4 Definitions

Active Router - the router that is currently forwarding packets

for the virtual router

Standby Router - the primary backup router

Standby Group - the set of routers participating in HSRP that

jointly emulate a virtual router

Hello Time - the interval between successive HSRP Hello

messages from a given router

Hold Time - the interval between the receipt of a Hello

message and the presumption that the sending

router has failed

5 Protocol

Within a standby group, the routers periodically advertise state

information using various messages.

5.1 Packet formats

The standby protocol runs on top of UDP, and uses port number 1985.

Packets are sent to multicast address 224.0.0.2 with TTL 1.

Routers use their actual IP address as the source address for

protocol packets, not the virtual IP address. This is necessary so

that the HSRP routers can identify each other.

The format of the data portion of the UDP datagram is:

Li, et. al. Informational [Page 4]

RFC 2281 Cisco HSRP March 1998

Page 31: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 31

1 2 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Version | Op Code | State | Hellotime |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Holdtime | Priority | Group | Reserved |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Authentication Data |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Authentication Data |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Virtual IP Address |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Version: 1 octet

The version of the HSRP messages. This document describes version

0.

Op Code: 1 octet

The Op Code describes the type of message contained in this

packet. Possible values are:

0 - Hello

1 - Coup

2 - Resign

Hello messages are sent to indicate that a router is running and

is capable of becoming the active or standby router.

Coup messages are sent when a router wishes to become the active

router.

Resign messages are sent when a router no longer wishes to be the

active router.

State: 1 octet

Internally, each router in the standby group implements a state

machine. The State field describes the current state of the

router sending the message. Details on the individual states are

described below. Possible values are:

Li, et. al. Informational [Page 5]

RFC 2281 Cisco HSRP March 1998

Page 32: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 32

0 - Initial

1 - Learn

2 - Listen

4 - Speak

8 - Standby

16 - Active

Hellotime: 1 octet

This field is only meaningful in Hello messages. It contains the

approximate period between the Hello messages that the router

sends. The time is given in seconds.

If the Hellotime is not configured on a router, then it MAY be

learned from the Hello message from the active router. The

Hellotime SHOULD only be learned if no Hellotime is configured and

the Hello message is authenticated. A router that sends a Hello

message MUST insert the Hellotime that it is using in the

Hellotime field in the Hello message. If the Hellotime is not

learned from a Hello message from the active router and it is not

manually configured, a default value of 3 seconds is RECOMMENDED.

Holdtime: 1 octet

This field is only meaningful in Hello messages. It contains the

amount of time that the current Hello message should be considered

valid. The time is given in seconds.

If a router sends a Hello message, then receivers should consider

that Hello message to be valid for one Holdtime. The Holdtime

SHOULD be at least three times the value of the Hellotime and MUST

be greater than the Hellotime. If the Holdtime is not configured

on a router, then it MAY be learned from the Hello message from

the active router. The Holdtime SHOULD only be learned if the

Hello message is authenticated. A router that sends a Hello

message MUST insert the Holdtime that it is using in the Holdtime

field in the Hello message.

A router which is in active state MUST NOT learn new values for

the Hellotime and the Holdtime from other routers, although it may

continue to use values which it learned from the previous active

router. It MAY also use the Hellotime and Holdtime values learned

through manual configuration. The active router MUST NOT use one

configured time and one learned time. If the Holdtime is not

learned and it is not manually configured, a default value of 10

seconds is RECOMMENDED.

Li, et. al. Informational [Page 6]

RFC 2281 Cisco HSRP March 1998

Page 33: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 33

Priority: 1 octet

This field is used to elect the active and standby routers. When

comparing priorities of two different routers, the router with the

numerically higher priority wins. In the case of routers with

equal priority the router with the higher IP address wins.

Group: 1 octet

This field identifies the standby group. For Token Ring, values

between 0 and 2 inclusive are valid. For other media values

between 0 and 255 inclusive are valid.

Authentication Data: 8 octets

This field contains a clear-text 8 character reused password.

If no authentication data is configured, the RECOMMENDED default

value is 0x63 0x69 0x73 0x63 0x6F 0x00 0x00 0x00.

Virtual IP Address: 4 octets

The virtual IP address used by this group.

If the virtual IP address is not configured on a router, then it

MAY be learned from the Hello message from the active router. An

address SHOULD only be learned if no address was configured and

the Hello message is authenticated.

5.2 Operational parameters

The following information MUST be known to each router in the standby

group. The mechanisms used to determine this information are outside

of the scope of this document.

Standby group number

Virtual MAC address

Priority

Authentication Data

Hellotime

Holdtime

Li, et. al. Informational [Page 7]

RFC 2281 Cisco HSRP March 1998

Page 34: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 34

The following information MUST be known to at least one router in

each standby group and MAY be known by any of the other routers in

the group.

Virtual IP Address

The following information MAY be configured on any router:

Preemption capability

If a router has higher priority than the active router and

preemption is configured, it MAY take over as the active router

using a Coup message.

5.3 States

Each router in the group participates in the protocol by implementing

a simple state machine. This specification describes the externally

visible behavior of this state machine. Implementations MAY vary

their internal implementations within the functional description of

the state machine.

All routers begin in the Initial state. This section discusses the

intent of each state. For specific details on the actions taken in

each state, please see the state transition table in section 5.7.

1. Initial

This is the starting state and indicates that HSRP is not running.

This state is entered via a configuration change or when an

interface first comes up.

2. Learn

The router has not determined the virtual IP address, and not yet

seen an authenticated Hello message from the active router. In

this state the router is still waiting to hear from the active

router.

3. Listen

The router knows the virtual IP address, but is neither the active

router nor the standby router. It listens for Hello messages from

those routers.

Li, et. al. Informational [Page 8]

RFC 2281 Cisco HSRP March 1998

Page 35: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 35

4. Speak

The router sends periodic Hello messages and is actively

participating in the election of the active and/or standby router.

A router cannot enter Speak state unless it has the virtual IP

address.

5. Standby

The router is a candidate to become the next active router and

sends periodic Hello messages. Excluding transient conditions,

there MUST be at most one router in the group in Standby state.

6. Active

The router is currently forwarding packets that are sent to the

group's virtual MAC address. The router sends periodic Hello

messages. Excluding transient conditions, there MUST be at most

one router in Active state in the group.

5.4 Timers

Each router maintains three timers, an Active timer, a Standby timer,

and a Hello timer.

The Active timer is used to monitor the active router. The active

timer is started anytime an authenticated Hello message is seen from

the active router. It is set to expire in the Holdtime seen in the

Hello message.

The Standby timer is used to monitor the standby router The Standby

timer is started anytime an authenticated Hello message is seen from

the standby router. It is set to expire in the Holdtime seen in the

Hello message.

The Hello timer expires once per Hellotime period. If the router is

in Speak, Standby, or Active states, it should generate a Hello

message upon Hello timer expiry. The Hello timer MUST be jittered.

5.5 Events

These are the events in the HSRP finite state machine.

a - HSRP is configured on an enabled interface.

b - HSRP is disabled on an interface or the interface is disabled.

Li, et. al. Informational [Page 9]

RFC 2281 Cisco HSRP March 1998

Page 36: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 36

c - Active timer expiry. The Active timer was set to the Holdtime

when the last Hello message was seen from the active router.

d - Standby timer expiry. The Standby timer was set to the

Holdtime when the last Hello message was seen from the standby

router.

e - Hello timer expiry. The periodic timer for sending Hello

messages has expired.

f - Receipt of a Hello message of higher priority from a router in

Speak state.

g - Receipt of a Hello message of higher priority from the active

router.

h - Receipt of a Hello message of lower priority from the active

router.

i - Receipt of a Resign message from the active router.

j - Receipt of a Coup message from a higher priority router.

k - Receipt of a Hello message of higher priority from the standby

router.

l - Receipt of a Hello message of lower priority from the standby

router.

5.6 Actions

This section specifies the actions to be taken as part of the state

machine.

A Start Active Timer

If this action occurred as the result of the receipt of a an

authenticated Hello message from the active router, the Active

timer is set to the Holdtime field in the Hello message.

Otherwise the Active timer is set to the current Holdtime value

in use by this router. The Active timer is then started.

B Start Standby Timer

If this action occurred as the result of the receipt of an

authenticated Hello message from the standby router, the

Standby timer is set to the Holdtime field in the Hello

message. Otherwise the Standby timer is set to the current

hold time value in use by this router. The Standby timer is

then started.

Li, et. al. Informational [Page 10]

RFC 2281 Cisco HSRP March 1998

Page 37: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 37

C Stop Active Timer

The Active timer is stopped.

D Stop Standby Timer

The Standby timer is stopped.

E Learn Parameters

This action is taken when an authenticated message is received

from the active router. If the virtual IP address for this

group was not manually configured, the virtual IP address MAY

be learned from the message. The router MAY learn Hellotime

and Holdtime values from the message.

F Send Hello Message

The router sends a Hello message with its current State,

Hellotime and Holdtime.

G Send Coup Message

The router sends a Coup message to inform the active router

that there is a higher priority router available.

H Send Resign Message

The router sends a Resign message to allow another router to

become the active router.

I Send Gratuitous ARP Message

The router broadcasts an ARP response packet advertising the

group's virtual IP address and virtual MAC address. The packet

is sent using the virtual MAC address as the source MAC address

in the link layer header, as well as within the ARP packet.

5.7 State Transitions

This table describes the state transitions of the state machine. For

each event and current state of the router, the router MUST perform

the set of actions specified and transition to the designated state.

If no action is specified, no action should be taken. If no state

change is specified, no state change should be performed.

The notation used in this table has the specified set of actions

listed as letters corresponding to the actions listed in section 5.6.

The next state is listed as a number as specified in section 5.3. A

slash ('/') separates the actions and states. Certain state

transitions have alternatives which depend on external state.

Alternatives are separated by a '|'. See the attached notes for

details on these transitions.

Li, et. al. Informational [Page 11]

RFC 2281 Cisco HSRP March 1998

Page 38: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 38

States

+-----+----------+----------+----------+----------+----------+----------+

| | 1 | 2 | 3 | 4 | 5 | 6 |

| | Initial | Learn | Listen | Speak | Standby | Active |

+-----+----------+----------+----------+----------+----------+----------+

|Event| |

+-----+----------+----------+----------+----------+----------+----------+

| a | AB/2|3+ | | | | | |

+-----+----------+----------+----------+----------+----------+----------+

| b | | CD/1 | CD/1 | CD/1 | CD/1 | CDH/1 |

+-----+----------+----------+----------+----------+----------+----------+

| c | | | AB/4 | | CDFI/6 | |

+-----+----------+----------+----------+----------+----------+----------+

| d | | | B/4 | D/5 | | |

+-----+----------+----------+----------+----------+----------+----------+

| e | | | | F | F | F |

+-----+----------+----------+----------+----------+----------+----------+

| f | | | | B/3 | B/3 | |

+-----+----------+----------+----------+----------+----------+----------+

| g | | EAB/3 | EA | EA | EA | AB/4 |

+-----+----------+----------+----------+----------+----------+----------+

| h | | EAB/3 | A|BGFI/6*| A|BGFI/6*| A|BGFI/6*| G |

+-----+----------+----------+----------+----------+----------+----------+

| i | | | AB/4 | A | CFI/6 | |

+-----+----------+----------+----------+----------+----------+----------+

| j | | | | | | ABH/4 |

+-----+----------+----------+----------+----------+----------+----------+

| k | | | B | B/3 | B/3 | B |

+-----+----------+----------+----------+----------+----------+----------+

| l | | | B/4 | D/5 | | B |

+-----+----------+----------+----------+----------+----------+----------+

Notes

+ If the virtual IP address is configured, set state 3 (Listen) If

the virtual IP address is not configured, set state 2 (Learn). In

either case do actions A and B.

* If the router is configured to preempt do actions B, G, F, and I

and set state to 6 (Active). If the router is not configured to

preempt do actions A with no state change.

Li, et. al. Informational [Page 12]

RFC 2281 Cisco HSRP March 1998

Page 39: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 39

6 MAC Address Considerations

6.1 General

Each HSRP group has an associated well known virtual MAC address. On

token ring networks, these addresses are actually functional

addresses. The three addresses 0xC0 0x00 0x00 0x01 0x00 0x00, 0xC0

0x00 0x00 0x02 0x00 0x00, and 0xC0 0x00 0x00 0x04 0x00 0x00

correspond to groups 0, 1, and 2 respectively.

On other media, the virtual MAC addresses are 0x00 0x00 0x0C 0x07

0xAC XX where XX represents the HSRP group number. Routers which

implement HSRP SHOULD use well-known HSRP MAC addresses as the

group's virtual MAC address whenever possible.

The active router MUST accept and forward traffic that is destined

for the group's virtual MAC address. It MUST stop accepting or

forwarding such traffic when the router leaves the Active state.

If and only if the router is in the Active state, the router MUST use

the group's virtual MAC address as the source MAC address for its

Hello messages. This is necessary in order to allow learning bridges

to be able to determine which LAN segment the virtual MAC address

currently belongs to.

For each group, there is one virtual IP address and one virtual MAC

address. This is a desirable situation, since the ARP table entries

in the end stations do not need to change over time as the HSRP

active router moves from one router to another.

Additionally, for HSRP to work in bridging environments, the bridges

must be able to quickly update themselves as the virtual MAC address

"moves". Although learning bridges typically are able to do this,

some have been known to have problems with this. It is RECOMMENDED

that only true learning bridges be used with HSRP.

The movement of the virtual MAC address can cause further undesirable

side effects in environments where additional state is tied to the

MAC address. For example on Token Ring, if Source Route Bridging is

in use, a RIF will be stored with the virtual MAC address in a host's

RIF cache. The RIF indicates the path and final ring used to reach

the MAC address. As routers transition into Active state, they will

not be able to affect the RIF caches on the hosts on the bridged

ring. This may lead to packets being bridged to the ring for the

previous active router.

Li, et. al. Informational [Page 13]

RFC 2281 Cisco HSRP March 1998

Page 40: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 40

In such circumstances, a router MAY use its normal MAC addresses as

the virtual MAC address. This method of operation is strongly

discouraged. In this mode, the virtual IP address will map to a

different MAC address over time. This can create problems for end

stations, since ARP tables assume a relatively static mapping between

MAC address and IP address. These ARP tables are normally updated

when the end stations receive the gratuitous ARP responses generated

by a router that enters the active state.

6.2 Address Filter

As noted, routers currently emulating a virtual router adopt their

group's MAC and IP addresses. MAC addresses are typically provided

in an address filter or 'list' of MAC addresses in a router's

interface controller. It is desirable for routers to be able to add

one or more virtual MAC addresses to their controllers' MAC address

filter while maintaining their primary MAC addresses.

Unfortunately, some interface controllers support address filtering

for only one unicast MAC address. Or, in the case of Token Ring, the

functional address which HSRP should use is already in use for some

other protocol. In these cases, such routers can still implement

HSRP, but the protocol must change the interface's primary MAC

address when assuming or relinquishing control as the active router.

This is potentially problematic because some traffic may otherwise

wish to use the router's primary MAC address. However, the problem

MAY be mitigated by having the router send out gratuitous ARP packets

regarding its non-HSRP IP addresses. Through this, other network

entities using IP should update their ARP tables to reflect that the

router is now using a group virtual MAC address rather than its

primary MAC address.

Some protocols may not be able to run simultaneously with the standby

protocol due to the interface primary MAC address change. For

example, DECnet phase IV and HSRP will not be able to run at the same

time on some equipment.

6.3 ICMP Redirect

While running HSRP, it is important to prevent the host from

discovering the primary MAC addresses of the routers in its standby

group. Thus, any protocol that informs a host of a router's primary

address should be disabled. Thus, routers participating in HSRP on

an interface MUST NOT send ICMP redirects on that interface.

Li, et. al. Informational [Page 14]

RFC 2281 Cisco HSRP March 1998

Page 41: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 41

6.4 Proxy ARP

Typically, hosts learn the HSRP virtual IP address through the

configuration of their default router. These hosts then send packets

for destinations outside of the LAN to the virtual IP address. In

some environments, hosts may instead make use of proxy ARP in order

to route off of the LAN. In this case, the hosts use the MAC address

that is supplied in proxy ARP responses. HSRP functionality is

maintained if the proxy ARP responses specify the HSRP virtual MAC

address.

If an HSRP router is configured to support proxy ARP with HSRP, then

the router MUST specify the HSRP virtual MAC address in any proxy ARP

responses it generates. These proxy ARP responses MUST not be

suppressed based upon HSRP state. Suppression based upon state could

result in lack of any proxy ARP response being generated, since these

proxy ARP responses may be suppressed due to other reasons, such as

split-horizon rules.

7. Security Considerations

This protocol does not provide security. The authentication field

found within the message is useful for preventing misconfiguration.

The protocol is easily subverted by an active intruder on the LAN.

This can result in a packet black hole and a denial-of-service

attack. It is difficult to subvert the protocol from outside the LAN

as most routers will not forward packets addressed to the all-routers

multicast address (224.0.0.2).

8. References

[1] Deering, S., "ICMP Router Discovery Messages", RFC 1256,

September 1991.

[2] United States Patent. Patent Number : 5,473,599. Standby Router

Protocol. Date of Patent: Dec. 5, 1995.

[3] Bradner, S., "Key words for use in RFCs to Indicate Requirement

Levels", BCP 14, RFC 2119, March 1997.

Li, et. al. Informational [Page 15]

RFC 2281 Cisco HSRP March 1998

Page 42: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 42

9. Authors' Addresses

Tony Li

Juniper Networks, Inc.

3260 Jay St.

Santa Clara, CA 95054

Phone: (408) 327-1900

EMail: [email protected]

Bruce Cole

Juniper Networks, Inc.

3260 Jay St.

Santa Clara, CA 95054

Phone: (408) 327-1900

EMail: [email protected]

Phil Morton

Cisco Systems

170 Tasman Dr.

San Jose, CA 95143

Phone: (408) 526-7632

EMail: [email protected]

Dawn Li

Cisco Systems

170 Tasman Dr.

San Jose, CA 95143

Phone: (408) 527-2014

EMail: [email protected]

Li, et. al. Informational [Page 16]

RFC 2281 Cisco HSRP March 1998

Page 43: Writing a Parser From Wire to Window

Writing a Parser from Wire to Window P a g e | 43

10. Full Copyright Statement

Copyright (C) The Internet Society (1998). All Rights Reserved.

This document and translations of it may be copied and furnished to

others, and derivative works that comment on or otherwise explain it

or assist in its implementation may be prepared, copied, published

and distributed, in whole or in part, without restriction of any

kind, provided that the above copyright notice and this paragraph are

included on all such copies and derivative works. However, this

document itself may not be modified in any way, such as by removing

the copyright notice or references to the Internet Society or other

Internet organizations, except as needed for the purpose of

developing Internet standards in which case the procedures for

copyrights defined in the Internet Standards process must be

followed, or as required to translate it into languages other than

English.

The limited permissions granted above are perpetual and will not be

revoked by the Internet Society or its successors or assigns.

This document and the information contained herein is provided on an

"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING

TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING

BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION

HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF

MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Li, et. al. Informational [Page 17]