ruby で pcap を読む
tcpdump
やtshark
などでキャプチャーしたパケットを,ruby で読むためのライブラリがあります.
The Ruby Toolbox などで検索すると山のように出てきますが,いくつか良さそうなのを紹介します.
PacketFu
例: IP パケットのsource address だけを抽出したい場合:
require 'packetfu' packets = PacketFu::PcapPackets.new.read(File.read('sample.pcap')) packets.each do |p| packet = PacketFu::Packet.parse(p.data) puts packet.respond_to?(:ip_saddr) && packet.ip_saddr end
PacketFu::Packet#inspect
がちゃんとあってprint デバッグしやすい
[1] pry(main)> p packet --EthHeader------------------------------------------------- eth_dst fe:ff:20:00:01:00 PacketFu::EthMac eth_src 00:00:01:00:00:00 PacketFu::EthMac eth_proto 0x0800 StructFu::Int16 --IPHeader-------------------------------------------------- ip_v 4 Fixnum ip_hl 5 Fixnum ip_tos 0 StructFu::Int8 ip_len 48 StructFu::Int16 ip_id 0x0f41 StructFu::Int16 ip_frag 16384 StructFu::Int16 ip_ttl 128 StructFu::Int8 ip_proto 6 StructFu::Int8 ip_sum 0x91eb StructFu::Int16 ip_src 145.254.160.237 PacketFu::Octets ip_dst 65.208.228.223 PacketFu::Octets --TCPHeader------------------------------------------------- tcp_src 3372 StructFu::Int16 tcp_dst 80 StructFu::Int16 tcp_seq 0x38affe13 StructFu::Int32 tcp_ack 0x00000000 StructFu::Int32 tcp_hlen 7 PacketFu::TcpHlen tcp_reserved 0 PacketFu::TcpReserved tcp_ecn 0 PacketFu::TcpEcn tcp_flags ....S. PacketFu::TcpFlags tcp_win 8760 StructFu::Int16 tcp_sum 0xc30c StructFu::Int16 tcp_urg 0 StructFu::Int16 tcp_opts MSS:1460,NOP,NOP,SACKOK PacketFu::TcpOptions
- C拡張ではないため 遅い
pcap / ruby-pcap
例: IP パケットのsource address だけを抽出したい場合:
require 'pcap' packets = Pcap::Capture.open_offline('sample.pcap') packets.each do |packet| puts packet.ip? && packet.ip_src end
このgem はruby 2.2 に対応してません.(2015-07-27 追記: 対応されました)
本体に取り込まれるまでは
gem install specific_install gem specific_install https://github.com/codeout/ruby-pcap
or
git clone https://github.com/codeout/ruby-pcap cd ruby-pcap gem build pcap.gemspec gem install --local pcap-0.7.7.gem
でインストールできます.
ほか
いろいろありますが,各種プロトコルヘッダーにアクセスするAPI が少なくて使いやすいとは言えません.
ベンチマーク: PacketFu vs. pcap
10,000 パケットの処理時間:
user system total real PacketFu 3.760000 0.040000 3.800000 ( 3.857046) pcap 0.010000 0.000000 0.010000 ( 0.012067)
やはり差が出ます.パフォーマンスが要求される場合は pcap オススメ.
ベンチマーク用コード:
require 'packetfu' require 'pcap' require 'benchmark' Benchmark.bm do |x| packets = PacketFu::PcapPackets.new.read(File.read('sample.pcap')) x.report('PacketFu') do packets.each do |p| packet = PacketFu::Packet.parse(p.data) packet.respond_to?(:ip_saddr) && packet.ip_saddr end end packets = Pcap::Capture.open_offline('sample.pcap') x.report('pcap') do packets.each do |packet| packet.ip? && packet.ip_src end end end
別途PacketFu をプロファイリングしてみても,パケットのparse が遅そうです.
% cumulative self self total time seconds seconds calls ms/call ms/call name 6.44 10.16 10.16 297491 0.03 0.05 StructFu::Int#read 4.46 17.20 7.04 171613 0.04 0.06 PacketFu::EthPacket.can_parse? 3.33 22.45 5.25 70757 0.07 0.15 PacketFu::IPPacket.can_parse? 3.02 27.22 4.77 296710 0.02 0.02 PacketFu.force_binary 3.00 31.96 4.74 44296 0.11 0.18 PacketFu::EthOui#read 2.45 35.83 3.87 12958 0.30 4.20 PacketFu::IPHeader#read 2.30 39.46 3.63 8866 0.41 2.22 PacketFu::TCPPacket#tcp_calc_sum 2.17 42.89 3.43 1149060 0.00 0.00 String#[] 2.09 46.19 3.30 17732 0.19 1.64 PacketFu::TcpOptions#read 2.09 49.49 3.30 27390 0.12 0.30 PacketFu::TcpOption#read 2.08 52.77 3.28 79465 0.04 0.06 PacketFu::Packet.layer 1.93 55.81 3.04 155600 0.02 0.03 StructFu::Int#to_i 1.84 58.72 2.91 966615 0.00 0.00 Struct#[] 1.83 61.61 2.89 159701 0.02 1.18 PacketFu::Packet.parse 1.80 64.45 2.84 8866 0.32 4.71 PacketFu::TCPHeader#read 1.79 67.28 2.83 278978 0.01 0.03 Struct#force_binary