packetfu 1.1.1 → 1.1.2
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/{README → README.rdoc} +2 -2
- metadata +58 -94
- data/INSTALL +0 -40
- data/LICENSE +0 -28
- data/examples/100kpackets.rb +0 -41
- data/examples/ackscan.rb +0 -38
- data/examples/arp.rb +0 -60
- data/examples/arphood.rb +0 -59
- data/examples/dissect_thinger.rb +0 -22
- data/examples/ethernet.rb +0 -10
- data/examples/examples.rb +0 -3
- data/examples/ids.rb +0 -4
- data/examples/idsv2.rb +0 -6
- data/examples/new-simple-stats.rb +0 -52
- data/examples/oui.txt +0 -84177
- data/examples/packetfu-shell.rb +0 -113
- data/examples/simple-sniffer.rb +0 -40
- data/examples/simple-stats.rb +0 -50
- data/examples/slammer.rb +0 -33
- data/examples/uniqpcap.rb +0 -15
- data/lib/packetfu.rb +0 -147
- data/lib/packetfu/capture.rb +0 -169
- data/lib/packetfu/config.rb +0 -58
- data/lib/packetfu/inject.rb +0 -65
- data/lib/packetfu/packet.rb +0 -533
- data/lib/packetfu/pcap.rb +0 -594
- data/lib/packetfu/protos/arp.rb +0 -268
- data/lib/packetfu/protos/eth.rb +0 -296
- data/lib/packetfu/protos/hsrp.rb +0 -206
- data/lib/packetfu/protos/icmp.rb +0 -179
- data/lib/packetfu/protos/invalid.rb +0 -55
- data/lib/packetfu/protos/ip.rb +0 -378
- data/lib/packetfu/protos/ipv6.rb +0 -250
- data/lib/packetfu/protos/tcp.rb +0 -1127
- data/lib/packetfu/protos/udp.rb +0 -240
- data/lib/packetfu/structfu.rb +0 -294
- data/lib/packetfu/utils.rb +0 -194
- data/lib/packetfu/version.rb +0 -50
- data/test/all_tests.rb +0 -41
- data/test/arp_test.pcap +0 -0
- data/test/eth_test.pcap +0 -0
- data/test/ethpacket_spec.rb +0 -74
- data/test/icmp_test.pcap +0 -0
- data/test/ip_test.pcap +0 -0
- data/test/packet_spec.rb +0 -73
- data/test/packet_subclasses_spec.rb +0 -13
- data/test/packetfu_spec.rb +0 -90
- data/test/ptest.rb +0 -16
- data/test/sample-ipv6.pcap +0 -0
- data/test/sample.pcap +0 -0
- data/test/sample2.pcap +0 -0
- data/test/sample_hsrp_pcapr.cap +0 -0
- data/test/structfu_spec.rb +0 -335
- data/test/tcp_spec.rb +0 -101
- data/test/tcp_test.pcap +0 -0
- data/test/test_arp.rb +0 -135
- data/test/test_eth.rb +0 -91
- data/test/test_hsrp.rb +0 -20
- data/test/test_icmp.rb +0 -54
- data/test/test_inject.rb +0 -31
- data/test/test_invalid.rb +0 -28
- data/test/test_ip.rb +0 -69
- data/test/test_ip6.rb +0 -68
- data/test/test_octets.rb +0 -37
- data/test/test_packet.rb +0 -174
- data/test/test_pcap.rb +0 -209
- data/test/test_structfu.rb +0 -112
- data/test/test_tcp.rb +0 -327
- data/test/test_udp.rb +0 -73
- data/test/udp_test.pcap +0 -0
- data/test/vlan-pcapr.cap +0 -0
data/lib/packetfu/protos/arp.rb
DELETED
@@ -1,268 +0,0 @@
|
|
1
|
-
module PacketFu
|
2
|
-
|
3
|
-
# ARPHeader is a complete ARP struct, used in ARPPacket.
|
4
|
-
#
|
5
|
-
# ARP is used to discover the machine address of nearby devices.
|
6
|
-
#
|
7
|
-
# See http://www.networksorcery.com/enp/protocol/arp.htm for details.
|
8
|
-
#
|
9
|
-
# ==== Header Definition
|
10
|
-
#
|
11
|
-
# Int16 :arp_hw Default: 1 # Ethernet
|
12
|
-
# Int16 :arp_proto, Default: 0x8000 # IP
|
13
|
-
# Int8 :arp_hw_len, Default: 6
|
14
|
-
# Int8 :arp_proto_len, Default: 4
|
15
|
-
# Int16 :arp_opcode, Default: 1 # 1: Request, 2: Reply, 3: Request-Reverse, 4: Reply-Reverse
|
16
|
-
# EthMac :arp_src_mac # From eth.rb
|
17
|
-
# Octets :arp_src_ip # From ip.rb
|
18
|
-
# EthMac :arp_dst_mac # From eth.rb
|
19
|
-
# Octets :arp_dst_ip # From ip.rb
|
20
|
-
# String :body
|
21
|
-
class ARPHeader < Struct.new(:arp_hw, :arp_proto, :arp_hw_len,
|
22
|
-
:arp_proto_len, :arp_opcode,
|
23
|
-
:arp_src_mac, :arp_src_ip,
|
24
|
-
:arp_dst_mac, :arp_dst_ip,
|
25
|
-
:body)
|
26
|
-
include StructFu
|
27
|
-
|
28
|
-
def initialize(args={})
|
29
|
-
src_mac = args[:arp_src_mac] || (args[:config][:eth_src] if args[:config])
|
30
|
-
src_ip_bin = args[:arp_src_ip] || (args[:config][:ip_src_bin] if args[:config])
|
31
|
-
|
32
|
-
super(
|
33
|
-
Int16.new(args[:arp_hw] || 1),
|
34
|
-
Int16.new(args[:arp_proto] ||0x0800),
|
35
|
-
Int8.new(args[:arp_hw_len] || 6),
|
36
|
-
Int8.new(args[:arp_proto_len] || 4),
|
37
|
-
Int16.new(args[:arp_opcode] || 1),
|
38
|
-
EthMac.new.read(src_mac),
|
39
|
-
Octets.new.read(src_ip_bin),
|
40
|
-
EthMac.new.read(args[:arp_dst_mac]),
|
41
|
-
Octets.new.read(args[:arp_dst_ip]),
|
42
|
-
StructFu::String.new.read(args[:body])
|
43
|
-
)
|
44
|
-
end
|
45
|
-
|
46
|
-
# Returns the object in string form.
|
47
|
-
def to_s
|
48
|
-
self.to_a.map {|x| x.to_s}.join
|
49
|
-
end
|
50
|
-
|
51
|
-
# Reads a string to populate the object.
|
52
|
-
def read(str)
|
53
|
-
force_binary(str)
|
54
|
-
return self if str.nil?
|
55
|
-
self[:arp_hw].read(str[0,2])
|
56
|
-
self[:arp_proto].read(str[2,2])
|
57
|
-
self[:arp_hw_len].read(str[4,1])
|
58
|
-
self[:arp_proto_len].read(str[5,1])
|
59
|
-
self[:arp_opcode].read(str[6,2])
|
60
|
-
self[:arp_src_mac].read(str[8,6])
|
61
|
-
self[:arp_src_ip].read(str[14,4])
|
62
|
-
self[:arp_dst_mac].read(str[18,6])
|
63
|
-
self[:arp_dst_ip].read(str[24,4])
|
64
|
-
self[:body].read(str[28,str.size])
|
65
|
-
self
|
66
|
-
end
|
67
|
-
|
68
|
-
# Setter for the ARP hardware type.
|
69
|
-
def arp_hw=(i); typecast i; end
|
70
|
-
# Getter for the ARP hardware type.
|
71
|
-
def arp_hw; self[:arp_hw].to_i; end
|
72
|
-
# Setter for the ARP protocol.
|
73
|
-
def arp_proto=(i); typecast i; end
|
74
|
-
# Getter for the ARP protocol.
|
75
|
-
def arp_proto; self[:arp_proto].to_i; end
|
76
|
-
# Setter for the ARP hardware type length.
|
77
|
-
def arp_hw_len=(i); typecast i; end
|
78
|
-
# Getter for the ARP hardware type length.
|
79
|
-
def arp_hw_len; self[:arp_hw_len].to_i; end
|
80
|
-
# Setter for the ARP protocol length.
|
81
|
-
def arp_proto_len=(i); typecast i; end
|
82
|
-
# Getter for the ARP protocol length.
|
83
|
-
def arp_proto_len; self[:arp_proto_len].to_i; end
|
84
|
-
# Setter for the ARP opcode.
|
85
|
-
def arp_opcode=(i); typecast i; end
|
86
|
-
# Getter for the ARP opcode.
|
87
|
-
def arp_opcode; self[:arp_opcode].to_i; end
|
88
|
-
# Setter for the ARP source MAC address.
|
89
|
-
def arp_src_mac=(i); typecast i; end
|
90
|
-
# Getter for the ARP source MAC address.
|
91
|
-
def arp_src_mac; self[:arp_src_mac].to_s; end
|
92
|
-
# Getter for the ARP source IP address.
|
93
|
-
def arp_src_ip=(i); typecast i; end
|
94
|
-
# Setter for the ARP source IP address.
|
95
|
-
def arp_src_ip; self[:arp_src_ip].to_s; end
|
96
|
-
# Setter for the ARP destination MAC address.
|
97
|
-
def arp_dst_mac=(i); typecast i; end
|
98
|
-
# Setter for the ARP destination MAC address.
|
99
|
-
def arp_dst_mac; self[:arp_dst_mac].to_s; end
|
100
|
-
# Setter for the ARP destination IP address.
|
101
|
-
def arp_dst_ip=(i); typecast i; end
|
102
|
-
# Getter for the ARP destination IP address.
|
103
|
-
def arp_dst_ip; self[:arp_dst_ip].to_s; end
|
104
|
-
|
105
|
-
# Set the source MAC address in a more readable way.
|
106
|
-
def arp_saddr_mac=(mac)
|
107
|
-
mac = EthHeader.mac2str(mac)
|
108
|
-
self[:arp_src_mac].read(mac)
|
109
|
-
self.arp_src_mac
|
110
|
-
end
|
111
|
-
|
112
|
-
# Get a more readable source MAC address.
|
113
|
-
def arp_saddr_mac
|
114
|
-
EthHeader.str2mac(self[:arp_src_mac].to_s)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Set the destination MAC address in a more readable way.
|
118
|
-
def arp_daddr_mac=(mac)
|
119
|
-
mac = EthHeader.mac2str(mac)
|
120
|
-
self[:arp_dst_mac].read(mac)
|
121
|
-
self.arp_dst_mac
|
122
|
-
end
|
123
|
-
|
124
|
-
# Get a more readable source MAC address.
|
125
|
-
def arp_daddr_mac
|
126
|
-
EthHeader.str2mac(self[:arp_dst_mac].to_s)
|
127
|
-
end
|
128
|
-
|
129
|
-
# Set a more readable source IP address.
|
130
|
-
def arp_saddr_ip=(addr)
|
131
|
-
self[:arp_src_ip].read_quad(addr)
|
132
|
-
end
|
133
|
-
|
134
|
-
# Get a more readable source IP address.
|
135
|
-
def arp_saddr_ip
|
136
|
-
self[:arp_src_ip].to_x
|
137
|
-
end
|
138
|
-
|
139
|
-
# Set a more readable destination IP address.
|
140
|
-
def arp_daddr_ip=(addr)
|
141
|
-
self[:arp_dst_ip].read_quad(addr)
|
142
|
-
end
|
143
|
-
|
144
|
-
# Get a more readable destination IP address.
|
145
|
-
def arp_daddr_ip
|
146
|
-
self[:arp_dst_ip].to_x
|
147
|
-
end
|
148
|
-
|
149
|
-
# Readability aliases
|
150
|
-
|
151
|
-
alias :arp_src_mac_readable :arp_saddr_mac
|
152
|
-
alias :arp_dst_mac_readable :arp_daddr_mac
|
153
|
-
alias :arp_src_ip_readable :arp_saddr_ip
|
154
|
-
alias :arp_dst_ip_readable :arp_daddr_ip
|
155
|
-
|
156
|
-
def arp_proto_readable
|
157
|
-
"0x%04x" % arp_proto
|
158
|
-
end
|
159
|
-
|
160
|
-
end # class ARPHeader
|
161
|
-
|
162
|
-
# ARPPacket is used to construct ARP packets. They contain an EthHeader and an ARPHeader.
|
163
|
-
# == Example
|
164
|
-
#
|
165
|
-
# require 'packetfu'
|
166
|
-
# arp_pkt = PacketFu::ARPPacket.new(:flavor => "Windows")
|
167
|
-
# arp_pkt.arp_saddr_mac="00:1c:23:44:55:66" # Your hardware address
|
168
|
-
# arp_pkt.arp_saddr_ip="10.10.10.17" # Your IP address
|
169
|
-
# arp_pkt.arp_daddr_ip="10.10.10.1" # Target IP address
|
170
|
-
# arp_pkt.arp_opcode=1 # Request
|
171
|
-
#
|
172
|
-
# arp_pkt.to_w('eth0') # Inject on the wire. (requires root)
|
173
|
-
# arp_pkt.to_f('/tmp/arp.pcap') # Write to a file.
|
174
|
-
#
|
175
|
-
# == Parameters
|
176
|
-
#
|
177
|
-
# :flavor
|
178
|
-
# Sets the "flavor" of the ARP packet. Choices are currently:
|
179
|
-
# :windows, :linux, :hp_deskjet
|
180
|
-
# :eth
|
181
|
-
# A pre-generated EthHeader object. If not specified, a new one will be created.
|
182
|
-
# :arp
|
183
|
-
# A pre-generated ARPHeader object. If not specificed, a new one will be created.
|
184
|
-
# :config
|
185
|
-
# A hash of return address details, often the output of Utils.whoami?
|
186
|
-
class ARPPacket < Packet
|
187
|
-
|
188
|
-
attr_accessor :eth_header, :arp_header
|
189
|
-
|
190
|
-
def self.can_parse?(str)
|
191
|
-
return false unless EthPacket.can_parse? str
|
192
|
-
return false unless str.size >= 28
|
193
|
-
return false unless str[12,2] == "\x08\x06"
|
194
|
-
true
|
195
|
-
end
|
196
|
-
|
197
|
-
def read(str=nil,args={})
|
198
|
-
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
199
|
-
@eth_header.read(str)
|
200
|
-
@arp_header.read(str[14,str.size])
|
201
|
-
@eth_header.body = @arp_header
|
202
|
-
super(args)
|
203
|
-
self
|
204
|
-
end
|
205
|
-
|
206
|
-
def initialize(args={})
|
207
|
-
@eth_header = EthHeader.new(args).read(args[:eth])
|
208
|
-
@arp_header = ARPHeader.new(args).read(args[:arp])
|
209
|
-
@eth_header.eth_proto = "\x08\x06"
|
210
|
-
@eth_header.body=@arp_header
|
211
|
-
|
212
|
-
# Please send more flavors to todb+packetfu@planb-security.net.
|
213
|
-
# Most of these initial fingerprints come from one (1) sample.
|
214
|
-
case (args[:flavor].nil?) ? :nil : args[:flavor].to_s.downcase.intern
|
215
|
-
when :windows; @arp_header.body = "\x00" * 64 # 64 bytes of padding
|
216
|
-
when :linux; @arp_header.body = "\x00" * 4 + # 32 bytes of padding
|
217
|
-
"\x00\x07\x5c\x14" + "\x00" * 4 +
|
218
|
-
"\x00\x0f\x83\x34" + "\x00\x0f\x83\x74" +
|
219
|
-
"\x01\x11\x83\x78" + "\x00\x00\x00\x0c" +
|
220
|
-
"\x00\x00\x00\x00"
|
221
|
-
when :hp_deskjet; # Pads up to 60 bytes.
|
222
|
-
@arp_header.body = "\xe0\x90\x0d\x6c" +
|
223
|
-
"\xff\xff\xee\xee" + "\x00" * 4 +
|
224
|
-
"\xe0\x8f\xfa\x18\x00\x20"
|
225
|
-
else; @arp_header.body = "\x00" * 18 # Pads up to 60 bytes.
|
226
|
-
end
|
227
|
-
|
228
|
-
@headers = [@eth_header, @arp_header]
|
229
|
-
super
|
230
|
-
end
|
231
|
-
|
232
|
-
# Generates summary data for ARP packets.
|
233
|
-
def peek_format
|
234
|
-
peek_data = ["A "]
|
235
|
-
peek_data << "%-5d" % self.to_s.size
|
236
|
-
peek_data << arp_saddr_mac
|
237
|
-
peek_data << "(#{arp_saddr_ip})"
|
238
|
-
peek_data << "->"
|
239
|
-
peek_data << case arp_daddr_mac
|
240
|
-
when "00:00:00:00:00:00"; "Bcast00"
|
241
|
-
when "ff:ff:ff:ff:ff:ff"; "BcastFF"
|
242
|
-
else; arp_daddr_mac
|
243
|
-
end
|
244
|
-
peek_data << "(#{arp_daddr_ip})"
|
245
|
-
peek_data << ":"
|
246
|
-
peek_data << case arp_opcode
|
247
|
-
when 1; "Requ"
|
248
|
-
when 2; "Repl"
|
249
|
-
when 3; "RReq"
|
250
|
-
when 4; "RRpl"
|
251
|
-
when 5; "IReq"
|
252
|
-
when 6; "IRpl"
|
253
|
-
else; "0x%02x" % arp_opcode
|
254
|
-
end
|
255
|
-
peek_data.join
|
256
|
-
end
|
257
|
-
|
258
|
-
# While there are lengths in ARPPackets, there's not
|
259
|
-
# much to do with them.
|
260
|
-
def recalc(args={})
|
261
|
-
@headers[0].inspect
|
262
|
-
end
|
263
|
-
|
264
|
-
end
|
265
|
-
|
266
|
-
end
|
267
|
-
|
268
|
-
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
data/lib/packetfu/protos/eth.rb
DELETED
@@ -1,296 +0,0 @@
|
|
1
|
-
module PacketFu
|
2
|
-
|
3
|
-
# EthOui is the Organizationally Unique Identifier portion of a MAC address, used in EthHeader.
|
4
|
-
#
|
5
|
-
# See the OUI list at http://standards.ieee.org/regauth/oui/oui.txt
|
6
|
-
#
|
7
|
-
# ==== Header Definition
|
8
|
-
#
|
9
|
-
# Fixnum :b0
|
10
|
-
# Fixnum :b1
|
11
|
-
# Fixnum :b2
|
12
|
-
# Fixnum :b3
|
13
|
-
# Fixnum :b4
|
14
|
-
# Fixnum :b5
|
15
|
-
# Fixnum :local
|
16
|
-
# Fixnum :multicast
|
17
|
-
# Int16 :oui, Default: 0x1ac5 :)
|
18
|
-
class EthOui < Struct.new(:b5, :b4, :b3, :b2, :b1, :b0, :local, :multicast, :oui)
|
19
|
-
|
20
|
-
# EthOui is unusual in that the bit values do not enjoy StructFu typing.
|
21
|
-
def initialize(args={})
|
22
|
-
args[:local] ||= 0
|
23
|
-
args[:oui] ||= 0x1ac # :)
|
24
|
-
args.each_pair {|k,v| args[k] = 0 unless v}
|
25
|
-
super(args[:b5], args[:b4], args[:b3], args[:b2],
|
26
|
-
args[:b1], args[:b0], args[:local], args[:multicast],
|
27
|
-
args[:oui])
|
28
|
-
end
|
29
|
-
|
30
|
-
# Returns the object in string form.
|
31
|
-
def to_s
|
32
|
-
byte = 0
|
33
|
-
byte += 0b10000000 if b5.to_i == 1
|
34
|
-
byte += 0b01000000 if b4.to_i == 1
|
35
|
-
byte += 0b00100000 if b3.to_i == 1
|
36
|
-
byte += 0b00010000 if b2.to_i == 1
|
37
|
-
byte += 0b00001000 if b1.to_i == 1
|
38
|
-
byte += 0b00000100 if b0.to_i == 1
|
39
|
-
byte += 0b00000010 if local.to_i == 1
|
40
|
-
byte += 0b00000001 if multicast.to_i == 1
|
41
|
-
[byte,oui].pack("Cn")
|
42
|
-
end
|
43
|
-
|
44
|
-
# Reads a string to populate the object.
|
45
|
-
def read(str)
|
46
|
-
force_binary(str)
|
47
|
-
return self if str.nil?
|
48
|
-
if 1.respond_to? :ord
|
49
|
-
byte = str[0].ord
|
50
|
-
else
|
51
|
-
byte = str[0]
|
52
|
-
end
|
53
|
-
self[:b5] = byte & 0b10000000 == 0b10000000 ? 1 : 0
|
54
|
-
self[:b4] = byte & 0b01000000 == 0b01000000 ? 1 : 0
|
55
|
-
self[:b3] = byte & 0b00100000 == 0b00100000 ? 1 : 0
|
56
|
-
self[:b2] = byte & 0b00010000 == 0b00010000 ? 1 : 0
|
57
|
-
self[:b1] = byte & 0b00001000 == 0b00001000 ? 1 : 0
|
58
|
-
self[:b0] = byte & 0b00000100 == 0b00000100 ? 1 : 0
|
59
|
-
self[:local] = byte & 0b00000010 == 0b00000010 ? 1 : 0
|
60
|
-
self[:multicast] = byte & 0b00000001 == 0b00000001 ? 1 : 0
|
61
|
-
self[:oui] = str[1,2].unpack("n").first
|
62
|
-
self
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
# EthNic is the Network Interface Controler portion of a MAC address, used in EthHeader.
|
68
|
-
#
|
69
|
-
# ==== Header Definition
|
70
|
-
#
|
71
|
-
# Fixnum :n1
|
72
|
-
# Fixnum :n2
|
73
|
-
# Fixnum :n3
|
74
|
-
#
|
75
|
-
class EthNic < Struct.new(:n0, :n1, :n2)
|
76
|
-
|
77
|
-
# EthNic does not enjoy StructFu typing.
|
78
|
-
def initialize(args={})
|
79
|
-
args.each_pair {|k,v| args[k] = 0 unless v}
|
80
|
-
super(args[:n0], args[:n1], args[:n2])
|
81
|
-
end
|
82
|
-
|
83
|
-
# Returns the object in string form.
|
84
|
-
def to_s
|
85
|
-
[n0,n1,n2].map {|x| x.to_i}.pack("C3")
|
86
|
-
end
|
87
|
-
|
88
|
-
# Reads a string to populate the object.
|
89
|
-
def read(str)
|
90
|
-
force_binary(str)
|
91
|
-
return self if str.nil?
|
92
|
-
self[:n0], self[:n1], self[:n2] = str[0,3].unpack("C3")
|
93
|
-
self
|
94
|
-
end
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
# EthMac is the combination of an EthOui and EthNic, used in EthHeader.
|
99
|
-
#
|
100
|
-
# ==== Header Definition
|
101
|
-
#
|
102
|
-
# EthOui :oui # See EthOui
|
103
|
-
# EthNic :nic # See EthNic
|
104
|
-
class EthMac < Struct.new(:oui, :nic)
|
105
|
-
|
106
|
-
def initialize(args={})
|
107
|
-
super(
|
108
|
-
EthOui.new.read(args[:oui]),
|
109
|
-
EthNic.new.read(args[:nic]))
|
110
|
-
end
|
111
|
-
|
112
|
-
# Returns the object in string form.
|
113
|
-
def to_s
|
114
|
-
"#{self[:oui]}#{self[:nic]}"
|
115
|
-
end
|
116
|
-
|
117
|
-
# Reads a string to populate the object.
|
118
|
-
def read(str)
|
119
|
-
force_binary(str)
|
120
|
-
return self if str.nil?
|
121
|
-
self.oui.read str[0,3]
|
122
|
-
self.nic.read str[3,3]
|
123
|
-
self
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
# EthHeader is a complete Ethernet struct, used in EthPacket.
|
129
|
-
# It's the base header for all other protocols, such as IPHeader,
|
130
|
-
# TCPHeader, etc.
|
131
|
-
#
|
132
|
-
# For more on the construction on MAC addresses, see
|
133
|
-
# http://en.wikipedia.org/wiki/MAC_address
|
134
|
-
#
|
135
|
-
# TODO: Need to come up with a good way of dealing with vlan
|
136
|
-
# tagging. Having a usually empty struct member seems weird,
|
137
|
-
# but there may not be another way to do it if I want to preserve
|
138
|
-
# the Eth-ness of vlan-tagged 802.1Q packets. Also, may as well
|
139
|
-
# deal with 0x88a8 as well (http://en.wikipedia.org/wiki/802.1ad)
|
140
|
-
#
|
141
|
-
# ==== Header Definition
|
142
|
-
#
|
143
|
-
# EthMac :eth_dst # See EthMac
|
144
|
-
# EthMac :eth_src # See EthMac
|
145
|
-
# Int16 :eth_proto, Default: 0x8000 # IP 0x0800, Arp 0x0806
|
146
|
-
# String :body
|
147
|
-
class EthHeader < Struct.new(:eth_dst, :eth_src, :eth_proto, :body)
|
148
|
-
include StructFu
|
149
|
-
|
150
|
-
def initialize(args={})
|
151
|
-
super(
|
152
|
-
EthMac.new.read(args[:eth_dst]),
|
153
|
-
EthMac.new.read(args[:eth_src]),
|
154
|
-
Int16.new(args[:eth_proto] || 0x0800),
|
155
|
-
StructFu::String.new.read(args[:body])
|
156
|
-
)
|
157
|
-
end
|
158
|
-
|
159
|
-
# Setter for the Ethernet destination address.
|
160
|
-
def eth_dst=(i); typecast(i); end
|
161
|
-
# Getter for the Ethernet destination address.
|
162
|
-
def eth_dst; self[:eth_dst].to_s; end
|
163
|
-
# Setter for the Ethernet source address.
|
164
|
-
def eth_src=(i); typecast(i); end
|
165
|
-
# Getter for the Ethernet source address.
|
166
|
-
def eth_src; self[:eth_src].to_s; end
|
167
|
-
# Setter for the Ethernet protocol number.
|
168
|
-
def eth_proto=(i); typecast(i); end
|
169
|
-
# Getter for the Ethernet protocol number.
|
170
|
-
def eth_proto; self[:eth_proto].to_i; end
|
171
|
-
|
172
|
-
# Returns the object in string form.
|
173
|
-
def to_s
|
174
|
-
self.to_a.map {|x| x.to_s}.join
|
175
|
-
end
|
176
|
-
|
177
|
-
# Reads a string to populate the object.
|
178
|
-
def read(str)
|
179
|
-
force_binary(str)
|
180
|
-
return self if str.nil?
|
181
|
-
self[:eth_dst].read str[0,6]
|
182
|
-
self[:eth_src].read str[6,6]
|
183
|
-
self[:eth_proto].read str[12,2]
|
184
|
-
self[:body].read str[14,str.size]
|
185
|
-
self
|
186
|
-
end
|
187
|
-
|
188
|
-
# Converts a readable MAC (11:22:33:44:55:66) to a binary string.
|
189
|
-
# Readable MAC's may be split on colons, dots, spaces, or underscores.
|
190
|
-
#
|
191
|
-
# irb> PacketFu::EthHeader.mac2str("11:22:33:44:55:66")
|
192
|
-
#
|
193
|
-
# #=> "\021\"3DUf"
|
194
|
-
def self.mac2str(mac)
|
195
|
-
if mac.split(/[:\x2d\x2e\x5f]+/).size == 6
|
196
|
-
ret = mac.split(/[:\x2d\x2e\x20\x5f]+/).collect {|x| x.to_i(16)}.pack("C6")
|
197
|
-
else
|
198
|
-
raise ArgumentError, "Unkown format for mac address."
|
199
|
-
end
|
200
|
-
return ret
|
201
|
-
end
|
202
|
-
|
203
|
-
# Converts a binary string to a readable MAC (11:22:33:44:55:66).
|
204
|
-
#
|
205
|
-
# irb> PacketFu::EthHeader.str2mac("\x11\x22\x33\x44\x55\x66")
|
206
|
-
#
|
207
|
-
# #=> "11:22:33:44:55:66"
|
208
|
-
def self.str2mac(mac='')
|
209
|
-
if mac.to_s.size == 6 && mac.kind_of?(::String)
|
210
|
-
ret = mac.unpack("C6").map {|x| sprintf("%02x",x)}.join(":")
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
# Sets the source MAC address in a more readable way.
|
215
|
-
def eth_saddr=(mac)
|
216
|
-
mac = EthHeader.mac2str(mac)
|
217
|
-
self[:eth_src].read mac
|
218
|
-
self[:eth_src]
|
219
|
-
end
|
220
|
-
|
221
|
-
# Gets the source MAC address in a more readable way.
|
222
|
-
def eth_saddr
|
223
|
-
EthHeader.str2mac(self[:eth_src].to_s)
|
224
|
-
end
|
225
|
-
|
226
|
-
# Set the destination MAC address in a more readable way.
|
227
|
-
def eth_daddr=(mac)
|
228
|
-
mac = EthHeader.mac2str(mac)
|
229
|
-
self[:eth_dst].read mac
|
230
|
-
self[:eth_dst]
|
231
|
-
end
|
232
|
-
|
233
|
-
# Gets the destination MAC address in a more readable way.
|
234
|
-
def eth_daddr
|
235
|
-
EthHeader.str2mac(self[:eth_dst].to_s)
|
236
|
-
end
|
237
|
-
|
238
|
-
# Readability aliases
|
239
|
-
|
240
|
-
alias :eth_dst_readable :eth_daddr
|
241
|
-
alias :eth_src_readable :eth_saddr
|
242
|
-
|
243
|
-
def eth_proto_readable
|
244
|
-
"0x%04x" % eth_proto
|
245
|
-
end
|
246
|
-
|
247
|
-
end
|
248
|
-
|
249
|
-
# EthPacket is used to construct Ethernet packets. They contain an
|
250
|
-
# Ethernet header, and that's about it.
|
251
|
-
#
|
252
|
-
# == Example
|
253
|
-
#
|
254
|
-
# require 'packetfu'
|
255
|
-
# eth_pkt = PacketFu::EthPacket.new
|
256
|
-
# eth_pkt.eth_saddr="00:1c:23:44:55:66"
|
257
|
-
# eth_pkt.eth_daddr="00:1c:24:aa:bb:cc"
|
258
|
-
#
|
259
|
-
# eth_pkt.to_w('eth0') # Inject on the wire. (require root)
|
260
|
-
#
|
261
|
-
class EthPacket < Packet
|
262
|
-
attr_accessor :eth_header
|
263
|
-
|
264
|
-
def self.can_parse?(str)
|
265
|
-
# XXX Temporary fix. Need to extend the EthHeader class to handle more.
|
266
|
-
valid_eth_types = [0x0800, 0x0806, 0x86dd]
|
267
|
-
return false unless str.size >= 14
|
268
|
-
type = str[12,2].unpack("n").first rescue nil
|
269
|
-
return false unless valid_eth_types.include? type
|
270
|
-
true
|
271
|
-
end
|
272
|
-
|
273
|
-
def read(str=nil,args={})
|
274
|
-
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
275
|
-
@eth_header.read(str)
|
276
|
-
super(args)
|
277
|
-
return self
|
278
|
-
end
|
279
|
-
|
280
|
-
# Does nothing, really, since there's no length or
|
281
|
-
# checksum to calculate for a straight Ethernet packet.
|
282
|
-
def recalc(args={})
|
283
|
-
@headers[0].inspect
|
284
|
-
end
|
285
|
-
|
286
|
-
def initialize(args={})
|
287
|
-
@eth_header = EthHeader.new(args).read(args[:eth])
|
288
|
-
@headers = [@eth_header]
|
289
|
-
super
|
290
|
-
end
|
291
|
-
|
292
|
-
end
|
293
|
-
|
294
|
-
end
|
295
|
-
|
296
|
-
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|