# Create Custom Protocol

PacketGen allows you adding your own header classes.

## Quick start

To add a new/custom header, you first have to define the new header class. For example:

```ruby
module MyModule
 class MyHeader < PacketGen::Header::Base
   define_field :field1, PacketGen::Types::Int32
   define_field :field2, PacketGen::Types::Int32
 end
end
```

Then, class must be declared to PacketGen:

```ruby
PacketGen::Header.add_class MyModule::MyHeader
```

Finally, bindings must be declared:

```ruby
# bind MyHeader as IP protocol number 254 (needed by Packet#parse and Packet#add)
PacketGen::Header::IP.bind_header MyModule::MyHeader, protocol: 254
```

And use it:

```ruby
pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
pkt.myheader.field2.read 0x01
```

## Add a new header type, in more detail

### Define a header class

A new header class should inherit from [`PacketGen::Header::Base`](http://www.rubydoc.info/gems/packetgen/PacketGen/Header/Base) class (or from `PacketGen::Header::ASN1Base`, see [below](/wiki/advanced-use/create-custom-protocol.md#add-a-new-asn-1-header)). This base class implements minimal API (see [below](/wiki/advanced-use/create-custom-protocol.md#header-minimal-api)) to parse a header from binary string, or to generate binary string from a header class.

`PacketGen::Header::Base` inherits from [`PacketGen::Types::Fields`](http://www.rubydoc.info/gems/packetgen/PacketGen/Types/Fields), which is a class to define headers or anything else with a binary format containing multiple fields.

Here, magical method is `define_field`. This method define a field and its name. You may set as fields as you need. A type may be a predefined type from `PacketGen::Types`, or any other `PacketGen::Types::Fields` subclass:

```ruby
class CustomField < PacketGen::Types::Fields
  # add a 16-bit integer field
  define_field :one, PacketGen::Types::Int16
  # add another one
  define_field :two, PacketGen::Types::Int16
end

class ExampleHeader < PacketGen::Header::Base
  # define a first 32-bit integer field
  define_field :first, PacketGen::Types::Int32
  # add a custom field
  define_field :custom, CustomField
  # add a 8-bit integer field
  define_field :int8, PacketGen::Types::Int8

  # then bit fields on int8 field
  #  ack and error are boolean flags, as no size is specified (default to 1)
  #  rsv is a 4-bit field
  #  type is a 2-bit field
  define_bit_fields_on :int8, :ack, :error, :rsv, 4, :type, 2
end
```

Here, some example to access these fields:

```ruby
example = ExampleHeader.new

example.first        #=> Integer
example.first = 0x12345678

example.custom       #=> CustomField
example.custom.one   #=> Integer

example.ack?         #=> Boolean
example.ack = true

example.type         #=> Integer
example.type = 1
```

### Builtin Types

All builtin types are listed [here](/wiki/getting-started/packetgen-structure.md#types).

### Add some methods to a header class

By default, all fields will have accessors with the good type. By example, a `Types::Int32` field may be accessed as an Integer, a `Types::String` may be accessed as a String, etc.

But, for some reason, you may need to add another accessor, or a method to compute some protocol data.

To do that, you have to understand Fields model. A field may be accessed through its accessor, as already seen. But it may also be accessed through Fields' hash.

Fields class defines `#[]` method to access to field object by its name. By example, with our previously defined ExampleHeader:

```ruby
example.first     #=> Integer
example[:first]   #=> PacketGen::Types::Int32
```

Sometimes, you will need to access real field object. All field objects have common methods:

* `#read` reads a binary string to set object,
* `#to_s` gives binary string from object,
* `#sz` gives binary size.

As an example of method in a header class, we will define one to calculate `first` field, which value should be size of custom field:

```ruby
class ExampleHeader
  # compute first field
  def calc_first
    self[:first].read self[:custom].sz
  end
end
```

## Add a new ASN.1 header

Some header may be defined using ASN.1 notation, like SNMP.

To define such a header, use [`PacketGen::Header::ASN1Base`](http://www.rubydoc.info/gems/packetgen/PacketGen/Header/ASN1Base) as base class:

```ruby
class SNMP < PacketGen::Header::ASN1Base
  sequence :message,
            content: [enumerated(:version, value: 'v2c',
                                 enum: { 'v1' => 0, 'v2c' => 1, 'v2' => 2, 'v3' =>}),
                      octet_string(:community, value: 'public'),
                      model(:data, PDUClass)]

  define_attributes :version, :community
end
```

This definition uses lots of stuffs from [`rasn1`](https://github.com/sdaubert/rasn1/wiki) gem:

* `sequence` defines a ASN.1 Sequence named `message`. This sequence contains:
  * an Enumerated named `version`,
  * an Octet String named `community`,
  * and a PDUClass (subclass of [`RASN1::Model`](http://www.rubydoc.info/gems/rasn1/RASN1/Model)) named `data` and not defined here.

`define_attributes` is a helper method to declare attributes from some ASN.1 fields. This helps to mimic standard header behavior.

## Packet magics

`PacketGen::Packet` defines some magics using specific header methods.

If your header class has a checksum field and/or a length field, `Packet` provides magic for them. You have to define a `#calc_checksum` and/or a `calc_length` which appropriately set checksum and/or length fields respectively. Then `Packet#calc_checksum` and `Packet#calc_length` will calculate all checksum and length fields in all headers, including yours.

If your header class is not an application layer one, you should define a `body` field of type `PacketGen::Types::String`. This will allow `Packet#parse` to automagically parse headers embedded in yours. Same magic will happen for `Packet#to_s`, `Packet#encapsulate`, `Packet#decapsulate` and `Packet#add`.

## Header minimal API

`PacketGen::Header::Base` and `PacketGen::Header::ASN1Base` are provided to simplify writing of new headers. But they may not be so useful for some protocol types.

So, here is minimal API needed by PacketGen to handle a header class.

A header MUST have accessors:

* `#packet` : get/set packet to which header belongs.

A header MUST respond to:

* `#protocol_name`: get protocol name, usually class name as a String, without

  &#x20;module path,
* `#method_name`: get method name, usually same as protocol name but downcase.

  This name is used as accessor from packet to access header object,
* `#read`: method to parse binary string and decode header,
* `#to_s`: method to generate binary data of header to send on wire,
* `#parse?`: return `true` if decoded header is correct. Used when guessing if

  header may be decoded from binary string. An example of use if checking first

  4-bit field for IP version in a IP/IPv6 header.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://packetgen.gitbook.io/wiki/advanced-use/create-custom-protocol.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
