bettercap 1.1.10 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/TODO.md +8 -7
  3. data/bin/bettercap +8 -2
  4. data/lib/bettercap.rb +5 -4
  5. data/lib/bettercap/context.rb +54 -8
  6. data/lib/bettercap/discovery/agents/arp.rb +17 -4
  7. data/lib/bettercap/discovery/agents/base.rb +16 -52
  8. data/lib/bettercap/discovery/agents/icmp.rb +25 -17
  9. data/lib/bettercap/discovery/agents/udp.rb +9 -20
  10. data/lib/bettercap/discovery/{discovery.rb → thread.rb} +10 -9
  11. data/lib/bettercap/error.rb +1 -2
  12. data/lib/bettercap/factories/{firewall_factory.rb → firewall.rb} +11 -7
  13. data/lib/bettercap/factories/{parser_factory.rb → parser.rb} +13 -3
  14. data/lib/bettercap/factories/{spoofer_factory.rb → spoofer.rb} +10 -3
  15. data/lib/bettercap/firewalls/base.rb +76 -0
  16. data/lib/bettercap/firewalls/linux.rb +26 -16
  17. data/lib/bettercap/firewalls/osx.rb +22 -13
  18. data/lib/bettercap/firewalls/redirection.rb +15 -1
  19. data/lib/bettercap/httpd/server.rb +5 -0
  20. data/lib/bettercap/logger.rb +29 -8
  21. data/lib/bettercap/network.rb +105 -105
  22. data/lib/bettercap/options.rb +99 -41
  23. data/lib/bettercap/packet_queue.rb +92 -0
  24. data/lib/bettercap/proxy/certstore.rb +49 -43
  25. data/lib/bettercap/proxy/module.rb +4 -2
  26. data/lib/bettercap/proxy/proxy.rb +7 -2
  27. data/lib/bettercap/proxy/request.rb +28 -16
  28. data/lib/bettercap/proxy/response.rb +23 -2
  29. data/lib/bettercap/proxy/stream_logger.rb +6 -0
  30. data/lib/bettercap/proxy/streamer.rb +13 -5
  31. data/lib/bettercap/proxy/thread_pool.rb +6 -14
  32. data/lib/bettercap/shell.rb +5 -3
  33. data/lib/bettercap/sniffer/parsers/base.rb +7 -1
  34. data/lib/bettercap/sniffer/parsers/custom.rb +6 -1
  35. data/lib/bettercap/sniffer/parsers/ftp.rb +8 -5
  36. data/lib/bettercap/sniffer/parsers/httpauth.rb +4 -1
  37. data/lib/bettercap/sniffer/parsers/https.rb +4 -1
  38. data/lib/bettercap/sniffer/parsers/irc.rb +8 -5
  39. data/lib/bettercap/sniffer/parsers/mail.rb +8 -5
  40. data/lib/bettercap/sniffer/parsers/ntlmss.rb +21 -18
  41. data/lib/bettercap/sniffer/parsers/post.rb +4 -1
  42. data/lib/bettercap/sniffer/parsers/url.rb +4 -1
  43. data/lib/bettercap/sniffer/sniffer.rb +7 -3
  44. data/lib/bettercap/spoofers/arp.rb +69 -94
  45. data/lib/bettercap/spoofers/base.rb +132 -0
  46. data/lib/bettercap/spoofers/icmp.rb +200 -0
  47. data/lib/bettercap/spoofers/none.rb +8 -2
  48. data/lib/bettercap/target.rb +117 -90
  49. data/lib/bettercap/update_checker.rb +6 -0
  50. data/lib/bettercap/version.rb +3 -1
  51. metadata +24 -8
  52. data/lib/bettercap/base/ifirewall.rb +0 -46
  53. data/lib/bettercap/base/ispoofer.rb +0 -32
@@ -11,9 +11,10 @@ This project is released under the GPL 3 license.
11
11
  =end
12
12
  require 'thread'
13
13
 
14
- # Tnx to Puma ThreadPool!
15
14
  module BetterCap
16
15
  module Proxy
16
+ # Thread pool class used by the BetterCap::Proxy::Proxy.
17
+ # Tnx to Puma ThreadPool!
17
18
  class ThreadPool
18
19
 
19
20
  # Maintain a minimum of +min+ and maximum of +max+ threads
@@ -21,7 +22,6 @@ class ThreadPool
21
22
  #
22
23
  # The block passed is the work that will be performed in each
23
24
  # thread.
24
- #
25
25
  def initialize(min, max, *extra, &block)
26
26
  @not_empty = ConditionVariable.new
27
27
  @not_full = ConditionVariable.new
@@ -46,12 +46,10 @@ class ThreadPool
46
46
  @mutex.synchronize do
47
47
  @min.times { spawn_thread }
48
48
  end
49
-
50
- @clean_thread_locals = false
51
49
  end
52
50
 
53
- attr_reader :spawned, :trim_requested
54
- attr_accessor :clean_thread_locals
51
+ # Number of spawned threads in the pool.
52
+ attr_reader :spawned
55
53
 
56
54
  # How many objects have yet to be processed by the pool?
57
55
  #
@@ -104,12 +102,6 @@ class ThreadPool
104
102
 
105
103
  break unless continue
106
104
 
107
- if @clean_thread_locals
108
- Thread.current.keys.each do |key|
109
- Thread.current[key] = nil unless key == :__recursive_key__
110
- end
111
- end
112
-
113
105
  begin
114
106
  block.call(work, *extra)
115
107
  rescue Exception
@@ -184,7 +176,7 @@ class ThreadPool
184
176
 
185
177
  # Tell all threads in the pool to exit and wait for them to finish.
186
178
  #
187
- def shutdown
179
+ def shutdown( join_threads = true )
188
180
  threads = @mutex.synchronize do
189
181
  @shutdown = true
190
182
  @not_empty.broadcast
@@ -193,7 +185,7 @@ class ThreadPool
193
185
  @workers.dup
194
186
  end
195
187
 
196
- threads.each(&:join)
188
+ threads.each(&:join) if join_threads
197
189
 
198
190
  @spawned = 0
199
191
  @workers = []
@@ -8,10 +8,11 @@ This project is released under the GPL 3 license.
8
8
  require 'bettercap/error'
9
9
 
10
10
  module BetterCap
11
+ # Class responsible of executing various shell commands.
11
12
  module Shell
12
13
  class << self
13
-
14
- #return the output of command
14
+ # Execute +command+ and return its output.
15
+ # Raise +BetterCap::Error+ if the return code is not 0.
15
16
  def execute(command)
16
17
  r = ''
17
18
  10.times do
@@ -29,14 +30,15 @@ module Shell
29
30
  r
30
31
  end
31
32
 
33
+ # Get the +iface+ network interface configuration.
32
34
  def ifconfig(iface = '')
33
35
  self.execute( "LANG=en && ifconfig #{iface}" )
34
36
  end
35
37
 
38
+ # Get the ARP table cached on this computer.
36
39
  def arp
37
40
  self.execute( 'LANG=en && arp -a -n' )
38
41
  end
39
-
40
42
  end
41
43
  end
42
44
  end
@@ -10,12 +10,17 @@ This project is released under the GPL 3 license.
10
10
 
11
11
  =end
12
12
  module BetterCap
13
- class BaseParser
13
+ module Parsers
14
+ # Base class for BetterCap::Parsers.
15
+ class Base
16
+ # Initialize this parser.
14
17
  def initialize
15
18
  @filters = []
16
19
  @name = 'BASE'
17
20
  end
18
21
 
22
+ # This method will be called from the BetterCap::Sniffer for each
23
+ # incoming packet ( +pkt ) and will apply the parser filter to it.
19
24
  def on_packet( pkt )
20
25
  s = pkt.to_s
21
26
  @filters.each do |filter|
@@ -26,3 +31,4 @@ class BaseParser
26
31
  end
27
32
  end
28
33
  end
34
+ end
@@ -12,10 +12,15 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
14
  module BetterCap
15
- class CustomParser < BaseParser
15
+ module Parsers
16
+ # Parser used when the "--custom-parser EXPRESSION" command line
17
+ # argument is specified.
18
+ class Custom < Base
19
+ # Initialize the parser given the +filter+ Regexp object.
16
20
  def initialize( filter )
17
21
  @filters = [ filter ]
18
22
  @name = 'DATA'
19
23
  end
20
24
  end
21
25
  end
26
+ end
@@ -12,10 +12,13 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
14
  module BetterCap
15
- class FtpParser < BaseParser
16
- def initialize
17
- @filters = [ /(USER|PASS)\s+.+/ ]
18
- @name = 'FTP'
19
- end
15
+ module Parsers
16
+ # FTP authentication parser.
17
+ class Ftp < Base
18
+ def initialize
19
+ @filters = [ /(USER|PASS)\s+.+/ ]
20
+ @name = 'FTP'
21
+ end
22
+ end
20
23
  end
21
24
  end
@@ -14,7 +14,9 @@ require 'colorize'
14
14
  require 'base64'
15
15
 
16
16
  module BetterCap
17
- class HttpauthParser < BaseParser
17
+ module Parsers
18
+ # HTTP basic and digest authentication parser.
19
+ class Httpauth < Base
18
20
  def on_packet( pkt )
19
21
  lines = pkt.to_s.split("\n")
20
22
  hostname = nil
@@ -41,3 +43,4 @@ class HttpauthParser < BaseParser
41
43
  end
42
44
  end
43
45
  end
46
+ end
@@ -14,7 +14,9 @@ require 'colorize'
14
14
  require 'resolv'
15
15
 
16
16
  module BetterCap
17
- class HttpsParser < BaseParser
17
+ module Parsers
18
+ # HTTPS connections parser.
19
+ class Https < Base
18
20
  @@prev = nil
19
21
 
20
22
  def on_packet( pkt )
@@ -39,3 +41,4 @@ class HttpsParser < BaseParser
39
41
  end
40
42
  end
41
43
  end
44
+ end
@@ -12,10 +12,13 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
14
  module BetterCap
15
- class IrcParser < BaseParser
16
- def initialize
17
- @filters = [ /NICK\s+.+/, /NS IDENTIFY\s+.+/, /nickserv :identify\s+.+/ ]
18
- @name = 'IRC'
19
- end
15
+ module Parsers
16
+ # IRC protocol parser.
17
+ class Irc < Base
18
+ def initialize
19
+ @filters = [ /NICK\s+.+/, /NS IDENTIFY\s+.+/, /nickserv :identify\s+.+/ ]
20
+ @name = 'IRC'
21
+ end
22
+ end
20
23
  end
21
24
  end
@@ -12,10 +12,13 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
14
  module BetterCap
15
- class MailParser < BaseParser
16
- def initialize
17
- @filters = [ /(\d+ )?(auth|authenticate) ([a-z\-_0-9]+)/i ]
18
- @name = 'MAIL'
19
- end
15
+ module Parsers
16
+ # POP/IMAP authentication parser.
17
+ class Mail < Base
18
+ def initialize
19
+ @filters = [ /(\d+ )?(auth|authenticate) ([a-z\-_0-9]+)/i ]
20
+ @name = 'MAIL'
21
+ end
22
+ end
20
23
  end
21
24
  end
@@ -13,26 +13,29 @@ require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
 
15
15
  module BetterCap
16
- class NtlmssParser < BaseParser
17
- def bin2hex( data )
18
- hex = ''
19
- data.each_byte do |byte|
20
- if /[[:print:]]/ === byte.chr
21
- hex += byte.chr
22
- else
23
- hex += "\\x" + byte.to_s(16)
24
- end
25
- end
26
-
27
- hex
16
+ module Parsers
17
+ # NTLMSS traffic parser.
18
+ class Ntlmss < Base
19
+ # Convert binary +data+ into human readable hexadecimal representation.
20
+ def bin2hex( data )
21
+ hex = ''
22
+ data.each_byte do |byte|
23
+ if /[[:print:]]/ === byte.chr
24
+ hex += byte.chr
25
+ else
26
+ hex += "\\x" + byte.to_s(16)
27
+ end
28
28
  end
29
+ hex
30
+ end
29
31
 
30
- def on_packet( pkt )
31
- s = pkt.to_s
32
- if s =~ /NTLMSSP\x00\x03\x00\x00\x00.+/
33
- # TODO: Parse NTLMSSP packet.
34
- StreamLogger.log_raw( pkt, 'NTLMSS', bin2hex( pkt.payload ) )
35
- end
32
+ def on_packet( pkt )
33
+ s = pkt.to_s
34
+ if s =~ /NTLMSSP\x00\x03\x00\x00\x00.+/
35
+ # TODO: Parse NTLMSSP packet.
36
+ StreamLogger.log_raw( pkt, 'NTLMSS', bin2hex( pkt.payload ) )
36
37
  end
38
+ end
39
+ end
37
40
  end
38
41
  end
@@ -13,7 +13,9 @@ require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
 
15
15
  module BetterCap
16
- class PostParser < BaseParser
16
+ module Parsers
17
+ # HTTP POST requests parser.
18
+ class Post < Base
17
19
  def on_packet( pkt )
18
20
  s = pkt.to_s
19
21
  if s =~ /POST\s+[^\s]+\s+HTTP.+/
@@ -22,3 +24,4 @@ class PostParser < BaseParser
22
24
  end
23
25
  end
24
26
  end
27
+ end
@@ -13,7 +13,9 @@ require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
 
15
15
  module BetterCap
16
- class UrlParser < BaseParser
16
+ module Parsers
17
+ # HTTP GET requests parser.
18
+ class Url < Base
17
19
  def on_packet( pkt )
18
20
  s = pkt.to_s
19
21
  if s =~ /GET\s+([^\s]+)\s+HTTP.+Host:\s+([^\s]+).+/m
@@ -26,3 +28,4 @@ class UrlParser < BaseParser
26
28
  end
27
29
  end
28
30
  end
31
+ end
@@ -10,11 +10,12 @@ This project is released under the GPL 3 license.
10
10
 
11
11
  =end
12
12
  require 'bettercap/logger'
13
- require 'bettercap/factories/parser_factory'
14
13
  require 'colorize'
15
14
  require 'packetfu'
16
15
 
17
16
  module BetterCap
17
+ # Class responsible of loading BetterCap::Parsers instances and performing
18
+ # network packet sniffing and dumping.
18
19
  class Sniffer
19
20
  include PacketFu
20
21
 
@@ -23,6 +24,9 @@ class Sniffer
23
24
  @@pcap = nil
24
25
  @@cap = nil
25
26
 
27
+ # Start a new thread that will sniff packets from the network and pass
28
+ # each one of them to the BetterCap::Parsers instances loaded inside the
29
+ # +ctx+ BetterCap::Context instance.
26
30
  def self.start( ctx )
27
31
  Thread.new do
28
32
  Logger.info 'Starting sniffer ...'
@@ -94,9 +98,9 @@ class Sniffer
94
98
  end
95
99
 
96
100
  if @@ctx.options.custom_parser.nil?
97
- @@parsers = ParserFactory.load_by_names @@ctx.options.parsers
101
+ @@parsers = Factories::Parser.load_by_names @@ctx.options.parsers
98
102
  else
99
- @@parsers = ParserFactory.load_custom @@ctx.options.custom_parser
103
+ @@parsers = Factories::Parser.load_custom @@ctx.options.custom_parser
100
104
  end
101
105
 
102
106
  @@cap = Capture.new(
@@ -9,15 +9,18 @@ Blog : http://www.evilsocket.net/
9
9
  This project is released under the GPL 3 license.
10
10
 
11
11
  =end
12
+ require 'bettercap/spoofers/base'
12
13
  require 'bettercap/error'
13
14
  require 'bettercap/context'
14
- require 'bettercap/base/ispoofer'
15
15
  require 'bettercap/network'
16
16
  require 'bettercap/logger'
17
17
  require 'colorize'
18
18
 
19
19
  module BetterCap
20
- class ArpSpoofer < ISpoofer
20
+ module Spoofers
21
+ # This class is responsible of performing ARP spoofing on the network.
22
+ class Arp < Base
23
+ # Initialize the BetterCap::Spoofers::Arp object.
21
24
  def initialize
22
25
  @ctx = Context.get
23
26
  @gateway = nil
@@ -27,16 +30,12 @@ class ArpSpoofer < ISpoofer
27
30
  @capture = nil
28
31
  @running = false
29
32
 
30
- Logger.info "Getting gateway #{@ctx.gateway} MAC address ..."
31
-
32
- hw = Network.get_hw_address( @ctx.ifconfig, @ctx.gateway )
33
- raise BetterCap::Error, "Couldn't determine router MAC" if hw.nil?
34
-
35
- @gateway = Target.new( @ctx.gateway, hw )
36
-
37
- Logger.info " #{@gateway}"
33
+ update_gateway!
38
34
  end
39
35
 
36
+ # Send a spoofed ARP reply to the target identified by the +daddr+ IP address
37
+ # and +dmac+ MAC address, spoofing the +saddr+ IP address and +smac+ MAC
38
+ # address as the source device.
40
39
  def send_spoofed_packet( saddr, smac, daddr, dmac )
41
40
  pkt = PacketFu::ARPPacket.new
42
41
  pkt.eth_saddr = smac
@@ -47,9 +46,10 @@ class ArpSpoofer < ISpoofer
47
46
  pkt.arp_daddr_ip = daddr
48
47
  pkt.arp_opcode = 2
49
48
 
50
- pkt.to_w(@ctx.ifconfig[:iface])
49
+ @ctx.packets.push(pkt)
51
50
  end
52
51
 
52
+ # Start the ARP spoofing.
53
53
  def start
54
54
  Logger.info "Starting ARP spoofer ( #{@ctx.options.half_duplex ? 'Half' : 'Full'} Duplex ) ..."
55
55
 
@@ -58,89 +58,11 @@ class ArpSpoofer < ISpoofer
58
58
 
59
59
  @ctx.firewall.enable_forwarding(true) unless @forwarding
60
60
 
61
- @sniff_thread = Thread.new do
62
- Logger.info 'ARP watcher started ...'
63
- begin
64
- @capture = PacketFu::Capture.new(
65
- iface: @ctx.options.iface,
66
- filter: 'arp',
67
- start: true
68
- )
69
- rescue Exception => e
70
- Logger.error e.message
71
- end
72
-
73
- @capture.stream.each do |p|
74
- begin
75
- pkt = PacketFu::Packet.parse p
76
- # we're only interested in 'who-has' packets
77
- if pkt.arp_opcode == 1 and pkt.arp_dst_mac.to_s == '00:00:00:00:00:00'
78
- is_from_us = ( pkt.arp_src_ip.to_s == @ctx.ifconfig[:ip_saddr] )
79
- unless is_from_us
80
- Logger.info "[ARP] #{pkt.arp_src_ip.to_s} is asking who #{pkt.arp_dst_ip.to_s} is."
81
-
82
- send_spoofed_packet pkt.arp_dst_ip.to_s, @ctx.ifconfig[:eth_saddr], pkt.arp_src_ip.to_s, pkt.arp_src_mac.to_s
83
- end
84
- end
85
- rescue Exception => e
86
- Logger.error e.message
87
- end
88
- end
89
- end
90
-
91
- @spoof_thread = Thread.new do
92
- prev_size = @ctx.targets.size
93
- loop do
94
- if not @running
95
- Logger.debug 'Stopping spoofing thread ...'
96
- Thread.exit
97
- break
98
- end
99
-
100
- size = @ctx.targets.size
101
-
102
- if size > prev_size
103
- Logger.warn "Aquired #{size - prev_size} new targets."
104
- elsif size < prev_size
105
- Logger.warn "Lost #{prev_size - size} targets."
106
- end
107
-
108
- Logger.debug "Spoofing #{@ctx.targets.size} targets ..."
109
-
110
- @ctx.targets.each do |target|
111
- # targets could change, update mac addresses if needed
112
- if target.mac.nil?
113
- hw = Network.get_hw_address( @ctx.ifconfig, target.ip )
114
- if hw.nil?
115
- Logger.warn "Couldn't determine target #{ip} MAC!"
116
- next
117
- else
118
- Logger.info " Target MAC : #{hw}"
119
- target.mac = hw
120
- end
121
- # target was specified by MAC address
122
- elsif target.ip_refresh
123
- ip = Network.get_ip_address( @ctx, target.mac )
124
- if ip.nil?
125
- Logger.warn "Couldn't determine target #{target.mac} IP!"
126
- next
127
- else
128
- Logger.info "Target #{target.mac} IP : #{ip}" if target.ip.nil? or target.ip != ip
129
- target.ip = ip
130
- end
131
- end
132
-
133
- send_spoofed_packet( @gateway.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
134
- send_spoofed_packet( target.ip, @ctx.ifconfig[:eth_saddr], @gateway.ip, @gateway.mac ) unless @ctx.options.half_duplex
135
- end
136
-
137
- prev_size = @ctx.targets.size
138
-
139
- sleep(1)
140
- end
141
- end
61
+ @sniff_thread = Thread.new { arp_watcher }
62
+ @spoof_thread = Thread.new { arp_spoofer }
142
63
  end
143
64
 
65
+ # Stop the ARP spoofing, reset firewall state and restore targets ARP table.
144
66
  def stop
145
67
  raise 'ARP spoofer is not running' unless @running
146
68
 
@@ -158,14 +80,67 @@ class ArpSpoofer < ISpoofer
158
80
  Logger.info "Restoring ARP table of #{@ctx.targets.size} targets ..."
159
81
 
160
82
  @ctx.targets.each do |target|
161
- unless target.mac.nil?
83
+ unless target.ip.nil? or target.mac.nil?
162
84
  begin
163
85
  send_spoofed_packet( @gateway.ip, @gateway.mac, target.ip, target.mac )
164
86
  send_spoofed_packet( target.ip, target.mac, @gateway.ip, @gateway.mac ) unless @ctx.options.half_duplex
165
87
  rescue; end
166
88
  end
167
89
  end
168
- sleep 1
169
90
  end
91
+
92
+ private
93
+
94
+ def update_targets!
95
+ @ctx.targets.each do |target|
96
+ # targets could change, update mac addresses if needed
97
+ if target.mac.nil?
98
+ hw = Network.get_hw_address( @ctx.ifconfig, target.ip )
99
+ if hw.nil?
100
+ Logger.warn "Couldn't determine target #{ip} MAC!"
101
+ next
102
+ else
103
+ Logger.info " Target MAC : #{hw}"
104
+ target.mac = hw
105
+ end
106
+ # target was specified by MAC address
107
+ elsif target.ip_refresh
108
+ ip = Network.get_ip_address( @ctx, target.mac )
109
+ if ip.nil?
110
+ Logger.warn "Couldn't determine target #{target.mac} IP!"
111
+ next
112
+ else
113
+ Logger.info "Target #{target.mac} IP : #{ip}" if target.ip.nil? or target.ip != ip
114
+ target.ip = ip
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ def arp_spoofer
121
+ spoof_loop(1) { |target|
122
+ unless target.ip.nil? or target.mac.nil?
123
+ send_spoofed_packet( @gateway.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
124
+ send_spoofed_packet( target.ip, @ctx.ifconfig[:eth_saddr], @gateway.ip, @gateway.mac ) unless @ctx.options.half_duplex
125
+ end
126
+ }
127
+ end
128
+
129
+ def arp_watcher
130
+ Logger.info 'ARP watcher started ...'
131
+
132
+ sniff_packets('arp') { |pkt|
133
+ # we're only interested in 'who-has' packets
134
+ if pkt.arp_opcode == 1 and pkt.arp_dst_mac.to_s == '00:00:00:00:00:00'
135
+ is_from_us = ( pkt.arp_src_ip.to_s == @ctx.ifconfig[:ip_saddr] )
136
+ unless is_from_us
137
+ Logger.info "[ARP] #{pkt.arp_src_ip.to_s} is asking who #{pkt.arp_dst_ip.to_s} is."
138
+
139
+ send_spoofed_packet pkt.arp_dst_ip.to_s, @ctx.ifconfig[:eth_saddr], pkt.arp_src_ip.to_s, pkt.arp_src_mac.to_s
140
+ end
141
+ end
142
+ }
143
+ end
144
+ end
170
145
  end
171
146
  end