packetfu 1.0.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.
Files changed (52) hide show
  1. data/.document +4 -0
  2. data/CHANGES +36 -0
  3. data/INSTALL +40 -0
  4. data/LICENSE +28 -0
  5. data/README +25 -0
  6. data/TODO +25 -0
  7. data/examples/ackscan.rb +38 -0
  8. data/examples/arp.rb +60 -0
  9. data/examples/arphood.rb +56 -0
  10. data/examples/ethernet.rb +10 -0
  11. data/examples/examples.rb +3 -0
  12. data/examples/ids.rb +4 -0
  13. data/examples/idsv2.rb +6 -0
  14. data/examples/oui.txt +84177 -0
  15. data/examples/packetfu-shell.rb +111 -0
  16. data/examples/simple-stats.rb +42 -0
  17. data/examples/slammer.rb +33 -0
  18. data/examples/uniqpcap.rb +15 -0
  19. data/lib/packetfu.rb +108 -0
  20. data/lib/packetfu/arp.rb +239 -0
  21. data/lib/packetfu/capture.rb +169 -0
  22. data/lib/packetfu/config.rb +55 -0
  23. data/lib/packetfu/eth.rb +264 -0
  24. data/lib/packetfu/icmp.rb +153 -0
  25. data/lib/packetfu/inject.rb +65 -0
  26. data/lib/packetfu/invalid.rb +41 -0
  27. data/lib/packetfu/ip.rb +318 -0
  28. data/lib/packetfu/ipv6.rb +230 -0
  29. data/lib/packetfu/packet.rb +492 -0
  30. data/lib/packetfu/pcap.rb +502 -0
  31. data/lib/packetfu/structfu.rb +274 -0
  32. data/lib/packetfu/tcp.rb +1061 -0
  33. data/lib/packetfu/udp.rb +210 -0
  34. data/lib/packetfu/utils.rb +182 -0
  35. data/test/all_tests.rb +37 -0
  36. data/test/ptest.rb +10 -0
  37. data/test/sample.pcap +0 -0
  38. data/test/sample2.pcap +0 -0
  39. data/test/test_arp.rb +135 -0
  40. data/test/test_eth.rb +90 -0
  41. data/test/test_icmp.rb +54 -0
  42. data/test/test_inject.rb +33 -0
  43. data/test/test_invalid.rb +28 -0
  44. data/test/test_ip.rb +69 -0
  45. data/test/test_ip6.rb +68 -0
  46. data/test/test_octets.rb +37 -0
  47. data/test/test_packet.rb +41 -0
  48. data/test/test_pcap.rb +210 -0
  49. data/test/test_structfu.rb +112 -0
  50. data/test/test_tcp.rb +327 -0
  51. data/test/test_udp.rb +73 -0
  52. metadata +144 -0
@@ -0,0 +1,210 @@
1
+ module PacketFu
2
+
3
+ # UDPHeader is a complete UDP struct, used in UDPPacket. Many Internet-critical protocols
4
+ # rely on UDP, such as DNS and World of Warcraft.
5
+ #
6
+ # For more on UDP packets, see http://www.networksorcery.com/enp/protocol/udp.htm
7
+ #
8
+ # ==== Header Definition
9
+ # Int16 :udp_src
10
+ # Int16 :udp_dst
11
+ # Int16 :udp_len Default: calculated
12
+ # Int16 :udp_sum Default: 0. Often calculated.
13
+ # String :body
14
+ class UDPHeader < Struct.new(:udp_src, :udp_dst, :udp_len, :udp_sum, :body)
15
+
16
+ include StructFu
17
+
18
+ def initialize(args={})
19
+ super(
20
+ Int16.new(args[:udp_src]),
21
+ Int16.new(args[:udp_dst]),
22
+ Int16.new(args[:udp_len] || udp_calc_len),
23
+ Int16.new(args[:udp_sum]),
24
+ StructFu::String.new.read(args[:body])
25
+ )
26
+ end
27
+
28
+ # Returns the object in string form.
29
+ def to_s
30
+ self.to_a.map {|x| x.to_s}.join
31
+ end
32
+
33
+ # Reads a string to populate the object.
34
+ def read(str)
35
+ force_binary(str)
36
+ return self if str.nil?
37
+ self[:udp_src].read(str[0,2])
38
+ self[:udp_dst].read(str[2,2])
39
+ self[:udp_len].read(str[4,2])
40
+ self[:udp_sum].read(str[6,2])
41
+ self[:body].read(str[8,str.size])
42
+ self
43
+ end
44
+
45
+ # Setter for the UDP source port.
46
+ def udp_src=(i); typecast i; end
47
+ # Getter for the UDP source port.
48
+ def udp_src; self[:udp_src].to_i; end
49
+ # Setter for the UDP destination port.
50
+ def udp_dst=(i); typecast i; end
51
+ # Getter for the UDP destination port.
52
+ def udp_dst; self[:udp_dst].to_i; end
53
+ # Setter for the length field. Usually should be recalc()'ed instead.
54
+ def udp_len=(i); typecast i; end
55
+ # Getter for the length field.
56
+ def udp_len; self[:udp_len].to_i; end
57
+ # Setter for the checksum. Usually should be recalc()'ed instad.
58
+ def udp_sum=(i); typecast i; end
59
+ # Getter for the checksum.
60
+ def udp_sum; self[:udp_sum].to_i; end
61
+
62
+ # Returns the true length of the UDP packet.
63
+ def udp_calc_len
64
+ body.to_s.size + 8
65
+ end
66
+
67
+ # Recalculates calculated fields for UDP.
68
+ def udp_recalc(args=:all)
69
+ arg = arg.intern if arg.respond_to? :intern
70
+ case args
71
+ when :udp_len
72
+ self.udp_len = udp_calc_len
73
+ when :all
74
+ self.udp_recalc(:udp_len)
75
+ else
76
+ raise ArgumentError, "No such field `#{arg}'"
77
+ end
78
+ end
79
+
80
+ # Equivalent to udp_src.to_i
81
+ def udp_sport
82
+ self.udp_src
83
+ end
84
+
85
+ # Equivalent to udp_src=
86
+ def udp_sport=(arg)
87
+ self.udp_src=(arg)
88
+ end
89
+
90
+ # Equivalent to udp_dst
91
+ def udp_dport
92
+ self.udp_dst
93
+ end
94
+
95
+ # Equivalent to udp_dst=
96
+ def udp_dport=(arg)
97
+ self.udp_dst=(arg)
98
+ end
99
+
100
+ end
101
+
102
+ # UDPPacket is used to construct UDP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader.
103
+ #
104
+ # == Example
105
+ #
106
+ # udp_pkt = PacketFu::UDPPacket.new
107
+ # udp_pkt.udp_src=rand(0xffff-1024) + 1024
108
+ # udp_pkt.udp_dst=53
109
+ #
110
+ # udp_pkt.ip_saddr="1.2.3.4"
111
+ # udp_pkt.ip_daddr="10.20.30.40"
112
+ #
113
+ # udp_pkt.recalc
114
+ # udp_pkt.to_f('/tmp/udp.pcap')
115
+ #
116
+ # == Parameters
117
+ #
118
+ # :eth
119
+ # A pre-generated EthHeader object.
120
+ # :ip
121
+ # A pre-generated IPHeader object.
122
+ # :flavor
123
+ # TODO: Sets the "flavor" of the UDP packet. UDP packets don't tend have a lot of
124
+ # flavor, but their underlying ip headers do.
125
+ # :config
126
+ # A hash of return address details, often the output of Utils.whoami?
127
+ class UDPPacket < Packet
128
+
129
+ attr_accessor :eth_header, :ip_header, :udp_header
130
+
131
+ def initialize(args={})
132
+ @eth_header = EthHeader.new(args).read(args[:eth])
133
+ @ip_header = IPHeader.new(args).read(args[:ip])
134
+ @ip_header.ip_proto=0x11
135
+ @udp_header = UDPHeader.new(args).read(args[:icmp])
136
+ @ip_header.body = @udp_header
137
+ @eth_header.body = @ip_header
138
+ @headers = [@eth_header, @ip_header, @udp_header]
139
+ super
140
+ udp_calc_sum
141
+ end
142
+
143
+ # udp_calc_sum() computes the UDP checksum, and is called upon intialization.
144
+ # It usually should be called just prior to dropping packets to a file or on the wire.
145
+ def udp_calc_sum
146
+ # This is /not/ delegated down to @udp_header since we need info
147
+ # from the IP header, too.
148
+ checksum = (ip_src.to_i >> 16)
149
+ checksum += (ip_src.to_i & 0xffff)
150
+ checksum += (ip_dst.to_i >> 16)
151
+ checksum += (ip_dst.to_i & 0xffff)
152
+ checksum += 0x11
153
+ checksum += udp_len.to_i
154
+ checksum += udp_src.to_i
155
+ checksum += udp_dst.to_i
156
+ checksum += udp_len.to_i
157
+ if udp_len.to_i >= 8
158
+ # For IP trailers. This isn't very reliable. :/
159
+ real_udp_payload = payload.to_s[0,(udp_len.to_i-8)]
160
+ else
161
+ # I'm not going to mess with this right now.
162
+ real_udp_payload = payload
163
+ end
164
+ chk_payload = (real_udp_payload.size % 2 == 0 ? real_udp_payload : real_udp_payload + "\x00")
165
+ chk_payload.unpack("n*").each {|x| checksum = checksum+x}
166
+ checksum = checksum % 0xffff
167
+ checksum = 0xffff - checksum
168
+ checksum == 0 ? 0xffff : checksum
169
+ @udp_header.udp_sum = checksum
170
+ end
171
+
172
+ # udp_recalc() recalculates various fields of the UDP packet. Valid arguments are:
173
+ #
174
+ # :all
175
+ # Recomputes all calculated fields.
176
+ # :udp_sum
177
+ # Recomputes the UDP checksum.
178
+ # :udp_len
179
+ # Recomputes the UDP length.
180
+ def udp_recalc(args=:all)
181
+ case args
182
+ when :udp_len
183
+ @udp_header.udp_recalc
184
+ when :udp_sum
185
+ udp_calc_sum
186
+ when :all
187
+ @udp_header.udp_recalc
188
+ udp_calc_sum
189
+ else
190
+ raise ArgumentError, "No such field `#{arg}'"
191
+ end
192
+ end
193
+
194
+ # Peek provides summary data on packet contents.
195
+ def peek(args={})
196
+ peek_data = ["U "]
197
+ peek_data << "%-5d" % self.to_s.size
198
+ peek_data << "%-21s" % "#{self.ip_saddr}:#{self.udp_sport}"
199
+ peek_data << "->"
200
+ peek_data << "%21s" % "#{self.ip_daddr}:#{self.udp_dport}"
201
+ peek_data << "%23s" % "I:"
202
+ peek_data << "%04x" % self.ip_id
203
+ peek_data.join
204
+ end
205
+
206
+ end
207
+
208
+ end
209
+
210
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
@@ -0,0 +1,182 @@
1
+ require 'singleton'
2
+ module PacketFu
3
+
4
+ # Utils is a collection of various and sundry network utilities that are useful for packet
5
+ # manipulation.
6
+ class Utils
7
+
8
+ # Returns the MAC address of an IP address, or nil if it's not responsive to arp. Takes
9
+ # a dotted-octect notation of the target IP address, as well as a number of parameters:
10
+ #
11
+ # === Parameters
12
+ # :eth_saddr
13
+ # Source MAC address. Defaults to "00:00:00:00:00:00".
14
+ # :ip_saddr
15
+ # Source IP address. Defaults to "0.0.0.0"
16
+ # :flavor
17
+ # The flavor of the ARP request. Defaults to :none.
18
+ # :timeout
19
+ # Timeout in seconds. Defaults to 3.
20
+ #
21
+ # === Example
22
+ # PacketFu::Utils::arp("192.168.1.1") #=> "00:18:39:01:33:70"
23
+ # PacketFu::Utils::arp("192.168.1.1", :timeout => 5, :flavor => :hp_deskjet)
24
+ #
25
+ # === Warning
26
+ #
27
+ # It goes without saying, spewing forged ARP packets on your network is a great way to really
28
+ # irritate your co-workers.
29
+ def self.arp(target_ip,args={})
30
+ arp_pkt = PacketFu::ARPPacket.new(:flavor => (args[:flavor] || :none))
31
+ arp_pkt.eth_saddr = arp_pkt.arp_saddr_mac = (args[:eth_saddr] || ($packetfu_default.config[:eth_saddr] if $packetfu_default) || "00:00:00:00:00:00" )
32
+ arp_pkt.eth_daddr = "ff:ff:ff:ff:ff:ff"
33
+ arp_pkt.arp_daddr_mac = "00:00:00:00:00:00"
34
+ arp_pkt.arp_saddr_ip = (args[:ip_saddr] || ($packetfu_default.config[:ip_saddr] if $packetfu_default) || "0.0.0.0")
35
+ arp_pkt.arp_daddr_ip = target_ip
36
+ iface = (args[:iface] || ($packetfu_default.iface if $packetfu_default) || "eth0")
37
+ # Stick the Capture object in its own thread.
38
+ cap_thread = Thread.new do
39
+ target_mac = nil
40
+ cap = PacketFu::Capture.new(:iface => iface, :start => true,
41
+ :filter => "arp src #{target_ip} and ether dst #{arp_pkt.eth_saddr}")
42
+ arp_pkt.to_w(iface) # Shorthand for sending single packets to the default interface.
43
+ timeout = 0
44
+ while target_mac.nil? && timeout <= (args[:timeout] || 3)
45
+ if cap.save > 0
46
+ arp_response = PacketFu::Packet.parse(cap.array[0])
47
+ target_mac = arp_response.arp_saddr_mac if arp_response.arp_saddr_ip = target_ip
48
+ end
49
+ timeout += 0.1
50
+ sleep 0.1 # Check for a response ten times per second.
51
+ end
52
+ target_mac
53
+ end # cap_thread
54
+ cap_thread.value
55
+ end
56
+
57
+ # Discovers the local IP and Ethernet address, which is useful for writing
58
+ # packets you expect to get a response to. Note, this is a noisy
59
+ # operation; a UDP packet is generated and dropped on to the default (or named)
60
+ # interface, and then captured (which means you need to be root to do this).
61
+ #
62
+ # whoami? returns a hash of :eth_saddr, :eth_src, :ip_saddr, :ip_src,
63
+ # :eth_dst, and :eth_daddr (the last two are usually suitable for a
64
+ # gateway mac address). It's most useful as an argument to PacketFu::Config.new.
65
+ #
66
+ # === Parameters
67
+ # :iface => "eth0"
68
+ # An interface to listen for packets on. Note that since we rely on the OS to send the probe packet,
69
+ # you will need to specify a target which will use this interface.
70
+ # :target => "1.2.3.4"
71
+ # A target IP address. By default, a packet will be sent to a random address in the 177/8 network.
72
+ # Since this network is IANA reserved (for now), this network should be handled by your default gateway
73
+ # and default interface.
74
+ def self.whoami?(args={})
75
+ if args[:iface] =~ /^lo/ # Linux loopback more or less. Need a switch for windows loopback, too.
76
+ dst_host = "127.0.0.1"
77
+ else
78
+ dst_host = (args[:target] || IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET).to_s)
79
+ end
80
+
81
+ dst_port = rand(0xffff-1024)+1024
82
+ msg = "PacketFu whoami? packet #{(Time.now.to_i + rand(0xffffff)+1)}"
83
+ cap = PacketFu::Capture.new(:iface => (args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo" ), :start => true, :filter => "udp and dst host #{dst_host} and dst port #{dst_port}")
84
+ UDPSocket.open.send(msg,0,dst_host,dst_port)
85
+ cap.save
86
+ pkt = Packet.parse(cap.array[0]) unless cap.save.zero?
87
+ timeout = 0
88
+ while timeout < 1 # Sometimes packet generation can be a little pokey.
89
+ if pkt
90
+ timeout = 1.1 # Cancel the timeout
91
+ if pkt.payload == msg
92
+ my_data = {
93
+ :iface => args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo",
94
+ :pcapfile => args[:pcapfile] || "/tmp/out.pcap",
95
+ :eth_saddr => pkt.eth_saddr,
96
+ :eth_src => pkt.eth_src.to_s,
97
+ :ip_saddr => pkt.ip_saddr,
98
+ :ip_src => pkt.ip_src.to_s,
99
+ :eth_dst => pkt.eth_dst.to_s,
100
+ :eth_daddr => pkt.eth_daddr
101
+ }
102
+ else raise SecurityError,
103
+ "whoami() packet doesn't match sent data. Something fishy's going on."
104
+ end
105
+ else
106
+ sleep 0.1; timeout += 0.1
107
+ cap.save
108
+ pkt = Packet.parse(cap.array[0]) unless cap.save.zero?
109
+ end
110
+ raise SocketError, "Didn't receive the whomi() packet." if !pkt
111
+ cap = nil
112
+ end
113
+ my_data
114
+ end
115
+
116
+ # This is a brute-force approach at trying to find a suitable interface with an IP address.
117
+ def self.lookupdev
118
+ # XXX cycle through eth0-9 and wlan0-9, and if a cap start throws a RuntimeErorr (and we're
119
+ # root), it's not a good interface. Boy, really ought to fix lookupdev directly with another
120
+ # method that returns an array rather than just the first candidate.
121
+ end
122
+
123
+ # Handles ifconfig for various (okay, one) platforms. Mac guys, fix this and submit a patch!
124
+ # Will have Windows done shortly.
125
+ #
126
+ # Takes an argument (either string or symbol) of the interface to look up, and
127
+ # returns a hash which contains at least the :iface element, and if configured,
128
+ # these additional elements:
129
+ #
130
+ # :eth_saddr # A human readable MAC address
131
+ # :eth_src # A packed MAC address
132
+ # :ip_saddr # A dotted-quad string IPv4 address
133
+ # :ip_src # A packed IPv4 address
134
+ # :ip4_obj # An IPAddr object with bitmask
135
+ # :ip6_saddr # A colon-delimited hex IPv6 address, with bitmask
136
+ # :ip6_obj # An IPAddr object with bitmask
137
+ #
138
+ # === Example
139
+ # PacketFu::Utils.ifconfig :wlan0 # Not associated yet
140
+ # #=> {:eth_saddr=>"00:1d:e0:73:9d:ff", :eth_src=>"\000\035\340s\235\377", :iface=>"wlan0"}
141
+ # PacketFu::Utils.ifconfig("eth0") # Takes 'eth0' as default
142
+ # #=> {:eth_saddr=>"00:1c:23:35:70:3b", :eth_src=>"\000\034#5p;", :ip_saddr=>"10.10.10.9", :ip4_obj=>#<IPAddr: IPv4:10.10.10.0/255.255.254.0>, :ip_src=>"\n\n\n\t", :iface=>"eth0", :ip6_saddr=>"fe80::21c:23ff:fe35:703b/64", :ip6_obj=>#<IPAddr: IPv6:fe80:0000:0000:0000:0000:0000:0000:0000/ffff:ffff:ffff:ffff:0000:0000:0000:0000>}
143
+ # PacketFu::Utils.ifconfig :lo
144
+ # #=> {:ip_saddr=>"127.0.0.1", :ip4_obj=>#<IPAddr: IPv4:127.0.0.0/255.0.0.0>, :ip_src=>"\177\000\000\001", :iface=>"lo", :ip6_saddr=>"::1/128", :ip6_obj=>#<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>}
145
+ def self.ifconfig(iface='eth0')
146
+ ret = {}
147
+ iface = iface.to_s.scan(/[0-9A-Za-z]/).join # Sanitizing input, no spaces, semicolons, etc.
148
+ case RUBY_PLATFORM
149
+ when /linux/i
150
+ ifconfig_data = %x[ifconfig #{iface}]
151
+ if ifconfig_data =~ /#{iface}/i
152
+ ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
153
+ else
154
+ raise ArgumentError, "Cannot ifconfig #{iface}"
155
+ end
156
+ real_iface = ifconfig_data.first
157
+ ret[:iface] = real_iface.split.first.downcase
158
+ if real_iface =~ /[\s]HWaddr[\s]+([0-9a-fA-F:]{17})/i
159
+ ret[:eth_saddr] = $1.downcase
160
+ ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
161
+ end
162
+ ifconfig_data.each do |s|
163
+ case s
164
+ when /inet addr:[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9])(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]))?/i
165
+ ret[:ip_saddr] = $1
166
+ ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
167
+ ret[:ip4_obj] = IPAddr.new($1)
168
+ ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
169
+ when /inet6 addr:[\s]*([0-9a-fA-F:\x2f]+)/
170
+ ret[:ip6_saddr] = $1
171
+ ret[:ip6_obj] = IPAddr.new($1)
172
+ end
173
+ end
174
+ end # linux
175
+ ret
176
+ end
177
+
178
+ end
179
+
180
+ end
181
+
182
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This test suite passes on:
4
+ # ruby-1.8.6-p399 [ x86_64 ]
5
+ # ruby-1.8.7-p174 [ x86_64 ]
6
+ # ruby-1.8.7-p249 [ x86_64 ]
7
+ # ruby-1.9.1-p378 [ x86_64 ]
8
+ # PacketFu (but not pcaprub) passes on:
9
+ # ruby-1.9.2-head [ x86_64 ]
10
+
11
+ require 'test/unit'
12
+ $: << File.expand_path(File.dirname(__FILE__) + "/../lib/")
13
+ require 'packetfu'
14
+
15
+ #Note that the Ruby stock unit tester runs this all out
16
+ #of order, does funny things with class variables, etc.
17
+
18
+ require 'test_structfu'
19
+ require 'test_pcap'
20
+ require 'test_invalid'
21
+ require 'test_eth' # Creates eth_test.pcap
22
+ require 'test_octets'
23
+ require 'test_packet'
24
+ require 'test_arp' # Creates arp_test.pcap
25
+ require 'test_ip' # Creates ip_test.pcap
26
+ require 'test_icmp' # Creates icmp_test.pcap
27
+ require 'test_udp' # Creates udp_test.pcap
28
+ require 'test_tcp'
29
+ require 'test_ip6'
30
+
31
+ if Process.euid.zero?
32
+ require 'test_inject'
33
+ else
34
+ $stderr.puts "** WARNING ** test_inject not tested, needs root access."
35
+ end
36
+
37
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib/")
3
+ require 'pcaprub'
4
+ require 'packetfu'
5
+ include PacketFu
6
+
7
+ puts Pcap.lookupdev
8
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
9
+
10
+
Binary file
Binary file
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ $: << File.expand_path(File.dirname(__FILE__) + "/../lib/")
4
+ require 'packetfu'
5
+ class ArpTest < Test::Unit::TestCase
6
+ include PacketFu
7
+
8
+ def test_arp_header
9
+ a = ARPHeader.new
10
+ assert_kind_of ARPHeader, a
11
+ assert_kind_of StructFu::Int16, a[:arp_hw]
12
+ assert_kind_of Fixnum, a.arp_hw
13
+ assert_kind_of Octets, a[:arp_src_ip]
14
+ assert_kind_of String, a.arp_src_ip
15
+ assert_kind_of EthMac, a[:arp_dst_mac]
16
+ assert_kind_of String, a.arp_dst_mac
17
+ assert_kind_of StructFu::String, a.body
18
+ end
19
+
20
+ def test_read_header
21
+ a = ARPHeader.new
22
+ sample_arp = "000108000604000200032f1a74dec0a80102001b1151b7cec0a80169"
23
+ sample_arp = sample_arp.scan(/../).map {|x| x.to_i(16)}.pack("C*")
24
+ a.read(sample_arp)
25
+ assert_equal(sample_arp, a.to_s)
26
+ assert_equal("192.168.1.105", a.arp_daddr_ip)
27
+ assert_equal("192.168.1.2", a.arp_saddr_ip)
28
+ assert_equal("00:1b:11:51:b7:ce", a.arp_daddr_mac)
29
+ assert_equal("00:03:2f:1a:74:de", a.arp_saddr_mac)
30
+ end
31
+
32
+ def test_arp_read
33
+ a = ARPPacket.new
34
+ sample_arp = "001b1151b7ce00032f1a74de0806000108000604000200032f1a74dec0a80102001b1151b7cec0a80169c0a80169"
35
+ sample_arp = sample_arp.scan(/../).map {|x| x.to_i(16)}.pack("C*")
36
+ a.read(sample_arp)
37
+ assert_equal(sample_arp, a.to_s)
38
+ end
39
+
40
+ def test_write_ip
41
+ a = ARPPacket.new
42
+ a.arp_saddr_ip="1.2.3.4"
43
+ a.arp_daddr_ip="5.6.7.8"
44
+ assert_equal("1.2.3.4",a.arp_saddr_ip)
45
+ assert_equal("5.6.7.8",a.arp_daddr_ip)
46
+ assert_equal("\x01\x02\x03\x04",a.arp_src_ip)
47
+ assert_equal("\x05\x06\x07\x08",a.arp_dst_ip)
48
+ end
49
+
50
+ def test_write_mac
51
+ a = ARPPacket.new
52
+ a.arp_saddr_mac = "00:01:02:03:04:05"
53
+ a.arp_daddr_mac = "00:06:07:08:09:0a"
54
+ assert_equal("00:01:02:03:04:05",a.arp_saddr_mac)
55
+ assert_equal("00:06:07:08:09:0a",a.arp_daddr_mac)
56
+ assert_equal("\x00\x01\x02\x03\x04\x05",a.arp_src_mac)
57
+ assert_equal("\x00\x06\x07\x08\x09\x0a",a.arp_dst_mac)
58
+ end
59
+
60
+ def test_arp_flavors
61
+ a = ARPPacket.new(:flavor => "Windows")
62
+ assert_equal("\x00" * 64, a.payload)
63
+ a = ARPPacket.new(:flavor => "Linux")
64
+ assert_equal(32, a.payload.size)
65
+ a = ARPPacket.new(:flavor => :hp_deskjet)
66
+ assert_equal(18, a.payload.size)
67
+ a = ARPPacket.new
68
+ assert_equal("\x00" * 18, a.payload)
69
+ end
70
+
71
+ def test_arp_create
72
+ sample_arp = "000108000604000200032f1a74dec0a80102001b1151b7cec0a80169"
73
+ sample_arp = sample_arp.scan(/../).map {|x| x.to_i(16)}.pack("C*")
74
+ a = ARPPacket.new
75
+ assert_kind_of ARPPacket, a
76
+ a.arp_hw = 1
77
+ a.arp_proto = 0x0800
78
+ a.arp_hw_len = 6
79
+ a.arp_proto_len = 4
80
+ a.arp_opcode = 2
81
+ a.arp_src_mac = "\x00\x03\x2f\x1a\x74\xde"
82
+ a.arp_src_ip = "\xc0\xa8\x01\x02"
83
+ a.arp_dst_mac = "\x00\x1b\x11\x51\xb7\xce"
84
+ a.arp_dst_ip = "\xc0\xa8\x01\x69"
85
+ a.payload = ""
86
+ assert_equal(sample_arp,a.to_s[14,0xffff])
87
+ end
88
+
89
+ def test_arp_new
90
+ sample_arp = "000108000604000200032f1a74dec0a80102001b1151b7cec0a80169c0a80169"
91
+ sample_arp = sample_arp.scan(/../).map {|x| x.to_i(16)}.pack("C*")
92
+ arp = ARPPacket.new(:arp_hw => 1, :arp_proto => 0x0800,
93
+ :arp_opcode => 2, :arp_src_ip => "\xc0\xa8\x01\x02")
94
+ assert_kind_of ARPPacket, arp
95
+ arp.arp_hw_len = 6
96
+ arp.arp_proto_len = 4
97
+ arp.arp_src_mac = "\x00\x03\x2f\x1a\x74\xde"
98
+ arp.arp_dst_mac = "\x00\x1b\x11\x51\xb7\xce"
99
+ arp.arp_dst_ip = "\xc0\xa8\x01\x69"
100
+ arp.payload = "\xc0\xa8\x01\x69"
101
+ assert_equal(sample_arp,arp.to_s[14,0xffff])
102
+ end
103
+
104
+ def test_arp_peek
105
+ a = ARPPacket.new
106
+ puts "\n"
107
+ puts "ARP Peek format: "
108
+ puts a.peek
109
+ puts "\n"
110
+ assert_equal(66,a.peek.size)
111
+ end
112
+
113
+ def test_arp_pcap
114
+ a = ARPPacket.new
115
+ assert_kind_of ARPPacket, a
116
+ a.to_f('arp_test.pcap','w')
117
+ a.arp_hw = 1
118
+ a.arp_proto = 0x0800
119
+ a.arp_hw_len = 6
120
+ a.arp_proto_len = 4
121
+ a.arp_opcode = 2
122
+ a.arp_src_mac = "\x00\x03\x2f\x1a\x74\xde"
123
+ a.arp_src_ip = "\xc0\xa8\x01\x02"
124
+ a.arp_dst_mac = "\x00\x1b\x11\x51\xb7\xce"
125
+ a.arp_dst_ip = "\xc0\xa8\x01\x69"
126
+ a.payload = ""
127
+ a.eth_daddr = "00:1b:11:51:b7:ce"
128
+ a.eth_saddr = "00:03:2f:1a:74:de"
129
+ a.to_f('arp_test.pcap','a')
130
+ end
131
+
132
+ end
133
+
134
+
135
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby