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:
|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.
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)
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)
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:
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)
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.
This module provides classes to define signed and unsigned integers bit fields, from 8-bit to 64-bit.
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)
Default helper classes use network byte order (big-endian):
Little-endian helper classes:
Big-endian helper classes:
This module provides classes to define float (32-bit) and double (64-bit) fields.
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)
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.
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.
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))