今回はRaw Socketを使ってみる その3にARPを表示する機能を付けた。しかし、問題がある。今回、俺がパディングのことを理解してなかったため、相手からARPが送られてきた場合、ARPの後のEthernetフレームを正しく読み込めないという問題が発生している。ARPだけじゃなくてIPでもサイズがものすごく小さければ同じ問題が起こる。次までの課題にしよう。今回は新たに追加したarp.rbと変更を加えたether.rbと本体であるsocket.rbのソースだけ載せておく。arp.rbで無駄なことをしているのはLinuxに用意されているC言語の構造体と同じような構造にしようと思ったためである
socket.rb
require 'socket' require './ether' require './ip' require './arp' require './tcp' require './udp' socket = Socket.open(Socket::PF_INET, Socket::SOCK_PACKET, Ethernet::ETH_P_ALL) buff = '' loop do if(buff.size < 1600) buff << socket.read(8192) end ether_header = EtherHeader.new(buff) puts 'Ethernetフレーム' puts "送信元MACアドレス #{ether_header.ether_shost}" puts "送信先MACアドレス #{ether_header.ether_dhost}" puts sprintf("EtherType = 0x%X", ether_header.ether_type) buff.slice!(0..13) if(ether_header.ether_type == Ethernet::ETHERTYPE_IP) puts 'IPパケット' ip_header = IPHeader.new(buff) puts "送信元IPアドレス #{ip_header.ip_src}" puts "送信先IPアドレス #{ip_header.ip_dst}" puts "プロトコル番号 #{ip_header.ip_p}" puts "サイズ #{ip_header.ip_len}" buff.slice!(0..(ip_header.ip_hl << 2) - 1) transport_size = 0 if(ip_header.ip_p == Socket::IPPROTO_TCP) puts 'TCPパケット' tcp_header = TCPHeader.new(buff) puts "送信元ポート番号 #{tcp_header.th_sport}" puts "送信先ポート番号 #{tcp_header.th_dport}" transport_size = tcp_header.th_off << 2 buff.slice!(0..transport_size - 1) size = ip_header.ip_len - (ip_header.ip_hl << 2) - transport_size - 1 if(tcp_header.th_dport == 80 && size > 0) contents = buff.slice(0..size) puts contents end elsif(ip_header.ip_p == Socket::IPPROTO_UDP) puts 'UDPパケット' udp_header = UDPHeader.new(buff) puts "送信元ポート番号 #{udp_header.uh_sport}" puts "送信先ポート番号 #{udp_header.uh_dport}" transport_size = udp_header.uh_ulen buff.slice!(0..transport_size - 1) end size = (ip_header.ip_len - (ip_header.ip_hl << 2) - transport_size - 1) if(size > 0) buff.slice!(0..size) end elsif(ether_header.ether_type == Ethernet::ETHERTYPE_ARP) puts 'ARPパケット' arp = ARP.new(buff) puts "送信元MACアドレス #{arp.arp_sha}" puts "送信元IPアドレス #{arp.arp_spa}" puts "送信先MACアドレス #{arp.arp_tha}" puts "送信先IPアドレス #{arp.arp_tpa}" buff.slice!(0..27) end puts end
ether.rb
class EtherHeader attr_reader :ether_dhost attr_reader :ether_shost attr_reader :ether_type def initialize(frame) @ether_dhost = mac_tos(frame, 0) @ether_shost = mac_tos(frame, 6) @ether_type = (frame[12] << 8) + frame[13] end def mac_tos(frame, index) return sprintf('%02X:%02X:%02X:%02X:%02X:%02X', frame[index], frame[index + 1], frame[index + 2], frame[index + 3], frame[index + 4], frame[index + 5]) end end class Ethernet ETH_P_ALL = 0x300 ETHERTYPE_IP = 0x800 ETHERTYPE_ARP = 0x806 end
arp.rb
class ARP class ARPHeader #ARPヘッダのreader attr_reader :ar_hrd attr_reader :ar_pro attr_reader :ar_hlen attr_reader :ar_plen attr_reader :ar_op def initialize(packet) @ar_hrd = (packet[0] << 8) + packet[1] @ar_pro = (packet[2] << 8) + packet[3] @ar_hlen = packet[4] @ar_plen = packet[5] @ar_op = (packet[6] << 8) + packet[7] end end #ARPヘッダと同じ値を持つ変数のreader attr_reader :arp_hrd attr_reader :arp_pro attr_reader :arp_hlen attr_reader :arp_plen attr_reader :arp_op #ARP本体のreader attr_reader :ea_hdr attr_reader :arp_sha attr_reader :arp_spa attr_reader :arp_tha attr_reader :arp_tpa def initialize(packet) @ea_hdr = ARPHeader.new(packet) @arp_hrd = @ea_hdr.ar_hrd @arp_pro = @ea_hdr.ar_pro @arp_hlen = @ea_hdr.ar_hlen @arp_plen = @ea_hdr.ar_plen @arp_op = @ea_hdr.ar_op @arp_sha = mac_to_s(packet, 8) @arp_spa = ip_to_s(packet, 14) @arp_tha = mac_to_s(packet, 18) @arp_tpa = ip_to_s(packet, 24) end def mac_to_s(packet, index) return sprintf('%02X:%02X:%02X:%02X:%02X:%02X', packet[index], packet[index + 1], packet[index + 2], packet[index + 3], packet[index + 4], packet[index + 5]) end def ip_to_s(packet, index) return sprintf("%d.%d.%d.%d", packet[index], packet[index + 1], packet[index + 2], packet[index + 3]) end end