概要
- Ruby で Raw Socket を使ってICMP Echo をヘッダから作っていこうと思う
- 以下画像のICMPヘッダ部分を作成する
- 目的はRaw Socketの扱い方に慣れること
- 2進数の取り扱いが難しかったので、とりあえずイメージしやすいように文字列型で扱う。
- 慣れてきたらビット演算に移行する
- 実装も適当なので参考はほどほどに
コードメモ
require "socket" require_relative "util" socket = Socket.open( Socket::AF_INET, Socket::SOCK_RAW, Socket::IPPROTO_ICMP ) # ヘッダ情報の定義 type = 0x08 # Echo Requestなので固定 code = 0x00 # Echo Requestなので固定(レスポンス時にはエラーの原因等に応じてコードが入る) id = 0x0001# 識別子。今回は↑の図の通り1(16bit) seq_number = 0x0016 # 上図にしたがう data_value = 0x6162 # 上図にしたがう # チェックサムの計算 # https://qiita.com/kure/items/fa7e665c2259375d9a81 checksum_value = checksum(type, code, id, seq_number, data_value) # 送信するビット配列を構成する data = type.to_s(2).rjust(8, "0") + code.to_s(2).rjust(8, "0") + checksum_value.to_s(2).rjust(16, "0") + id.to_s(2).rjust(16, "0") + seq_number.to_s(2).rjust(16, "0") + data_value.to_s(2).rjust(16, "0") # ASCIIコードで文字列に変換 data_byte_arr = data.scan(/.{1,#{8}}/) # 1Byteずつの配列に変換 data_byte_arr.map! { |byte| byte.to_i(2).chr } trans_data = data_byte_arr.join # ソケットアドレス構造体を pack した文字列 # https://docs.ruby-lang.org/ja/latest/class/Socket.html#S_PACK_SOCKADDR_IN sockaddr = Socket.sockaddr_in(nil, "n.n.n.n") # n.n.n.n は適当なIPアドレスに読み替えること # 送信 socket.send(trans_data, 0, sockaddr)
# 引数 num<2進数> を桁数ketanum から多かったら桁上りする # @param num String "11001100110100011" # @param keta_num Integer 16 def ketaagari_check(num, keta_num = 16) # 桁上りさせる桁数 agaru_num = num.length - keta_num # 本来の桁数の値 moto_value = num[agaru_num, keta_num] # 桁上げする値 agaru_value = num[0, agaru_num] # 足す sum = moto_value.to_i(2) + agaru_value&.to_i(2) # ビット反転 sum ^ 0xffff end # チェックサムを計算する # @param type [Integer] # @param code [Integer] # @param id [Integer] # @param seq_number [Iteger] # @param data [Integer] # @return [Integer] def checksum(type, code, id, seq_number, data) bit_1= type.to_s(2).rjust(8, "0") + code.to_s(2).rjust(8, "0") bit_2 = id.to_s(2).rjust(16, "0") bit_3 = seq_number.to_s(2).rjust(16, "0") # data を16bitごとに分ける必要がある # https://qiita.com/paty-fakename/items/990fe9d57864054409e1 data_arr = data.to_s(2).rjust(16, "0").scan(/.{1,#{16}}/) data_arr_int = data_arr.map do |data| data.to_i(2) end data_sum = data_arr_int.sum result = bit_1.to_i(2) + bit_2.to_i(2) + bit_3.to_i(2) + data_sum ketaagari_check(result.to_s(2).rjust(16, "0")) end
参考
(1)メモ帳 チェックサム http://specialimpact.blog22.fc2.com/blog-entry-47.html
(2)Internet Control Message Protocol - Wikipedia https://ja.wikipedia.org/wiki/Internet_Control_Message_Protocol