Ruby でパケットパーサを作ろう
-
Upload
yasuhito-takamiya -
Category
Technology
-
view
1.651 -
download
0
description
Transcript of Ruby でパケットパーサを作ろう
![Page 1: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/1.jpg)
Rubyで
パケットのパーサとジェネレータを作ろう
高宮 安仁Trema チーム
![Page 2: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/2.jpg)
短く
読みやすく
改造しやすい
トポロジ探索サンプルコード
![Page 3: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/3.jpg)
パーサジェネレータ
コントローラ
LLDPPacketOut
LLDPPacketIn
![Page 4: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/4.jpg)
PacketFuhttps://github.com/todb/packetfu
お,おう...
![Page 5: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/5.jpg)
Rackethttp://spoofed.org/files/racket/
おしい...
![Page 6: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/6.jpg)
LLDPの生成
LLDPのパース理想
![Page 7: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/7.jpg)
BinDatahttps://github.com/dmendel/bindata
LLDPクラス
宣言的DSL
パーサジェネレータLldp#readLldp.new
![Page 8: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/8.jpg)
BinDataで
実際に作ってみよう
![Page 9: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/9.jpg)
LLDPフレームフォーマット
![Page 10: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/10.jpg)
class Lldp < BinData::Record mac_address :destination_mac mac_address :source_mac uint16 :ether_type, ... chassis_id_tlv :chassis_id port_id_tlv :port_id ttl_tlv :ttl, ... array :optional_tlv, ...
![Page 11: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/11.jpg)
# ロードrequire “lldp”
# LLDPのパースLldp.read(packet_in.data).dpid #=> 12345
# 生成lldp = Lldp.new(:destination_mac => ..., :source_mac => ...)
# 送信send_packet_out(dpid, :data => lldp.to_binary, ...)
![Page 12: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/12.jpg)
class Lldp < BinData::Record mac_address :destination_mac mac_address :source_mac uint16 :ether_type, ... chassis_id_tlv :chassis_id port_id_tlv :port_id ttl_tlv :ttl, ... array :optional_tlv, ...end
型 :フィールド名
![Page 13: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/13.jpg)
プリミティブ型• string, stringz
• int8, uint16, int32be,...
• bit1, bit2, bit4_le,...
• float_le, double_be,...
![Page 14: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/14.jpg)
ユーザ定義型
• mac_address
• chassis_id_tlv
• port_id_tlv
• ttl_tlv
• optional_tlv
![Page 15: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/15.jpg)
ボトムアップに型を定義
![Page 16: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/16.jpg)
class FooBarTlv < BinData::Primitive ... bit7 :tlv_type, :value => xxx bit9 :tlv_info_length string :foobar, :read_length => :tlv_info_length
![Page 17: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/17.jpg)
だいたいパースできた!
![Page 18: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/18.jpg)
Optional TLV
BinDataでどう表す?
0~N個
![Page 19: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/19.jpg)
Optional TLVの種類
• Port Description (4)
• System Name (5)
• System Description (6)
• System Capabilities (7)
• Management Address (8)
※カッコ内は TLV type の値
よく使う
![Page 20: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/20.jpg)
Optional TLV1~N個
• Optional TLV (type = 4~8, 0) の Array
• Array の終端は End of LLDPDU
![Page 21: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/21.jpg)
BinDataで
OptionalTLVを扱うには?
![Page 22: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/22.jpg)
CODE
![Page 23: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/23.jpg)
class Lldp < BinData::Record ... array :optional_tlv, :type => :optional_tlv, :read_until => lambda { element.end_of_lldpdu? }
Optional TLV
End of LLDPDU で終わる Optional TLV 配列
![Page 24: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/24.jpg)
class OptionalTlv < BinData::Record ... bit7 :tlv_type ... def end_of_lldpdu? tlv_type == 0 end
終端のチェック
![Page 25: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/25.jpg)
![Page 26: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/26.jpg)
class OptionalTlv < BinData::Record bit7 :tlv_type bit9 :tlv_info_length choice :tlv_value, ... :selection => :chooser do end_of_lldpdu_value 0 port_description_value 4 system_name_value 5 system_description_value 6 system_capabilities_value 7 management_address_value 8 string “unknown” end
共通
どれかが入る
![Page 27: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/27.jpg)
choice :tlv_value, ... :selection => :chooser do end_of_lldpdu_value 0 port_description_value 4 ... string “unknown” ...
def chooser if tlv_type == 0 or (4 <= tlv_type and tlv_type <= 8) tlv_type else "unknown" end end
![Page 28: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/28.jpg)
class SystemNameValue < BinData::Record stringz :system_nameend
![Page 29: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/29.jpg)
すべてパースできた!
![Page 30: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/30.jpg)
ちょっとひと手間でこのうまさ
![Page 31: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/31.jpg)
Lldp.new(:destination_mac => [0x11, 0x22, ...], ...)
# vs.
Lldp.new(:destination_mac => "11:22:33:44:55:66", ...)
どちらが使いやすい?
![Page 32: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/32.jpg)
class MacAddress < BinData::Primitive array :octets, :type => :uint8, :initial_length => 6
def set value self.octets = value.split(/:/).collect do | each | each.hex end end
文字列→整数配列
![Page 33: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/33.jpg)
ちゃんと動く?
![Page 34: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/34.jpg)
describe OptionalTlv do subject { OptionalTlv.read( data ) }
context "when parsing End Of LLDPDU TLV" do let( :data ) { [ 0x00, 0x00 ].pack( "C*" ) }
its( :tlv_type ) { should eq 0 } its( :tlv_info_length ) { should eq 0 } its( :tlv_value ) { should eq "tlv_info_string" => "" } end
...
RSpec でテスト
ここまでやれば一級品
![Page 35: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/35.jpg)
成果物
APC 近藤氏寄贈
https://github.com/yasuhito/ruby_topology
![Page 36: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/36.jpg)
• 「○○パケットを処理するには」
• BinData を使えばきれいに書ける
•誰か一緒にやりませんか!!
Tremaレシピ集
![Page 37: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/37.jpg)
• pcap を集める人
•コーディングする人
•実環境でテストする人
共同開発
![Page 38: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/38.jpg)
Trema TremaEdge
paper-houseTrema::Packet
![Page 39: Ruby でパケットパーサを作ろう](https://reader034.fdocuments.us/reader034/viewer/2022051112/55921fce1a28abe4598b45e1/html5/thumbnails/39.jpg)
gemの作者になろう