5.3 Implementing a Workable Datagram Service

That last section was a bit of a rant. Sorry. Time to pick up the pieces and move on. Let's talk about how the parts that work actually work.

As with the Name Service, each Datagram Service packet has a header. The datagram header is 10 bytes long, arranged as follows :

graphics/121fig01.gif

Here is a quick rundown of the fields:

MSG_TYPE (1 byte)

This field is something like the OPCODE field in the Name Service header. It indicates which type of Datagram Service message is being sent. It has the following possible values:

 0x10 == DIRECT_UNIQUE DATAGRAM         0x11 == DIRECT_GROUP DATAGRAM         0x12 == BROADCAST DATAGRAM         0x13 == DATAGRAM ERROR         0x14 == DATAGRAM QUERY REQUEST         0x15 == DATAGRAM POSITIVE QUERY RESPONSE         0x16 == DATAGRAM NEGATIVE QUERY RESPONSE 

The first three of these represent unicast, multicast, and broadcast datagrams, respectively. The DATAGRAM ERROR packet is used to report errors within the Datagram Service. (There are only three errors defined in the RFCs.) The final three types are used in the query mechanism described earlier.

FLAGS (1 byte)

This field breaks down into a set of bitwise subfields:

graphics/121fig02.gif

SNT : Sending Node Type

This subfield has the following set of possible values (in binary):

 00 == B node         01 == P node         10 == M node         11 == NBDD 

Microsoft did not implement the NBDD. They did, however, introduce H mode. In practice the value 11 is used to indicate that the sending node is an H node.

F: FIRST flag

Indicates that this is the first (and possibly only) packet in a fragmented series.

M: MORE flag

Indicates that the message is fragmented, and that the next fragment should follow.

The F and M flags are used to manage fragmented messages, which will be described in more detail real soon now.

DGM_ID (2 bytes)

The Datagram ID is similar in purpose to the NAME_TRN_ID field in Name Service headers. There should be a unique DGM_ID for each (conceptual) call to the NetBIOS Send Specific Datagram or Send Broadcast Datagram functions. More about this when we discuss fragmented messages.

SOURCE_IP (4 bytes)

The IP address of the originating node. If the datagram is being relayed via the NBDD, then the IP header (at the IP layer of the stack, rather than the NBT layer) will contain the IP address of the NBDD. The SOURCE_IP field keeps track of the IP address of the node that actually composed the datagram.

SOURCE_PORT (2 bytes)

As with the SOURCE_IP field, this field indicates the UDP port used by the originating node.

The above fields are common to all Datagram Service messages. RFC 1002 includes two more fields in its header layout: the DGM_LENGTH and PACKET_OFFSET fields. It is kind of silly to have those fields in the header, as they are specific to the messages which actually carry a data payload: the DIRECT_UNIQUE , DIRECT_GROUP , and BROADCAST DATAGRAM message types.

Since the DGM_LENGTH and PACKET_OFFSET fields are associated with the datagram transport messages, we will break with tradition and put those fields together with the message structure. Here is a record layout:

graphics/123fig01.gif

DGM_LENGTH (2 bytes)

The formula given for calculating the value of the DGM_LENGTH field is:

 DGM_LENGTH = length( SOURCE_NAME )            + length( DESTINATION_NAME )            + length( USER_DATA ) 

That is, the number of bytes following the PACKET_OFFSET field. [2]

[2] This field is probably not even used by most implementations . For a long time, Samba miscalculated the DGM_LENGTH field by including the length of the 14-byte RFC header. This bug (fixed as of 2.2.4) did not seem to cause any trouble.

PACKET_OFFSET (2 bytes)

Used in conjunction with the F and M flags in the header to allow reconstruction of fragmented NetBIOS datagrams.

SOURCE_NAME (34..255 bytes)

The encoded NBT name of the sending application. Recall that NBT names are communication endpoints in much the same way that a UDP or TCP port is an endpoint. The correct SOURCE_NAME must be supplied to identify the service or application that sent the datagram.

DESTINATION_NAME (34..255 bytes)

The encoded NBT name of the destination application or service. For broadcast datagrams, the DESTINATION_NAME will be the wildcard name an asterisk (' * ') followed by fifteen nul bytes. The NBT name must include the Scope ID (even if it is the default empty scope, "" ).

USER_DATA (0..512 bytes)

The actual data to be transmitted.

That's basically all there is to it, with the exception of fragmentation. The example packet below describes an unfragmented message.

 DATAGRAM_HEADER (unfragmented)   {   MSG_TYPE = <10 = unicast, 11 = multicast, 12 = broadcast>   FLAGS     {     SNT = <Node type, as shown above>     F   = TRUE   (This is the first in the series)     M   = FALSE  (No additional fragments follow)     }   DGM_ID      = <Datagram identifier>   SOURCE_IP   = <IP address of the originating node>   SOURCE_PORT = <Originating UDP port>   } DATAGRAM_DATA   {   DGM_LENGTH       = <Name lengths plus USER_DATA length>   PACKET_OFFSET    = 0   SOURCE_NAME      = <Fully encoded NBT name of the sender>   DESTINATION_NAME = <Fully encoded NBT name of the receiver>   USER_DATA        = <Datagram payload>   } 

Some quick notes:

  • The DGM_ID should be unique with respect to other active datagrams originating from the same source. With 64K values from which to choose, this should not be difficult.

  • Once again, the SOURCE_IP , SOURCE_PORT , and SOURCE_NAME are all relative to the originator of the datagram. An NBDD does not alter these fields when it relays a message.

  • NBT datagrams are sent within scope. The Scope ID must be present in the SOURCE_NAME and DESTINATION_NAME fields, even if it is the empty scope ( "" ).

5.3.1 Fragmenting Datagrams

A little way back, we mentioned the NetBIOS API Send Specific Datagram and Send Broadcast Datagram functions. These functions each accept up to 512 bytes of data on input. Given that number, the maximum on-the-wire size of an NBT datagram is:

 10 bytes (Header) +   2 bytes (DGM_LENGTH) +   2 bytes (PACKET_OFFSET) + 255 bytes (maximum size of SOURCE_NAME) + 255 bytes (maximum size of DESTINATION_NAME) + 512 bytes (maximum size of USER_DATA) -----  1036 bytes 

and that, of course, does not include the UDP and IP headers. The whole thing is fairly chunky easily more than double the size of the data actually being sent.

The RFC authors were concerned that the UDP transport might not carry datagrams that big, so they provided a mechanism for breaking the USER_DATA into smaller fragments, like so:

first fragment

 FLAGS.F       = TRUE (This is the first fragment) FLAGS.M       = TRUE (Additional fragments follow) PACKET_OFFSET = 0 

middle fragments

 FLAGS.F       = FALSE (This is the  not  the first fragment) FLAGS.M       = TRUE  (Additional fragments follow) PACKET_OFFSET = <Data offset of fragment> 

final fragment

 FLAGS.F       = FALSE (This is  not  the first fragment) FLAGS.M       = FALSE (No more fragments follow) PACKET_OFFSET = <Data offset of fragment> 

The value of the PACKET_OFFSET field is the sum of the lengths of all previous fragments. This value is included in the message so that the receiver can keep the fragments in sync as it rebuilds the original USER_DATA . This is necessary, because datagrams do not always arrive in the order in which they were sent.

Now that you have learned all of that, you can forget most of it. As is typical for the Datagram Service, the fragmentation feature is rarely if ever used. The IP layer has its own mechanism for handling large packets so NBT datagram fragmentation is redundant.

It is possible that someone, somewhere, has implemented fragmentation, so an NBT implementation should be prepared to deal with it. One simple option is to discard fragments. This can be considered valid because the Datagram Service is considered "unreliable."

Something else to keep in mind: The 512-byte maximum size for the USER_DATA field is required at the NetBIOS layer, but not the NBT layer. Since the NetBIOS API is not required for implementing NBT, you mustn't expect that the datagrams you receive will fit within the limit. Code defensively.

5.3.2 Receiving Datagrams

NBT receives datagram messages on UDP port 138, so clients must listen on that port at the UDP level. When a message datagram is received ( MSG_TYPE is one of 0x10 , 0x11 , or 0x12 ) the DESTINATION_NAME is checked against the local name table. If the name is not found, the client should reply with a DATAGRAM ERROR MESSAGE . The available error codes are:

 0x82 == DESTINATION NAME NOT PRESENT     0x83 == INVALID SOURCE NAME FORMAT     0x84 == INVALID DESTINATION NAME FORMAT 

The first value is used whenever the DESTINATION_NAME is not in the local name table at the receiving end. The other two codes are sent whenever the source or destination NBT names, respectively, cannot be parsed.

If the name is found in the local table, then the datagram may be passed to any application or service that is listening for the given DESTINATION_NAME . The NetBIOS API provides the Receive Specific Datagram and Receive Broadcast Datagram calls for this purpose.

If there are no Receive Datagram requests waiting, the datagram is quietly discarded.

NBDD processing (for those bold enough to want to implement an NBDD) is similar. When the NBDD receives a datagram it will search the NBNS database instead of the local name table. Error messages are returned as above for missing or malformed names.

One more note: As a safety precaution, the receiving node should probably verify that the SOURCE_IP field in the datagram header matches either the source address in the IP header, or the NBDD address (if there is one).

5.3.3 Querying the NBDD

The NBDD query message is simply an NBT Datagram Service header with the DESTINATION_NAME appended:

 DATAGRAM_HEADER   {   MSG_TYPE = 0x14 (DATAGRAM QUERY REQUEST)   FLAGS     {     SNT = <Node type>     F   = TRUE     M   = FALSE     }   DGM_ID      = <Datagram identifier>   SOURCE_IP   = <IP address of the originating node>   SOURCE_PORT = <Originating UDP port>   } DATAGRAM_DATA   {   DESTINATION_NAME = <Encoded NBT name of the intended receiver>   } 

If there is an NBDD, and if it can relay the request, it will change the MSG_TYPE field to 0x15 ( POSITIVE QUERY RESPONSE ) and echo the packet back to the sender. If the NBDD is unwilling or unable to relay the message it will set MSG_TYPE to 0x16 ( NEGATIVE QUERY RESPONSE ) before sending the reply.

5.3.4 The Second Least Well Understood Aspect of NBT

It really should have been much simpler, but given the design flaws and implementation errors it is no wonder people have trouble with the Datagram Service. Our hope is that this section has cleared things up a bit, and explained the problems well enough to make them easier to solve.

Just to finish up, here are a few tips:

  • The NBDD should never relay datagrams to itself. If the NBDD host is also an NBT end node, then it must deliver datagrams to itself and then pass them along to the NBDD . There is no way to know if a received datagram is intended for the end node or the NBDD.

  • Likewise, if a host is acting as both end node and NBDD, the end node processing should not generate DESTINATION NAME NOT PRESENT ( 0x82 ) errors. The datagram should be passed along to the NBDD instead.

  • The NBNS should store all IP addresses associated with a group name. If necessary, it can return the local broadcast IP address (255.255.255.255) in response to name queries, thus maintaining compatibly with Microsoft's WINS. Storing all group name IP addresses is necessary for NBDD implementation.

  • Set a limit on the size of the IP list to which an NBDD will relay messages.

  • Don't worry about it. If you get the basics right, your system will work well enough. Very few systems expect a complete and proper NBT Datagram Service implementation.



Implementing CIFS. The Common Internet File System
Implementing CIFS: The Common Internet File System
ISBN: 013047116X
EAN: 2147483647
Year: 2002
Pages: 210

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net