packetgen 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -48,14 +48,14 @@ module PacketGen
48
48
  #
49
49
  # # encrypt ESP payload
50
50
  # cipher = OpenSSL::Cipher.new('aes-128-gcm')
51
- # cipher.encrypt
51
+ # cipher.encrypt!
52
52
  # cipher.key = 16bytes_key
53
53
  # iv = 8bytes_iv
54
54
  # esp.esp.encrypt! cipher, iv, salt: 4bytes_gcm_salt
55
55
  #
56
56
  # === Decrypt a ESP packet using CBC mode and HMAC-SHA-256
57
57
  # cipher = OpenSSL::Cipher.new('aes-128-cbc')
58
- # cipher.decrypt
58
+ # cipher.decrypt!
59
59
  # cipher.key = 16bytes_key
60
60
  #
61
61
  # hmac = OpenSSL::HMAC.new(hmac_key, OpenSSL::Digest::SHA256.new)
@@ -21,7 +21,7 @@ module PacketGen
21
21
  # pkt.eth # => PacketGen::Header::Eth
22
22
  #
23
23
  # == Ethernet attributes
24
- # eth.dst = "00:01:02:03:04:05'
24
+ # eth.dst = "00:01:02:03:04:05"
25
25
  # eth.src # => "00:01:01:01:01:01"
26
26
  # eth[:src] # => PacketGen::Header::Eth::MacAddr
27
27
  # eth.ethertype # => 16-bit Integer
@@ -77,13 +77,6 @@ module PacketGen
77
77
  end
78
78
  end
79
79
 
80
- # @private snap length for PCAPRUB
81
- PCAP_SNAPLEN = 0xffff
82
- # @private promiscuous (or not) for PCAPRUB
83
- PCAP_PROMISC = false
84
- # @private timeout for PCAPRUB
85
- PCAP_TIMEOUT = 1
86
-
87
80
  # @!attribute dst
88
81
  # @return [MacAddr] Destination MAC address
89
82
  define_field :dst, MacAddr, default: '00:00:00:00:00:00'
@@ -107,6 +107,9 @@ module PacketGen
107
107
  end
108
108
  end
109
109
 
110
+ # IP Ether type
111
+ ETHERTYPE = 0x0800
112
+
110
113
  # @!attribute u8
111
114
  # First byte of IP header. May be accessed through {#version} and {#ihl}.
112
115
  # @return [Integer] first byte of IP header.
@@ -153,7 +156,7 @@ module PacketGen
153
156
  # @!attribute flag_df
154
157
  # @return [Boolean] Don't Fragment flag
155
158
  # @!attribute flag_mf
156
- # @return [Boolena] More Fragment flags
159
+ # @return [Boolean] More Fragment flags
157
160
  # @!attribute fragment_offset
158
161
  # @return [Integer] 13-bit fragment offset
159
162
  define_bit_fields_on :frag, :flag_rsv, :flag_df, :flag_mf, :fragment_offset, 13
@@ -222,11 +225,19 @@ module PacketGen
222
225
  end
223
226
  str
224
227
  end
228
+
229
+ # Check version field
230
+ # @see [Base#parse?]
231
+ def parse?
232
+ version == 4
233
+ end
225
234
  end
226
235
 
227
236
  self.add_class IP
228
237
 
229
- Eth.bind_header IP, ethertype: 0x800
238
+ Eth.bind_header IP, ethertype: IP::ETHERTYPE
239
+ SNAP.bind_header IP, proto_id: IP::ETHERTYPE
240
+ Dot1q.bind_header IP, ethertype: IP::ETHERTYPE
230
241
  IP.bind_header IP, protocol: 4
231
242
  end
232
243
  end
@@ -114,6 +114,9 @@ module PacketGen
114
114
  end
115
115
  end
116
116
 
117
+ # IPv6 Ether type
118
+ ETHERTYPE = 0x86dd
119
+
117
120
  # @!attribute u32
118
121
  # First 32-bit word of IPv6 header
119
122
  # @return [Integer]
@@ -212,11 +215,19 @@ module PacketGen
212
215
  end
213
216
  str
214
217
  end
218
+
219
+ # Check version field
220
+ # @see [Base#parse?]
221
+ def parse?
222
+ version == 6
223
+ end
215
224
  end
216
225
 
217
226
  self.add_class IPv6
218
227
 
219
- Eth.bind_header IPv6, ethertype: 0x86DD
228
+ Eth.bind_header IPv6, ethertype: IPv6::ETHERTYPE
229
+ SNAP.bind_header IPv6, proto_id: IPv6::ETHERTYPE
230
+ Dot1q.bind_header IPv6, ethertype: IPv6::ETHERTYPE
220
231
  IP.bind_header IPv6, protocol: 41 # 6to4
221
232
  end
222
233
  end
@@ -0,0 +1,56 @@
1
+ # coding: utf-8
2
+ # This file is part of PacketGen
3
+ # See https://github.com/sdaubert/packetgen for more informations
4
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
+ # This program is published under MIT license.
6
+
7
+ module PacketGen
8
+ module Header
9
+
10
+ # Logical-Link Control header
11
+ #
12
+ # A LLC header consists of:
13
+ # * a {#dsap} ({Types::Int8}),
14
+ # * a {#ssap} ({Types::Int8}),
15
+ # * a {#control} ({Types::Int8}),
16
+ # * and a {#body} (a {Types::String} or another {Base} class).
17
+ # @author Sylvain Daubert
18
+ class LLC < Base
19
+ # @!attribute dsap
20
+ # @return [Integer] 8-bit dsap value
21
+ define_field :dsap, Types::Int8
22
+ # @!attribute ssap
23
+ # @return [Integer] 8-bit ssap value
24
+ define_field :ssap, Types::Int8
25
+ # @!attribute control
26
+ # @return [Integer] 8-bit control value
27
+ define_field :control, Types::Int8
28
+ # @!attribute body
29
+ # @return [Types::String,Header::Base]
30
+ define_field :body, Types::String
31
+ end
32
+ self.add_class LLC
33
+ Dot11::Data.bind_header LLC, op: :and, type: 2, :wep? => false
34
+
35
+ # Sub-Network Access Protocol
36
+ #
37
+ # A SNAP header consists of:
38
+ # * a {#oui} ({Types::OUI}),
39
+ # * a {#proto_id} ({Types::Int16}),
40
+ # * and a {#body} (a {Types::String} or another {Base} class).
41
+ # @author Sylvain Daubert
42
+ class SNAP < Base
43
+ # @!attribute oui
44
+ # @return [Types::OUI]
45
+ define_field :oui, Types::OUI
46
+ # @!attribute proto_id
47
+ # @return [Integer] 16-bit protocol id
48
+ define_field :proto_id, Types::Int16
49
+ # @!attribute body
50
+ # @return [Types::String,Header::Base]
51
+ define_field :body, Types::String
52
+ end
53
+ self.add_class SNAP
54
+ LLC.bind_header SNAP, op: :and, dsap: 170, ssap: 170, control: 3
55
+ end
56
+ end
@@ -57,7 +57,7 @@ module PacketGen
57
57
  # @param [#to_s] body
58
58
  # @return [String]
59
59
  def self.inspect_body(body)
60
- return '' if body.nil?
60
+ return '' if body.nil? or body.empty?
61
61
  str = dashed_line('Body', 2)
62
62
  str << (0..15).to_a.map { |v| " %02d" % v}.join << "\n"
63
63
  str << '-' * INSPECT_MAX_WIDTH << "\n"
@@ -319,11 +319,14 @@ module PacketGen
319
319
  if prev_header
320
320
  bindings = prev_header.class.known_headers[header.class]
321
321
  if bindings.nil?
322
- msg = "#{prev_header.class} knowns no layer association with #{protocol}. "
323
- msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, "
324
- msg << "#{prev_header.protocol_name.downcase}_proto_field: "
325
- msg << "value_for_#{protocol.downcase})"
326
- raise ArgumentError, msg
322
+ bindings = prev_header.class.known_headers[header.class.superclass]
323
+ if bindings.nil?
324
+ msg = "#{prev_header.class} knowns no layer association with #{protocol}. "
325
+ msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, "
326
+ msg << "#{prev_header.protocol_name.downcase}_proto_field: "
327
+ msg << "value_for_#{protocol.downcase})"
328
+ raise ArgumentError, msg
329
+ end
327
330
  end
328
331
  bindings.set(prev_header) if !bindings.empty? and !parsing
329
332
  prev_header[:body] = header
@@ -359,8 +362,9 @@ module PacketGen
359
362
  last_known_hdr = @headers.last
360
363
  search_header(last_known_hdr) do |nh|
361
364
  str = last_known_hdr.body
362
- add_header nh.new, parsing: true
363
- @headers[-1, 1] = @headers.last.read(str)
365
+ nheader = nh.new
366
+ nheader = nheader.read(str)
367
+ add_header nheader, parsing: true
364
368
  end
365
369
  decode_packet_bottom_up = (@headers.last != last_known_hdr)
366
370
  end
@@ -368,7 +372,7 @@ module PacketGen
368
372
 
369
373
  def search_header(hdr)
370
374
  hdr.class.known_headers.each do |nh, bindings|
371
- if bindings.check?(hdr)
375
+ if bindings.check?(hdr) and hdr.parse?
372
376
  yield nh
373
377
  break
374
378
  end
@@ -21,6 +21,17 @@ module PacketGen
21
21
 
22
22
  # IEEE 802.3 Ethernet (10Mb, 100Mb, 1000Mb, and up)
23
23
  LINKTYPE_ETHERNET = 1
24
+ # Raw IP; the packet begins with an IPv4 or IPv6 header, with the "version"
25
+ # field of the header indicating whether it's an IPv4 or IPv6 header.
26
+ LINKTYPE_RAW = 101
27
+ # IEEE 802.11 wireless LAN
28
+ LINKTYPE_IEEE802_11 = 105
29
+ # RadioTap link layer informations + IEEE 802.11 wireless LAN
30
+ LINKTYPE_IEEE802_11_RADIOTAP = 127
31
+ # Per-Packet Information information, as specified by the Per-Packet Information
32
+ # Header Specification, followed by a packet with the LINKTYPE_ value specified
33
+ # by the +pph_dlt+ field of that header.
34
+ LINKTYPE_PPI = 192
24
35
  # Raw IPv4; the packet begins with an IPv4 header.
25
36
  LINKTYPE_IPV4 = 228
26
37
  # Raw IPv6; the packet begins with an IPv6 header.
@@ -12,9 +12,12 @@ module PacketGen
12
12
  # Known link types
13
13
  KNOWN_LINK_TYPES = {
14
14
  LINKTYPE_ETHERNET => 'Eth',
15
+ LINKTYPE_IEEE802_11 => 'Dot11',
16
+ LINKTYPE_IEEE802_11_RADIOTAP => 'RadioTap',
17
+ LINKTYPE_PPI => 'PPI',
15
18
  LINKTYPE_IPV4 => 'IP',
16
19
  LINKTYPE_IPV6 => 'IPv6'
17
- }
20
+ }.freeze
18
21
 
19
22
  # Get file sections
20
23
  # @return [Array]
@@ -74,7 +77,7 @@ module PacketGen
74
77
  end
75
78
  end
76
79
 
77
- # Give an array of parsed packets (raw data from packets).
80
+ # Give an array of raw packets (raw data from packets).
78
81
  # If a block is given, yield raw packet data from the given file.
79
82
  # @overload read_packet_bytes(fname)
80
83
  # @param [String] fname pcapng file name
@@ -11,3 +11,4 @@ require_relative 'types/int_string'
11
11
  require_relative 'types/fields'
12
12
  require_relative 'types/array'
13
13
  require_relative 'types/tlv'
14
+ require_relative 'types/oui'
@@ -6,8 +6,84 @@
6
6
  module PacketGen
7
7
  module Types
8
8
 
9
- # @abstract
10
- # Set of fields
9
+ # @abstract Set of fields
10
+ # This class is a base class to define headers or anything else with a binary
11
+ # format containing multiple fields.
12
+ #
13
+ # == Basics
14
+ # A {Fields} subclass is generaly composed of multiple binary fields. These fields
15
+ # have each a given type. All types from {Types} module are supported, and all
16
+ # {Fields} subclasses may also be used as field type.
17
+ #
18
+ # To define a new subclass, it has to inherit from {Fields}. And some class
19
+ # methods have to be used to declare attributes/fields:
20
+ # class MyBinaryStructure < PacketGen::Types::Fields
21
+ # # define a first Int8 attribute, with default value: 1
22
+ # define_field :attr1, PacketGen::Types::Int8, default: 1
23
+ # #define a second attribute, of kind Int32
24
+ # define_field :attr2, PacketGen::Types::Int32
25
+ # end
26
+ #
27
+ # These defintions create 4 methods: +#attr1+, +#attr1=+, +#attr2+ and +#attr2=+.
28
+ # All these methods take and/or return Integers.
29
+ #
30
+ # Fields may also be accessed through {#[]} ans {#[]=}. These methods give access
31
+ # to type object:
32
+ # mybs = MyBinaryStructure.new
33
+ # mybs.attr1 # => Integer
34
+ # mybs[:attr1] # => PacketGen::Types::Int8
35
+ #
36
+ # {#initialize} accepts an option hash to populate attributes. Keys are attribute
37
+ # name symbols, and values are those expected by writer accessor.
38
+ #
39
+ # {#read} is able to populate object from a binary string.
40
+ #
41
+ # {#to_s} returns binary string from object.
42
+ #
43
+ # == Add Fields
44
+ # {.define_field} adds a field to Fields subclass. A lot of field types may be
45
+ # defined: integer types, string types (to handle a stream of bytes). More
46
+ # complex field types may be defined using others Fields subclasses:
47
+ # # define a 16-bit little-endian integer field, named type
48
+ # define_field :type, PacketGen::Types::Int16le
49
+ # # define a string field
50
+ # define_field :body, PacketGen::Types::String
51
+ # # define afield using a complex type (Fields subclass)
52
+ # define_field :mac_addr, PacketGen::Eth::MacAddr
53
+ #
54
+ # This example creates six methods on our Fields subclass: +#type+, +#type=+,
55
+ # +#body+, +#body=+, +#mac_addr+ and +#mac_addr=+.
56
+ #
57
+ # {.define_field} has many options (third optional Hash argument):
58
+ # * +:default+ gives default field value. It may be a simple value (an Integer
59
+ # for an Int field, for example) or a lambda,
60
+ # * +:builder+ to give a builder/constructor lambda to create field. The lambda
61
+ # takes one argument: {Fields} subclass object owning field.
62
+ # For example:
63
+ # # 32-bit integer field defaulting to 1
64
+ # define_field :type, PacketGen::Types::Int32, default: 1
65
+ # # 16-bit integer field, created with a random value. Each instance of this
66
+ # # object will have a different value.
67
+ # define_field :id, PacketGen::Types::Int16, default: ->{ rand(65535) }
68
+ # # a size field
69
+ # define_field :body_size, PacketGen::Type::Int16
70
+ # # String field which length is taken from body_size field
71
+ # define_field :body, PacketGen::Type::String, builder: ->(obj) { PacketGen::Type::String.new('', length_from: obj[:body_size]) }
72
+ #
73
+ # {.define_field_before} and {.define_field_after} are also defined to relatively
74
+ # create a field from anoher one (for example, when adding a field in a subclass).
75
+ # == Generating bit fields
76
+ # {.define_bit_fields_on} creates a bit field on a previuously declared integer
77
+ # field. For example, +frag+ field in IP header:
78
+ # define_field :frag, Types::Int16, default: 0
79
+ # define_bit_fields_on :frag, :flag_rsv, :flag_df, :flag_mf, :fragment_offset, 13
80
+ #
81
+ # This example generates methods:
82
+ # * +#frag+ and +#frag=+ to access +frag+ field as a 16-bit integer,
83
+ # * +#flag_rsv?+, +#flag_rsv=+, +#flag_df?+, +#flag_df=+, +#flag_mf?+ and +#flag_mf=+
84
+ # to access Boolean RSV, MF and DF flags from +frag+ field,
85
+ # * +#fragment_offset+ and +#fragment_offset=+ to access 13-bit integer fragment
86
+ # offset subfield from +frag+ field.
11
87
  # @author Sylvain Daubert
12
88
  class Fields
13
89
 
@@ -187,7 +263,7 @@ module PacketGen
187
263
 
188
264
  # Create a new header object
189
265
  # @param [Hash] options Keys are symbols. They should have name of object
190
- # attributes, as defined by {.define_field} and by {.define_bit_field}.
266
+ # attributes, as defined by {.define_field} and by {.define_bit_fields_on}.
191
267
  def initialize(options={})
192
268
  @fields = {}
193
269
  self.class.class_eval { @field_defs }.each do |field, ary|
@@ -235,12 +311,6 @@ module PacketGen
235
311
  @ordered_fields ||= self.class.class_eval { @ordered_fields }
236
312
  end
237
313
 
238
- # Return header protocol name
239
- # @return [String]
240
- def protocol_name
241
- self.class.to_s.sub(/.*::/, '')
242
- end
243
-
244
314
  # Populate object from a binary string
245
315
  # @param [String] str
246
316
  # @return [Fields] self
@@ -283,7 +353,7 @@ module PacketGen
283
353
  fields.map { |f| force_binary @fields[f].to_s }.join
284
354
  end
285
355
 
286
- # Size of object as binary strinf
356
+ # Size of object as binary string
287
357
  # @return [nteger]
288
358
  def sz
289
359
  to_s.size
@@ -59,7 +59,7 @@ module PacketGen
59
59
  @length.read @string.length
60
60
  end
61
61
 
62
- # Give binary string length
62
+ # Give binary string length (including +length+ field)
63
63
  # @return [Integer]
64
64
  def sz
65
65
  to_s.size
@@ -0,0 +1,48 @@
1
+ # coding: utf-8
2
+ # This file is part of PacketGen
3
+ # See https://github.com/sdaubert/packetgen for more informations
4
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
+ # This program is published under MIT license.
6
+
7
+ module PacketGen
8
+ module Types
9
+
10
+ # OUI type, defined as a set of 3 bytes
11
+ # oui = OUI.new
12
+ # oui.from_human('00:01:02')
13
+ # oui.to_human # => "00:01:02"
14
+ #@author Sylvain Daubert
15
+ class OUI < Types::Fields
16
+ # @attribute b2
17
+ # @return [Integer] left-most byte
18
+ define_field :b2, Types::Int8
19
+ # @attribute b1
20
+ # @return [Integer] center byte
21
+ define_field :b1, Types::Int8
22
+ # @attribute b0
23
+ # @return [Integer] right-most byte
24
+ define_field :b0, Types::Int8
25
+
26
+ # Read a human-readable string to populate object
27
+ # @param [String] str
28
+ # @return [OUI] self
29
+ def from_human(str)
30
+ return self if str.nil?
31
+ bytes = str.split(/:/)
32
+ unless bytes.size == 3
33
+ raise ArgumentError, 'not a OUI'
34
+ end
35
+ self[:b2].read(bytes[0].to_i(16))
36
+ self[:b1].read(bytes[1].to_i(16))
37
+ self[:b0].read(bytes[2].to_i(16))
38
+ self
39
+ end
40
+
41
+ # Get OUI in human readable form (colon-separated bytes)
42
+ # @return [String]
43
+ def to_human
44
+ fields.map { |m| "#{'%02x' % self[m]}" }.join(':')
45
+ end
46
+ end
47
+ end
48
+ end
@@ -7,15 +7,15 @@
7
7
  module PacketGen
8
8
  module Types
9
9
 
10
- # This class is just like regular String. It only adds #read and #sz methods
10
+ # This class is just like regular String. It only adds {#read} and {#sz} methods
11
11
  # to be compatible with others {Types}.
12
12
  # @author Sylvain Daubert
13
13
  class String < ::String
14
14
 
15
15
  # @param [String] str
16
16
  # @param [Hash] options
17
- # @option options [Types::Int] :length_from object from which takes length when
18
- # reading
17
+ # @option options [Types::Int,Proc] :length_from object or proc from which
18
+ # takes length when reading
19
19
  def initialize(str='', options={})
20
20
  super(str)
21
21
  @length_from = options[:length_from]
@@ -25,7 +25,12 @@ module PacketGen
25
25
  # @return [String] self
26
26
  def read(str)
27
27
  s = str.to_s
28
- s = s[0, @length_from.to_i] unless @length_from.nil?
28
+ s = case @length_from
29
+ when Int
30
+ s[0, @length_from.to_i]
31
+ else
32
+ s
33
+ end
29
34
  self.replace s
30
35
  self
31
36
  end