Preserve last version without PacketFu
This commit is contained in:
parent
3ad32310bb
commit
99c5e76d5d
57
arp_client.rb
Normal file
57
arp_client.rb
Normal file
@ -0,0 +1,57 @@
|
||||
require_relative 'ethernet_device'
|
||||
require_relative 'ethernet_frame'
|
||||
require_relative 'arp_packet'
|
||||
|
||||
module ArpClient
|
||||
@@eth_dev = nil
|
||||
@@mac = nil
|
||||
@@ip = nil
|
||||
@@arp_table = { '255.255.255.255': 'FF:FF:FF:FF:FF:FF' }
|
||||
@@arp_queue = {}
|
||||
|
||||
def self.initialize(eth_device, mac, ip)
|
||||
@@eth_dev = eth_device
|
||||
@@mac = mac
|
||||
@@ip = ip
|
||||
|
||||
resp_packet = ARPPacket.new(nil, ARPPacket::REQUEST, mac, ip, '00:00:00:00:00:00', ip)
|
||||
eth_device.send_frame(EthernetFrame::ARP, 'FF:FF:FF:FF:FF:FF', resp_packet.bytes)
|
||||
|
||||
resp_packet = ARPPacket.new(nil, ARPPacket::REPLY, mac, ip, mac, ip)
|
||||
eth_device.send_frame(EthernetFrame::ARP, 'FF:FF:FF:FF:FF:FF', resp_packet.bytes)
|
||||
|
||||
eth_device.register_callback(EthernetFrame::ARP) do |frame, device|
|
||||
packet = ARPPacket.new(frame.data)
|
||||
@@arp_table[packet.src_ip]=packet.src_mac
|
||||
if packet.is_request?
|
||||
puts "Received ARP request from #{packet.src_ip} (#{packet.src_mac}) for #{packet.dest_ip}"
|
||||
if packet.dest_ip == @@ip
|
||||
resp_packet = ARPPacket.new(nil, ARPPacket::REPLY, mac, ip, 'FF:FF:FF:FF:FF:FF', packet.src_ip)
|
||||
puts 'Sending ARP response'
|
||||
device.send_frame(EthernetFrame::ARP, packet.src_mac, resp_packet.bytes)
|
||||
end
|
||||
else
|
||||
puts "Received ARP response for #{packet.src_ip} (#{packet.src_mac})"
|
||||
next unless @@arp_queue[packet.src_ip]
|
||||
@@arp_queue[packet.src_ip].each do |packet|
|
||||
@@eth_dev.send_frame(EthernetFrame::IPV4, frame.src_mac, packet.bytes)
|
||||
end
|
||||
@@arp_queue[packet.src_ip] = []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.send_packet(packet)
|
||||
dest_ip = packet.dest_ip
|
||||
if @@arp_table[dest_ip]
|
||||
@@eth_dev.send_frame(EthernetFrame::IPV4, @@arp_table[dest_ip], packet.bytes)
|
||||
else
|
||||
@@arp_queue[dest_ip] = [] unless @@arp_queue[dest_ip]
|
||||
@@arp_queue[dest_ip].push packet
|
||||
puts "Sending ARP request for #{dest_ip}"
|
||||
packet = ARPPacket.new(nil, ARPPacket::REQUEST, @@mac, @@ip, '00:00:00:00:00:00', dest_ip)
|
||||
@@eth_dev.send_frame(EthernetFrame::ARP, 'FF:FF:FF:FF:FF:FF', packet.bytes)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,52 +1,48 @@
|
||||
require_relative "net_utils.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'net_utils'
|
||||
|
||||
class ARPPacket
|
||||
IP4=0x800
|
||||
REQUEST=1
|
||||
REPLY=2
|
||||
IP4 = 0x800
|
||||
REQUEST = 1
|
||||
REPLY = 2
|
||||
|
||||
@@empty_reply=[0,1,8,0,6,4,0]
|
||||
@@empty_reply = [0, 1, 8, 0, 6, 4, 0]
|
||||
|
||||
attr_reader :bytes
|
||||
|
||||
def initialize(bytes,op=nil,src_mac=nil,src_ip=nil,dest_ip=nil)
|
||||
def initialize(bytes, op = nil, src_mac = nil, src_ip = nil, dest_mac = nil, dest_ip = nil)
|
||||
if !bytes
|
||||
@bytes=@@empty_reply
|
||||
@bytes+=[op&0xFF]
|
||||
@bytes+=NetUtils.decode_mac(src_mac)
|
||||
@bytes+=NetUtils.decode_ip4(src_ip)
|
||||
@bytes+=[0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]
|
||||
@bytes+=NetUtils.decode_ip4(dest_ip)
|
||||
@bytes = @@empty_reply
|
||||
@bytes += [op & 0xFF]
|
||||
@bytes += NetUtils.decode_mac(src_mac)
|
||||
@bytes += NetUtils.decode_ip4(src_ip)
|
||||
@bytes += NetUtils.decode_mac(dest_mac)
|
||||
@bytes += NetUtils.decode_ip4(dest_ip)
|
||||
else
|
||||
@bytes=bytes
|
||||
@bytes = bytes
|
||||
end
|
||||
end
|
||||
|
||||
def dest_ip
|
||||
if proto_type==IP4
|
||||
return NetUtils.format_ip4(@bytes[24, 4])
|
||||
end
|
||||
NetUtils.format_ip4(@bytes[24, 4]) if proto_type == IP4
|
||||
end
|
||||
|
||||
def src_mac
|
||||
return NetUtils.format_mac(@bytes[8, 6])
|
||||
NetUtils.format_mac(@bytes[8, 6])
|
||||
end
|
||||
|
||||
|
||||
def src_ip
|
||||
if proto_type==IP4
|
||||
return NetUtils.format_ip4(@bytes[14, 4])
|
||||
end
|
||||
NetUtils.format_ip4(@bytes[14, 4]) if proto_type == IP4
|
||||
end
|
||||
|
||||
def proto_type
|
||||
type=@bytes[3]
|
||||
type+=@bytes[2]<<8
|
||||
return type
|
||||
type = @bytes[3]
|
||||
type += @bytes[2] << 8
|
||||
type
|
||||
end
|
||||
|
||||
def is_request?
|
||||
return @bytes[7]==1
|
||||
@bytes[7] == 1
|
||||
end
|
||||
|
||||
end
|
||||
|
103
dhcp_option.rb
103
dhcp_option.rb
@ -1,88 +1,87 @@
|
||||
require_relative "net_utils.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'net_utils'
|
||||
|
||||
class DHCPOption
|
||||
|
||||
attr_reader :bytes
|
||||
|
||||
SubnetMask=1
|
||||
Router=3
|
||||
DNServer=6
|
||||
BroadcastAddr=28
|
||||
RequestedIP=50
|
||||
LeaseTime=51
|
||||
MessageType=53
|
||||
ServerIdentifier=54
|
||||
RequestList=55
|
||||
RenewalTime=58
|
||||
RebindingTime=59
|
||||
End=255
|
||||
SubnetMask = 1
|
||||
Router = 3
|
||||
DNServer = 6
|
||||
BroadcastAddr = 28
|
||||
RequestedIP = 50
|
||||
LeaseTime = 51
|
||||
MessageType = 53
|
||||
ServerIdentifier = 54
|
||||
RequestList = 55
|
||||
RenewalTime = 58
|
||||
RebindingTime = 59
|
||||
End = 255
|
||||
|
||||
@@lengths={
|
||||
SubnetMask=>4,Router=>4,BroadcastAddr=>4,
|
||||
RequestedIP=>4,LeaseTime=>4,MessageType=>1,
|
||||
ServerIdentifier=>4,RenewalTime=>4,RebindingTime=>4,
|
||||
End=>0
|
||||
@@lengths = {
|
||||
SubnetMask => 4, Router => 4, BroadcastAddr => 4,
|
||||
RequestedIP => 4, LeaseTime => 4, MessageType => 1,
|
||||
ServerIdentifier => 4, RenewalTime => 4, RebindingTime => 4,
|
||||
End => 0
|
||||
}
|
||||
|
||||
def decode_type()
|
||||
def decode_type
|
||||
case @bytes[0]
|
||||
when SubnetMask
|
||||
return "Subnet Mask"
|
||||
return 'Subnet Mask'
|
||||
when Router
|
||||
return "Router"
|
||||
return 'Router'
|
||||
when DNServer
|
||||
return "DNS Servers"
|
||||
return 'DNS Servers'
|
||||
when BroadcastAddr
|
||||
return "Broadcast Address"
|
||||
return 'Broadcast Address'
|
||||
when LeaseTime
|
||||
return "Lease Time"
|
||||
return 'Lease Time'
|
||||
when MessageType
|
||||
return "Message Type"
|
||||
return 'Message Type'
|
||||
when ServerIdentifier
|
||||
return "Server Identifier"
|
||||
return 'Server Identifier'
|
||||
when RequestList
|
||||
return "Request List"
|
||||
return 'Request List'
|
||||
when RenewalTime
|
||||
return "Renewal Time"
|
||||
return 'Renewal Time'
|
||||
when RebindingTime
|
||||
return "Rebinding Time"
|
||||
return 'Rebinding Time'
|
||||
when End
|
||||
return "End"
|
||||
return 'End'
|
||||
end
|
||||
return "unknown"
|
||||
'unknown'
|
||||
end
|
||||
|
||||
def type
|
||||
return @bytes[0]
|
||||
@bytes[0]
|
||||
end
|
||||
|
||||
def initialize(type,*args)
|
||||
args=args.flatten
|
||||
@bytes=[type,@@lengths[type]]
|
||||
if @@lengths[type]==nil
|
||||
@bytes[1]=args.length
|
||||
end
|
||||
@bytes+=args
|
||||
def initialize(type, *args)
|
||||
args = args.flatten
|
||||
@bytes = [type, @@lengths[type]]
|
||||
@bytes[1] = args.length if @@lengths[type].nil?
|
||||
@bytes += args
|
||||
@bytes.flatten!
|
||||
end
|
||||
|
||||
def data()
|
||||
def data
|
||||
case @bytes[0]
|
||||
when SubnetMask,Router,BroadcastAddr,RequestedIP,ServerIdentifier
|
||||
return NetUtils.format_ip4(@bytes[2,4])
|
||||
when SubnetMask, Router, BroadcastAddr, RequestedIP, ServerIdentifier
|
||||
NetUtils.format_ip4(@bytes[2, 4])
|
||||
when MessageType
|
||||
return @bytes[2]
|
||||
when LeaseTime,RenewalTime,RebindingTime
|
||||
return NetUtils.decode_int(@bytes[2,4])
|
||||
@bytes[2]
|
||||
when LeaseTime, RenewalTime, RebindingTime
|
||||
NetUtils.decode_int(@bytes[2, 4])
|
||||
when DNServer
|
||||
num_ips=@bytes[1]/4
|
||||
i=2
|
||||
ips=[]
|
||||
num_ips = @bytes[1] / 4
|
||||
i = 2
|
||||
ips = []
|
||||
num_ips.times do
|
||||
ips.push NetUtils.format_ip4(@bytes[i,4])
|
||||
i+=4
|
||||
ips.push NetUtils.format_ip4(@bytes[i, 4])
|
||||
i += 4
|
||||
end
|
||||
return ips
|
||||
ips
|
||||
end
|
||||
end
|
||||
end
|
||||
|
116
dhcp_packet.rb
116
dhcp_packet.rb
@ -1,85 +1,79 @@
|
||||
require_relative "net_utils.rb"
|
||||
require_relative "dhcp_option.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'net_utils'
|
||||
require_relative 'dhcp_option'
|
||||
|
||||
class DHCPPacket
|
||||
DISCOVER=1
|
||||
OFFER=2
|
||||
REQUEST=3
|
||||
DECLINE=4
|
||||
ACK=5
|
||||
NACK=6
|
||||
RELEASE=7
|
||||
INFORM=8
|
||||
DISCOVER = 1
|
||||
OFFER = 2
|
||||
REQUEST = 3
|
||||
DECLINE = 4
|
||||
ACK = 5
|
||||
NACK = 6
|
||||
RELEASE = 7
|
||||
INFORM = 8
|
||||
|
||||
@@requests=[DISCOVER,REQUEST,RELEASE]
|
||||
@@requests = [DISCOVER, REQUEST, RELEASE]
|
||||
|
||||
attr_reader :bytes,:options
|
||||
attr_reader :bytes, :options
|
||||
|
||||
def initialize(bytes,my_ip=nil,mac=nil,type=nil,options=[])
|
||||
def initialize(bytes, my_ip = nil, mac = nil, type = nil, options = [])
|
||||
if !bytes
|
||||
if @@requests.include? type
|
||||
op=1
|
||||
else
|
||||
op=2
|
||||
op = if @@requests.include? type
|
||||
1
|
||||
else
|
||||
2
|
||||
end
|
||||
@bytes = [op, 1, 6, 0, 0x4, 0x57, 0x70, 0x99, 0, 0, 0x80, 0]
|
||||
@bytes += if my_ip
|
||||
NetUtils.decode_ip4(my_ip)
|
||||
else
|
||||
[0, 0, 0, 0]
|
||||
end
|
||||
@bytes += [0] * 12
|
||||
@bytes += NetUtils.decode_mac(mac)
|
||||
@bytes += [0] * 202
|
||||
@bytes += [0x63, 0x82, 0x53, 0x63] # DHCP magic cookie
|
||||
@bytes += DHCPOption.new(DHCPOption::MessageType, type).bytes
|
||||
options.each do |option|
|
||||
@bytes += option.bytes
|
||||
end
|
||||
@bytes=[op,1,6,0,0x4,0x57,0x70,0x99,0,0,0x80,0]
|
||||
if my_ip
|
||||
@bytes+=NetUtils.decode_ip4(my_ip)
|
||||
else
|
||||
@bytes+=[0,0,0,0]
|
||||
end
|
||||
12.times do
|
||||
@bytes.push 0
|
||||
end
|
||||
@bytes+=NetUtils.decode_mac(mac)
|
||||
10.times do
|
||||
@bytes.push 0
|
||||
end
|
||||
192.times do
|
||||
@bytes.push 0
|
||||
end
|
||||
@bytes+=[0x63,0x82,0x53,0x63] # DHCP magic cookie
|
||||
@bytes+=DHCPOption.new(DHCPOption::MessageType,type).bytes
|
||||
for option in options
|
||||
@bytes+=option.bytes
|
||||
end
|
||||
@bytes+=DHCPOption.new(DHCPOption::End,type).bytes
|
||||
@bytes += DHCPOption.new(DHCPOption::End, type).bytes
|
||||
else
|
||||
@bytes=bytes.unpack("C*")
|
||||
@bytes = bytes.unpack('C*')
|
||||
end
|
||||
|
||||
option_bytes=@bytes[240..-1]
|
||||
@options=[]
|
||||
i=0
|
||||
while i<option_bytes.length
|
||||
break if option_bytes[i]==0xff
|
||||
opt_bytes=[option_bytes[i],option_bytes[i+1]]
|
||||
i+=2
|
||||
option_bytes[i-1].ord.times do
|
||||
option_bytes = @bytes[240..-1]
|
||||
@options = []
|
||||
i = 0
|
||||
while i < option_bytes.length
|
||||
break if option_bytes[i] == 0xff
|
||||
|
||||
opt_bytes = [option_bytes[i], option_bytes[i + 1]]
|
||||
i += 2
|
||||
option_bytes[i - 1].ord.times do
|
||||
opt_bytes.push option_bytes[i]
|
||||
i+=1
|
||||
i += 1
|
||||
end
|
||||
@options.push DHCPOption.new(opt_bytes[0],opt_bytes[2..-1])
|
||||
@options.push DHCPOption.new(opt_bytes[0], opt_bytes[2..-1])
|
||||
end
|
||||
end
|
||||
|
||||
def mac()
|
||||
return NetUtils.format_mac(@bytes[28,6])
|
||||
def mac
|
||||
NetUtils.format_mac(@bytes[28, 6])
|
||||
end
|
||||
|
||||
def yiaddr()
|
||||
return NetUtils.format_ip4(@bytes[16,4])
|
||||
def yiaddr
|
||||
NetUtils.format_ip4(@bytes[16, 4])
|
||||
end
|
||||
|
||||
def siaddr()
|
||||
return NetUtils.format_ip4(@bytes[20,4])
|
||||
def siaddr
|
||||
NetUtils.format_ip4(@bytes[20, 4])
|
||||
end
|
||||
|
||||
def type()
|
||||
for option in @options
|
||||
if option.type==DHCPOption::MessageType
|
||||
return option.data
|
||||
end
|
||||
def type
|
||||
@options.each do |option|
|
||||
return option.data if option.type == DHCPOption::MessageType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,14 +1,20 @@
|
||||
require "socket"
|
||||
require "io/wait"
|
||||
require_relative "ethernet_frame.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'socket'
|
||||
require 'io/wait'
|
||||
require_relative 'ethernet_frame'
|
||||
|
||||
require 'hexdump'
|
||||
require 'packetfu'
|
||||
|
||||
class EthernetDevice
|
||||
BUFFER_SIZE = 16777216
|
||||
BUFFER_SIZE = 16_777_216
|
||||
|
||||
def initialize(iface,mac)
|
||||
@mac=mac
|
||||
@callbacks=[]
|
||||
def initialize(iface, mac)
|
||||
@mac = mac
|
||||
@callbacks = []
|
||||
@pcap_file = "ns-#{iface}.pcap"
|
||||
PacketFu::PcapFile.new.write(@pcap_file)
|
||||
|
||||
# Make an ifreq structure
|
||||
ifreq_size = 0x0028 # Size in bytes of the ifreq structure
|
||||
@ -25,7 +31,7 @@ class EthernetDevice
|
||||
# Pull the bytes containing the result out of the string
|
||||
# (where the ifr_ifindex field would be)
|
||||
index = ifreq[Socket::IFNAMSIZ, ifindex_size]
|
||||
eth_p_all = 0x0300 # Receive every packet
|
||||
eth_p_all = 0x0300 # Receive every packet
|
||||
# Make a sockaddr_ll structure
|
||||
sockaddr_ll_size = 0x0014 # Size in bytes of the sockaddr_ll structure
|
||||
|
||||
@ -37,45 +43,37 @@ class EthernetDevice
|
||||
@socket.bind(sockaddr_ll)
|
||||
|
||||
Thread.new do
|
||||
while true
|
||||
loop do
|
||||
if @socket.ready?
|
||||
frame=EthernetFrame.new(@socket.recv(BUFFER_SIZE).bytes)
|
||||
frame = EthernetFrame.new(@socket.recv(BUFFER_SIZE).bytes)
|
||||
got_frame(frame)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def send_frame(type,dest_mac,data)
|
||||
broadcast=dest_mac=="FF:FF:FF:FF:FF:FF"
|
||||
# if broadcast
|
||||
# puts "Broadcast #{NetUtils.decode_ethertype(type)} frame"
|
||||
# else
|
||||
# puts "#{NetUtils.decode_ethertype(type)} frame to #{dest_mac}"
|
||||
# end
|
||||
@socket.print EthernetFrame.new(nil,type,@mac,dest_mac,data).bytes.pack("C*")
|
||||
def send_frame(type, dest_mac, data)
|
||||
frame = EthernetFrame.new(nil, type, @mac, dest_mac, data)
|
||||
pkt = PacketFu::Packet.parse(frame.bytes.pack('C*'))
|
||||
PacketFu::PcapFile.new.array_to_file({ file: @pcap_file, array: [pkt], append: true })
|
||||
frame.dump
|
||||
@socket.print frame.bytes.pack('C*')
|
||||
end
|
||||
|
||||
def register_callback(type,&callback)
|
||||
@callbacks[type]=callback
|
||||
def register_callback(type, &callback)
|
||||
@callbacks[type] = callback
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def got_frame(frame)
|
||||
return if frame.dest_mac!=@mac and frame.dest_mac!="FF:FF:FF:FF:FF:FF"
|
||||
broadcast=frame.dest_mac=="FF:FF:FF:FF:FF:FF"
|
||||
# if broadcast
|
||||
# if NetUtils.decode_ethertype(frame.ethertype)=="Unknown"
|
||||
# puts "Unknown broadcast frame from #{frame.src_mac}"
|
||||
# else
|
||||
# puts "Broadcast #{NetUtils.decode_ethertype(frame.ethertype)} frame from #{frame.src_mac}"
|
||||
# end
|
||||
# else
|
||||
# puts "#{NetUtils.decode_ethertype(frame.ethertype)} frame from #{frame.src_mac}"
|
||||
# end
|
||||
@callbacks[frame.ethertype].call(frame,self) if @callbacks[frame.ethertype]
|
||||
end
|
||||
return if (frame.dest_mac != @mac) && (frame.dest_mac != 'FF:FF:FF:FF:FF:FF')
|
||||
|
||||
pkt = PacketFu::Packet.parse(frame.bytes.pack('C*'))
|
||||
PacketFu::PcapFile.new.array_to_file({ file: @pcap_file, array: [pkt], append: true })
|
||||
frame.dump
|
||||
@callbacks[frame.ethertype]&.call(frame, self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -1,20 +1,21 @@
|
||||
require "crc"
|
||||
require_relative "net_utils.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'net_utils'
|
||||
|
||||
class EthernetFrame
|
||||
ARP=0x0806
|
||||
IPV4=0x0800
|
||||
IPV6=0x86DD
|
||||
ARP = 0x0806
|
||||
IPV4 = 0x0800
|
||||
IPV6 = 0x86DD
|
||||
|
||||
attr_reader :bytes
|
||||
|
||||
def initialize(bytes,type=nil,src_mac=nil,dest_mac=nil,data=nil)
|
||||
def initialize(bytes, type = nil, src_mac = nil, dest_mac = nil, data = nil)
|
||||
if !bytes
|
||||
@bytes=[]
|
||||
@bytes = []
|
||||
@bytes += NetUtils.decode_mac(dest_mac)
|
||||
@bytes += NetUtils.decode_mac(src_mac)
|
||||
@bytes[12] = (type&0xFF00)>>8
|
||||
@bytes[13] = type&0xFF
|
||||
@bytes[12] = (type & 0xFF00) >> 8
|
||||
@bytes[13] = type & 0xFF
|
||||
@bytes += data
|
||||
else
|
||||
@bytes = bytes
|
||||
@ -22,20 +23,48 @@ class EthernetFrame
|
||||
end
|
||||
|
||||
def dest_mac
|
||||
return NetUtils.format_mac(@bytes[0, 6])
|
||||
NetUtils.format_mac(@bytes[0, 6])
|
||||
end
|
||||
|
||||
def src_mac
|
||||
return NetUtils.format_mac(@bytes[6, 6])
|
||||
NetUtils.format_mac(@bytes[6, 6])
|
||||
end
|
||||
|
||||
def ethertype
|
||||
type=@bytes[13]
|
||||
type+=@bytes[12]<<8
|
||||
return type
|
||||
type = @bytes[13]
|
||||
type += @bytes[12] << 8
|
||||
type
|
||||
end
|
||||
|
||||
def data
|
||||
return @bytes[14...-4] # Drop the first 14 bytes (MAC Header) and last 4 bytes (CRC Checksum)
|
||||
@bytes[14..-1] # Drop the first 14 bytes (MAC Header) and last 4 bytes (CRC Checksum)
|
||||
end
|
||||
|
||||
def dump
|
||||
broadcast = dest_mac == 'FF:FF:FF:FF:FF:FF'
|
||||
if broadcast
|
||||
if NetUtils.decode_ethertype(ethertype) == 'Unknown'
|
||||
if src_mac == MAC
|
||||
print 'Sending unknown broadcast frame'
|
||||
else
|
||||
print 'Received unknown broadcast frame'
|
||||
end
|
||||
else
|
||||
if src_mac == MAC
|
||||
print "Sending broadcast #{NetUtils.decode_ethertype(ethertype)} frame"
|
||||
else
|
||||
print "Received broadcast #{NetUtils.decode_ethertype(ethertype)} frame"
|
||||
end
|
||||
end
|
||||
else
|
||||
if src_mac == MAC
|
||||
print "Sending #{NetUtils.decode_ethertype(ethertype)} frame"
|
||||
else
|
||||
print "Received #{NetUtils.decode_ethertype(ethertype)} frame"
|
||||
end
|
||||
end
|
||||
puts src_mac == MAC ? (broadcast ? '' : " to #{dest_mac}") : " from #{src_mac}"
|
||||
# puts "Bytes:"
|
||||
# print bytes.pack("C*").hexdump
|
||||
end
|
||||
end
|
||||
|
@ -1,71 +1,42 @@
|
||||
require_relative "ipv4_packet.rb"
|
||||
require_relative "ethernet_frame.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'ipv4_packet'
|
||||
require_relative 'arp_client'
|
||||
|
||||
class IPV4Device
|
||||
attr_reader :ip
|
||||
# @@ip_to_mac={"10.0.0.180"=>"d4:61:9d:18:72:08"}
|
||||
@@ip_to_mac={}
|
||||
|
||||
def initialize(dev, ip)
|
||||
@callbacks = []
|
||||
@dev = dev
|
||||
@ip = ip
|
||||
|
||||
def initialize(dev,ip)
|
||||
@callbacks=[]
|
||||
@dev=dev
|
||||
@ip=ip
|
||||
|
||||
dev.register_callback(EthernetFrame::IPV4) do |frame,device|
|
||||
packet=IPV4Packet.new(frame.data)
|
||||
dev.register_callback(EthernetFrame::IPV4) do |frame, _device|
|
||||
packet = IPV4Packet.new(frame.data)
|
||||
got_packet(packet)
|
||||
end
|
||||
end
|
||||
|
||||
def send_packet(type,dest_ip,data,broad=false)
|
||||
if broad
|
||||
mac="FF:FF:FF:FF:FF:FF"
|
||||
dest_ip="255.255.255.255"
|
||||
else
|
||||
if !@@ip_to_mac[dest_ip]
|
||||
puts "Sending ARP for #{dest_ip}"
|
||||
packet=ARPPacket.new(nil,ARPPacket::REQUEST,MAC,ip,dest_ip)
|
||||
@dev.send_frame(EthernetFrame::ARP,"FF:FF:FF:FF:FF:FF",packet.bytes)
|
||||
until @@ip_to_mac[dest_ip]
|
||||
puts "NO MAP"
|
||||
sleep 1
|
||||
end
|
||||
puts "MAP"
|
||||
end
|
||||
mac=@@ip_to_mac[dest_ip]
|
||||
end
|
||||
@dev.send_frame(EthernetFrame::IPV4,mac,IPV4Packet.new(nil,type,@ip,dest_ip,data).bytes)
|
||||
def send_packet(type, dest_ip, data)
|
||||
packet = IPV4Packet.new(nil, type, @ip, dest_ip, data)
|
||||
packet.dump(@ip)
|
||||
ArpClient.send_packet(packet)
|
||||
end
|
||||
|
||||
def register_callback(type,&callback)
|
||||
@callbacks[type]=[] if !@callbacks[type]
|
||||
def register_callback(type, &callback)
|
||||
@callbacks[type] = [] unless @callbacks[type]
|
||||
|
||||
@callbacks[type].push callback
|
||||
end
|
||||
|
||||
def map_arp(ip,mac)
|
||||
@@ip_to_mac[ip]=mac
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def got_packet(packet)
|
||||
return if packet.dest_ip!=@ip and packet.dest_ip!="255.255.255.255"
|
||||
broadcast=(packet.dest_ip=="255.255.255.255")
|
||||
# if broadcast
|
||||
# if NetUtils.decode_ip4type(packet.type)=="Unknown"
|
||||
# puts "Unknown broadcast packet from #{packet.src_ip}"
|
||||
# else
|
||||
# puts "Broadcast #{NetUtils.decode_ip4type(packet.type)} packet from #{packet.src_ip}"
|
||||
# end
|
||||
# else
|
||||
# puts "#{NetUtils.decode_ip4type(packet.type)} packet from #{packet.src_ip}"
|
||||
# end
|
||||
if @callbacks[packet.type]
|
||||
for callback in @callbacks[packet.type]
|
||||
callback.call(packet,self)
|
||||
end
|
||||
return if (packet.dest_ip != @ip) && (packet.dest_ip != '255.255.255.255')
|
||||
|
||||
packet.dump(@ip)
|
||||
@callbacks[packet.type]&.each do |callback|
|
||||
callback.call(packet, self)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,51 +1,67 @@
|
||||
require_relative "net_utils.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'net_utils'
|
||||
|
||||
class IPV4Packet
|
||||
UDP=0x11
|
||||
ICMP = 0x1
|
||||
UDP = 0x11
|
||||
|
||||
attr_reader :bytes
|
||||
|
||||
def initialize(bytes,type=nil,src_ip=nil,dest_ip=nil,data=nil)
|
||||
def initialize(bytes, type = nil, src_ip = nil, dest_ip = nil, data = nil)
|
||||
if !bytes
|
||||
@bytes=[0x45,0,0,0xe6,79,0,0,0,255,0,0,0]
|
||||
size=20+data.length
|
||||
@bytes[2]=(size&0xFF00)>>8
|
||||
@bytes[3]=size&0xFF
|
||||
@bytes[9]=type
|
||||
@bytes+=NetUtils.decode_ip4(src_ip)
|
||||
@bytes+=NetUtils.decode_ip4(dest_ip)
|
||||
i=0
|
||||
checksum=0
|
||||
10.times do
|
||||
checksum+=(@bytes[i]<<8)+@bytes[i+1]
|
||||
i+=2
|
||||
end
|
||||
while (checksum>>16)!=0
|
||||
checksum=(checksum&0xffff)+(checksum>>16)
|
||||
end
|
||||
checksum=(~checksum)&0xFFFF
|
||||
@bytes[10]=(checksum&0xFF00)>>8
|
||||
@bytes[11]=(checksum&0xFF)
|
||||
@bytes+=data
|
||||
size = 20 + data.length
|
||||
@bytes = [0x45, 0, size >> 8 , size & 0xFF, 79, 0, 0, 0, 255, type, 0, 0]
|
||||
# @bytes[2] = size >> 8
|
||||
# @bytes[3] = size & 0xFF
|
||||
# @bytes[9] = type
|
||||
@bytes += NetUtils.decode_ip4(src_ip)
|
||||
@bytes += NetUtils.decode_ip4(dest_ip)
|
||||
@bytes[10], @bytes[11] = NetUtils.compute_checksum(@bytes[(0...20)])
|
||||
@bytes += data
|
||||
else
|
||||
@bytes=bytes
|
||||
@bytes = bytes
|
||||
end
|
||||
end
|
||||
|
||||
def src_ip()
|
||||
return NetUtils.format_ip4(@bytes[12,4])
|
||||
def src_ip
|
||||
NetUtils.format_ip4(@bytes[12, 4])
|
||||
end
|
||||
|
||||
def dest_ip()
|
||||
return NetUtils.format_ip4(@bytes[16,4])
|
||||
def dest_ip
|
||||
NetUtils.format_ip4(@bytes[16, 4])
|
||||
end
|
||||
|
||||
def type()
|
||||
return @bytes[9]
|
||||
def type
|
||||
@bytes[9]
|
||||
end
|
||||
|
||||
def data()
|
||||
length=(@bytes[0]&0xF)*4
|
||||
return @bytes[(length)..-1]
|
||||
def data
|
||||
length = (@bytes[0] & 0xF) * 4
|
||||
@bytes[length..-1]
|
||||
end
|
||||
|
||||
def dump(my_ip)
|
||||
broadcast = (dest_ip == '255.255.255.255')
|
||||
if broadcast
|
||||
if NetUtils.decode_ip4type(type) == 'Unknown'
|
||||
if src_ip == my_ip
|
||||
print 'Sending unknown broadcast packet'
|
||||
else
|
||||
print 'Received unknown broadcast packet'
|
||||
end
|
||||
elsif src_ip == my_ip
|
||||
print "Sending broadcast #{NetUtils.decode_ip4type(type)} packet"
|
||||
else
|
||||
print "Received broadcast #{NetUtils.decode_ip4type(type)} packet"
|
||||
end
|
||||
else
|
||||
if src_ip == my_ip
|
||||
print "Sending #{NetUtils.decode_ip4type(type)} packet"
|
||||
else
|
||||
print "Received #{NetUtils.decode_ip4type(type)} packet"
|
||||
end
|
||||
end
|
||||
puts src_ip == my_ip ? (broadcast ? '' : " to #{dest_ip}") : " from #{src_ip}"
|
||||
end
|
||||
end
|
||||
|
157
main.rb
157
main.rb
@ -1,96 +1,117 @@
|
||||
require_relative "ethernet_frame.rb"
|
||||
require_relative "arp_packet.rb"
|
||||
require_relative "ethernet_device.rb"
|
||||
require_relative "ipv4_packet.rb"
|
||||
require_relative "ipv4_device.rb"
|
||||
require_relative "udp_datagram.rb"
|
||||
require_relative "my_udp_socket.rb"
|
||||
require_relative "dhcp_packet.rb"
|
||||
require_relative "dhcp_option.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "hexdump"
|
||||
require_relative 'ethernet_device'
|
||||
require_relative 'ipv4_packet'
|
||||
require_relative 'ipv4_device'
|
||||
require_relative 'my_udp_socket'
|
||||
require_relative 'dhcp_packet'
|
||||
require_relative 'dhcp_option'
|
||||
require_relative 'udp_device'
|
||||
require_relative 'arp_client'
|
||||
require_relative 'net_utils'
|
||||
|
||||
ip="0.0.0.0"
|
||||
MAC="08:00:27:D3:71:E7"
|
||||
require 'macaddr'
|
||||
require 'json'
|
||||
require 'packetfu'
|
||||
|
||||
eth_device=EthernetDevice.new("enp0s8",MAC)
|
||||
ip = "10.0.0.#{ARGV[0]}"
|
||||
|
||||
ipv4_device=IPV4Device.new(eth_device,ip)
|
||||
iface = JSON.parse(`ip -json l`).reject { _1['ifname'] == 'lo' }[0]
|
||||
|
||||
udp_socket=MyUDPSocket.new(ipv4_device,68,true)
|
||||
udp_socket.connect("255.255.255.255",67)
|
||||
sys_mac = iface['address']
|
||||
|
||||
option=DHCPOption.new(DHCPOption::RequestList,1,3,6)
|
||||
sys_mac[0]="0"
|
||||
sys_mac[1]="A"
|
||||
|
||||
packet=DHCPPacket.new(nil,nil,MAC,DHCPPacket::DISCOVER,[option])
|
||||
MAC = sys_mac.upcase
|
||||
|
||||
puts "Using interface #{iface['ifname']} with MAC #{MAC}"
|
||||
|
||||
|
||||
def send_packet(socket,msg,interval,packet)
|
||||
$t=Thread.new do
|
||||
while true
|
||||
puts msg if msg
|
||||
socket.send(packet.bytes.pack("C*"))
|
||||
sleep interval
|
||||
end
|
||||
end
|
||||
end
|
||||
eth_device = EthernetDevice.new(iface['ifname'], MAC)
|
||||
|
||||
send_packet(udp_socket,nil,10,packet)
|
||||
ipv4_device = IPV4Device.new(eth_device, ip)
|
||||
|
||||
packet=nil
|
||||
while true
|
||||
data,src_ip,src_port=udp_socket.read
|
||||
packet=DHCPPacket.new(data)
|
||||
break if packet.mac==MAC and packet.type==DHCPPacket::OFFER
|
||||
end
|
||||
udp_device = UDPDevice.new(ipv4_device)
|
||||
|
||||
$t.exit
|
||||
# udp_socket = MyUDPSocket.new(ipv4_device, 68, true)
|
||||
# udp_socket.connect('255.255.255.255', 67)
|
||||
|
||||
option1=DHCPOption.new(DHCPOption::RequestedIP,NetUtils.decode_ip4(packet.yiaddr))
|
||||
# option = DHCPOption.new(DHCPOption::RequestList, 1, 3, 6)
|
||||
|
||||
option2=DHCPOption.new(DHCPOption::ServerIdentifier,NetUtils.decode_ip4(packet.siaddr))
|
||||
# packet = DHCPPacket.new(nil, nil, MAC, DHCPPacket::DISCOVER, [option])
|
||||
|
||||
packet=DHCPPacket.new(nil,nil,MAC,DHCPPacket::REQUEST,[option1,option2])
|
||||
# def send_packet(socket, msg, interval, packet)
|
||||
# $t = Thread.new do
|
||||
# loop do
|
||||
# puts msg if msg
|
||||
# socket.send(packet.bytes.pack('C*'))
|
||||
# sleep interval
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
send_packet(udp_socket,nil,10,packet)
|
||||
# send_packet(udp_socket,nil,10,packet)
|
||||
|
||||
packet=nil
|
||||
while true
|
||||
data,src_ip,src_port=udp_socket.read
|
||||
packet=DHCPPacket.new(data)
|
||||
break if packet.mac==MAC and packet.type==DHCPPacket::ACK or packet.type==DHCPPacket::NACK
|
||||
end
|
||||
# packet=nil
|
||||
# while true
|
||||
# data,src_ip,src_port=udp_socket.read
|
||||
# packet=DHCPPacket.new(data)
|
||||
# break if packet.mac==MAC and packet.type==DHCPPacket::OFFER
|
||||
# end
|
||||
|
||||
$t.exit
|
||||
# $t.exit
|
||||
|
||||
if packet.type==DHCPPacket::NACK
|
||||
puts "Could not obtain IP"
|
||||
exit 1
|
||||
else
|
||||
puts "Obtained IP #{packet.yiaddr}"
|
||||
end
|
||||
# option1=DHCPOption.new(DHCPOption::RequestedIP,NetUtils.decode_ip4(packet.yiaddr))
|
||||
|
||||
udp_socket.close
|
||||
# option2=DHCPOption.new(DHCPOption::ServerIdentifier,NetUtils.decode_ip4(packet.siaddr))
|
||||
|
||||
ip=packet.yiaddr
|
||||
# packet=DHCPPacket.new(nil,nil,MAC,DHCPPacket::REQUEST,[option1,option2])
|
||||
|
||||
ipv4_device=IPV4Device.new(eth_device,ip)
|
||||
# send_packet(udp_socket,nil,10,packet)
|
||||
|
||||
eth_device.register_callback(EthernetFrame::ARP) do |frame,device|
|
||||
packet=ARPPacket.new(frame.data)
|
||||
if packet.is_request?
|
||||
resp_packet=ARPPacket.new(nil,ARPPacket::REPLY,MAC,ip,packet.src_ip)
|
||||
device.send_frame(EthernetFrame::ARP,packet.src_mac,resp_packet.bytes)
|
||||
ipv4_device.map_arp(packet.src_ip,packet.src_mac)
|
||||
else
|
||||
ipv4_device.map_arp(packet.src_ip,packet.src_mac)
|
||||
end
|
||||
# packet=nil
|
||||
# while true
|
||||
# data,src_ip,src_port=udp_socket.read
|
||||
# packet=DHCPPacket.new(data)
|
||||
# break if packet.mac==MAC and packet.type==DHCPPacket::ACK or packet.type==DHCPPacket::NACK
|
||||
# end
|
||||
|
||||
# $t.exit
|
||||
|
||||
# if packet.type==DHCPPacket::NACK
|
||||
# puts "Could not obtain IP"
|
||||
# exit 1
|
||||
# else
|
||||
# puts "Obtained IP #{packet.yiaddr}"
|
||||
# end
|
||||
|
||||
# udp_socket.close
|
||||
|
||||
# ip=packet.yiaddr
|
||||
|
||||
# ipv4_device=IPV4Device.new(eth_device,ip)
|
||||
|
||||
ArpClient.initialize(eth_device, MAC, ip)
|
||||
|
||||
ipv4_device.register_callback(IPV4Packet::ICMP) do |packet, _device|
|
||||
data = packet.data
|
||||
data[0]=0
|
||||
data[2]=0
|
||||
data[3]=0
|
||||
data[2],data[3] = NetUtils.compute_checksum(data)
|
||||
ipv4_device.send_packet(IPV4Packet::ICMP, packet.src_ip, data)
|
||||
end
|
||||
|
||||
|
||||
udp_socket=MyUDPSocket.new(ipv4_device,7)
|
||||
udp_socket = MyUDPSocket.new(udp_device, 7)
|
||||
|
||||
while true
|
||||
msg,src_ip,src_port=udp_socket.read
|
||||
udp_socket.send(msg,src_ip,src_port)
|
||||
if ip == '10.0.0.6'
|
||||
udp_socket.send("hi", '10.0.0.5', 7)
|
||||
end
|
||||
|
||||
loop do
|
||||
msg, src_ip, src_port = udp_socket.read
|
||||
puts "Echo got message #{msg}"
|
||||
udp_socket.send(msg, src_ip, src_port)
|
||||
end
|
||||
|
@ -1,62 +1,50 @@
|
||||
require "set"
|
||||
require_relative "udp_datagram.rb"
|
||||
require_relative "ipv4_packet.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'set'
|
||||
require_relative 'udp_datagram'
|
||||
require_relative 'ipv4_packet'
|
||||
|
||||
require 'hexdump'
|
||||
|
||||
class MyUDPSocket
|
||||
@@ports=Set.new()
|
||||
@@ports = Set.new
|
||||
|
||||
|
||||
def initialize(ipv4_device,port=nil,broad=false)
|
||||
@dev=ipv4_device
|
||||
@broad=broad
|
||||
@packets=[]
|
||||
def initialize(udp_device, port = nil)
|
||||
@dev = udp_device
|
||||
@packets = []
|
||||
|
||||
if port
|
||||
@port=port
|
||||
@port = port
|
||||
else
|
||||
@port=1025+rand(65536)
|
||||
while @@ports.include? @port
|
||||
@port=1025+rand(65536)
|
||||
end
|
||||
@port = rand(1025..66_560)
|
||||
@port = rand(1025..66_560) while @@ports.include? @port
|
||||
end
|
||||
|
||||
@@ports.add(@port)
|
||||
|
||||
@dev.register_callback(IPV4Packet::UDP) do |packet,device|
|
||||
datagram=UDPDatagram.new(packet.data)
|
||||
if datagram.dest_port==@port
|
||||
#puts "Datagram from #{packet.src_ip}:#{datagram.src_port} to port #{datagram.dest_port} with data #{datagram.data.chomp}"
|
||||
@packets.push({:src_ip=>packet.src_ip,:udp_datagram=>datagram})
|
||||
end
|
||||
@dev.register_callback(@port) do |src_ip, datagram|
|
||||
@packets.push({ src_ip: src_ip, udp_datagram: datagram })
|
||||
end
|
||||
end
|
||||
|
||||
def connect(ip,port)
|
||||
@bind_ip=ip
|
||||
@bind_port=port
|
||||
def connect(ip, port)
|
||||
@bind_ip = ip
|
||||
@bind_port = port
|
||||
end
|
||||
|
||||
def send(msg,host=nil,port=nil)
|
||||
if host
|
||||
datagram=UDPDatagram.new(nil,@port,port,msg)
|
||||
# puts "Datagram from port #{datagram.src_port} to #{host}:#{datagram.dest_port} with data #{datagram.data.chomp}"
|
||||
@dev.send_packet(IPV4Packet::UDP,host,datagram.bytes,@broad)
|
||||
else
|
||||
datagram=UDPDatagram.new(nil,@port,@bind_port,msg)
|
||||
# puts "Datagram from port #{datagram.src_port} to #{@bind_ip}:#{datagram.dest_port} with data #{datagram.data.chomp}"
|
||||
@dev.send_packet(IPV4Packet::UDP,@bind_ip,datagram.bytes,@broad)
|
||||
def send(msg, host = nil, port = nil)
|
||||
@dev.send_datagram(msg, @port, host || @bind_ip, port || @bind_port)
|
||||
end
|
||||
|
||||
def read
|
||||
while @packets.empty?
|
||||
end
|
||||
packet = @packets.shift
|
||||
[packet[:udp_datagram].data, packet[:src_ip], packet[:udp_datagram].src_port]
|
||||
end
|
||||
|
||||
def read()
|
||||
until @packets.length!=0
|
||||
end
|
||||
packet=@packets.shift
|
||||
return [packet[:udp_datagram].data,packet[:src_ip],packet[:udp_datagram].src_port]
|
||||
end
|
||||
|
||||
def close()
|
||||
def close
|
||||
@@ports.delete @port
|
||||
@dev.delete_callback(@port)
|
||||
end
|
||||
|
||||
end
|
||||
|
75
net_utils.rb
75
net_utils.rb
@ -1,5 +1,7 @@
|
||||
require_relative "ethernet_frame.rb"
|
||||
require_relative "ipv4_packet.rb"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'ethernet_frame'
|
||||
require_relative 'ipv4_packet'
|
||||
|
||||
class NetUtils
|
||||
def self.format_mac(mac_bytes)
|
||||
@ -9,58 +11,71 @@ class NetUtils
|
||||
end
|
||||
|
||||
def self.format_ip4(ip_bytes)
|
||||
ip_bytes.map do |byte|
|
||||
byte.to_s
|
||||
end.join('.')
|
||||
ip_bytes.map(&:to_s).join('.')
|
||||
end
|
||||
|
||||
def self.decode_ethertype(ethertype)
|
||||
case ethertype
|
||||
when EthernetFrame::ARP
|
||||
return "ARP"
|
||||
'ARP'
|
||||
when EthernetFrame::IPV4
|
||||
return "IPv4"
|
||||
'IPv4'
|
||||
when EthernetFrame::IPV6
|
||||
return "IPv6"
|
||||
'IPv6'
|
||||
else
|
||||
return "Unknown"
|
||||
'Unknown'
|
||||
end
|
||||
end
|
||||
|
||||
def self.decode_ip4type(type)
|
||||
case type
|
||||
when IPV4Packet::UDP
|
||||
return "UDP"
|
||||
'UDP'
|
||||
when IPV4Packet::ICMP
|
||||
'ICMP'
|
||||
else
|
||||
return "Unknown"
|
||||
'Unknown'
|
||||
end
|
||||
end
|
||||
|
||||
def self.decode_ip4(ip)
|
||||
ip=ip.split(".")
|
||||
ip[0]=ip[0].to_i
|
||||
ip[1]=ip[1].to_i
|
||||
ip[2]=ip[2].to_i
|
||||
ip[3]=ip[3].to_i
|
||||
return ip
|
||||
ip = ip.split('.')
|
||||
ip[0] = ip[0].to_i
|
||||
ip[1] = ip[1].to_i
|
||||
ip[2] = ip[2].to_i
|
||||
ip[3] = ip[3].to_i
|
||||
ip
|
||||
end
|
||||
|
||||
def self.decode_mac(mac)
|
||||
mac=mac.split(":")
|
||||
mac[0]=mac[0].to_i(16)
|
||||
mac[1]=mac[1].to_i(16)
|
||||
mac[2]=mac[2].to_i(16)
|
||||
mac[3]=mac[3].to_i(16)
|
||||
mac[4]=mac[4].to_i(16)
|
||||
mac[5]=mac[5].to_i(16)
|
||||
return mac
|
||||
mac = mac.split(':')
|
||||
mac[0] = mac[0].to_i(16)
|
||||
mac[1] = mac[1].to_i(16)
|
||||
mac[2] = mac[2].to_i(16)
|
||||
mac[3] = mac[3].to_i(16)
|
||||
mac[4] = mac[4].to_i(16)
|
||||
mac[5] = mac[5].to_i(16)
|
||||
mac
|
||||
end
|
||||
|
||||
def self.decode_int(bytes)
|
||||
num=bytes[3]
|
||||
num+=bytes[2]<<8
|
||||
num+=bytes[1]<<16
|
||||
num+=bytes[0]<<24
|
||||
return num
|
||||
num = bytes[3]
|
||||
num += bytes[2] << 8
|
||||
num += bytes[1] << 16
|
||||
num += bytes[0] << 24
|
||||
num
|
||||
end
|
||||
|
||||
def self.compute_checksum(bytes)
|
||||
i = 0
|
||||
checksum = 0
|
||||
(bytes.length / 2).times do
|
||||
checksum += (bytes[i] << 8) + bytes[i + 1]
|
||||
i += 2
|
||||
end
|
||||
checksum += bytes[-1] if bytes.length.odd?
|
||||
checksum = (checksum & 0xFFFF) + (checksum >> 16) while (checksum >> 16) != 0
|
||||
checksum = (~checksum) & 0xFFFF
|
||||
[checksum >> 8, checksum & 0xFF]
|
||||
end
|
||||
end
|
||||
|
28
structs.c
28
structs.c
@ -1,28 +0,0 @@
|
||||
struct ifreq {
|
||||
char ifr_name[IFNAMSIZ]; /* Interface name */
|
||||
union {
|
||||
struct sockaddr ifr_addr;
|
||||
struct sockaddr ifr_dstaddr;
|
||||
struct sockaddr ifr_broadaddr;
|
||||
struct sockaddr ifr_netmask;
|
||||
struct sockaddr ifr_hwaddr;
|
||||
short ifr_flags;
|
||||
int ifr_ifindex;
|
||||
int ifr_metric;
|
||||
int ifr_mtu;
|
||||
struct ifmap ifr_map;
|
||||
char ifr_slave[IFNAMSIZ];
|
||||
char ifr_newname[IFNAMSIZ];
|
||||
char *ifr_data;
|
||||
};
|
||||
};
|
||||
|
||||
struct sockaddr_ll {
|
||||
unsigned short sll_family; /* Always AF_PACKET */
|
||||
unsigned short sll_protocol; /* Physical-layer protocol */
|
||||
int sll_ifindex; /* Interface number */
|
||||
unsigned short sll_hatype; /* ARP hardware type */
|
||||
unsigned char sll_pkttype; /* Packet type */
|
||||
unsigned char sll_halen; /* Length of address */
|
||||
unsigned char sll_addr[8]; /* Physical-layer address */
|
||||
};
|
@ -1,51 +1,51 @@
|
||||
class UDPDatagram
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UDPDatagram
|
||||
attr_reader :bytes
|
||||
|
||||
def initialize(bytes,src_port=nil,dst_port=nil,data=nil)
|
||||
def initialize(bytes, src_port = nil, dst_port = nil, data = nil)
|
||||
if !bytes
|
||||
@bytes=[]
|
||||
@bytes[0]=(src_port&0xFF00)>>8
|
||||
@bytes[1]=src_port&0xFF
|
||||
@bytes[2]=(dst_port&0xFF00)>>8
|
||||
@bytes[3]=dst_port&0xFF
|
||||
length=data.to_s.bytesize+8
|
||||
@bytes[4]=(length&0xFF00)>>8
|
||||
@bytes[5]=length&0xFF
|
||||
@bytes[6]=0
|
||||
@bytes[7]=0
|
||||
@bytes+=data.to_s.unpack("C*")
|
||||
@bytes = []
|
||||
@bytes[0] = (src_port & 0xFF00) >> 8
|
||||
@bytes[1] = src_port & 0xFF
|
||||
@bytes[2] = (dst_port & 0xFF00) >> 8
|
||||
@bytes[3] = dst_port & 0xFF
|
||||
length = data.to_s.bytesize + 8
|
||||
@bytes[4] = (length & 0xFF00) >> 8
|
||||
@bytes[5] = length & 0xFF
|
||||
@bytes[6] = 0
|
||||
@bytes[7] = 0
|
||||
@bytes += data.to_s.unpack('C*')
|
||||
else
|
||||
@bytes=bytes
|
||||
@bytes = bytes
|
||||
end
|
||||
end
|
||||
|
||||
def src_port
|
||||
src_port=@bytes[1]
|
||||
src_port+=@bytes[0]<<8
|
||||
return src_port
|
||||
src_port = @bytes[1]
|
||||
src_port += @bytes[0] << 8
|
||||
src_port
|
||||
end
|
||||
|
||||
def dest_port
|
||||
dest_port=@bytes[3]
|
||||
dest_port+=@bytes[2]<<8
|
||||
return dest_port
|
||||
dest_port = @bytes[3]
|
||||
dest_port += @bytes[2] << 8
|
||||
dest_port
|
||||
end
|
||||
|
||||
def data
|
||||
return @bytes[8,data_length].pack("C*")
|
||||
@bytes[8, data_length].pack('C*')
|
||||
end
|
||||
|
||||
def data_length()
|
||||
return length-8
|
||||
def data_length
|
||||
length - 8
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def length()
|
||||
length=@bytes[5]
|
||||
length+=@bytes[4]<<8
|
||||
return length
|
||||
def length
|
||||
len = @bytes[5]
|
||||
len += @bytes[4] << 8
|
||||
len
|
||||
end
|
||||
|
||||
end
|
||||
|
56
udp_device.rb
Normal file
56
udp_device.rb
Normal file
@ -0,0 +1,56 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'udp_datagram'
|
||||
require_relative 'ipv4_packet'
|
||||
|
||||
require 'hexdump'
|
||||
|
||||
class UDPDevice
|
||||
def initialize(dev)
|
||||
@callbacks = {}
|
||||
@dev = dev
|
||||
@packets = []
|
||||
|
||||
dev.register_callback(IPV4Packet::UDP) do |packet, _device|
|
||||
datagram = UDPDatagram.new(packet.data)
|
||||
got_datagram(packet.src_ip, datagram)
|
||||
end
|
||||
end
|
||||
|
||||
def send_datagram(msg, src_port, host, port)
|
||||
datagram = UDPDatagram.new(nil, src_port, port, msg)
|
||||
puts "Sending datagram from port #{datagram.src_port} to #{host}:#{datagram.dest_port}"
|
||||
# puts "Data:"
|
||||
# print datagram.data.hexdump
|
||||
# puts "Bytes:"
|
||||
# puts datagram.bytes.pack("C*").hexdump
|
||||
@dev.send_packet(IPV4Packet::UDP, host, datagram.bytes)
|
||||
end
|
||||
|
||||
def register_callback(port, &callback)
|
||||
raise Errno::EADDRINUSE, "bind(2) for \"#{dev.ip}\" port #{port}" if @callbacks[port]
|
||||
|
||||
@callbacks[port] = callback
|
||||
end
|
||||
|
||||
def delete_callback(port)
|
||||
@callbacks.delete port
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def got_datagram(src_ip, datagram)
|
||||
print "Received datagram from #{src_ip}:#{datagram.src_port} to port #{datagram.dest_port}"
|
||||
if @callbacks[datagram.dest_port]
|
||||
puts ''
|
||||
# puts 'Data:'
|
||||
# print datagram.data.hexdump
|
||||
else
|
||||
puts ' dropped'
|
||||
end
|
||||
# puts "Data:"
|
||||
# puts datagram.data.hexdump
|
||||
@callbacks[datagram.dest_port]&.call(src_ip, datagram)
|
||||
end
|
||||
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user