@(#)jmqpacket.txt 1.2 05/01/18 Hawk (JMQ 4.0) Packet Format Version 3.0.1c 1/18/2005 Introduction ------------ This technical note specifies the packet format used by SunONE MEssage Queue. The design of this format is driven by the following requirements: 1. Simplicity. Where possible the packet format should be keep as simple as possible. 2. Efficient routing. In the case when there are no message selectors, the router must be able to route messages without inspecting the properties or message body. 3. Not space wasteful. The format should attempt to be relatively compact, but not at the sacrifice of simplicity. 4. Optimized for JMS messaging, but flexible enough to handle more generic messages (like router service messages). 5. Provide some degree of version control and ability to accommodate future needs. This document gives a general description of all fields in the packet. The assumption is that for each different packet type an accompanying profile will describe how this packet format is applied specifically to packets of that type. This document does not define the packet types, nor does it specify what kind of messages are exchanged between the routers and clients. Versions -------- The packet format has gone through a number of versions: Packet Version Product (iMQ) Version -------------- --------------------- 1.0.3 iMQ 2.0 2.0.0 iMQ 3.0 3.0.1 MQ 3.0.1 3.0.1a MQ 3.5 3.0.1b MQ 3.6 3.0.1c MQ 4.0 Versions ending with "a", "b", etc. are considered compatible with previous versions. The "a", "b", etc. is just used to indicate that a (compatible) change was made to the packet format. Overview -------- The packet is divided into four sections: 1. A 72 byte fixed header 2. A variable header that contains a list of fields. Primarily strings and a transaction ID. 3. The property data 4. The message data For the sake of simplicity we limit the size of the fields to the following: 1 bit (in a group of 8 bits) 8 bits 16 bits 32 bits 64 bits This results in some waste (for example the priority field could be 4 bits instead of 8), but we deem this an acceptable compromise between simplicity and space efficiency. All values must fall on byte boundaries. Additionally some portions of the packet (such as the fixed header) are defined so that the fields fall on the proper bit boundary (i.e. a 32 bit boundary for ints, 64 bit boundary for longs, etc). This reduces waste as well as makes it easier to map to C structs. All values are in big-endian byte orer (high-order byte first). This is called "network byte order" and is also the same as is defined by the Java programming language. Packet Format ------------- 0 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| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | magic # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | version | packet type | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | size | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + expiration + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + timestamp + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | | + source IP + | | + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | source port + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | sequence number + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | property offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | property size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | priority | encryption |$|$|$|$|$|Z|B|C|T|F|L|A|S|P|R|Q| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + consumer ID + 72 bytes| | --------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Variable Items +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | DEST=1 | dest length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | destination (UTF-8)... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | MSGID=2 | msgid length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | message ID (UTF-8)... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | CORID=3 | corid length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | correlation ID (UTF-8)... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | REPTO=4 | repto length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | reply to (UTF-8)... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TYPE=5 | type length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | type (UTF-8)... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | DESTCLASS=6 | class name length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | destination class name (UTF-8)... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | REPTOCLASS=7 | class name length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | reply to class name (UTF-8)... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TRANSACTIONID=8 | 8 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + transaction ID (long) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | PRODUCERID=9 | 8 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + producer ID (long) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0 | null padded to 32 bit boundry | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + property data + | . . . | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + message data + | . . . | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ magic: 32 bits A unique 32 bit value that will never change from packet version to packet version. This value helps us to detect bogus data on the wire (although it certainly does not guarantee it). This value is defined to be: 469754818 version: 16 bits A unique 16 bit value that changes with each incompatible change to the packet format. packet type: 16 bits Type of packet. This identifies what is contained in the property and message data section of the packet as well as some of the header fields. A seperate document will deal with defining the packet types and specifying further details on how each field of the packet is used. size: 32 bits Size of entire packet (including first 12 bytes: magic, version, type and size). NOTE: In order to maintain the ability to detect and handle multiple versions of JMQ packet formats it is required that these 4 fields (12 bytes) are not changed between packet versions. ------------------------------------------------------------------------------ sequence number: 32 bits Sequence number generate by original sender. This number must be increased monotonically for every message sent by the sender. It serves two purposes: - When used in conjunction with timestamp, sender IP and sender port results in a unique identifier for the message. - Can be used to sequence message order expiration: 64 bits Expiration timestamp for message timestamp: 64 bits Timestamp of when message was sent by original sender source IP: 128 bits IPv6 address of the originator (original sender) of the message. If we are running on an IPv4 network (highly likely for the near future) this is an IPv4-mapped IPv6 address. I.e. it is 80 0 bits followed by 16 1 bits followed by the IPv4 address: | 80 bits | 16 | 32 bits | +---------------------+----+-------------------+ |0000.............0000|FFFF| IPv4 adress | +---------------------+----+-------------------+ See RFCs 2373, 1883, and 1993 for more info on IPv6 source port: 32 bits Port number of the originator (original sender) of the message transaction ID: 32 bits Transaction identifier stamped on every packet produced by a transacted session. property offset: 32 bits Byte offset from start of packet to start of properties data. property data size: 32 bits Size of the property data priority: 8 bits Priority of this message. encryption: 8 bits (unsigned) encryption type. If we do encryption other than SSL, it's possible we may need a field describing what type of encryption has been used on the property and message data section of the packet. As of 3.0.1 this field is unused. one bit flags (16 bits) P: 1 = persistent, 0 = nonpersistent R: 1 = redelivered, 0 = not redelivered Q: 1 = DEST is a queue, 0 = DEST is a topic S: 1 = msg selectors processed, 0 = msg selectors not processed A: 1 = send reply for this misg, 0 = do not send reply L: 1 = last message in a set of messages, 0 = not last message F: 1 = flow of JMS msgs is paused for connection, 0 = not paused T: 1 = packet is part of a transaction, 0 = packet is not part of a transaction. C: 1 = flow of JMS msgs is paused for consumer, 0 = not paused. B: 1 = packet generated by server. 0 = packet generated by client. Z: 1 = packet body is compressed. 0 = packet body not compressed. I: 1 = packet is indempotent (ignore any errors which indicate the message is being reprocessed). 0 = packet is not indempotent. $: Reserved for later use The S bit is used on messages delivered from the router to a client. If 1 then the router has already filtered the message based on message selectors. If 0 then the router has not, and the client must filter the message through the message selectors. The A bit ("acknowledge" bit for historical reasons) is set on any packet the expects a reply. This serves two primary purposes: 1. For some packet types (ie a send of a JMS message) a reply may be optional. 2. When the server gets a packet it does not recognize it can use this bit to determine if it should reply with some "not implemented" status (as defined by the higher level JMQ protocol). The L bit is used for those cases when the client has requested a set of messages, and needs to know when the last message in the set has arrived (for example when getting messages via a QueueBrowser). The F bit is used for packet flow control at a connection level. This is used for just JMS message types. When a client receives a packet with this bit set it knows the server will not send any more JMS message packets on the connection until the server is requested to do so (see JMQ protocol specification for more info). The T bit is used to indicate that a packet is part of a transaction and that the transaction id is set on the packet. The C bit is used for packet flow control at a consumer level. This is used for just JMS message types. When a client receives a packet with this bit set it knows the server will not send any more JMS message packets for the consumer until the server is requested to do so (see JMQ protocol specification for more info). The B bit is used to indicate that a packet was generated by the server (and not a client). Used in debugging. The Z bit is used to indicate that the packet body is compressed. Additional information is carried in application specific properties. The I bit is used when a client is re-sending an identical packet (either because a failover occured or the broker never responded to the original message). It may not be supported on all packets. consumer ID: 64 bits A unique ID that corresponds to a particular consumer. When a client registers a consumer with the broker a consumer ID is allocated for that consumer (this allocation was done by the client in iMQ 2.0, it will be done by the broker in 2.1). Whenever a message matches a consumer's interest the broker tags the message with the corresponding ID before delivering it to the client. The client may then use this ID to dispatch the message to the particular message consumer. Without this ID the client would have to inspect the message destination and possibly run message selectors to determine the exact listener to dispatch the message to. Note that this is the only field in the packet that the router must change for every client it delivers a particular message to. In addition to being used to map JMS messages to a particular consumer, this field is also used to correlate JMQ control packets and their corresponding reply. For example if the client sends a control packet to the broker it will set the consumer ID in the control packet. The broker then copies this value into the reply packet so that the client can correlate it to the original request. Variable Header items: 16 bit type, 16 bit (unsigned) length, bytes o Variable number of items. o Items are contiguous. I.e. they are NOT null padded to next 32 bit boundary. o Items NOT null terminated. o List must be terminated by a type of 0 (TYPE=0). This 0 type is then followed by null padding until the next 32 bit boundary so that the property data starts on a 32 bit boundary. o If there are no items then the property data must start immediately after the fixed header. I.e. property_offset would be 72 and there would be no TYPE=0 list terminator. All the following are string types whose data bytes are a UTF-8 encoded string: DEST=1 Destination (topic or queue name) MSGID=2 JMSMessageID CORID=3 JMSCorrelationID REPTO=4 JMSReplyTo Destination TYPE=5 JMSType DESTCLASS=6 Class name for Destination REPTOCLASS=7 Class name for JMQReplyTo Destination The following items are longs (8 bytes): TRANSACTIONID=8 Transaction ID PRODUCERID=9 Producer ID The Class name fields are typically used by the clients so that they can create the proper class for the particular destination (ie a Queue, or Topic, or TemporaryQueue, etc). o When parsing this portion of the packet, any unrecoginized types should be skipped and ignored. property data Property data. Prior to 3.0.1 this was a serialized java.util.Hashtable. As of 3.0.1 it was changed to a simpler marshalling format to improve performance and provide language independence. Marshalled property data is now of the format: 0 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 # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | property name length | property name (UTF-8) . . . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | property value type | property value . . . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ . . . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Where version #: A version number. Current version is 1. size : number of properties marshalled These are followed by a series of name/value pairs. The name is marshalled as a length followed by the UTF-8 representation of the string. The value is marhsalled as a type followed by the value. The supported types and their corresponding values are as follow. Note that the exact data formats are as defined by the corresponding write methods for java.io.DataOutputStream: BOOLEAN=1 8 bit integer (1=true, 0=false) BYTE=2 8 bit integer (signed) SHORT=3 16 bit integer (signed) INTEGER=4 32 bit integer (signed) LONG=5 64 bit integer (signed) FLOAT=6 32 bit floating point as specified by the Java method Float.floatToIntBits() DOUBLE=7 64 bit floating point as specified by the Java method Float.floatToLongBits() STRING=8 Short length followed by UTF-8 encoded string OBJECT=9 Short length followed by serialized Java object If there is no property data then property_offset should specify the start of the message data and property_size should be 0. message data message data. Format defined by profile for the specific packet type. Correlation Between JMS Headers and Packet Fields ------------------------------------------------- JMSDestination destination JMSDeliveryMode P bit. If additional delivery modes are added later, then we will use additional reserved flag bits. JMSRedelivered R bit JMSExpiration expiration JMSPriority priority JMSTimestamp timestamp JMSMessageID default message id is derived from timestamp, sequence number, sender port and sender IP. Could look something like: nnn-nnn.nnn.nnn.nnn-nnnn-nnnnnnnnnnnn ^ ^ ^ ^ seq IP port timestamp If the client sets the JMSMessageID then it is stored as a header string field. For internal use (ie checking for dups) JMQ should always use the timestamp, seq #, port and IP; NOT the client provided MessageID. JMSCorrelationID Set by client. Stored as a header string field. JMSReplyTo Set by client. Stored as a header string field. JMSType Set by client. Stored as a header string field. JMSX* Stored as a property ============================================================================== History Changes In Version 2.0.0 (the version used in iMQ 3.0) -------------------------------------------------------- 1. Moved the 32 bit transaction ID from the fixed header to the variable header and made it a 64 bit value. 2. Made the Consumer ID a 64 bit value. These two changes left the fixed header size unchanged at 72 bytes. 3. Added T bit Changes In Version 3.0.1 (the version used in iMQ 3.0.1) -------------------------------------------------------- 1. Use new marshalled properties format instead of a serialized java.util.properties object 2. Added C bit? Changes In Version 3.0.1a (the version used in MQ 3.5) -------------------------------------------------------- 1. Added PRODUCERID to variable portion of packet. 2. Added B bit These changes did not materially alter the packet format, therefore the packet version was not rev'd. Changes In Version 3.0.1b (the version used in MQ 3.6) -------------------------------------------------------- 1. Added Z bit This changes did not materially alter the packet format, therefore the packet version was not rev'd. ============================================================================== Open Issues ----------- 1. Can we use a numeric value for the destination ID? There has been some suggestion that we could improve performance of both the router and the client if we hashed the destination to a unique numeric value. We could ship both in the packet, but then the router wouldn't need to parse the string section of the packet and both the router and client could dispatch messages based on a numeric comparison instead of a string. My current inclination is to leave this as a future optimization. To support this we would probably do something like: - Add a 32 bit "destination hash" - Introduce a Create Destination service message that passes the string destination to the server. The server then generates the destination hash and returns it to the client. 2. We've had a customer request to (optionally) add timestamp information to the packet so we can track how long it has taken a message to pass through various portions of the system. To do this we would probably want to add the timestamp info to the end of the packet. That means we would need to: - Add a message size field so we know when the message data ends and where the timestamps begin. - Add timestamp info to end of packet. The timestamp info would need to have some information to identify where the timestamp was generated. For example: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + timestamp + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | | + IP address + | | + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | port + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | whence + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ where "IP" and "port" are the IP address and port number of the server that generated "timestamp" and "whence" is some constant giving an indication of at what stage the timestamp was made. For example a router may want to timestamp a packet right after it read it off a socket and right before it is going to write it to the destination socket. This needs some more thought. Closed Issues ------------- 1. Should the property data be padded so that the message data starts on a 32 bit boundary? There is no need for the message data to start on a 32 bit boundary since this data is considered a variable length payload that will be interpreted as a stream of bytes. I.e. we don't need to optimize for an implementation to do a read into a fixed struct (which could be used by C implementations of the fixed header). 2. Should we handle splitting large JMS messages into multiple JMQ packets? We we rely on TCP to split large JMQ packets into smaller datagrams with the assumption that the threading model used in the server will efficiently handle lengthy reads. To support splitting large messgaes we would require a 32 bit 'count' field similar to what the current InterAgent protocol uses. If and when we supported UDP we could split large JMQ packets into smaller ones, then wrap the smaller packets with the information needed to reassimilate into the full JMQ packet. 3. Should we have the various "size" fields be 64 bits instead of 32? A packet size type of 32 bits gives us a max packet size of over 2 GB which is already much larger than the largest message we'd want to support. Supporting a 64 bit value would be misleading. 4. Do we need a checksum field? There is no checksum field because we rely on TCP to reliably deliver the lower level packets in the correct order. If and when we support UDP we may add a checksum to the wrapping header to ensure data integrity. 5. Should we support IPv6 addresses (128 bit addresses)? The packet format now supports IPv6 6. Is there anything else we need to support authentication? At this time we don't foresee any additional requirements for supporting authentication. The assumption is that authentication will be made at the connection level and once the server accepts the connection it will trust any packets written to that connection. 7. Is there anything else we need to support firewall tunneling? At this time we don't foresee any additional requirements for supporting firewall tunneling. The assumption is that tunneling will be handled by a different protocol. Either by using a different wire protocol directly (HTTP) or by wrapping the packets with a different protocol. 8. Is there anything we can do to help implementations efficiently support concurrency, especially the router? A router will treat a packet as mostly read-only. The plan is to implement an imutable class that supports this packet format. The write method of the class will accept parameters for the fields the router may want to modify and therefore we can avoid synchronization since the state of the class is never changed. By grouping the fields that may change at the end of the fixed header we reduce the number of writes needed to write the packet.