Table Of Contents

4. Single fields

4.1. Bit fields

A field to represent single bit fields.

API reference: BitField

A packet might be formed by multiple fields that can be single bit fields, numeric fields, etc. Sometimes, byte-aligned fields are also formed by bit fields internally. The purpose of BitField is to provide these single bit fields.

For example, the first byte of the IP header is formed by two nibbles:

version hlen
4 bits 4 bits

The first nibble, version, can be constructed by the following piece of code:

>>> bf = BitField("version", 4, 15)
>>> print bf
(version = 0x0F)

That is, a 4 bits field with a default, optional, value 15.

4.1.1. Booleans

A bit field for representing a boolean type.

API reference: Boolean

A Boolean is a BitField with a single bit. So only True or False can be set.

>>> b = Boolean("Option", True)
>>> print b
(Option = True)

There a couple of helper function to enable or disable the field value:

>>> b.disable()
>>> print b
(Option = False)
>>> b.enable()
>>> print b
(Option = True)

4.1.2. Flags

A bit field for representing a flag.

API reference: Flag

A Flag is a BitField with a single bit. As in the Boolean type, it can have only two values Active and Inactive. Basically, it is just a helper class to print Active or Inactive instead of True or False. The same behavior could be achieved by using the Boolean type.

>>> f = Flag("Flag", Flag.Active)
>>> print f
(Flag = Active)

There a couple of helper function to activate or deactivate the flag:

>>> f.deactivate()
>>> print f
(Flag = Inactive)
>>> f.activate()
>>> print f
(Flag = Active)

4.1.3. Masks

A field to represent bit masks.

API reference: Mask

A bit mask is a field where each bit has a different meaning. Multiples bits can be set or unset at once, but each one will represent a single thing. Bit masks are commonly used to define a set of options.

For example, we can define a 2-bit mask of whether our packet includes audio, video or both:

audio/video
2 bits

This can be simply constructed by the following code:

>>> m = Mask("audio/video", 0, AUDIO = 0x01, VIDEO = 0x02)
>>> m.mask(m.AUDIO | m.VIDEO)
>>> print m
(audio/video = 0x03)

We can also unmask a single option:

>>> m.unmask(m.AUDIO)
>>> print m
(audio/video = 0x02)

Or unmask all of them with set_value:

>>> m.set_value(0)
>>> print m
(audio/video = 0x00)

4.2. Numeric fields

An abstract class for numeric values (integer or real).

API reference: Value

This is the base class for numeric fields. Internally, it uses Python’s struct module to define the numeric value size and the byte order (little-endian or big-endian).

The following code creates an 32-bit unsigned integer with a little-endian byte ordering.

>>> v32 = Value("value", "<I", 67436735)
>>> print v32
(value = 67436735)

Fortunately, BitPacket already defines most of numeric values that are commonly used.

4.2.1. Integer fields

This module provides classes to define signed and unsigned integers bit fields, from 8-bit to 64-bit.

4.2.1.1. Signed and unsigned

Multiple signed and unsigned integer classes are available. It is, for example, very easy to create a new 16-bit signed integer bit field:

>>> value = Int16("int16", -1345)
>>> print value
(int16 = -1345)

or a 16-bit unsigned one:

>>> value = UInt16("uint16", 0x8000)
>>> print value
(uint16 = 32768)

4.2.1.2. Helper classes

Default helper classes use network byte order (big-endian):

Size Unsigned Signed
8 UInt8 Int8
16 UInt16 Int16
32 UInt32 Int32
64 UInt64 Int64

Little-endian helper classes:

Size Unsigned Signed
8 UInt8LE Int8LE
16 UInt16LE Int16LE
32 UInt32LE Int32LE
64 UInt64LE Int64LE

Big-endian helper classes:

Size Unsigned Signed
8 UInt8BE Int8BE
16 UInt16BE Int16BE
32 UInt32BE Int32BE
64 UInt64BE Int64BE

4.2.2. Real fields

This module provides classes to define float (32-bit) and double (64-bit) fields.

4.2.2.1. Floats and doubles

A float value can be easily created with the Float class:

>>> value = Float("f", 1.967834)
>>> print value
(f = 1.96783399582)

Some times, it is also useful to see the hexadecimal value that forms this float number.

>>> print value.str_hex_value()
0x3FFBE1FC

The same might be applied for doubles:

>>> value = Double("f", 0.0087552)
>>> print value
(f = 0.0087552)

4.2.2.2. Helper classes

Default helper classes use network byte order (big-endian):

Size Class
32 Float
64 Double

Endianness helper classes:

Size Little-Endian Big-Endian
32 FloatLE FloatBE
64 DoubleLE DoubleBE

4.3. String and text fields

4.3.1. String of characters

Fields to store a string of characters.

API reference: String

A String field lets you store a string of characters of any size. The length of the string needs to be specified at creation time.

The simplest case is a string with a fixed length. In the next example we create a string field with sixteen characters:

>>> data = String("data", 16)
>>> data.set_value("this is a string")
>>> print data
(data = 0x74686973206973206120737472696E67)

As usual, we can easily get back the original string:

>>> "".join(data.value())
'this is a string'

Note that, above, “print data” returns a human-readable string with hexadecimal values and “data.value()” returns the actual string.

For convenience, there is a Text field that inherits from String and always returns the actual string.

>>> text = Text("text", 16)
>>> text.set_value("this is a string")
>>> print text
(text = this is a string)

So, Text is supposedly to be used with only text while String is to be used with any character.

4.3.2. Unpacking strings

Instead of a fixed length, we can specify a length function that will tell us what length the string should have. In the following structure we create a Structure with a numeric “length” field and a string of unknown size.

length string
1 byte length

BitPacket already provides a helper the Data field which contains a length field and a string.

>>> packet = Structure("string")
>>> l = UInt8("length")
>>> s = String("data", lambda root: root["length"])
>>> packet.append(l)
>>> packet.append(s)

If we print the initial contents of the structure we can see that the length is 0 and that we still have an empty string.

>>> print packet
(string =
  (length = 0)
  (data = ))

We can try to assign a value to our string directly and see what happens:

>>> try:
...   s.set_value("this is a string")
... except ValueError as err:
...   print "Error: %s" % err
Error: Data length must be 0 (16 given)

A ValueError exception is raised indicating that string length should be 0. This is because the “length” field has not been assigned a value yet.

Finally, we can provide to the structure all the necessary information, for example, in an array:

>>> data = array.array("B", [0x10, 0x74, 0x68, 0x69, 0x73, 0x20,
...                          0x69, 0x73, 0x20, 0x61, 0x20, 0x73,
...                          0x74, 0x72, 0x69, 0x6E, 0x67])
>>> packet.set_array(data)
>>> print packet
(string =
  (length = 16)
  (data = 0x74686973206973206120737472696E67))

4.4. Meta fields

4.4.1. MetaField field

API reference: MetaField