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

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

Raw Socketを使ってみる その2

前回のはIPヘッダ(の一部)までしか表示してなかったけど、今回はTCPヘッダ(の一部)まで表示するようにした。まぁ、TCPパケットが来てくれないと意味がないんだけど、ブラウザとか立ち上げれば普通にヘッダの内容が見れる。readはどうやら、指定したサイズまで読み込まないと処理を返してくれないみたいなので、ブラウザのようなアプリケーションじゃないと、表示まで時間がかかるかもしれない。また、今回は1回で読み込んだ複数Ethernetフレームを処理してるけど、IPパケットでない場合はループを抜けるようにしている。ARPUDPも対応させようと思ったが長くなるのとやる気がなくなったため、対応させなかった。あとループを抜ける条件式は適当

require 'socket'

class EtherHeader
    attr_reader :ether_dhost
    attr_reader :ether_shost
    attr_reader :ether_type

    def initialize(frame, index)
        @ether_dhost = mac_tos(frame, index)
        @ether_shost = mac_tos(frame, index + 6)
        @ether_type = (frame[index + 12] << 8) + frame[index + 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 IPHeader
    attr_reader :version
    attr_reader :ip_hl
    attr_reader :ip_tos
    attr_reader :ip_len
    attr_reader :ip_id
    attr_reader :ip_off 
    attr_reader :ip_ttl
    attr_reader :ip_p
    attr_reader :ip_sum
    attr_reader :ip_src
    attr_reader :ip_dst

    def initialize(packet, index)
        @version = (packet[index] >> 4) & 0x0F
        @ip_hl = packet[index] & 0x0F
        @ip_tos = packet[index + 1]
        @ip_len = (packet[index + 2] << 8) + packet[index + 3]
        @ip_id = (packet[index + 4] << 8) + packet[index + 5]
        @ip_off = (packet[index + 6] << 8) + packet[index + 7]
        @ip_ttl = packet[index + 8]
        @ip_p = packet[index + 9]
        @ip_sum = (packet[index + 10] << 8) + packet[index + 11]
        @ip_src = ip_tos(packet, index + 12)
        @ip_dst = ip_tos(packet, index + 16)
    end

    def ip_tos(packet, index)
        return sprintf("%d.%d.%d.%d", packet[index], packet[index + 1], packet[index + 2], packet[index + 3])
    end
end

class TCPHeader
    attr_reader :th_sport
    attr_reader :th_dport
    attr_reader :th_seq
    attr_reader :th_ack
    attr_reader :th_off
    attr_reader :th_x2
    attr_reader :th_falgs
    attr_reader :th_win
    attr_reader :th_sum
    attr_reader :th_urp

    def initialize(packet, index)
        @th_sport = (packet[index] << 8) + packet[index + 1]
        @th_dport = (packet[index + 2] << 8) + packet[index + 3]
        @th_seq = (packet[index + 4] << 24) + (packet[index + 5] << 16) + (packet[index + 6] << 8) + packet[index + 7]
        @th_ack = (packet[index + 8] << 24) + (packet[index + 9] << 16) + (packet[index + 10] << 8) + packet[index + 11]
        @th_off = (packet[index + 12] >> 4) & 0x0F
        @th_x2 = packet[index + 12] &0x0F
        @th_flags = packet[index + 13]
        @th_win = (packet[index + 14] << 8) + packet[index + 15]
        @th_sum = (packet[index + 16] << 8) + packet[index + 17]
        @th_urp = (packet[index + 18] << 8) + packet[index + 19]
    end
end

ETH_P_ALL    = 0x0300
ETHERTYPE_IP = 0x800

socket = Socket.open(Socket::PF_INET, Socket::SOCK_PACKET, ETH_P_ALL)

index = 0
buff = socket.read(8192)

loop do
    if((8192 - index) < 40)
        break
    end

    ether_header = EtherHeader.new(buff, index)

    puts 'Ethernetフレーム'
    puts "送信元MACアドレス #{ether_header.ether_shost}"
    puts "送信先MACアドレス #{ether_header.ether_dhost}"
    puts sprintf("EtherType = 0x%X", ether_header.ether_type)
    index += 14

    if(ether_header.ether_type == ETHERTYPE_IP)
        puts 'IPパケット'
        ip_header = IPHeader.new(buff, index)
        puts "送信元IPアドレス #{ip_header.ip_src}"
        puts "送信先IPアドレス #{ip_header.ip_dst}"
        puts "プロトコル番号 #{ip_header.ip_p}"
        puts "サイズ #{ip_header.ip_len}"

        if(ip_header.ip_p == Socket::IPPROTO_TCP)
            tcp_header = TCPHeader.new(buff, index + (ip_header.ip_hl << 2))
            puts "送信元ポート番号 #{tcp_header.th_sport}"
            puts "送信先ポート番号 #{tcp_header.th_dport}"
       end

       index += ip_header.ip_len
    else
        break
    end

    puts
end