packetgen 3.1.4 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2adf2b07c2d2d334e4f0ae6e4e7b49571d4d436788bef51df6cf7c1a5aaba9c8
4
- data.tar.gz: 8997321a31379ea6952ab42a4642d98a717587ed777652ebdb9e25b5928cc8d4
3
+ metadata.gz: 476cbc2bd2b9fb254ea40fe0a07a4d703a0fc36e4accc823de7e83b640d95a2d
4
+ data.tar.gz: 375f31ea90589b2b0c0b257b8a3efaa66715e8b369c542124fe3dcac283d9221
5
5
  SHA512:
6
- metadata.gz: b5af6e0d04f8bb03af01d63cde3879a6a13c8a78477811be2b95fee5c597820f4714f8ac8b69f4aa8f04d3d5b81c5e25b5787815ee2237a8c3329721b5dc7895
7
- data.tar.gz: 6eebb203f8dfde49505987d16778c1ecbc81c463d6ac8b5f005e2c6f486166ba0cceabd848d3b3aeebe867b138993171026e8dcacfb04c84a126bc149ff5c616
6
+ metadata.gz: c518ff72cf70227c8f9ef38cce97ba5036eb3229051d38172ba947898b9051f508670d1e0106bac0f5edf577fe1450d5f71db557aa6968b7e3998945a87177d7
7
+ data.tar.gz: 9382d6f8a7542c8988dec0aaf92940f1518885133bf9e7c835478e787546309410c7e7bcfb4525c13fb7479a9cad9a7ece3e30a3d3c6bbb4f081c1cc48b7f02e
@@ -97,6 +97,12 @@ module PacketGen
97
97
  Interfacez.default
98
98
  end
99
99
 
100
+ # Get loopback network interface
101
+ # @return [String]
102
+ def self.loopback_iface
103
+ Interfacez.loopback
104
+ end
105
+
100
106
  # Shortcut to get a header class
101
107
  # @example builtin class
102
108
  # # same as PacketGen::Header::Dot11:Data.new(id: 0xfedc)
@@ -114,8 +120,9 @@ module PacketGen
114
120
  end
115
121
 
116
122
  require 'packetgen/deprecation'
117
- require 'packetgen/types'
118
123
  require 'packetgen/inspect'
124
+ require 'packetgen/inspectable'
125
+ require 'packetgen/types'
119
126
  require 'packetgen/pcapng'
120
127
  require 'packetgen/pcap'
121
128
  require 'packetgen/packet'
@@ -13,7 +13,7 @@ module PacketGen
13
13
  class Capture
14
14
  private
15
15
 
16
- attr_reader :filter, :cap_thread, :snaplen, :promisc
16
+ attr_reader :filter, :cap_thread, :snaplen, :promisc, :monitor
17
17
 
18
18
  public
19
19
 
@@ -41,14 +41,18 @@ module PacketGen
41
41
  # yielding. Default: +true+
42
42
  # @param [Integer] snaplen maximum number of bytes to capture for
43
43
  # each packet.
44
+ # @param [Boolean] monitor enable or disable monitor mode on interface (if supported by +iface+).
44
45
  # @since 2.0.0 remove old 1.x API
45
46
  # @since 3.0.0 arguments are kwargs and no more a hash
46
- def initialize(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil)
47
- @iface = iface || Interfacez.default || Interfacez.loopback
47
+ # @since 3.1.5 add monitor argument
48
+ # @author Sylvain Daubert
49
+ # @author optix2000 - add monitor argument
50
+ def initialize(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil)
51
+ @iface = iface || PacketGen.default_iface || PacketGen.loopback_iface
48
52
 
49
53
  @packets = []
50
54
  @raw_packets = []
51
- set_options iface, max, timeout, filter, promisc, parse, snaplen
55
+ set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
52
56
  end
53
57
 
54
58
  # Start capture
@@ -56,8 +60,11 @@ module PacketGen
56
60
  # @yieldparam [Packet,String] packet if a block is given, yield each
57
61
  # captured packet (Packet or raw data String, depending on +:parse+ option)
58
62
  # @since 3.0.0 arguments are kwargs and no more a hash
59
- def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, &block)
60
- set_options iface, max, timeout, filter, promisc, parse, snaplen
63
+ # @since 3.1.5 add monitor argument
64
+ # @author Sylvain Daubert
65
+ # @author optix2000 - add monitor argument
66
+ def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil, &block)
67
+ set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
61
68
 
62
69
  @cap_thread = Thread.new do
63
70
  PCAPRUBWrapper.capture(**capture_args) do |packet_data|
@@ -79,7 +86,7 @@ module PacketGen
79
86
 
80
87
  private
81
88
 
82
- def set_options(iface, max, timeout, filter, promisc, parse, snaplen)
89
+ def set_options(iface, max, timeout, filter, promisc, parse, snaplen, monitor)
83
90
  @max = max if max
84
91
  @filter = filter unless filter.nil?
85
92
  @timeout = timeout unless timeout.nil?
@@ -87,10 +94,11 @@ module PacketGen
87
94
  @snaplen = snaplen unless snaplen.nil?
88
95
  @parse = parse unless parse.nil?
89
96
  @iface = iface unless iface.nil?
97
+ @monitor = monitor unless monitor.nil?
90
98
  end
91
99
 
92
100
  def capture_args
93
- h = { iface: iface, filter: filter }
101
+ h = { iface: iface, filter: filter, monitor: monitor }
94
102
  h[:snaplen] = snaplen unless snaplen.nil?
95
103
  h[:promisc] = promisc unless promisc.nil?
96
104
  h
@@ -22,37 +22,43 @@ module PacketGen
22
22
  attr_reader :default_iface
23
23
 
24
24
  def initialize
25
- @default_iface = Interfacez.default || Interfacez.loopback
25
+ @default_iface = PacketGen.default_iface || PacketGen.loopback_iface
26
26
  @hwaddr = {}
27
27
  @ipaddr = {}
28
28
  @ip6addr = {}
29
29
 
30
- Interfacez.all do |iface_name|
31
- @hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
32
- @ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
33
- @ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
34
- end
30
+ initialize_local_addresses
35
31
  end
36
32
 
37
33
  # Get MAC address for given network interface
38
34
  # @param [String,nil] iface network interface. If +nil+, use default one.
39
35
  # @return [String]
40
36
  def hwaddr(iface=nil)
41
- @hwaddr[iface || @default_iface]
37
+ @hwaddr[iface || default_iface]
42
38
  end
43
39
 
44
40
  # Get IP address for given network interface
45
41
  # @param [String,nil] iface network interface. If +nil+, use default one.
46
42
  # @return [String]
47
43
  def ipaddr(iface=nil)
48
- @ipaddr[iface || @default_iface]
44
+ @ipaddr[iface || default_iface]
49
45
  end
50
46
 
51
47
  # Get IPv6 addresses for given network interface
52
48
  # @param [String,nil] iface network interface. If +nil+, use default one.
53
49
  # @return [Array<String>]
54
50
  def ip6addr(iface=nil)
55
- @ip6addr[iface || @default_iface]
51
+ @ip6addr[iface || default_iface]
52
+ end
53
+
54
+ private
55
+
56
+ def initialize_local_addresses
57
+ Interfacez.all do |iface_name|
58
+ @hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
59
+ @ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
60
+ @ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
61
+ end
56
62
  end
57
63
  end
58
64
  end
@@ -31,7 +31,7 @@ module PacketGen
31
31
  complete_deprecated_method_name = "#{base_name}#{deprecated_method}"
32
32
  complete_new_method_name = "#{base_name}#{new_method}" unless new_method.nil?
33
33
 
34
- file, line = caller(2..2).split(':')[0, 2]
34
+ file, line = caller(2..2).first.split(':')[0, 2]
35
35
  message = +"#{file}:#{line}: #{complete_deprecated_method_name} is deprecated"
36
36
  message << " in favor of #{complete_new_method_name}" unless new_method.nil?
37
37
  message << '. ' << self.removed(remove_version)
@@ -230,17 +230,7 @@ module PacketGen
230
230
 
231
231
  # @!attribute options
232
232
  # @return [RequestedOptions]
233
- define_field :options, RequestedOptions
234
-
235
- # Populate object from +str+
236
- # @param [String] str
237
- # @return [self]
238
- def read(str)
239
- self[:type].read str[0, 2]
240
- self[:length].read str[2, 2]
241
- self[:options].read str[4, self.length]
242
- self
243
- end
233
+ define_field :options, RequestedOptions, builder: ->(h, t) { t.new(length_from: h[:length]) }
244
234
 
245
235
  # Get human-readable data
246
236
  # @return [String]
@@ -35,7 +35,7 @@ module PacketGen
35
35
  while !str.empty? && (self.size < @counter.to_i)
36
36
  question = Question.new(@dns).read(str)
37
37
  str.slice!(0, question.sz)
38
- self.push question
38
+ push question
39
39
  end
40
40
  self
41
41
  end
@@ -28,7 +28,7 @@ module PacketGen
28
28
  define_field :dlt, Types::Int32le
29
29
  # @!attribute ppi_fields
30
30
  # @return [Type::String] concatenation of PPI fields
31
- define_field :ppi_fields, Types::String
31
+ define_field :ppi_fields, Types::String, builder: ->(h, t) { t.new(length_from: -> { h.length - 8 }) }
32
32
  # @!attribute body
33
33
  # @return [Type::String]
34
34
  define_field :body, Types::String
@@ -36,21 +36,6 @@ module PacketGen
36
36
  # @return [Boolean] align flag from {#flags} attribute
37
37
  define_bit_fields_on :flags, :reserved, 7, :align
38
38
 
39
- # @param [String] str
40
- # @return [PPI] self
41
- def read(str)
42
- return self if str.nil?
43
-
44
- force_binary str
45
- self[:version].read str[0, 1]
46
- self[:flags].read str[1, 1]
47
- self[:length].read str[2, 2]
48
- self[:dlt].read str[4, 4]
49
- self[:ppi_fields].read str[8, length - 8]
50
- self[:body].read str[length, str.size]
51
- self
52
- end
53
-
54
39
  # Check version field
55
40
  # @see [Base#parse?]
56
41
  def parse?
@@ -91,26 +76,11 @@ module PacketGen
91
76
  define_field :present_flags, Types::Int32le
92
77
  # @!attribute radio_fields
93
78
  # @return [Type::String] concatenation of RadioTap fields
94
- define_field :radio_fields, Types::String
79
+ define_field :radio_fields, Types::String, builder: ->(h, t) { t.new(length_from: -> { h.length - 8 }) }
95
80
  # @!attribute body
96
81
  # @return [Type::String]
97
82
  define_field :body, Types::String
98
83
 
99
- # @param [String] str
100
- # @return [RadioTap] self
101
- def read(str)
102
- return self if str.nil?
103
-
104
- force_binary str
105
- self[:version].read str[0, 1]
106
- self[:pad].read str[1, 1]
107
- self[:length].read str[2, 2]
108
- self[:present_flags].read str[4, 4]
109
- self[:radio_fields].read str[8, length - 8]
110
- self[:body].read str[length, str.size]
111
- self
112
- end
113
-
114
84
  # Check version field
115
85
  # @see [Base#parse?]
116
86
  def parse?
@@ -45,20 +45,7 @@ module PacketGen
45
45
  define_field :length, Types::Int16
46
46
  # @!attribute body
47
47
  # @return [Types::String,Header::Base]
48
- define_field :body, Types::String
49
-
50
- # Populate object from string
51
- # @param [String] str
52
- # @return [self]
53
- def read(str)
54
- return self if str.nil?
55
-
56
- self[:version].read(str[0, 1])
57
- self[:type].read(str[1, 1])
58
- self[:length].read(str[2, 2])
59
- self[:body].read(str[4, self.length])
60
- self
61
- end
48
+ define_field :body, Types::String, builder: ->(h, t) { t.new(length_from: h[:length]) }
62
49
 
63
50
  # Get human readable type
64
51
  # @return [String]
@@ -132,21 +132,17 @@ module PacketGen
132
132
  def read(str)
133
133
  super str
134
134
  return self unless self.class == EAP
135
-
136
- if type?
137
- obj = case self.type
138
- when 4
139
- EAP::MD5.new.read(str)
140
- when 13
141
- EAP::TLS.new.read(str)
142
- when 21
143
- EAP::TTLS.new.read(str)
144
- when 43
145
- EAP::FAST.new.read(str)
146
- else
147
- self
148
- end
149
- obj
135
+ return self unless type?
136
+
137
+ case self.type
138
+ when 4
139
+ EAP::MD5.new.read(str)
140
+ when 13
141
+ EAP::TLS.new.read(str)
142
+ when 21
143
+ EAP::TTLS.new.read(str)
144
+ when 43
145
+ EAP::FAST.new.read(str)
150
146
  else
151
147
  self
152
148
  end
@@ -33,6 +33,8 @@ module PacketGen
33
33
  # Ethernet MAC address, as a group of 6 bytes
34
34
  # @author Sylvain Daubert
35
35
  class MacAddr < Types::Fields
36
+ include Inspectable
37
+
36
38
  # @!attribute a0
37
39
  # @return [Integer] first byte from MacAddr
38
40
  define_field :a0, Types::Int8
@@ -75,6 +77,7 @@ module PacketGen
75
77
  def to_human
76
78
  fields.map { |m| '%02x' % self[m] }.join(':')
77
79
  end
80
+ alias format_inspect to_human
78
81
 
79
82
  def ==(other)
80
83
  other.is_a?(self.class) &&
@@ -12,6 +12,8 @@ module PacketGen
12
12
  # @abstract Base class for HTTP headers.
13
13
  # @author Kent 'picat' Gruber
14
14
  class Headers
15
+ include Inspectable
16
+
15
17
  # Underlying Headers data (or nil).
16
18
  # @return [Hash, nil]
17
19
  attr_reader :data
@@ -56,6 +58,7 @@ module PacketGen
56
58
  def to_human
57
59
  @data
58
60
  end
61
+ alias format_inspect to_human
59
62
 
60
63
  # Read human-readable data to populate header data.
61
64
  # @param [String, Hash] data
@@ -11,6 +11,8 @@ module PacketGen
11
11
  # IP address, as a group of 4 bytes
12
12
  # @author Sylvain Daubert
13
13
  class Addr < Types::Fields
14
+ include Inspectable
15
+
14
16
  # @!attribute a1
15
17
  # @return [Integer] IP address first byte
16
18
  define_field :a1, Types::Int8
@@ -47,6 +49,7 @@ module PacketGen
47
49
  def to_human
48
50
  fields.map { |f| self[f].to_i.to_s }.join('.')
49
51
  end
52
+ alias format_inspect to_human
50
53
 
51
54
  # Addr as an integer
52
55
  # @return [Integer]
@@ -14,6 +14,8 @@ module PacketGen
14
14
  # IPv6 address, as a group of 8 2-byte words
15
15
  # @author Sylvain Daubert
16
16
  class Addr < Types::Fields
17
+ include Inspectable
18
+
17
19
  # @!attribute a1
18
20
  # 1st 2-byte word of IPv6 address
19
21
  # @return [Integer]
@@ -73,6 +75,7 @@ module PacketGen
73
75
  def to_human
74
76
  IPAddr.new(to_a.map { |a| a.to_i.to_s(16) }.join(':')).to_s
75
77
  end
78
+ alias format_inspect to_human
76
79
 
77
80
  # Return an array of address 16-bit words
78
81
  # @return [Array<Integer>]
@@ -121,7 +121,7 @@ module PacketGen
121
121
  # @!attribute options
122
122
  # TCP options
123
123
  # @return [Options]
124
- define_field :options, TCP::Options
124
+ define_field :options, TCP::Options, builder: ->(h, t) { t.new(length_from: -> { h.data_offset > 5 ? (h.data_offset - 5) * 4 : 0 }) }
125
125
  # @!attribute body
126
126
  # @return [Types::String,Header::Base]
127
127
  define_field :body, Types::String
@@ -189,25 +189,6 @@ module PacketGen
189
189
  # @return [Boolean] 1-bit FIN flag
190
190
  define_bit_fields_on :u16, :_, 7, :flag_ns, :flag_cwr, :flag_ece, :flag_urg,
191
191
  :flag_ack, :flag_psh, :flag_rst, :flag_syn, :flag_fin
192
- # Read a TCP header from a string
193
- # @param [String] str binary string
194
- # @return [self]
195
- def read(str)
196
- return self if str.nil?
197
-
198
- force_binary str
199
- self[:sport].read str[0, 2]
200
- self[:dport].read str[2, 2]
201
- self[:seqnum].read str[4, 4]
202
- self[:acknum].read str[8, 4]
203
- self[:u16].read str[12, 2]
204
- self[:window].read str[14, 2]
205
- self[:checksum].read str[16, 2]
206
- self[:urg_pointer].read str[18, 2]
207
- self[:options].read str[20, (self.data_offset - 5) * 4] if self.data_offset > 5
208
- self[:body].read str[self.data_offset * 4..-1]
209
- self
210
- end
211
192
 
212
193
  # Compute checksum and set +checksum+ field
213
194
  # @return [Integer]
@@ -68,23 +68,7 @@ module PacketGen
68
68
  # @return [String]
69
69
  def self.inspect_attribute(attr, value, level=1)
70
70
  type = value.class.to_s.sub(/.*::/, '')
71
- val = case value
72
- when Types::Enum
73
- enum_human_hex(value.to_human, value.to_i, value.sz * 2)
74
- when Types::Int
75
- int_dec_hex(value, value.sz * 2)
76
- when Integer
77
- int_dec_hex(value, value.sz * 2)
78
- when String
79
- value.to_s.inspect
80
- else
81
- if value.respond_to? :to_human
82
- value.to_human
83
- else
84
- value.to_s.inspect
85
- end
86
- end
87
- self.format(type, attr, val, level)
71
+ self.format(type, attr, value.format_inspect, level)
88
72
  end
89
73
 
90
74
  # Format a ASN.1 attribute for +#inspect+.
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of PacketGen
4
+ # See https://github.com/sdaubert/packetgen for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ module PacketGen
9
+ # Module to add common methods to format types when inspecting packets/headers.
10
+ # @author Sylvain Daubert
11
+ # @since 3.1.5
12
+ module Inspectable
13
+ # Format attribute for inspecting
14
+ # @abstract should be overriden by types.
15
+ # @return [String]
16
+ def format_inspect
17
+ to_s.inspect
18
+ end
19
+ end
20
+ end
@@ -23,9 +23,19 @@ module PacketGen
23
23
  # @param [String] iface interface name
24
24
  # @param [Integer] snaplen
25
25
  # @param [Boolean] promisc
26
+ # @param [Boolean] monitor
26
27
  # @return [PCAPRUB::Pcap]
27
- def self.open_iface(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC)
28
- PCAPRUB::Pcap.open_live(iface, snaplen, promisc, TIMEOUT)
28
+ # @author Sylvain Daubert
29
+ # @author optix2000 - add support for setting monitor mode
30
+ # @since 3.1.5 add monitor argument
31
+ def self.open_iface(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, monitor: nil)
32
+ pcap = PCAPRUB::Pcap.create(iface)
33
+ pcap.setsnaplen(snaplen)
34
+ pcap.setpromisc(promisc)
35
+ pcap.settimeout(TIMEOUT)
36
+ # Monitor MUST be set before pcap is activated
37
+ pcap.setmonitor monitor unless monitor.nil?
38
+ pcap.activate
29
39
  end
30
40
 
31
41
  # Capture packets from a network interface
@@ -33,10 +43,14 @@ module PacketGen
33
43
  # @param [Integer] snaplen
34
44
  # @param [Boolean] promisc
35
45
  # @param [String] filter BPF filter
46
+ # @param [Boolean] monitor
36
47
  # @yieldparam [String] packet_data binary packet data
37
48
  # @return [void]
38
- def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil)
39
- pcap = self.open_iface(iface: iface, snaplen: snaplen, promisc: promisc)
49
+ # @author Sylvain Daubert
50
+ # @author optix2000 - add support for setting monitor mode
51
+ # @since 3.1.5 add monitor argument
52
+ def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil, monitor: nil)
53
+ pcap = self.open_iface(iface: iface, snaplen: snaplen, promisc: promisc, monitor: monitor)
40
54
  pcap.setfilter filter unless filter.nil?
41
55
  pcap.each do |packet_data|
42
56
  yield packet_data
@@ -58,6 +58,8 @@ module PacketGen
58
58
  # @since 3.1.0
59
59
  # @since 3.1.1 add +:aliases+ keyword to {#initialize}
60
60
  class AbstractTLV < Types::Fields
61
+ include Inspectable
62
+
61
63
  class <<self
62
64
  # @return [Hash]
63
65
  attr_accessor :aliases
@@ -169,8 +171,9 @@ module PacketGen
169
171
  # @return [String]
170
172
  def to_human
171
173
  my_value = self[:value].is_a?(String) ? self[:value].inspect : self[:value].to_human
172
- "type:%s,length:%u,value:#{my_value}" % [human_type, length]
174
+ "type:%s,length:%u,value:%s" % [human_type, length, my_value]
173
175
  end
176
+ alias format_inspect to_human
174
177
 
175
178
  private
176
179
 
@@ -27,6 +27,9 @@ module PacketGen
27
27
  # @author Sylvain Daubert
28
28
  class Array
29
29
  extend Forwardable
30
+ include Inspectable
31
+ include Enumerable
32
+ include LengthFrom
30
33
 
31
34
  # @!method [](index)
32
35
  # Return the element at +index+.
@@ -54,9 +57,6 @@ module PacketGen
54
57
  def_delegators :@array, :[], :clear, :each, :empty?, :first, :last, :size
55
58
  alias length size
56
59
 
57
- include Enumerable
58
- include LengthFrom
59
-
60
60
  # Separator used in {#to_human}.
61
61
  # May be ovverriden by subclasses
62
62
  HUMAN_SEPARATOR = ','
@@ -198,6 +198,7 @@ module PacketGen
198
198
  def to_human
199
199
  @array.map(&:to_human).join(self.class::HUMAN_SEPARATOR)
200
200
  end
201
+ alias format_inspect to_human
201
202
 
202
203
  private
203
204
 
@@ -11,6 +11,8 @@ module PacketGen
11
11
  # This class handles null-terminated strings (aka C strings).
12
12
  # @author Sylvain Daubert
13
13
  class CString < ::String
14
+ include Inspectable
15
+
14
16
  # @param [Hash] options
15
17
  # @option options [Integer] :static_length set a static length for this string
16
18
  def initialize(options={})
@@ -66,6 +68,7 @@ module PacketGen
66
68
  idx = self.index(+"\x00".encode(self.encoding)) || self.sz
67
69
  self[0, idx]
68
70
  end
71
+ alias format_inspect to_human
69
72
  end
70
73
  end
71
74
  end
@@ -71,6 +71,10 @@ module PacketGen
71
71
  def to_human
72
72
  @enum.key(to_i) || "<unknown:#{@value}>"
73
73
  end
74
+
75
+ def format_inspect
76
+ format_str % [to_human, to_i]
77
+ end
74
78
  end
75
79
 
76
80
  # Enumeration on one byte. See {Enum}.
@@ -119,6 +119,10 @@ module PacketGen
119
119
  # @return [Hash]
120
120
  # @since 3.1.0
121
121
  attr_reader :field_defs
122
+ # Get bit fields defintions for this class
123
+ # @return [Hash]
124
+ # @since 3.1.5
125
+ attr_reader :bit_fields
122
126
 
123
127
  # On inheritage, create +@field_defs+ class variable
124
128
  # @param [Class] klass
@@ -129,7 +133,7 @@ module PacketGen
129
133
  field_defs[k] = v.clone
130
134
  end
131
135
  ordered = @ordered_fields.clone
132
- bf = @bit_fields.clone
136
+ bf = bit_fields.clone
133
137
 
134
138
  klass.class_eval do
135
139
  @ordered_fields = ordered
@@ -172,37 +176,15 @@ module PacketGen
172
176
  # Define enumeration: hash's keys are +String+, and values are +Integer+.
173
177
  # @return [void]
174
178
  def define_field(name, type, options={})
175
- define = []
176
- if type < Types::Enum
177
- define << "def #{name}; self[:#{name}].to_i; end"
178
- define << "def #{name}=(val) self[:#{name}].value = val; end"
179
- else
180
- define << "def #{name}\n" \
181
- " if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
182
- " self[:#{name}].to_human\n" \
183
- " else\n" \
184
- " self[:#{name}]\n" \
185
- " end\n" \
186
- 'end'
187
- define << "def #{name}=(val)\n" \
188
- " if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
189
- " self[:#{name}].from_human val\n" \
190
- " else\n" \
191
- " self[:#{name}].read val\n" \
192
- " end\n" \
193
- 'end'
194
- end
195
-
196
- define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
197
- define.delete_at(0) if instance_methods.include? name
198
- class_eval define.join("\n")
179
+ fields << name
199
180
  field_defs[name] = FieldDef.new(type,
200
181
  options.delete(:default),
201
182
  options.delete(:builder),
202
183
  options.delete(:optional),
203
184
  options.delete(:enum),
204
185
  options)
205
- fields << name
186
+
187
+ add_methods(name, type)
206
188
  end
207
189
 
208
190
  # Define a field, before another one
@@ -300,8 +282,8 @@ module PacketGen
300
282
  total_size = type.new.width * 8
301
283
  idx = total_size - 1
302
284
 
303
- field = args.shift
304
- while field
285
+ until args.empty?
286
+ field = args.shift
305
287
  next unless field.is_a? Symbol
306
288
 
307
289
  size = if args.first.is_a? Integer
@@ -309,41 +291,13 @@ module PacketGen
309
291
  else
310
292
  1
311
293
  end
294
+
312
295
  unless field == :_
313
- shift = idx - (size - 1)
314
- field_mask = (2**size - 1) << shift
315
- clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
316
-
317
- if size == 1
318
- class_eval <<-METHODS
319
- def #{field}?
320
- val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
321
- val != 0
322
- end
323
- def #{field}=(v)
324
- val = v ? 1 : 0
325
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
326
- self[:#{attr}].value |= val << #{shift}
327
- end
328
- METHODS
329
- else
330
- class_eval <<-METHODS
331
- def #{field}
332
- (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
333
- end
334
- def #{field}=(v)
335
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
336
- self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
337
- end
338
- METHODS
339
- end
340
-
341
- @bit_fields[attr] = {} if @bit_fields[attr].nil?
342
- @bit_fields[attr][field] = size
296
+ add_bit_methods(attr, field, size, total_size, idx)
297
+ register_bit_field_size(attr, field, size)
343
298
  end
344
299
 
345
300
  idx -= size
346
- field = args.shift
347
301
  end
348
302
  end
349
303
 
@@ -352,7 +306,7 @@ module PacketGen
352
306
  # @return [void]
353
307
  # @since 2.8.4
354
308
  def remove_bit_fields_on(attr)
355
- fields = @bit_fields.delete(attr)
309
+ fields = bit_fields.delete(attr)
356
310
  return if fields.nil?
357
311
 
358
312
  fields.each do |field, size|
@@ -360,6 +314,62 @@ module PacketGen
360
314
  undef_method(size == 1 ? "#{field}?" : field)
361
315
  end
362
316
  end
317
+
318
+ private
319
+
320
+ def add_methods(name, type)
321
+ define = []
322
+ if type < Types::Enum
323
+ define << "def #{name}; self[:#{name}].to_i; end"
324
+ define << "def #{name}=(val) self[:#{name}].value = val; end"
325
+ else
326
+ define << "def #{name}\n" \
327
+ " to_and_from_human?(:#{name}) ? self[:#{name}].to_human : self[:#{name}]\n" \
328
+ 'end'
329
+ define << "def #{name}=(val)\n" \
330
+ " to_and_from_human?(:#{name}) ? self[:#{name}].from_human(val) : self[:#{name}].read(val)\n" \
331
+ 'end'
332
+ end
333
+
334
+ define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
335
+ define.delete_at(0) if instance_methods.include? name
336
+ class_eval define.join("\n")
337
+ end
338
+
339
+ def add_bit_methods(attr, name, size, total_size, idx)
340
+ shift = idx - (size - 1)
341
+ field_mask = (2**size - 1) << shift
342
+ clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
343
+
344
+ if size == 1
345
+ class_eval <<-METHODS
346
+ def #{name}?
347
+ val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
348
+ val != 0
349
+ end
350
+ def #{name}=(v)
351
+ val = v ? 1 : 0
352
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
353
+ self[:#{attr}].value |= val << #{shift}
354
+ end
355
+ METHODS
356
+ else
357
+ class_eval <<-METHODS
358
+ def #{name}
359
+ (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
360
+ end
361
+ def #{name}=(v)
362
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
363
+ self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
364
+ end
365
+ METHODS
366
+ end
367
+ end
368
+
369
+ def register_bit_field_size(attr, field, size)
370
+ bit_fields[attr] = {} if bit_fields[attr].nil?
371
+ bit_fields[attr][field] = size
372
+ end
363
373
  end
364
374
 
365
375
  # Create a new fields object
@@ -369,36 +379,13 @@ module PacketGen
369
379
  @fields = {}
370
380
  @optional_fields = {}
371
381
 
372
- field_defs = self.class.field_defs
373
382
  self.class.fields.each do |field|
374
- type = field_defs[field].type
375
- default = field_defs[field].default
376
- default = default.to_proc.call(self) if default.is_a?(Proc)
377
- builder = field_defs[field].builder
378
- optional = field_defs[field].optional
379
- enum = field_defs[field].enum
380
- field_options = field_defs[field].options
381
-
382
- @fields[field] = if builder
383
- builder.call(self, type)
384
- elsif enum
385
- type.new(enum)
386
- elsif !field_options.empty?
387
- type.new(field_options)
388
- else
389
- type.new
390
- end
391
-
392
- value = options[field] || default
393
- if value.class <= type
394
- @fields[field] = value
395
- elsif @fields[field].respond_to? :from_human
396
- @fields[field].from_human(value)
397
- end
398
-
399
- @optional_fields[field] = optional if optional
383
+ build_field field
384
+ initialize_value field, options[field]
385
+ initialize_optional field
400
386
  end
401
- self.class.class_eval { @bit_fields }.each do |_, hsh|
387
+
388
+ self.class.bit_fields.each do |_, hsh|
402
389
  hsh.each_key do |bit_field|
403
390
  self.send "#{bit_field}=", options[bit_field] if options[bit_field]
404
391
  end
@@ -528,7 +515,7 @@ module PacketGen
528
515
  # @return [Hash,nil] keys: bit fields, values: their size in bits
529
516
  # @since 2.8.3
530
517
  def bits_on(field)
531
- self.class.class_eval { @bit_fields }[field]
518
+ self.class.bit_fields[field]
532
519
  end
533
520
 
534
521
  private
@@ -547,6 +534,51 @@ module PacketGen
547
534
  def force_binary(str)
548
535
  PacketGen.force_binary(str)
549
536
  end
537
+
538
+ # @param [Symbol] attr attribute
539
+ # @return [Boolean] +tru+e if #from_human and #to_human are both defined for given attribute
540
+ def to_and_from_human?(attr)
541
+ self[attr].respond_to?(:to_human) && self[attr].respond_to?(:from_human)
542
+ end
543
+
544
+ def field_defs
545
+ self.class.field_defs
546
+ end
547
+
548
+ def build_field(field)
549
+ type = field_defs[field].type
550
+ builder = field_defs[field].builder
551
+ enum = field_defs[field].enum
552
+ field_options = field_defs[field].options
553
+
554
+ @fields[field] = if builder
555
+ builder.call(self, type)
556
+ elsif enum
557
+ type.new(enum)
558
+ elsif !field_options.empty?
559
+ type.new(field_options)
560
+ else
561
+ type.new
562
+ end
563
+ end
564
+
565
+ def initialize_value(field, val)
566
+ type = field_defs[field].type
567
+ default = field_defs[field].default
568
+ default = default.to_proc.call(self) if default.is_a?(Proc)
569
+
570
+ value = val || default
571
+ if value.class <= type
572
+ @fields[field] = value
573
+ elsif @fields[field].respond_to? :from_human
574
+ @fields[field].from_human(value)
575
+ end
576
+ end
577
+
578
+ def initialize_optional(field)
579
+ optional = field_defs[field].optional
580
+ @optional_fields[field] = optional if optional
581
+ end
550
582
  end
551
583
  end
552
584
  end
@@ -12,6 +12,8 @@ module PacketGen
12
12
  # @abstract
13
13
  # @author Sylvain Daubert
14
14
  class Int
15
+ include Inspectable
16
+
15
17
  # Integer value
16
18
  # @return [Integer]
17
19
  attr_accessor :value
@@ -80,6 +82,18 @@ module PacketGen
80
82
  def sz
81
83
  width
82
84
  end
85
+
86
+ # Format Int type when inspecting header or packet
87
+ # @return [String]
88
+ def format_inspect
89
+ format_str % [to_i.to_s, to_i]
90
+ end
91
+
92
+ private
93
+
94
+ def format_str
95
+ "%-16s (0x%0#{width * 2}x)"
96
+ end
83
97
  end
84
98
 
85
99
  # One byte unsigned integer
@@ -12,6 +12,8 @@ module PacketGen
12
12
  # By default, a null string will have one byte length (length byte set to 0).
13
13
  # @author Sylvain Daubert
14
14
  class IntString
15
+ include Inspectable
16
+
15
17
  # internal string
16
18
  # @return [String]
17
19
  attr_reader :string
@@ -76,6 +78,7 @@ module PacketGen
76
78
  def to_human
77
79
  @string
78
80
  end
81
+ alias format_inspect to_human
79
82
 
80
83
  # Set length from internal string length
81
84
  # @return [Integer]
@@ -14,6 +14,8 @@ module PacketGen
14
14
  # oui.to_human # => "00:01:02"
15
15
  # @author Sylvain Daubert
16
16
  class OUI < Types::Fields
17
+ include Inspectable
18
+
17
19
  # @attribute b2
18
20
  # @return [Integer] left-most byte
19
21
  define_field :b2, Types::Int8
@@ -44,6 +46,7 @@ module PacketGen
44
46
  def to_human
45
47
  fields.map { |m| '%02x' % self[m] }.join(':')
46
48
  end
49
+ alias format_inspect to_human
47
50
  end
48
51
  end
49
52
  end
@@ -14,6 +14,7 @@ module PacketGen
14
14
  # @author Sylvain Daubert
15
15
  class String < ::String
16
16
  include LengthFrom
17
+ include Inspectable
17
18
 
18
19
  # @return [Integer]
19
20
  attr_reader :static_length
@@ -31,6 +31,8 @@ module PacketGen
31
31
  # @deprecated Use {AbstractTLV} instead.
32
32
  # @since 3.1.0 deprecated
33
33
  class TLV < Fields
34
+ include Inspectable
35
+
34
36
  # @!attribute type
35
37
  # @return [Integer]
36
38
  define_field :type, Int8
@@ -139,6 +141,7 @@ module PacketGen
139
141
  "#{name} type:#{@typestr} length:#{@lenstr} value:#{value.inspect}" % [human_type,
140
142
  length]
141
143
  end
144
+ alias format_inspect to_human
142
145
 
143
146
  private
144
147
 
@@ -53,7 +53,7 @@ module PacketGen
53
53
  timeout = options[:timeout] || 1
54
54
  my_hwaddr = Config.instance.hwaddr(iface)
55
55
  arp_pkt = Packet.gen('Eth', dst: 'ff:ff:ff:ff:ff:ff', src: my_hwaddr)
56
- arp_pkt.add('ARP', sha: Config.instance.hwaddr, spa: Config.instance.ipaddr,
56
+ arp_pkt.add('ARP', sha: Config.instance.hwaddr(iface), spa: Config.instance.ipaddr(iface),
57
57
  tpa: ipaddr)
58
58
 
59
59
  capture = Capture.new(iface: iface, timeout: timeout, max: 1,
@@ -10,5 +10,5 @@
10
10
  # @author Sylvain Daubert
11
11
  module PacketGen
12
12
  # PacketGen version
13
- VERSION = '3.1.4'
13
+ VERSION = '3.1.5'
14
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: packetgen
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.4
4
+ version: 3.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvain Daubert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-22 00:00:00.000000000 Z
11
+ date: 2020-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: interfacez
@@ -208,6 +208,7 @@ files:
208
208
  - lib/packetgen/headerable.rb
209
209
  - lib/packetgen/inject.rb
210
210
  - lib/packetgen/inspect.rb
211
+ - lib/packetgen/inspectable.rb
211
212
  - lib/packetgen/packet.rb
212
213
  - lib/packetgen/pcap.rb
213
214
  - lib/packetgen/pcapng.rb
@@ -235,10 +236,11 @@ files:
235
236
  - lib/packetgen/utils.rb
236
237
  - lib/packetgen/utils/arp_spoofer.rb
237
238
  - lib/packetgen/version.rb
238
- homepage: https://github.com/sdaubert/packetgen
239
+ homepage:
239
240
  licenses:
240
241
  - MIT
241
242
  metadata:
243
+ homepage_uri: https://github.com/sdaubert/packetgen
242
244
  bug_tracker_uri: https://github.com/sdaubert/packetgen/issues
243
245
  documentation_uri: https://www.rubydoc.info/gems/packetgen
244
246
  source_code_uri: https://github.com/sdaubert/packetgen