マイペースなプログラミング日記

x86エミュレータやFPGA、WebGLにお熱なd-kamiがマイペースに書くブログ

Raw Socketを使ってみる その4

今回はRaw Socketを使ってみる その3ARPを表示する機能を付けた。しかし、問題がある。今回、俺がパディングのことを理解してなかったため、相手から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