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

DTMやプログラミングにお熱なd-kamiがマイペースに書くブログ

tracerouteもどきを更新しました!

古いプログラムなので、もう少し手入れをしたいけど、とりあえず修正版公開します。Ruby 1.9あたり以降なRubyが入っているLinuxで多分動くよ!Raspberry Piで動作確認済み。root権限で動かしてね!使用例

ruby modoki.rb google.com

綺麗になったらgithubに上げます(多分)

modoki.rb

require 'socket'
require './ip'
require './udp'
require './icmp'

def make_ip_header(src_ip, dst_ip, iplen)
    ip = IPHeader.new
    ip.version = 4
    ip.ip_tos = 0
    ip.ip_hl = 5
    ip.ip_id = 0
    ip.ip_len = iplen
    ip.ip_off = 0
    ip.ip_ttl = 64
    ip.ip_p = Socket::IPPROTO_UDP
    ip.ip_src = src_ip
    ip.ip_dst = dst_ip
    ip.ip_sum = 0

    return ip.to_byte_array()
end

def make_udp_header
    udp = UDPHeader.new
    udp.uh_sport = 33434
    udp.uh_dport = 33434
    udp.uh_ulen = 8
    udp.uh_sum = 0

    return udp.to_byte_array
end

if ARGV.size() < 1
    puts 'usage ruby modoki.rb dst_ip'
    exit(1)
end

send_sa = Socket.sockaddr_in(0, ARGV[0])
send_sd = Socket.open(Socket::AF_INET, Socket::SOCK_RAW, 255)

recv_sd = Socket.open(Socket::AF_INET, Socket::SOCK_RAW, Socket::IPPROTO_ICMP)
recv_sa = Socket.sockaddr_in(0, "0.0.0.0")

host = Socket.gethostbyname(ARGV[0])[3]
ip_addr = sprintf("%d.%d.%d.%d", host[0].unpack('C')[0], host[1].unpack('C')[0], host[2].unpack('C')[0], host[3].unpack('C')[0])

packet = make_ip_header("0.0.0.0", ip_addr, 28)
packet << make_udp_header()

puts "scanroute #{ip_addr}"

for ttl in 1..64
    print ttl
    packet[8] = [ttl & 0xFF].pack('C')
    i = 0
    ip_header = nil
    icmp = nil

    for i in 0..3
        if i == 3
            puts
            break
        end

        break_flag = false
        send_sd.send(packet, 0, send_sa)
        
        while true
            ret = IO::select([recv_sd], nil, nil, 1.0)
            
            if ret == nil || ret[0].size() == 0
                print ' ?'
                break
            end

            buff, sockaddr = recv_sd.recvfrom(8192)
            ip_header = IPHeader.new(buff)

            buff.slice!(0..(ip_header.ip_hl << 2) - 1)
            icmp = ICMP.new(buff)

            if((icmp.icmp_type == ICMP::TIMXCEED && icmp.icmp_code == ICMP::TIMXCEED_INTRANS) || icmp.icmp_type == ICMP::UNREACH)
                break_flag = true
                break
            end
        end

        if break_flag
            break
        end
    end

    if(i < 3)
        puts " #{ip_header.ip_src}"
        if(icmp.icmp_type == ICMP::UNREACH)
            if(icmp.icmp_code == ICMP::UNREACH_PORT)
                puts "Reach!"
            elsif(icmp.icmp_code == ICMP::UNREACH_HOST)
                puts "Host Unreachable"
            elsif(icmp.icmp_code == ICMP::UNREACH_NET)
                puts "Network Unreachable"
            end

            break
        end
    end
end

send_sd.close()
recv_sd.close()

ip.rb

class IPHeader
    attr_accessor :version
    attr_accessor :ip_hl
    attr_accessor :ip_tos
    attr_accessor :ip_len
    attr_accessor :ip_id
    attr_accessor :ip_off 
    attr_accessor :ip_ttl
    attr_accessor :ip_p
    attr_accessor :ip_sum
    attr_accessor :ip_src
    attr_accessor :ip_dst

    def initialize(packet = nil)
        if(packet == nil)
            return
        end

        @version = (packet[0].unpack('C')[0] >> 4) & 0x0F
        @ip_hl = packet[0].unpack('C')[0] & 0x0F
        @ip_tos = packet[1].unpack('C')[0]
        @ip_len = packet[2, 2].unpack('n')[0]
        @ip_id = packet[4, 2].unpack('n')[0]
        @ip_off = packet[6].unpack('n')[0]
        @ip_ttl = packet[8].unpack('C')[0]
        @ip_p = packet[9].unpack('C')[0]
        @ip_sum = packet[10].unpack('n')[0]
        @ip_src = ip_to_s(packet, 12)
        @ip_dst = ip_to_s(packet, 16)
    end

    def to_byte_array()
        array = "                    "
        array[0] = [((@version << 4) + @ip_hl) & 0xFF].pack('C')
        array[1] = [@ip_tos & 0xFF].pack('C')
        array[2] = [(@ip_len >> 8) & 0xFF].pack('C')
        array[3] = [@ip_len & 0xFF].pack('C');
        array[4] = [(@ip_id >> 8) & 0xFF].pack('C')
        array[5] = [@ip_id & 0xFF].pack('C')
        array[6] = [(@ip_off >> 8) & 0xFF].pack('C')
        array[7] = [@ip_off & 0xFF].pack('C')
        array[8] = [@ip_ttl & 0xFF].pack('C')
        array[9] = [@ip_p & 0xFF].pack('C')
        array[10] = [(@ip_sum >> 8) & 0xFF].pack('C')
        array[11] = [@ip_sum & 0xFF].pack('C')
        s_to_ip(array, 12, @ip_src)
        s_to_ip(array, 16, @ip_dst)

        return array
    end

    def s_to_ip(array, index, str_ip)
        split = str_ip.split('.')
        for i in 0..3
            array[index + i] = [split[i].to_i() & 0xFF].pack('C')
        end
    end

    def ip_to_s(packet, index)
        return sprintf("%d.%d.%d.%d", packet[index].unpack('C')[0], packet[index + 1].unpack('C')[0],
                       packet[index + 2].unpack('C')[0], packet[index + 3].unpack('C')[0])
    end
end

udp.rb

class UDPHeader
    attr_accessor :uh_sport
    attr_accessor :uh_dport
    attr_accessor :uh_ulen
    attr_accessor :uh_sum

    def initialize(packet = nil)
        if(packet == nil)
            return
        end

#        @uh_sport = (packet[0] << 8) + packet[1]
#        @uh_dport = (packet[2] << 8) + packet[3]
#        @uh_ulen = (packet[4] << 8) + packet[5]
#        @uh_sum = (packet[6] << 8) + packet[7]
    end

    def to_byte_array()
        array = "        "
        array[0] = [(@uh_sport >> 8) & 0xFF].pack('C')
        array[1] = [@uh_sport & 0xFF].pack('C')
        array[2] = [(@uh_dport >> 8) & 0xFF].pack('C')
        array[3] = [@uh_dport & 0xFF].pack('C')
        array[4] = [(@uh_ulen >> 8) & 0xFF].pack('C')
        array[5] = [@uh_ulen & 0xFF].pack('C')
        array[6] = [(@uh_sum >> 8) & 0xFF].pack('C')
        array[7] = [@uh_sum & 0xFF].pack('C')

        return array
    end
end

icmp.rb

class ICMP
    attr_reader :icmp_type
    attr_reader :icmp_code
    attr_reader :icmp_cksum

    ECHOREPLY      = 0
    UNREACH        = 3
    SOURCEQUENCH   = 4
    REDIRECT       = 5
    ECHO           = 8
    ROUTERADVERT   = 9
    ROUTERSOLICIT  = 10
    TIMXCEED       = 11
    PARAMPROB      = 12
    TSTAMP         = 13
    TSTAMPREPLY    = 14
    IREQ           = 15
    IREQREPLY      = 16
    MASKREQ        = 17
    MASKREPLY      = 18

    UNREACH_NET             = 0
    UNREACH_HOST            = 1
    UNREACH_PROTOCOL        = 2
    UNREACH_PORT            = 3
    UNREACH_NEEDFRAG        = 4
    UNREACH_SRCFAIL         = 5
    UNREACH_NET_UNKNOWN     = 6
    UNREACH_HOST_UNKNOWN    = 7
    UNREACH_ISOLATED        = 8
    UNREACH_NET_PROHIB      = 9
    UNREACH_HOST_PROHIB     = 10
    UNREACH_TOSNET          = 11
    UNREACH_TOSHOST         = 12
    UNREACH_FILTER_PROHIB   = 13
    UNREACH_HOST_PRECEDENCE = 14

    REDIRECT_NET     = 0
    REDIRECT_HOST    = 1
    REDIRECT_TOSNET  = 2
    REDIRECT_TOSHOST = 3

    TIMXCEED_INTRANS = 0
    TIMXCEED_REASS   = 1

    PARAMPROB_ERRATPTR  = 0
    PARAMPROB_OPTABSENT = 1
    PARAMPROB_LENGTH    = 2

    def initialize(packet)
        @icmp_type = packet[0].unpack('C')[0]
        @icmp_code = packet[1].unpack('C')[0]
        @icmp_cksum = packet[2..3].unpack('n')[0]
    end
end

参考書!

基礎からわかるTCP/IP ネットワーク実験プログラミング―Linux/FreeBSD対応

基礎からわかるTCP/IP ネットワーク実験プログラミング―Linux/FreeBSD対応