From 99c5e76d5dde9e65461d50f7739efbf2597ced89 Mon Sep 17 00:00:00 2001 From: pjht Date: Sun, 25 Apr 2021 08:13:16 -0500 Subject: [PATCH] Preserve last version without PacketFu --- arp_client.rb | 57 ++++++++++++++++ arp_packet.rb | 48 +++++++------- dhcp_option.rb | 103 +++++++++++++++-------------- dhcp_packet.rb | 116 ++++++++++++++++----------------- ethernet_device.rb | 66 +++++++++---------- ethernet_frame.rb | 59 ++++++++++++----- ipv4_device.rb | 73 +++++++-------------- ipv4_packet.rb | 82 +++++++++++++---------- main.rb | 157 +++++++++++++++++++++++++-------------------- my_udp_socket.rb | 70 +++++++++----------- net_utils.rb | 75 +++++++++++++--------- structs.c | 28 -------- udp_datagram.rb | 56 ++++++++-------- udp_device.rb | 56 ++++++++++++++++ 14 files changed, 579 insertions(+), 467 deletions(-) create mode 100644 arp_client.rb delete mode 100644 structs.c create mode 100644 udp_device.rb diff --git a/arp_client.rb b/arp_client.rb new file mode 100644 index 0000000..7e66869 --- /dev/null +++ b/arp_client.rb @@ -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 diff --git a/arp_packet.rb b/arp_packet.rb index 41464c4..aed8c41 100644 --- a/arp_packet.rb +++ b/arp_packet.rb @@ -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 diff --git a/dhcp_option.rb b/dhcp_option.rb index b08e699..54a36f5 100644 --- a/dhcp_option.rb +++ b/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 diff --git a/dhcp_packet.rb b/dhcp_packet.rb index 7657456..83c1bc8 100644 --- a/dhcp_packet.rb +++ b/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>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 diff --git a/ipv4_device.rb b/ipv4_device.rb index 42f6a7b..74cfab6 100644 --- a/ipv4_device.rb +++ b/ipv4_device.rb @@ -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 diff --git a/ipv4_packet.rb b/ipv4_packet.rb index 26764f2..a27ed4a 100644 --- a/ipv4_packet.rb +++ b/ipv4_packet.rb @@ -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 diff --git a/main.rb b/main.rb index 9de5f48..a136ba4 100644 --- a/main.rb +++ b/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 diff --git a/my_udp_socket.rb b/my_udp_socket.rb index f9aa781..a344fa9 100644 --- a/my_udp_socket.rb +++ b/my_udp_socket.rb @@ -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 diff --git a/net_utils.rb b/net_utils.rb index 243cdaa..f165cd9 100644 --- a/net_utils.rb +++ b/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 diff --git a/structs.c b/structs.c deleted file mode 100644 index ad83899..0000000 --- a/structs.c +++ /dev/null @@ -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 */ -}; diff --git a/udp_datagram.rb b/udp_datagram.rb index 94cae4b..41c5f78 100644 --- a/udp_datagram.rb +++ b/udp_datagram.rb @@ -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 diff --git a/udp_device.rb b/udp_device.rb new file mode 100644 index 0000000..c0c86c6 --- /dev/null +++ b/udp_device.rb @@ -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