packetgen 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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