Packet Builder Language ======================= :author: hhaim :email: :revnumber: 0.04 :quotes.++: :numbered: == change log [options="header",cols="^1,^h,a"] |================= | Version | name | meaning | 0.01 | hhaim | - first version | 0.02 | hhaim | - change the bool fields to properties - add external/internal property - add const property ( instead cant_change) - change TLV property - now learn the prev header - add choice of next protocol that is not base on a field ( TCP->IP->TCP) | 0.03 | ybrustin | - add MAC address regexp - add gui_representation class with data_type, form_type, combobox_values, data_type_regexp items to describe GUI view of field - rename choice attribute to value_based_next_header - fixed some typos | 0.04 | ybrustin | - change value_based_next_header, combobox_values (to be consistent with value_based_next_header) to dictionary - added value_based_next_class for options - move 'help' attribute to gui_representation - add link to headers.yaml (references at bottom of the page) |================= == A file format for GUI packet builder === Introduction We would like a file that will be read by GUI and will give us the ability to build packets using GUI The format should be *YAML* === High Level Requirement * Define a YAML object format for dynamic building of packets and a program that change various fields * Ability to *parse* back the same buffer that was created using this tool (reversibility) ** Ability to load packet from a pcap file and parse it * Ability to save the packet to a pcap file * Ability to save the packet and program in JSON format (same JSON-RPC format) * Set a value for any field of any protocol * Vary packet fields across packets at run time e.g. changing IP/MAC addresses * Stack protocols in any arbitrary order define in YAML format === Header that should be supported (first phase) ==== L2 * Ethernet * 802.3 * LLC SNAP * VLAN (with QinQ) stack * MPLS stack ==== L3 * ARP * IPv4 * IPv6 (4x header) * IP-in-IP a.k.a IP Tunnelling (6over4, 4over6, 4over4, 6over6) ==== L4 * TCP * UDP * ICMPv4 * ICMPv6 * IGMP ==== L7 anchor:Payload[] * Any text based protocol (HTTP, SIP, RTSP, NNTP etc.) ** random string ** repeat string * Pattern Binary ** repeat of value (e.g 0x55) ** random ** seq (1,2,3,3,4) ** User Hex Dump editor === YAML Format ==== Header section .Default Types anchor:Types[] [options="header",cols="1,2,3"] |================= | Field Name | meaning | size in bits | bit | describe the header object e.g tcp | 1 | uint8 | describe the header object e.g tcp | 8 | uint16 | the name in the GUI | 16 | uint32 | sub fields of this header | 32 | uint64 | sub fields of this header | 64 | other class type | name of other class. for example, "c-mac-addr"; take fields from there, optionally overload them later | The size taken from that class | Payload | xref:Payload[Payload] | total packet size - all header until now | vlen_t | in case of varible size header this include the size to the end of varible size header see example xref:IpvOption[Ipv4Option] |total size of the object |================= .Default Data_Type anchor:Data_Type[] [options="header",cols="1,2"] |================= | Field Name | meaning | none | use Hex Editor as Types | ipv4_t | 4 decimals 0-255 each | mac_addr_t | ([0-9a-fA-F]\{2\}:)\{5\}[0-9a-fA-F]\{2\} | ipv4_mask_t | should match uint32 type | ipv6_t | should have 16 bytes field size 8x16 | ipv6_mask_t | should have 16 bytes field size 8x16 | another header class | sub fields of this header | char_t | array of bytes , look into the array_size of cost string | var_char_t | array based on a field value look into | regexp_t | define a Java function that converts a reg exp string to a buffer see here xref:GenRegExp[RegExp] |================= .Default Form_Type anchor:Form_Type[] [options="header",cols="1,3"] |================= | Field Name | meaning | none | simple editing field | combo_with_edit | combo box with predefined choices, can edit the field value manually | combo_without_edit | combo box with predefined choices, can [underline]#not# edit the field value manually | checkbox | toggle bits values, if item is array of bits, display several checkboxes per number of bits |================= .Default Gui_Representation anchor:Gui_Representation[] [options="header",cols="1,^1,5,^1,10"] |================= | Field Name | value type | meaning | Link | Additional info | help | string | the name in the GUI | | | data_type | string | how to represent data | xref:Data_Type[Data_Type] | data_type could get data_type_regexp e.g data_type = "ipv4"; data_type = "regexp" data_type_regexp = "string that define regexp and Java function" | form_type | string | which editing form to use | xref:Form_Type[Form_Type] | for example for ip address use combobox with option to edit value manually or choose: key "localhost" value "127.0.0.1" etc. | combobox_values | dictionary | pairs of 'key - value' for combo_with/without_edit | | | data_type_regexp | string | in case it is reg_exp the name of the function | xref:GenRegExp[GenRegExp] | |================= .Default Properties anchor:Properties[] [options="header",cols="1,7"] |================= | Field Name | meaning | ipv4_checksum | auto calculates checksum on this header Ipv4 type | tcp_checksum | calculate TCP checksum | udp_checksum | calculate UDP checksum | ipv4_total_length | calculate ipv4 total length this pkt_size = header + reset of packet | tlv | TLV length of the header (inlcudes the prev field length) example ip-option, tcp-option | le | little endian. deault is big | const | const field for example the 4 version of ipv4 header - this GUI won't give option to change this field | external | marks the header as an external header for the GUI. for example IPv4 is external header and mac-addr is internal header ( compose external header) |================= .Field_Type anchor:Field_Type[] [options="header",cols="1,^1,30,^1,^1,30"] |================= | Field Name | value type | meaning | Default Value | Link | Example | class | string | describe the class type | in case class is defined no need to have name and vise versa | | class : tcp | name | string | describe the instance name | in case class is defined no need to have name and vise versa | | name : tcp | array_size | integer | how many objects of this type, default value is 1 | 1 | | array_size : 6 in case of mac-addr | type | string | type, see Types define the size | "uint8_t" | xref:Types[Types] | type : "uint32_t" type : "mac_addr" | gui_representation | dictionary | description of how to view/edit data in GUI | | xref:Gui_Representation[Gui_Representation] | xref:Gui_Representation_Example[Gui_Representation_Example] | default | array/value | default value in the packets , you can override value for subfields in parent see example | [0 ]x header size | | xref:Overide_Subfields_Example[Overide_Subfields_Example] | properies | array of string like masks | properies of this fields | [] | xref:Properties[Properties] | ["le","external"] , ["tlv","le","const"] | value_based_next_header | dictionary | define the next protocol based on a field value | none | xref:Value_Based_Next_Header[Value_Based_Next_Header] | | value_based_next_class | dictionary | define the next class based on a field value (useful for options) | none | xref:Value_Based_Next_Class[Value_Based_Next_Class] | | next_headers | string or type | a name of class that define the next or just an array | "none" | xref:Next_headers[Next_headers] | | fields | array | array of Field_Type | [] | | fields : [ ] | offset | integer/string | offset into the packet in bits, in case of auto add base of prev fields | "auto" | | | option | string | a java code that define a way to calculate varible size | "none" | | | |================= .Field_Type anchor:ConstHeadesClass[] [options="header",cols="^1,^10"] |================= | Field Name | value type | "root" | the root pointer to the start of blocks L2/802.3 etc | "end" | end TLV headers | "payload" | the rest of the packets as buffer/string etc |================= .Next_headers anchor:Next_headers[] Example of Next_headers [source,python] ---- - class : "next-example-t-1" gui_representation: help : "next-example-t-1" next_headers : ["ipv4", "ipv6, "tcp"] # option 1 define in the header itself - class : "tcp" gui_representation: help : "TCP header" properies : ["external"] next_headers : ["ipv4", "ipv6, "tcp"] fields : - name : "ver" # option 2 define throw a class - class : "tcp" gui_representation: help : "TCP header" properies : ["external"] next_headers : "next-example-t-1" # fields : - name : "ver" ---- .Value_Based_Next_Header anchor:Value_Based_Next_Header[] Example of value_based_next_header [source,python] ---- value_based_next_header: 0x0800: 'ipv4'# name of an external or internal class , the GUI should distinct betwean internal and external 0x0806: 'arp' 0x86DD: 'ipv6' 0x8100: 'vlan' 0x8847: 'mpls unicast' default: 'payload' # if no match for any of above ---- .Generic RegExp Edit Field anchor:GenRegExp[] This will define a regexp that match for user input and how to converts it to buffer of bytes [source,python] ---- class MyClass : public RegExpBase { public: string get_reg_exp_string( ) { return ((\d){1-3})[.]((\d){1-3})[.]((\d){1-3})[.]((\d){1-3})) } # in case of match buffer get_buffer(){ g= [get_group()[1].to_int()*256,get_group()[1].to_int()] # return list return (g) } } ---- ==== Relations between object headers There would be a root object to point to possible starting headers [source,python] ---- - class : "root" gui_representation: help : "Root" next_headers : [ "ethernet", "llc", "_802-3"] ---- So in a way you could define a tree like this [source,python] ---- root -> L2 ( Ethernet , 802.3 , LLC SNAP ) |( by field ) | ------------------------------------- ( VLAN (with QinQ), MPLS , ipv4, ipv6, ARP , ICMP ) | | | | | ipv4/ipv6 - - | | | | [Possibility - Ethernet/802.3/LLC SNAP) | UDP/TCP/Pyload Object | | for each option there tree of all the option --- - ---- ==== Rules * The size of the header and offset is automatically defined in default by the order of the fields ( inc by type size multiply by array_size) * It can be overrided by offset field ( put offset in the object ) and then an more advanced field can be shown earlier in the GUI * The packet size is defined before the headers. Header Should not be allowed to be added if the size + header size is bigger than packet size * "Payload" is predefined Fields that take the reset of the packet and user can edit it ( see xref:Payload[Payload] ) * There would be a spare field in the Stream object so GUI could add more metadata for reconstructing the builder types for example in this example Ethrenet/IP/TCP/IP/TCP you can't extrac from buffer alone that Payload is IP/TCP only the builder known that in build time. * Ip total length need to keep the total_pkt_size - this ip header . this should work for internal header too. * When GUI add header ("external") the total size of this header should be calculated ( varible size should be given a default - ipv4) === Examples ==== TLV (Ip option) anchor:IpvOption[], value_based_next_class anchor:Value_Based_Next_Class[] IP-option see link:http://tools.ietf.org/html/rfc791[ip_option] 0 : END 1 : Length 1 other : Byte : Length ( +first) |option [source,python] ---- - class : "ip_option_131" gui_representation: help : "ip_option" fields : - name : "length" # tree with leaf of bits gui_representation: help : "length" type : uint8 properties : ["tlv"] # the length include the prev field size (8 byte) - name : "pointer" # tree with leaf of bits type : uint8 - name : "buffer" # tree with leaf of bits type : "tlv_reset" - class : "default_ip4_option_tlv" gui_representation: help : "ip_option" fields : - name : "length" # tree with leaf of bits gui_representation: help : "length" type : uint8 properties : "tlv" # the length include the prev field size (8 byte) - name : "buffer" # tree with leaf of bits type : "vlen_t" - class : "ip_option" gui_representation: help : "ip_option" type : uint8 default : [0x01] value_based_next_class : 0x00 : "end" # reserve name for ending the loop 0x01 : "ip_option" # back to this header 0x131 : "ip_option_131" 0x0812: "gre" default : "default_ip4_option_tlv" ---- * case of varible length field ip_option example ==== Example TCP/IP [source,python] ---- - class : "c-mac-addr" type : "uint8" array_size : 6 default : [0x00, 0x00, 0x01, 0x00, 0x00, 0x00] gui_representation: data_type : "mac-addr_t" # format ([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2} help : "Mac addrees" - class : "ethernet" gui_representation: help : "Ethernet-L2" properties: ['external'] fields : - name : "Dst" gui_representation: help : "destination mac" type : "c-mac-addr" - name : "Src" gui_representation: help : "source mac" type : "c-mac-addr" - name: "Ethertype" gui_representation: help: "Ethertype" type: "uint16" default: [0x0800] value_based_next_header : 0x0800 : "ipv4" 0x86DD : "ipv6" 0x8100 : "vlan" 0x8847 : "mpls" #unicast default : "payload" - class : "ipv4" gui_representation: help : "Ipv4" fields : - name : "ver" gui_representation: help : "Version" type : "bit" array_size : 4 default : [0, 1, 0, 0] properties : ["const"] - name : "ihl" type : "bit" array_size : 4 default : [0, 1, 1, 1] properties : ["ipv4_ihl"] gui_representation: help : "IHL" form_type: "checkbox" .. - name : "hdr_chsum" gui_representation: help : "Header Checksum" default : [0x00,0x00] properties : ["ipv4_check_sum", "const"] - name : "total_len" gui_representation: help : "Total Length" default : [0x00,0x00] properties : ["ipv4_total_len", "const"] # auto calculate total_size-offset_header - name : "protocol" type : uint8 default : 0x06 value_based_next_header : &ipv4_next_header 0x06 : "tcp" 0x11 : "udp" 0x29 : "ipv6" 0x2F : "gre" default : "payload" gui_representation: help : "IPv4 next Protocol" form_type: "combo_without_edit" combobox_values: <<: *ipv4_next_header # take same choices as value_based_next_header - name : "src_addr" type : uint32 default : [16, 0, 0, 0] gui_representation: help : "Source Address" data_type : "ipv4" # reserve - name : "dst_addr" default : [48, 0, 0, 0] type : uint32 gui_representation: help : "Destination Address" data_type : "ipv4" # reserve form_type : "combo_with_edit" combobox_values: [127, 0, 0, 1]: 'localhost' [255, 255, 255, 255]: 'broadcast' - class : "tcp" gui_representation: help : "TCP" properties : ["external"] fields : - name : "src_port" gui_representation: help : "Source Port" default : [0x30,0x00] type : uint16 - name : "dest_port" gui_representation: help : "Source Port" default : [0x30,0x00] type : uint16 - name : "seq" gui_representation: help : "Seq Number" type : uint32 default : [0x30,0x00,00,00] - name : "ack" gui_representation: help : "Ack Number" type : uint32 default : [0x30,0x00,00,00] ... - name : "flags" # tree with leaf of bits gui_representation: help : "Ack Number" type : uint8 default : [0x30] fields : - name : "urg" help : "URG" type : bit default : [0x0] - name : "ack" help : "ACK" type : bit default : [0x1] .. - name : "checksum" gui_representation: help : "TCP Checksum" type : uint16 default : [0x00,0x00] properties : ["tcp_checksum"] # auto calculate total_size-offset_header - class : "root" # reserve gui_representation: help : "Root" next_headers : [ "ethrenet" ,"llc","_802-3"] --------------------------- ==== Overide subfields example anchor:Overide_Subfields_Example[] In this example parent class default value overrides default values of sub-fields ( 2 different mac-addr) [source,python] ---- - class : "c-mac-addr" type : "uint8" array_size : 6 gui_representation: help : "Mac addrees" data_type : "mac-addr_t" # format ([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2} default : [0x00,0x00,0x01,0x00,0x00,0x00] - class : "ethernet" gui_representation: help : "Ethernet-L2" properties : ["external"] default : [0x00,0x01,0x01,0x00,0x00,0x00, 0x00,0x02,0x02,0x00,0x00,0x00 ,0x08,00] # change the default of sub-fields . it is const size fields : - name : "Dst" gui_representation: help : "destination mac" type : "c-mac-addr" - name : "Src" gui_representation: help : "source mac" type : "c-mac-addr" - name : "ip_protocol" type : "uint16_t" default : [0x08,0x00] value_based_next_header : 0x0800 : "ipv4" 0x86DD : "ipv6" 0x8100 : "vlan" 0x8847 : "mpls unicast" default : "payload" ---- ==== Gui Representation example anchor:Gui_Representation_Example[] [underline]#In YAML:# [source,python] ---- - name: 'Flags' type: 'bit' array_size: 3 gui_representation: help: 'IPv4 Flags' form_type: 'checkbox' # can check each bit - name: 'dst_addr' default: [48, 0, 0, 0] type: uint32 gui_representation: help: 'IPv4 Destination Address' data_type: 'ipv4_t' # special representation case, show as 4 decimal numbers form_type: 'combo_with_edit' # can choose from pre-defined values or edit manually combobox_values: [127, 0, 0, 1]: 'localhost' [255, 255, 255, 255]: 'broadcast' - name: 'protocol' type: uint8 default: 0x06 value_based_next_header: &ipv4_next_header 0x06: 'tcp' 0x11: 'udp' default : "payload" gui_representation: help: 'IPv4 Protocol Field' form_type: 'combo_without_edit' # choose from supported protocols, no manual edit combobox_values: <<: *ipv4_next_header # take same choices as value_based_next_header ---- [underline]#In GUI:# checkbox for bits: image:images/checkbox.jpg[] editing in combo-box: image:images/combo_button_editing.jpg[] choosing from predefined values: image:images/combo_button_choosing.jpg[] ==== Union base TBD === Resource * link:yaml/headers.yaml[headers.yaml] * link:https://wireedit.com/[WireEdit] * link:http://ostinato.org/[ostinato] * link:http://www.slideshare.net/nlekh/ixiaexplorer[IxExplorer]