Trouble Accessing Some Fields with API

With the Network Monitor API you can access any field by adding its path and then accessing the offset, size or value using one of the Field Value Functions like NmGetFiledOffsetAndSize or NmGetFieldValueString. But for certain paths this does not work properly. In this blog we’ll discuss how to work around this problem.

Adding Fields to the Parser

To review, let’s first discuss how you access fields with the API. To provide an alternate high level view, let’s look at this diagram:

clip_image002

The green box at the end is our goal; at this point we can see the values associated with a data field for a specific frame. But to get to this point, we need to create a Frame Parser and apply it to a Raw Frame. The orange box represents different ways you can produce a Raw Frame handle. The Blue box describes the main steps needed to create a Frame Parser.

During this process you create a Parser Configuration handle which can optionally be optimized to look at specific fields, filters, and properties (white box). This optimization can be overridden, but for fastest parsing you’ll want to pass NmFrameParserOptimizeOption.NmParserOptimizeFull as the last parameter to NmCreateFrameParser (purple box). In either case, a Frame Parser can be used to break apart a Raw Frame object.

And now with a Raw Frame and a Frame Parser we can access the data in a given frame. We can also evaluate filters against the frame and access properties from the frame parser. As another reference, this blog has more details about the API. Also the examples in the help file, in particular the “Iterating Frame Field Data” is a good reference for this article.

With all this talk of Handles, it’s good to bring up that each should be closed after you use them by calling NmCloseHandle. A clue that something has gone wrong in this regard is that you might get errors after iterating 1000 frames, as this is the default number of open raw and parsed frame handles we allow.

Determining the Path to Add to NmAddField

To access an IPv4 source address, you would add “IPv4.SourceAddress” as a data field argument to NmAddField. In some cases the data field you need to add isn’t so obvious. When you have a problem discovering the path you can use what is returned from right clicking in the UI and selecting “Add Selected Value to Display Filter”. And most of the times this works great.

The Problem

But in some cases, the path returned by right clicking does not work when trying to call one of the NmGetField type calls or NmGetParsedFieldInfo. What you’ll find is that NmAddField works successfully, and the calls return without an error. But the results, such as its offset, size and value, will be zero. Or in the case of NmGetParsedInfo, the data structure returned is populated with zeros.

As it turns out, the culprit here is how the parser code (NPL) is defined. In almost every case in our parsers the instantiated object has the same name as the data type. So in our parsers we could have a protocol defined as follows:

 Protocol ProtX 
{     
    UINT32 yyy; 
}

And then at some point it can be instantiated in another protocol or structure, for instance:

ProtX ProtX;

In this case, adding the field as provided by the local path to the protocol path works perfectly. So when you call NmAddField, the following statement can be accessed just fine.

NmAddField(myFrameParser, “ProtX.yyy”, &myID);

However, there are a few instances in our parser code where this convention is not followed. The instance that prompted me to write this blog was in the ETL parsers for NDIS events. Instead we did something like this:

ProtX myProtX;

And while this is perfectly legal, this confuses our API and causes it not to work correctly. In fact a perfectly legal path you can use in the UI is ProtX.yyy. At the top level we use the protocol type definition as the root of a data field name. But for the API, referencing the field this way doesn’t work properly and API calls returns with zeroed out information as mentioned above.

The Workaround

The solution is to reference the protocol or structure using a parent path object you started with above. For instance if the instantiation was as follows:

 Protocol Frame
{    
    ProtX myProtX;
}

You can reference the data field of yyy using Frame.myProtX.yyy when you call NmAddField.

Difficult Decisions

When delivering a product, you often have to make conscious difficult decisions about which bugs to fix and which you choose to let in the wild. One of the deciding factors in this case was if there is a work around. And in this case it’s not a difficult work around, but only if you know there’s a problem to begin with.