2008-10-10 02:23:05 +00:00
|
|
|
|
|
|
|
module PacketFu
|
|
|
|
|
|
|
|
# Octets implements the addressing scheme for IP.
|
|
|
|
#
|
|
|
|
# ==== Header Definition
|
|
|
|
#
|
|
|
|
# uint8 :o1
|
|
|
|
# uint8 :o2
|
|
|
|
# uint8 :o3
|
|
|
|
# uint8 :o4
|
|
|
|
class Octets < BinData::MultiValue
|
|
|
|
uint8 :o1
|
|
|
|
uint8 :o2
|
|
|
|
uint8 :o3
|
|
|
|
uint8 :o4
|
|
|
|
|
|
|
|
# Returns an address in dotted-quad format.
|
|
|
|
def to_x
|
|
|
|
ip_str = [o1, o2, o3, o4].join('.')
|
|
|
|
IPAddr.new(ip_str).to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns an address in numerical format.
|
|
|
|
def to_i
|
|
|
|
ip_str = [o1, o2, o3, o4].join('.')
|
|
|
|
IPAddr.new(ip_str).to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns an address as an array of numbers.
|
|
|
|
def to_ary
|
|
|
|
[o1,o2,o3,o4]
|
|
|
|
end
|
|
|
|
|
|
|
|
alias to_a to_ary
|
|
|
|
end
|
|
|
|
|
|
|
|
# IPHeader is a complete IP struct, used in IPPacket. Most traffic on most networks today is IP-based.
|
|
|
|
#
|
|
|
|
# For more on IP packets, see http://www.networksorcery.com/enp/protocol/ip.htm
|
|
|
|
#
|
|
|
|
# ==== Header Definition
|
|
|
|
#
|
|
|
|
# bit4 :ip_v, :initial_value => 4
|
|
|
|
# bit4 :ip_hl, :initial_value => 5
|
|
|
|
# uint8 :ip_tos, :initial_value => 0 # TODO: Break out the bits
|
|
|
|
# uint16be :ip_len, :initial_value => lambda { ip_calc_len }
|
|
|
|
# uint16be :ip_id, :initial_value => lambda { ip_calc_id } # IRL, hardly random.
|
|
|
|
# uint16be :ip_frag, :initial_value => 0 # TODO: Break out the bits
|
|
|
|
# uint8 :ip_ttl, :initial_value => 0xff # Changes per flavor
|
|
|
|
# uint8 :ip_proto, :initial_value => 0x01 # TCP: 0x06, UDP 0x11, ICMP 0x01
|
|
|
|
# uint16be :ip_sum, :initial_value => lambda { ip_calc_sum }
|
|
|
|
# octets :ip_src # No value as this is a MultiValue
|
|
|
|
# octets :ip_dst # Ditto.
|
|
|
|
# rest :body
|
|
|
|
class IPHeader < BinData::MultiValue
|
|
|
|
|
|
|
|
bit4 :ip_v, :initial_value => 4
|
|
|
|
bit4 :ip_hl, :initial_value => 5
|
|
|
|
uint8 :ip_tos, :initial_value => 0 # TODO: Break out the bits
|
|
|
|
uint16be :ip_len, :initial_value => lambda { ip_calc_len }
|
|
|
|
uint16be :ip_id, :initial_value => lambda { ip_calc_id } # IRL, hardly random.
|
|
|
|
uint16be :ip_frag, :initial_value => 0 # TODO: Break out the bits
|
|
|
|
uint8 :ip_ttl, :initial_value => 0xff # Changes per flavor
|
|
|
|
uint8 :ip_proto,:initial_value => 0x01 # TCP: 0x06, UDP 0x11, ICMP 0x01
|
|
|
|
uint16be :ip_sum, :initial_value => lambda { ip_calc_sum }
|
|
|
|
octets :ip_src # No value as this is a MultiValue
|
|
|
|
octets :ip_dst # Ditto.
|
|
|
|
rest :body
|
|
|
|
|
|
|
|
# Creates a new IPHeader object, and intialize with a random IPID.
|
|
|
|
def initialize(*args)
|
|
|
|
@random_id = rand(0xffff)
|
|
|
|
super
|
|
|
|
end
|
|
|
|
|
|
|
|
# Calulcate the true length of the packet.
|
|
|
|
def ip_calc_len
|
|
|
|
(ip_hl * 4) + body.to_s.length
|
|
|
|
end
|
|
|
|
|
|
|
|
# Calculate the true checksum of the packet.
|
|
|
|
# (Yes, this is the long way to do it, but it's e-z-2-read for mathtards like me.)
|
|
|
|
def ip_calc_sum
|
|
|
|
checksum = (((ip_v << 4) + ip_hl) << 8) +ip_tos
|
|
|
|
checksum += ip_len
|
|
|
|
checksum += ip_id
|
|
|
|
checksum += ip_frag
|
|
|
|
checksum += (ip_ttl << 8) + ip_proto
|
|
|
|
checksum += (ip_src.to_i >> 16)
|
|
|
|
checksum += (ip_src.to_i & 0xffff)
|
|
|
|
checksum += (ip_dst.to_i >> 16)
|
|
|
|
checksum += (ip_dst.to_i & 0xffff)
|
|
|
|
checksum = checksum % 0xffff
|
|
|
|
checksum = 0xffff - checksum
|
|
|
|
checksum == 0 ? 0xffff : checksum
|
|
|
|
end
|
|
|
|
|
|
|
|
# Retrieve the IP ID
|
|
|
|
def ip_calc_id
|
|
|
|
@random_id
|
|
|
|
end
|
|
|
|
|
|
|
|
# Sets a more readable IP address. If you wants to manipulate individual octets,
|
|
|
|
# (eg, for host scanning in one network), it would be better use ip_src.o1 through
|
|
|
|
# ip_src.o4 instead.
|
|
|
|
def ip_saddr=(addr)
|
|
|
|
addr = IPHeader.octet_array(addr)
|
|
|
|
ip_src.o1 = addr[0]
|
|
|
|
ip_src.o2 = addr[1]
|
|
|
|
ip_src.o3 = addr[2]
|
|
|
|
ip_src.o4 = addr[3]
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns a more readable IP source address.
|
|
|
|
def ip_saddr
|
|
|
|
[ip_src.o1,ip_src.o2,ip_src.o3,ip_src.o4].join('.')
|
|
|
|
end
|
|
|
|
|
|
|
|
# Sets a more readable IP address.
|
|
|
|
def ip_daddr=(addr)
|
|
|
|
addr = IPHeader.octet_array(addr)
|
|
|
|
ip_dst.o1 = addr[0]
|
|
|
|
ip_dst.o2 = addr[1]
|
|
|
|
ip_dst.o3 = addr[2]
|
|
|
|
ip_dst.o4 = addr[3]
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns a more readable IP destination address.
|
|
|
|
def ip_daddr
|
|
|
|
[ip_dst.o1,ip_dst.o2,ip_dst.o3,ip_dst.o4].join('.')
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# Translate various formats of IPv4 Addresses to an array of digits.
|
|
|
|
def self.octet_array(addr)
|
|
|
|
if addr.class == String
|
|
|
|
oa = addr.split('.').collect {|x| x.to_i}
|
|
|
|
elsif addr.class == Fixnum
|
|
|
|
oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
|
|
|
|
elsif addr.class == Bignum
|
|
|
|
oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
|
|
|
|
elsif addr.class == Array
|
|
|
|
oa = addr
|
|
|
|
else
|
|
|
|
raise ArgumentError, "IP Address should be a dotted quad string, an array of ints, or a bignum"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Recalculate the calculated IP fields. Valid arguments are:
|
|
|
|
# :all :ip_len :ip_sum :ip_id
|
|
|
|
def ip_recalc(arg=:all)
|
|
|
|
case arg
|
|
|
|
when :ip_len
|
|
|
|
self.ip_len=ip_calc_len
|
|
|
|
when :ip_sum
|
|
|
|
self.ip_sum=ip_calc_sum
|
|
|
|
when :ip_id
|
|
|
|
@random_id = rand(0xffff)
|
|
|
|
when :all
|
|
|
|
self.ip_id= ip_calc_id
|
|
|
|
self.ip_len= ip_calc_len
|
|
|
|
self.ip_sum= ip_calc_sum
|
|
|
|
else
|
|
|
|
raise ArgumentError, "No such field `#{arg}'"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end # class IPHeader
|
|
|
|
|
|
|
|
# IPPacket is used to construct IP packets. They contain an EthHeader, an IPHeader, and usually
|
|
|
|
# a transport-layer protocol such as UDPHeader, TCPHeader, or ICMPHeader.
|
|
|
|
#
|
|
|
|
# == Example
|
|
|
|
#
|
|
|
|
# require 'packetfu'
|
|
|
|
# ip_pkt = PacketFu::IPPacket.new
|
|
|
|
# ip_pkt.ip_saddr="10.20.30.40"
|
|
|
|
# ip_pkt.ip_daddr="192.168.1.1"
|
|
|
|
# ip_pkt.ip_proto=1
|
|
|
|
# ip_pkt.ip_ttl=64
|
|
|
|
# ip_pkt.ip_payload="\x00\x00\x12\x34\x00\x01\x00\x01"+
|
|
|
|
# "Lovingly hand-crafted echo responses delivered directly to your door."
|
|
|
|
# ip_pkt.recalc
|
|
|
|
# ip_pkt.to_f('/tmp/ip.pcap')
|
|
|
|
#
|
|
|
|
# == Parameters
|
|
|
|
#
|
|
|
|
# :eth
|
|
|
|
# A pre-generated EthHeader object.
|
|
|
|
# :ip
|
|
|
|
# A pre-generated IPHeader object.
|
|
|
|
# :flavor
|
|
|
|
# TODO: Sets the "flavor" of the IP packet. This might include known sets of IP options, and
|
|
|
|
# certainly known starting TTLs.
|
|
|
|
# :config
|
|
|
|
# A hash of return address details, often the output of Utils.whoami?
|
|
|
|
class IPPacket < Packet
|
|
|
|
|
|
|
|
attr_accessor :eth_header, :ip_header
|
|
|
|
|
|
|
|
def ethernet?; true; end
|
|
|
|
def ip?; true; end
|
|
|
|
|
|
|
|
# Creates a new IPPacket object.
|
|
|
|
def initialize(args={})
|
|
|
|
@eth_header = (args[:eth] || EthHeader.new)
|
|
|
|
@ip_header = (args[:ip] || IPHeader.new)
|
|
|
|
@eth_header.body=@ip_header
|
|
|
|
|
|
|
|
@headers = [@eth_header, @ip_header]
|
|
|
|
super
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
# Peek provides summary data on packet contents.
|
|
|
|
def peek(args={})
|
|
|
|
peek_data = ["I "]
|
|
|
|
peek_data << "%-5d" % self.to_s.size
|
|
|
|
peek_data << "%-21s" % "#{self.ip_saddr}"
|
|
|
|
peek_data << "->"
|
|
|
|
peek_data << "%21s" % "#{self.ip_daddr}"
|
|
|
|
peek_data << "%23s" % "I:"
|
|
|
|
peek_data << "%04x" % self.ip_id
|
|
|
|
peek_data.join
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2008-10-19 21:03:39 +00:00
|
|
|
end # module PacketFu
|