bettercap 1.6.0 → 1.6.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 51ee5970551588bd53601e02295aaa61d920ffac
4
- data.tar.gz: 6be88ed0348867abc1791019c53c0e610bcef1b0
3
+ metadata.gz: 490c8b7f32ca7a9764aba54e0b7a5e8fb6d42f0f
4
+ data.tar.gz: d004aed04c55b7244ca61fd9a1c0dbbdd86d9042
5
5
  SHA512:
6
- metadata.gz: 8def2b5962b579c2a0164d6e19ddf376cadf67bc73d21ec8084eaa762340c7b0d6bebc1eea7c535ece356c0bfe95bf9b78c03659a660f02470786344b83e8e1a
7
- data.tar.gz: 0135d3bcfe282cac63215b8ec54f78557b8d1347f9ee24a9cae535944f2287efbe52f5ea3eb8e3b113cae93326d7ed64dee852ae381895019e8a1630670c7b28
6
+ metadata.gz: 341644a5eb60a927046fd9c8300bbff5be5df6bacc8f4a381fa4d48624a52f712be07532869e73f63b4ecdbad207dbee8a30229236fbffd2c9edc733ceaeb809
7
+ data.tar.gz: 736d8de6d05140d0252c8abad7eb5e59fc69c84f8a2a4ff59b12e9d7e72f37eaeab0bf2e55dd77d74873c8d18c5d06a75a1bffa420c24dc75b6e9fbecfbc201f
@@ -19,6 +19,8 @@ Encoding.default_internal = Encoding::UTF_8
19
19
 
20
20
  require 'packetfu'
21
21
  require 'em-proxy'
22
+ require 'eventmachine'
23
+ require 'socket'
22
24
  require 'webrick'
23
25
  require 'rubydns'
24
26
  require 'colorize'
@@ -80,12 +80,6 @@ class Context
80
80
 
81
81
  # Update the Context state parsing network related informations.
82
82
  def update!
83
- gw = @options.core.gateway || Network.get_gateway
84
- raise BetterCap::Error, "Could not detect the gateway address for interface #{@options.core.iface}, "\
85
- 'make sure you\'ve specified the correct network interface to use and to have the '\
86
- 'correct network configuration, this could also happen if bettercap '\
87
- 'is launched from a virtual environment.' unless Network::Validator.is_ip?(gw)
88
-
89
83
  unless @options.core.use_mac.nil?
90
84
  cfg = PacketFu::Utils.ifconfig @options.core.iface
91
85
  raise BetterCap::Error, "Could not determine IPv4 address of '#{@options.core.iface}', make sure this interface "\
@@ -100,14 +94,34 @@ class Context
100
94
 
101
95
  cfg = PacketFu::Utils.ifconfig @options.core.iface
102
96
  raise BetterCap::Error, "Could not determine IPv4 address of '#{@options.core.iface}', make sure this interface "\
103
- 'is active and connected.' if cfg[:ip4_obj].nil?
97
+ 'is active and connected.' if ( cfg[:ip4_obj].nil? and cfg[:ip6_saddr].nil? )
98
+
99
+ # check if we're on an IPv6 interface
100
+ if @options.core.use_ipv6
101
+ @iface = Network::Target.new( cfg[:ip6_saddr], cfg[:eth_saddr], cfg[:ip6_obj], cfg[:iface] )
102
+ else
103
+ @iface = Network::Target.new( cfg[:ip_saddr], cfg[:eth_saddr], cfg[:ip4_obj], cfg[:iface] )
104
+ end
104
105
 
105
- @gateway = Network::Target.new gw
106
- @targets = @options.core.targets unless @options.core.targets.nil?
107
- @iface = Network::Target.new( cfg[:ip_saddr], cfg[:eth_saddr], cfg[:ip4_obj], cfg[:iface] )
108
106
  raise BetterCap::Error, "Could not determine MAC address of '#{@options.core.iface}', make sure this interface "\
109
107
  'is active and connected.' unless Network::Validator::is_mac?(@iface.mac)
110
108
 
109
+ if @options.core.use_ipv6
110
+ gw = @options.core.gateway || Network.get_ipv6_gateway
111
+ raise BetterCap::Error, "Could not detect the gateway address for interface #{@options.core.iface}, "\
112
+ 'make sure you\'ve specified the correct network interface to use and to have the '\
113
+ 'correct network configuration, this could also happen if bettercap '\
114
+ 'is launched from a virtual environment.' unless Network::Validator.is_ipv6?(gw)
115
+
116
+ else
117
+ gw = @options.core.gateway || Network.get_gateway
118
+ raise BetterCap::Error, "Could not detect the gateway address for interface #{@options.core.iface}, "\
119
+ 'make sure you\'ve specified the correct network interface to use and to have the '\
120
+ 'correct network configuration, this could also happen if bettercap '\
121
+ 'is launched from a virtual environment.' unless Network::Validator.is_ip?(gw)
122
+ end
123
+
124
+ @gateway = Network::Target.new gw
111
125
  Logger.info "[#{@iface.name.green}] #{@iface.to_s(false)}"
112
126
 
113
127
  Logger.debug '----- NETWORK INFORMATIONS -----'
@@ -116,9 +130,10 @@ class Context
116
130
  Logger.debug " local_ip = #{@iface.ip}"
117
131
  Logger.debug "--------------------------------\n"
118
132
 
133
+ @targets = @options.core.targets unless @options.core.targets.nil?
119
134
  @packets = Network::PacketQueue.new( @iface.name, @options.core.packet_throttle, 4 )
120
135
  # Spoofers need the context network data to be initialized.
121
- @spoofer = @options.spoof.parse_spoofers
136
+ @spoofer = @options.spoof.parse_spoofers(self)
122
137
 
123
138
  if @options.core.discovery?
124
139
  tstart = Time.now
@@ -204,7 +219,7 @@ class Context
204
219
 
205
220
  Logger.debug 'Disabling port redirections ...'
206
221
  @redirections.each do |r|
207
- @firewall.del_port_redirection( r )
222
+ @firewall.del_port_redirection( r, @options.core.use_ipv6 )
208
223
  end
209
224
 
210
225
  Logger.debug 'Restoring firewall state ...'
@@ -223,7 +238,7 @@ class Context
223
238
  @redirections = @options.get_redirections(@iface)
224
239
  @redirections.each do |r|
225
240
  Logger.debug "Redirecting #{r.protocol} traffic from #{r.src_address.nil? ? '*' : r.src_address}:#{r.src_port} to #{r.dst_address}:#{r.dst_port}"
226
- @firewall.add_port_redirection( r )
241
+ @firewall.add_port_redirection( r, @options.core.use_ipv6 )
227
242
  end
228
243
  end
229
244
 
@@ -250,6 +265,11 @@ class Context
250
265
  @proxies << Proxy::TCP::Proxy.new( @iface.ip, @options.proxies.tcp_proxy_port, @options.proxies.tcp_proxy_upstream_address, @options.proxies.tcp_proxy_upstream_port )
251
266
  end
252
267
 
268
+ # create UP proxy
269
+ if @options.proxies.udp_proxy
270
+ @proxies << Proxy::UDP::Proxy.new( @iface.ip, @options.proxies.udp_proxy_port, @options.proxies.udp_proxy_upstream_address, @options.proxies.udp_proxy_upstream_port )
271
+ end
272
+
253
273
  @proxies.each do |proxy|
254
274
  proxy.start
255
275
  end
@@ -0,0 +1,43 @@
1
+ # Parse the NDP table searching for new hosts.
2
+ module BetterCap
3
+ module Discovery
4
+ module Agents
5
+ # Class responsible of sending NDP probes to each possible IP on the network.
6
+ class Ndp < Discovery::Agents::Base
7
+ private
8
+
9
+ # Send a Neighbor Solicitation packet in order to update neighbor discovery table,
10
+ # with target's mac.
11
+ # The packet is a broadcast message, so mac is created by "33:33:ff" prefix plus the
12
+ # 24 least significant bits of the address.
13
+ # Similar rule applies to the destination address. Multicast address is formed from
14
+ # the network prefix ff02::1:ff00:0/104 and the 24 least significant bits of the address.
15
+ def get_probe( ip )
16
+ split_ip = ip.split(':')
17
+ dst_mac = "33:33:ff:" + split_ip[-2][-2,2] + ":" + split_ip[-1][0,2] + ":" + split_ip[-1][2,2]
18
+ dst_ip = "ff02::1:ff" + split_ip[-2][-2,2] + ":" + split_ip[-1]
19
+
20
+ p = PacketFu::NDPPacket.new
21
+
22
+ p.eth_daddr = dst_mac
23
+ p.eth_saddr = @ctx.iface.mac
24
+ p.eth_proto = 0x86dd
25
+
26
+ p.ipv6_saddr = @ctx.iface.ip
27
+ p.ipv6_daddr = dst_ip
28
+
29
+ p.ndp_type = 135
30
+ p.ndp_taddr = ip.to_s
31
+ p.ndp_opt_type = 1
32
+ p.ndp_opt_len = 1
33
+ p.ndp_lladdr = @ctx.iface.mac
34
+
35
+ p.ipv6_recalc
36
+ p.ndp_recalc
37
+
38
+ p
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -119,7 +119,7 @@ class Thread
119
119
 
120
120
  prev = @ctx.targets
121
121
 
122
- sleep(1) if @ctx.options.core.discovery?
122
+ sleep(1)
123
123
  end
124
124
  end
125
125
  end
@@ -81,13 +81,13 @@ class Base
81
81
 
82
82
  # Apply the +r+ BetterCap::Firewalls::Redirection port redirection object.
83
83
  # Raise NotImplementedError
84
- def add_port_redirection( r )
84
+ def add_port_redirection( r, use_ipv6 )
85
85
  not_implemented_method!
86
86
  end
87
87
 
88
88
  # Remove the +r+ BetterCap::Firewalls::Redirection port redirection object.
89
89
  # Raise NotImplementedError
90
- def del_port_redirection( r )
90
+ def del_port_redirection( r, use_ipv6 )
91
91
  not_implemented_method!
92
92
  end
93
93
 
@@ -25,6 +25,12 @@ class BSD < Base
25
25
  Shell.execute("sysctl -w net.inet.ip.forwarding=#{enabled ? 1 : 0}")
26
26
  end
27
27
 
28
+ # If +enabled+ is true will enable packet forwarding, otherwise it will
29
+ # disable it.
30
+ def enable_ipv6_forwarding(enabled)
31
+ Shell.execute("sysctl -w net.inet6.ip6.forwarding=#{enabled ? 1 : 0}")
32
+ end
33
+
28
34
  # If +enabled+ is true will enable packet icmp_echo_ignore_broadcasts, otherwise it will
29
35
  # disable it.
30
36
  def enable_icmp_bcast(enabled)
@@ -36,6 +42,11 @@ class BSD < Base
36
42
  Shell.execute('sysctl net.inet.ip.forwarding').strip.split(' ')[1] == '1'
37
43
  end
38
44
 
45
+ # Return true if packet forwarding for IPv6 is currently enabled, otherwise false.
46
+ def ipv6_forwarding_enabled?
47
+ Shell.execute('sysctl net.inet6.ip6.forwarding').strip.split(' ')[1] == '1'
48
+ end
49
+
39
50
  # This method is ignored on OSX.
40
51
  def enable_send_redirects(enabled); end
41
52
 
@@ -47,7 +58,7 @@ class BSD < Base
47
58
  end
48
59
 
49
60
  # Apply the +r+ BetterCap::Firewalls::Redirection port redirection object.
50
- def add_port_redirection( r )
61
+ def add_port_redirection( r, use_ipv6 )
51
62
  # create the pf config file
52
63
  File.open( @filename, 'a+t' ) do |f|
53
64
  f.write "#{gen_rule(r)}\n"
@@ -59,7 +70,7 @@ class BSD < Base
59
70
  end
60
71
 
61
72
  # Remove the +r+ BetterCap::Firewalls::Redirection port redirection object.
62
- def del_port_redirection( r )
73
+ def del_port_redirection( r, use_ipv6 )
63
74
  # remove the redirection rule from the existing file
64
75
  rule = gen_rule(r)
65
76
  rules = File.readlines(@filename).collect(&:strip).reject(&:empty?)
@@ -20,12 +20,20 @@ class Linux < Base
20
20
  IP_FORWARD_PATH = IPV4_PATH + "/ip_forward"
21
21
  ICMP_BCAST_PATH = IPV4_PATH + "/icmp_echo_ignore_broadcasts"
22
22
  SEND_REDIRECTS_PATH = IPV4_PATH + "/conf/all/send_redirects"
23
+ IPV6_PATH = "/proc/sys/net/ipv6"
24
+ IPV6_FORWARD_PATH = IPV6_PATH + "/conf/all/forwarding"
23
25
 
24
26
  def supported?
25
27
  # Avoids stuff like this https://github.com/evilsocket/bettercap/issues/341
26
28
  File.file?(IP_FORWARD_PATH)
27
29
  end
28
30
 
31
+ # If +enabled+ is true will enable packet forwarding, otherwise it will
32
+ # disable it.
33
+ def enable_ipv6_forwarding(enabled)
34
+ File.open(IPV6_FORWARD_PATH,'w') { |f| f.puts "#{enabled ? 1 : 0}"}
35
+ end
36
+
29
37
  # If +enabled+ is true will enable packet forwarding, otherwise it will
30
38
  # disable it.
31
39
  def enable_forwarding(enabled)
@@ -37,6 +45,12 @@ class Linux < Base
37
45
  File.open(IP_FORWARD_PATH) { |f| f.read.strip == '1' }
38
46
  end
39
47
 
48
+ # Return true if packet forwarding for IPv6 is currently enabled, otherwise false.
49
+ def ipv6_forwarding_enabled?
50
+ File.open(IPV6_FORWARD_PATH) { |f| f.read.strip == '1' }
51
+ end
52
+
53
+
40
54
  # If +enabled+ is true will enable packet icmp_echo_ignore_broadcasts, otherwise it will
41
55
  # disable it.
42
56
  def enable_icmp_bcast(enabled)
@@ -50,21 +64,38 @@ class Linux < Base
50
64
  end
51
65
 
52
66
  # Apply the +r+ BetterCap::Firewalls::Redirection port redirection object.
53
- def add_port_redirection( r )
67
+ def add_port_redirection( r, use_ipv6 )
68
+ table = 'iptables'
69
+ cal_dst_address = r.dst_address
70
+ if use_ipv6
71
+ table = 'ip6tables'
72
+ # Prevent sending out ICMPv6 Redirect packets.
73
+ Shell.execute("#{table} -I OUTPUT -p icmpv6 --icmpv6-type redirect -j DROP")
74
+
75
+ # Ipv6 uses a different ip + port representation
76
+ cal_dst_address = "[#{r.dst_address}]"
77
+ end
54
78
  # post route
55
- Shell.execute('iptables -t nat -I POSTROUTING -s 0/0 -j MASQUERADE')
79
+ Shell.execute("#{table} -t nat -I POSTROUTING -s 0/0 -j MASQUERADE")
56
80
  # accept all
57
- Shell.execute('iptables -P FORWARD ACCEPT')
81
+ Shell.execute("#{table} -P FORWARD ACCEPT")
58
82
  # add redirection
59
- Shell.execute("iptables -t nat -A PREROUTING -i #{r.interface} -p #{r.protocol} #{r.src_address.nil? ? '' : "-d #{r.src_address}"} --dport #{r.src_port} -j DNAT --to #{r.dst_address}:#{r.dst_port}")
83
+ Shell.execute("#{table} -t nat -A PREROUTING -i #{r.interface} -p #{r.protocol} #{r.src_address.nil? ? '' : "-d #{r.src_address}"} --dport #{r.src_port} -j DNAT --to #{cal_dst_address}:#{r.dst_port}")
60
84
  end
61
85
 
62
86
  # Remove the +r+ BetterCap::Firewalls::Redirection port redirection object.
63
- def del_port_redirection( r )
87
+ def del_port_redirection( r, use_ipv6 )
88
+ table = 'iptables'
89
+ cal_dst_address = r.dst_address
90
+ if use_ipv6
91
+ table = 'ip6tables'
92
+ # Ipv6 uses a different ip + port representation
93
+ cal_dst_address = "[#{r.dst_address}]"
94
+ end
64
95
  # remove post route
65
- Shell.execute('iptables -t nat -D POSTROUTING -s 0/0 -j MASQUERADE')
96
+ Shell.execute("#{table} -t nat -D POSTROUTING -s 0/0 -j MASQUERADE")
66
97
  # remove redirection
67
- Shell.execute("iptables -t nat -D PREROUTING -i #{r.interface} -p #{r.protocol} #{r.src_address.nil? ? '' : "-d #{r.src_address}"} --dport #{r.src_port} -j DNAT --to #{r.dst_address}:#{r.dst_port}")
98
+ Shell.execute("#{table} -t nat -D PREROUTING -i #{r.interface} -p #{r.protocol} #{r.src_address.nil? ? '' : "-d #{r.src_address}"} --dport #{r.src_port} -j DNAT --to #{cal_dst_address}:#{r.dst_port}")
68
99
  end
69
100
  end
70
101
  end
@@ -130,7 +130,7 @@ module PacketFu
130
130
  ret[:ip_src] = [IPAddr.new($1).to_i].pack('N')
131
131
  ret[:ip4_obj] = IPAddr.new($1)
132
132
  ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
133
- when /inet6 [a-z]+:[\s]*([0-9a-fA-F:\x2f]+)/
133
+ when /(fe80[^\/\s]*)/
134
134
  begin
135
135
  ret[:ip6_saddr] = $1
136
136
  ret[:ip6_obj] = IPAddr.new($1)
@@ -207,4 +207,222 @@ module PacketFu
207
207
  end
208
208
 
209
209
  end
210
+
211
+ class NDPHeader < Struct.new(:ndp_type, :ndp_code, :ndp_sum,
212
+ :ndp_reserved, :ndp_tgt, :ndp_opt_type,
213
+ :ndp_opt_len, :ndp_lla, :body)
214
+ include StructFu
215
+
216
+ PROTOCOL_NUMBER = 58
217
+
218
+ def initialize(args={})
219
+ super(
220
+ Int8.new(args[:ndp_type]),
221
+ Int8.new(args[:ndp_code]),
222
+ Int16.new(args[:ndp_sum]),
223
+ Int32.new(args[:ndp_reserved]),
224
+ AddrIpv6.new.read(args[:ndp_tgt] || ("\x00" * 16)),
225
+ Int8.new(args[:ndp_opt_type]),
226
+ Int8.new(args[:ndp_opt_len]),
227
+ EthMac.new.read(args[:ndp_lla])
228
+ )
229
+ end
230
+
231
+ # Returns the object in string form.
232
+ def to_s
233
+ self.to_a.map {|x| x.to_s}.join
234
+ end
235
+
236
+ # Reads a string to populate the object.
237
+ def read(str)
238
+ force_binary(str)
239
+ return self if str.nil?
240
+ self[:ndp_type].read(str[0,1])
241
+ self[:ndp_code].read(str[1,1])
242
+ self[:ndp_sum].read(str[2,2])
243
+ self[:ndp_reserved].read(str[4,4])
244
+ self[:ndp_tgt].read(str[8,16])
245
+ self[:ndp_opt_type].read(str[24,1])
246
+ self[:ndp_opt_len].read(str[25,1])
247
+ self[:ndp_lla].read(str[26,2])
248
+ self
249
+ end
250
+
251
+ # Setter for the type.
252
+ def ndp_type=(i); typecast i; end
253
+ # Getter for the type.
254
+ def ndp_type; self[:ndp_type].to_i; end
255
+ # Setter for the code.
256
+ def ndp_code=(i); typecast i; end
257
+ # Getter for the code.
258
+ def ndp_code; self[:ndp_code].to_i; end
259
+ # Setter for the checksum. Note, this is calculated automatically with
260
+ # ndp_calc_sum.
261
+ def ndp_sum=(i); typecast i; end
262
+ # Getter for the checksum.
263
+ def ndp_sum; self[:ndp_sum].to_i; end
264
+ # Setter for the reserved.
265
+ def ndp_reserved=(i); typecast i; end
266
+ # Getter for the reserved.
267
+ def ndp_reserved; self[:ndp_reserved].to_i; end
268
+ # Setter for the target address.
269
+ def ndp_tgt=(i); typecast i; end
270
+ # Getter for the target address.
271
+ def ndp_tgt; self[:ndp_tgt].to_i; end
272
+ # Setter for the options type field.
273
+ def ndp_opt_type=(i); typecast i; end
274
+ # Getter for the options type field.
275
+ def ndp_opt_type; self[:ndp_opt_type].to_i; end
276
+ # Setter for the options length.
277
+ def ndp_opt_len=(i); typecast i; end
278
+ # Getter for the options length.
279
+ def ndp_opt_len; self[:ndp_opt_len].to_i; end
280
+ # Setter for the link local address.
281
+ def ndp_lla=(i); typecast i; end
282
+ # Getter for the link local address.
283
+ def ndp_lla; self[:ndp_lla].to_s; end
284
+
285
+ # Get target address in a more readable form.
286
+ def ndp_taddr
287
+ self[:ndp_tgt].to_x
288
+ end
289
+
290
+ # Set the target address in a more readable form.
291
+ def ndp_taddr=(str)
292
+ self[:ndp_tgt].read_x(str)
293
+ end
294
+
295
+ # Sets the link local address in a more readable way.
296
+ def ndp_lladdr=(mac)
297
+ mac = EthHeader.mac2str(mac)
298
+ self[:ndp_lla].read mac
299
+ self[:ndp_lla]
300
+ end
301
+
302
+ # Gets the link local address in a more readable way.
303
+ def ndp_lladdr
304
+ EthHeader.str2mac(self[:ndp_lla].to_s)
305
+ end
306
+
307
+ def ndp_sum_readable
308
+ "0x%04x" % ndp_sum
309
+ end
310
+
311
+ # Set flag bits (First three are flag bits, the rest are reserved).
312
+ def ndp_set_flags=(bits)
313
+ case bits
314
+ when "000"
315
+ self.ndp_reserved = 0x00000000
316
+ when "001"
317
+ self.ndp_reserved = 0x20000000
318
+ when "010"
319
+ self.ndp_reserved = 0x40000000
320
+ when "011"
321
+ self.ndp_reserved = 0x60000000
322
+ when "100"
323
+ self.ndp_reserved = 0x80000000
324
+ when "101"
325
+ self.ndp_reserved = 0xa0000000
326
+ when "110"
327
+ self.ndp_reserved = 0xc0000000
328
+ when "111"
329
+ self.ndp_reserved = 0xe0000000
330
+ end
331
+ end
332
+
333
+ alias :ndp_tgt_readable :ndp_taddr
334
+ alias :ndp_lla_readable :ndp_lladdr
335
+
336
+ end
337
+
338
+ module NDPHeaderMixin
339
+ def ndp_type=(v); self.ndp_header.ndp_type= v; end
340
+ def ndp_type; self.ndp_header.ndp_type; end
341
+ def ndp_code=(v); self.ndp_header.ndp_code= v; end
342
+ def ndp_code; self.ndp_header.ndp_code; end
343
+ def ndp_sum=(v); self.ndp_header.ndp_sum= v; end
344
+ def ndp_sum; self.ndp_header.ndp_sum; end
345
+ def ndp_sum_readable; self.ndp_header.ndp_sum_readable; end
346
+ def ndp_reserved=(v); self.ndp_header.ndp_reserved= v; end
347
+ def ndp_reserved; self.ndp_header.ndp_reserved; end
348
+ def ndp_tgt=(v); self.ndp_header.ndp_tgt= v; end
349
+ def ndp_tgt; self.ndp_header.ndp_tgt; end
350
+ def ndp_taddr=(v); self.ndp_header.ndp_taddr= v; end
351
+ def ndp_taddr; self.ndp_header.ndp_taddr; end
352
+ def ndp_tgt_readable; self.ndp_header.ndp_tgt_readable; end
353
+ def ndp_opt_type=(v); self.ndp_header.ndp_opt_type= v; end
354
+ def ndp_opt_type; self.ndp_header.ndp_opt_type; end
355
+ def ndp_opt_len=(v); self.ndp_header.ndp_opt_len=v; end
356
+ def ndp_opt_len;self.ndp_header.ndp_opt_len; end
357
+ def ndp_lla=(v); self.ndp_header.ndp_lla=v; end
358
+ def ndp_lla;self.ndp_header.ndp_lla; end
359
+ def ndp_laddr=(v); self.ndp_header.ndp_laddr= v; end
360
+ def ndp_laddr; self.ndp_header.ndp_laddr; end
361
+ def ndp_lla_readable; self.ndp_header.ndp_lla_readable; end
362
+ def ndp_set_flags=(v); self.ndp_header.ndp_set_flags= v; end
363
+ end
364
+
365
+
366
+ class NDPPacket < Packet
367
+ include ::PacketFu::EthHeaderMixin
368
+ include ::PacketFu::IPv6HeaderMixin
369
+ include PacketFu::NDPHeaderMixin
370
+
371
+ attr_accessor :eth_header, :ipv6_header, :ndp_header
372
+
373
+ def initialize(args={})
374
+ @eth_header = EthHeader.new(args).read(args[:eth])
375
+ @ipv6_header = IPv6Header.new(args).read(args[:ipv6])
376
+ @ipv6_header.ipv6_next = PacketFu::NDPHeader::PROTOCOL_NUMBER
377
+ @ndp_header = NDPHeader.new(args).read(args[:ndp])
378
+
379
+ @ipv6_header.body = @ndp_header
380
+ @eth_header.body = @ipv6_header
381
+
382
+ @headers = [@eth_header, @ipv6_header, @ndp_header]
383
+ super
384
+ ndp_calc_sum
385
+ end
386
+
387
+ # Calculates the checksum for the object.
388
+ def ndp_calc_sum
389
+ checksum = 0
390
+
391
+ # Compute sum on pseudo-header
392
+ [ipv6_src, ipv6_dst].each do |iaddr|
393
+ 8.times { |i| checksum += (iaddr >> (i*16)) & 0xffff }
394
+ end
395
+ checksum += PacketFu::NDPHeader::PROTOCOL_NUMBER
396
+ checksum += ipv6_len
397
+ # Continue with entire ICMPv6 message.
398
+ checksum += (ndp_type.to_i << 8) + ndp_code.to_i
399
+ checksum += ndp_reserved.to_i >> 16
400
+ checksum += ndp_reserved.to_i & 0xffff
401
+ 8.times { |i| checksum += (ndp_tgt.to_i >> (i*16)) & 0xffff }
402
+ checksum += (ndp_opt_type.to_i << 8) + ndp_opt_len.to_i
403
+
404
+ mac2int = ndp_lla.to_s.unpack('H*').first.to_i(16)
405
+ 3.times { |i| checksum += (mac2int >> (i*16)) & 0xffff }
406
+
407
+ checksum = checksum % 0xffff
408
+ checksum = 0xffff - checksum
409
+ checksum == 0 ? 0xffff : checksum
410
+
411
+ end
412
+
413
+ # Recalculates the calculatable fields for NDP.
414
+ def ndp_recalc(arg=:all)
415
+ arg = arg.intern if arg.respond_to? :intern
416
+ case arg
417
+ when :ndp_sum
418
+ self.ndp_sum = ndp_calc_sum
419
+ when :all
420
+ self.ndp_sum = ndp_calc_sum
421
+ else
422
+ raise ArgumentError, "No such field `#{arg}'"
423
+ end
424
+ end
425
+
426
+ end
427
+
210
428
  end