Ruby/Pcap 拡張ライブラリを用いたパケットデータベースの実装例

TCP/IP と UDP/IP のパケットデータ(libpcap形式)をリレーショナルデータベースに格納し,集約,条件検索,CSV ファイルへのエクスポートを行う試み

【この Web ページを理解する前提となる関連事項】

前準備

概要


図.パケットデータベースの概要

libpcap 形式ファイルの例

libpcap 形式ファイルはWireshark を使いパケットをキャプチャすることで簡単に作ることができる.

Wireshark のサイトからサンプルデータをダウンロードすることも簡単です.

ソースコード

libpcap 形式のパケットデータをパケットにインポートするプログラム (Ruby プログラム)

#!/usr/bin/env ruby -wKU

require 'rubygems'
require 'sqlite3'
require 'pcap'

# SQLite 3 のデータベースファイル名を DBFILENAME に設定してください.
# Windows の場合は「\\」で区切る
DBFILENAME = "/home/kaneko/pcap/examples/packetdb.sqlite3"
DBDIR = File.dirname( DBFILENAME )
DBNAME = File.basename( DBFILENAME )

# データベースオープン
Dir.chdir( DBDIR )
db = SQLite 3::Database.new( DBFILENAME )

# (オプション)SQL を用いた packets テーブルの削除
sql = <<SQL
DROP TABLE tcppackets;
SQL
puts "drop table tcppackets..."
# 削除したいときは、ここの「#」を外す
#db.execute_batch(sql)

# SQL を用いた packets テーブルの定義
# id 属性は SQLite 3の側で自動的に連番(整数)を付ける
# その他の属性については Ruby/Pcap のドキュメントをみt欲しい
sql = <<SQL
create table tcppackets (
    id            INTEGER  PRIMARY KEY,
    time          REAL not null,
    size          INTEGER not null,
    tcp_data_len  INTEGER not null, 
    ip_src        INTEGER not null,
    tcp_sport     INTEGER not null,
    ip_dst        INTEGER not null,
    tcp_dport     INTEGER not null,
    tcp_flags_s   TEXT not null,
    tcp_seq       INTEGER not null );
create table udppackets (
    id            INTEGER  PRIMARY KEY,
    time          REAL not null,
    size          INTEGER not null,
    udp_len  INTEGER not null, 
    ip_src        INTEGER not null,
    udp_sport     INTEGER not null,
    ip_dst        INTEGER not null,
    udp_dport     INTEGER not null );  
SQL
puts "define table tcppackets..."
db.execute_batch(sql)

puts "insert values..."
# libpcap を使い libpcap 形式のファイル traffic.cap を読み込む
cap = Pcap::Capture.open_offline('traffic.cap')
cap.setfilter("ip")
cap.loop do |pkt|
  if pkt.ip? and pkt.tcp?
    db.execute( " insert into tcppackets values ( null, ?, ?, ?, ?, ?, ?, ?, ?, ? )", pkt.time.to_f, pkt.size, pkt.tcp_data_len, pkt.ip_src, pkt.tcp_sport, pkt.ip_dst, pkt.tcp_dport, pkt.tcp_flags_s, pkt.tcp_seq )
  end
  if pkt.ip? and pkt.udp?
    db.execute( " insert into udppackets values ( null, ?, ?, ?, ?, ?, ?, ? )", pkt.time.to_f, pkt.size, pkt.udp_len, pkt.ip_src, pkt.udp_sport, pkt.ip_dst, pkt.udp_dport )
  end
end
cap.close

# 問い合わせ(確認表示)
sql = <<SQL
SELECT * FROM tcppackets;
SQL
puts "query"
db.execute(sql) do |row|
    p row
end

# 問い合わせ(確認表示)
sql = <<SQL
SELECT * FROM udppackets;
SQL
puts "query"
db.execute(sql) do |row|
    p row
end

【実行手順】

  1. ソースコード中の次の2箇所を確認し,適切に書き換える
    • 入力である libpcap 形式パケットデータファイルのファイル名: traffic.cap
    • SQLite 3 データベースファイル名: /home/kaneko/pcap/examples/packetdb.sqlite3
  2. ソースコードを適切なファイル名で保存し,ruby 処理系を使って実行
    ruby <ソースコードのファイル名>
    
  3. テーブルの中身が表示される(エラーメッセージが出ない)ことを確認
  4. データベースファイルが出来ていることを確認

パケットの集約の例 (SQL プログラム)

■ Source IP Address による集約の例

SELECT count(*), ip_src
FROM tcppackets
group by ip_src
ODRER BY count(*)

【実行結果の例】

SQLite 3 コマンドラインシェル を使うのが簡単です.

SQLite 3 コマンドラインシェルでは,起動時に「sqlite packetsdb.sqlite3」のようにデータベースファイル名を指定することを忘れない.

■ Source IP Address と Source TCP Portによる集約

SELECT count(*), ip_src, tcp_sport
FROM tcppackets
group by ip_src, tcp_sport
ODRER BY count(*)

【実行結果の例】

パケットの条件検索の例 (SQL プログラム)

SELECT *
FROM tcppackets
WHERE tcp_sport=3813

【実行結果の例】

CSV ファイルへのエクスポート

SQLite 3 コマンドラインシェル を使うことで, CSV ファイルへのエクスポートが簡単にできる.

SQLite 3 コマンドラインシェルを起動し,次のコマンドを実行する

.mode csv
.output tcppackets.csv
SELECT * FROM tcppackets;
.output stdout
.exit