Taste of Tech Topics

Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Packetbeatをカスタマイズしてみた

こんにちは、イガラシです。

Acroquestでは社内勉強会がいくつかありますが、
その中の一つに、具体的に実践した内容を発表する会があります。
その会で先日、プロジェクトで取り組んだ「Packetbeatのカスタマイズ」を発表したのですが、
マニアックな内容にも関わらず、思いのほか好評だったので、ブログ記事として書きたいと思います。

Packetbeatのカスタマイズ、といっても公式に書いてある新しいプロトコルに対応する、というものではありません。
あるカプセル化されたパケットの中身を解析するため、Packetbeatが対応していないカプセル化プロトコルに対応しよう。
という少し(かなり)ニッチな対応を行いました。

今回対応したカプセル化プロトコルは、「NVGRE」になります。

パケットの解析処理

Packetbeatがキャプチャしたパケットを解析する処理は、gopacketによって行われています。
NVGREがGREの拡張だったので、gopacketが対応していたGREパケットの解析処理を修正して、NVGREパケットを解析できるようにしました。

NVGREの解析処理

gopacket/layers/gre.go

func (g *GRE) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
  g.ChecksumPresent = data[0]&0x80 != 0
  g.RoutingPresent = data[0]&0x40 != 0
  g.KeyPresent = data[0]&0x20 != 0
  g.SeqPresent = data[0]&0x10 != 0
 :
 :
  // ↓ここから修正したコード
  baseOffset = 4
  if g.ChecksumPresent {
    g.Checksum = binary.BigEndian.Uint16(data[baseOffset:baseOffset+2])
    g.Offset = binary.BigEndian.Uint16(data[baseOffset+2:baseOffset+4])
    baseOffset += 4
  }
  if g.KeyPresent {
    g.Key = binary.BigEndian.Uint32(data[baseOffset:baseOffset+4])
    baseOffset += 4
  }
 :
 :
}

元のソースでは、フラグによるオプションフィールドの有無に関わらず、フィールドの値を取得していました。
NVGREだと特定のフィールドしか設定されないため、フラグが有効な場合にフィールドの値を取得するように修正しました。

1.2.定数の定義
gopacket/layers/enums.go

const (
  EthernetTypeLLC                      EthernetType = 0
  EthernetTypeIPv4                     EthernetType = 0x0800
 :
  EthernetTypeEthernetCTP              EthernetType = 0x9000
  EthernetTypeTrasparentEtherBridge    EthernetType = 0x6558	// 追加した定数
)

NVGREで定義されている、ProtocolTypeの値(0x6558)を定数として追加します。


gopacket/layers/enums.go

func init() {
 :
  EthernetTypeMetadata[EthernetTypeLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC", LayerType: LayerTypeLLC}
  EthernetTypeMetadata[EthernetTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
 :
  EthernetTypeMetadata[EthernetTypeEAPOL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOL), Name: "EAPOL", LayerType: LayerTypeEAPOL}
  // ↓追加した定義↓
  EthernetTypeMetadata[EthernetTypeTrasparentEtherBridge] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "TransparentEthernetBridge", LayerType: LayerTypeEthernet}

ProtocolType別に、「Payload部分をどのプロトコルとして解釈するか?」という定義を追加します。
Packetbeatがパケットを解析するときに、この定義を使ってパケットの中身を解析していきます。


ここまでで、パケット解析ライブラリgopacketの拡張は完了です。

PacketbeatでNVGREに対応する

Packetbeatが、gopacketを使ってパケット解析を行う処理は、「decoder/decoder.go」に記述されています。
その中で、プロトコルスタックを順に解析する処理は、OnPacket()で行われています。

packetbeat/decoder/decoder.go

func (d *Decoder) OnPacket(data []byte, ci *gopacket.CaptureInfo) {

  for len(data) > 0 {
    err := current.DecodeFromBytes(data, d)
    if err != nil {
      logp.Info("packet decode failed with: %v", err)
      break
    }

    nextType := current.NextLayerType()
    data = current.LayerPayload()

    processed, err = d.process(&packet, currentType)
    if err != nil {
      logp.Info("Error processing packet: %v", err)
      break
    }
    if processed {
      break
    }

    // choose next decoding layer
    next, ok := d.decoders[nextType]
    if !ok {
      break
    }

    // jump to next layer
    current = next
    currentType = nextType
  }
}

OnPacketは、decodersで指定されたプロトコルの解析を行うようになっているので、decodersを初期化しているメソッドでGREを解析対象に含めるようにします。


packetbeat/decoder/decoder.go

func New(
  f *flows.Flows,
  datalink layers.LinkType,
  icmp4 icmp.ICMPv4Processor,
  icmp6 icmp.ICMPv6Processor,
  tcp tcp.Processor,
  udp udp.Processor,
) (*Decoder, error) {
 :
 :
  defaultLayerTypes := []gopacket.DecodingLayer{
    &d.sll,             // LinuxSLL
    &d.eth,             // Ethernet
    &d.lo,              // loopback on OS X
    &d.stD1Q,           // VLAN
    &d.stIP4, &d.stIP6, // IP
    &d.icmp4, &d.icmp6, // ICMP
    &d.tcp, &d.udp,     // TCP/UDP
  }
  d.AddLayers(defaultLayerTypes)
  d.AddLayer(&d.gre)	// 追加した行

「d.gre」は、Decoderのフィールド定義に追加しておきます。


ここまでの対応で、Packetbeatでカプセル化パケットNVGREを解析できるようになりました。
当初は、どうやってNVGREに対応すればよいか分からず、Packetbeat/gopacketのソースコードを読んでいました。
対応できてからは、以外と簡単に対応できるようになっているな、と思えるようになりました。

Packetbeatは便利ですし、このように改造もできるので、
是非みなさんも使ってみてください^^

Acroquest Technologyでは、キャリア採用を行っています。


  • ビッグデータHadoop/Spark、NoSQL)、データ分析(Elasticsearch、Python関連)、Web開発(SpringCloud/SpringBoot、AngularJS)といった最新のOSSを利用する開発プロジェクトに関わりたい。
  • マイクロサービスDevOpsなどの技術を使ったり、データ分析機械学習などのスキルを活かしたい。
  • 社会貢献性の高いプロジェクトや、顧客の価値を創造するようなプロジェクトで、提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や、対外的な勉強会の開催・参加を通した技術の発信、社内勉強会での技術情報共有により、エンジニアとして成長したい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
Elasticsearchを仕事で使いこみたいデータ分析エンジニア募集中! - Acroquest Technology株式会社のエンジニア中途・インターンシップ・契約・委託の求人 - Wantedlywww.wantedly.com