Validating Packet Formats

View Source

For demonstration purposes, let's move to a different example. Let's say we have an existing packet format, one that we would like to be able to extend in the future by providing different format IDs or types. Now, let's define an initial structure:

My Salute Format
Byte 0 Byte 1
Source Port Destination Port
Packet Type Payload Type Payload

Now, to start, let's say we want to define a "Hello" and a "Goodbye" packet. To differentiate between them, we can specify the value of their type. Let's make the "Hello" a Packet Type binary "00" and the "Goodbye" packet a "11":

Hello Format
Byte 0 Byte 1
Source Port Destination Port
Packet Type = 0b00 Payload Type Payload
Goodbye Format
Byte 0 Byte 1
Source Port Destination Port
Packet Type = 0b11 Payload Type Payload

To make this straightforward to represent, Etiquette allows specifying a fixed: value argument to each field. So let's proceed and create their definitions:

defmodule SaluteSpec do
  use Etiquette.Spec

  packet "My Salute Packet", id: :salute do
    field "Source Port", 8
    field "Destination Port", 8
    field "Packet Type", 2
    field "Payload Type", 6
    field "Payload", 8
  end

  packet "Hello Packet", id: :hello do
    field "Source Port", 8
    field "Destination Port", 8
    field "Packet Type", 2, fixed: 0b00
    field "Payload Type", 6
    field "Payload", 8
  end

  packet "Goodbye Packet", id: :goodbye do
    field "Source Port", 8
    field "Destination Port", 8
    field "Packet Type", 2, fixed: 0b11
    field "Payload Type", 6
    field "Payload", 8
  end
end

Now that we have created a packet containing an identifying element (the Packet Type), the generated functions for the Hello and Goodbye packets will use that information to validate the structure of provided data.

For example, we can now use

iex> SaluteSpec.is_hello?(<<0::8, 0::8, 0b00::2, 0x3F::6, "somedata">>)
true
iex> SaluteSpec.is_hello?(<<0::8, 0::8, 0b11::2, 0x3F::6, "somedata">>)
false

Which is using the expected fixed values to validate that the packets follow the defined specification.

If a packet specification has no specified fixed values for any of its fields, it is also possible to determine if a given bitstring fits a specification just by looking at the length. For example, A single byte of data cannot be a "Hello" packet by itself (because the specification has declared it to be at least 4 bytes), so is_hello? would be false in this case. However, if the argument of is_hello? is larger than the specified length of the packet, it can be true for the same reason that the generated parse functions can accept larger binaries than what they parse. See the length guide for more details on why this is the case.

Continue through the Packet Types and Subtypes guide to learn about other field arguments that can help you streamline the creation of packet variants.