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.
- data/.document +4 -0
- data/CHANGES +36 -0
- data/INSTALL +40 -0
- data/LICENSE +28 -0
- data/README +25 -0
- data/TODO +25 -0
- data/examples/ackscan.rb +38 -0
- data/examples/arp.rb +60 -0
- data/examples/arphood.rb +56 -0
- data/examples/ethernet.rb +10 -0
- data/examples/examples.rb +3 -0
- data/examples/ids.rb +4 -0
- data/examples/idsv2.rb +6 -0
- data/examples/oui.txt +84177 -0
- data/examples/packetfu-shell.rb +111 -0
- data/examples/simple-stats.rb +42 -0
- data/examples/slammer.rb +33 -0
- data/examples/uniqpcap.rb +15 -0
- data/lib/packetfu.rb +108 -0
- data/lib/packetfu/arp.rb +239 -0
- data/lib/packetfu/capture.rb +169 -0
- data/lib/packetfu/config.rb +55 -0
- data/lib/packetfu/eth.rb +264 -0
- data/lib/packetfu/icmp.rb +153 -0
- data/lib/packetfu/inject.rb +65 -0
- data/lib/packetfu/invalid.rb +41 -0
- data/lib/packetfu/ip.rb +318 -0
- data/lib/packetfu/ipv6.rb +230 -0
- data/lib/packetfu/packet.rb +492 -0
- data/lib/packetfu/pcap.rb +502 -0
- data/lib/packetfu/structfu.rb +274 -0
- data/lib/packetfu/tcp.rb +1061 -0
- data/lib/packetfu/udp.rb +210 -0
- data/lib/packetfu/utils.rb +182 -0
- data/test/all_tests.rb +37 -0
- data/test/ptest.rb +10 -0
- data/test/sample.pcap +0 -0
- data/test/sample2.pcap +0 -0
- data/test/test_arp.rb +135 -0
- data/test/test_eth.rb +90 -0
- data/test/test_icmp.rb +54 -0
- data/test/test_inject.rb +33 -0
- data/test/test_invalid.rb +28 -0
- data/test/test_ip.rb +69 -0
- data/test/test_ip6.rb +68 -0
- data/test/test_octets.rb +37 -0
- data/test/test_packet.rb +41 -0
- data/test/test_pcap.rb +210 -0
- data/test/test_structfu.rb +112 -0
- data/test/test_tcp.rb +327 -0
- data/test/test_udp.rb +73 -0
- metadata +144 -0
data/lib/packetfu/udp.rb
ADDED
@@ -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
|
data/test/all_tests.rb
ADDED
@@ -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
|
data/test/ptest.rb
ADDED
data/test/sample.pcap
ADDED
Binary file
|
data/test/sample2.pcap
ADDED
Binary file
|
data/test/test_arp.rb
ADDED
@@ -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
|