Skip to content

Instantly share code, notes, and snippets.

@kengonakajima
Created March 29, 2021 01:28
Show Gist options
  • Save kengonakajima/d3bc7b83893aba441f13e841bf3d07ac to your computer and use it in GitHub Desktop.
Save kengonakajima/d3bc7b83893aba441f13e841bf3d07ac to your computer and use it in GitHub Desktop.
pcap analyzer for game packets
#!ruby -Ku
# Author: takeda yoshiki
res_hash = {
"ret" => false,
"error" => "",
}
begin
require 'pp'
require 'json'
# 取得するデータ
# - だいたいの同時接続数
# - 送信頻度
# - サーバーの受信数/sec
# - サーバーの送信数/sec
# - 受信の平均パケットサイズ
# - 送信の平均パケットサイズ
# - 増幅率
def get_param(argv:ARGV, delimiter:[], default:"", split:"=")
ret = default
argv = argv.map{|r|r.split(split)}.flatten.select{|r|r!=split}
delimiter = [delimiter] unless delimiter.kind_of?(Array)
delimiter.each{|d|
unless (inx=argv.index(d)).nil?
ret = argv[inx+1] unless argv[inx+1].nil?
break
end
}
return ret
end
#サーバのアドレスを取得する
def get_server_addr(datas)
# pcapを取得したアドレスを特定する
addrs = datas.slice(0,10).map{|r|
[r["src"],r["dst"]]
}.flatten
server_addr = addrs.uniq.map do |a|
{
"addr"=>a,
"num"=>addrs.count{|x|x==a},
}
end.sort_by{|r|
-r["num"]
}[0]["addr"]
return server_addr
end
#接続しているクライアントを取得する
#※今は送信しか見ていない
def get_connection_clients(server_addr,datas)
return datas.select do |r|
r["dst"] == server_addr
end.map do|r|
r["src"]
end.sort.uniq
end
# - 1秒間あたりの同時接続クライアント数を取得
# TODO:余計なループがあるので後から治す。ぶっちゃけ全秒数の接続数取らなくてもいい。
def get_connection_num(server_addr,datas)
first = datas[0]["time"].to_i
last = datas.last["time"].to_i
return (first..last).map do |sec|
{
"time" => sec,
"count" => get_connection_clients(
server_addr,datas.select do |r|
r["time"].to_i == sec
end).count,
}
end
end
#特定秒のパケットのみ取り出す
def extract_data_of_sec(target_sec,datas)
return datas.select do |r|
r["time"].to_i == target_sec
end
end
def get_average_connection(server_addr,datas)
sec_of_connections = get_connection_num(server_addr,datas)
total = sec_of_connections.map do|r|
next r["count"].to_i
end.inject(:+)
return (total.to_f / sec_of_connections.length.to_f)
end
# データの特定の秒数を取得する
# 特定の1秒間のデータを取得する際に使用する
# 現状では、通信が落ち着くと思われる70%の時間を取得
def get_target_time(datas,target_ratio=0.7)
target_rows = datas[datas.length * target_ratio]
return target_rows["time"].to_i rescue 0
end
def get_server_send_late(datas,clients)
return clients.map do |client|
{
"client" => client,
"num" => datas.select{|r|
r["dst"]==client
}.length
}
end
end
# - 受信の平均パケットサイズ
def get_packet_size(server_addr,datas,src_or_dst)
length = datas.select{|r|r[src_or_dst]==server_addr}.length
return datas.select{|r|r[src_or_dst]==server_addr}
.map{|r|r["size"].to_i}.inject(:+) / length
end
if !(ENV["OS"].nil?) and ENV["OS"].index("Windows")
tshark='"C:/Program Files/Wireshark/tshark.exe"'
else
tshark=`which tshark`.strip
end
raise "plz spaciled -p [pcap]" if
(pcap = get_param(delimiter: ["-p","--pcap"],default:"")).length <= 0
# DIR="/root/d/support/s_スパイク・チュンソフト様/20210315_先方のtcpdump調査/20210312_lily_pcap"
lines=`#{tshark} -r "#{File.expand_path(pcap)}"`
raise "no pcap data." if lines.length <= 0
#連想配列にデータを変換
datas = lines.split("\n").map do |line|
arr = line.split("\s")
{
"seq" => arr[0],
"time" => arr[1],
"src" => arr[2],
"dst" => arr[4],
"protocol" => arr[5],
"size" => arr[6],
}
end
server_addr = get_server_addr(datas)
#中間よりちょっと後ろのデータを取る
target_sec = get_target_time(datas)
datas = extract_data_of_sec(target_sec,datas)
# - 同時接続数
connection_average = get_average_connection(server_addr,datas)
connection_clients = get_connection_clients(server_addr,datas)
# - 1クライアントあたりの送信頻度(最低,最高,平均値)
send_ratio_par_client = get_server_send_late(
datas,connection_clients).sort_by!{|r|r["num"]}
max_send_ratio_par_sec_for_client = send_ratio_par_client.sort_by {|r|r["num"]}.last["num"]
minimum_send_ratio_par_sec_for_client = send_ratio_par_client.sort_by {|r|-r["num"]}.last["num"]
average_send_ratio_par_sec_for_client = send_ratio_par_client.map{|r|r["num"]}.inject(:+) /send_ratio_par_client.length
# - サーバーの受信数/sec
server_receive_num_per_sec = datas.select{|r|r["dst"]==server_addr}.length
# - サーバーの送信数/sec
server_send_num_per_sec = datas.select{|r|r["src"]==server_addr}.length
ret = {
# - だいたいの同時接続数
"connection_client_average" => connection_average,
# - 送信頻度
"max_send_ratio_par_sec_for_client" =>max_send_ratio_par_sec_for_client,
"minimum_send_ratio_par_sec_for_client" =>minimum_send_ratio_par_sec_for_client,
"average_send_ratio_par_sec_for_client" =>average_send_ratio_par_sec_for_client,
# - サーバーの受信数/sec
"server_receive_num_per_sec" => server_receive_num_per_sec,
# - サーバーの送信数/sec
"server_send_num_per_sec" => server_send_num_per_sec,
# - 受信の平均パケットサイズ
"average_server_receive_packet_size" => get_packet_size(server_addr,datas,"dst"),
# - 送信の平均パケットサイズ
"average_server_send_packet_size" => get_packet_size(server_addr,datas,"src"),
# - 増幅率
"amplification_ratio" => get_packet_size(server_addr,datas,"src")/get_packet_size(server_addr,datas,"dst"),
}
res_hash["ret"] = ret
rescue => e
if e.message.length > 0
res_hash["error"] = "#{e.message}\\n#{e.backtrace}"
end
end
puts JSON.generate(res_hash)
@kengonakajima
Copy link
Author

[win]
C:\Users\y.takeda\Desktop\snippets\ruby\pcap>ruby pcap.rb -p 20210311_tcp.pcap
{"ret":{"connection_client_average":14.0,"max_send_ratio_par_sec_for_client":293,"minimum_send_ratio_par_sec_for_client":58,"average_send_ratio_par_sec_for_client":144,"server_receive_num_per_sec":2430,"server_send_num_per_sec":2018,"average_server_receive_packet_size":83,"average_server_send_packet_size":1694,"amplification_ratio":20},"error":""}

[linux]
-bash-4.2# ruby ./pcap.rb -p  hoge.pcap |jq
Running as user "root" and group "root". This could be dangerous.
{
  "ret": {
    "connection_client_average": 14,
    "max_send_ratio_par_sec_for_client": 293,
    "minimum_send_ratio_par_sec_for_client": 58,
    "average_send_ratio_par_sec_for_client": 144,
    "server_receive_num_per_sec": 2430,
    "server_send_num_per_sec": 2018,
    "average_server_receive_packet_size": 83,
    "average_server_send_packet_size": 1694,
    "amplification_ratio": 20
  },
  "error": ""
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment