This commit is contained in:
hoffer 2020-09-26 10:28:54 +08:00 committed by GitHub
commit 6d9b72393f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 380 additions and 95 deletions

View File

@ -2,13 +2,14 @@ package core
import ( import (
"fmt" "fmt"
"github.com/google/gopacket/pcap"
"log" "log"
"time"
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"github.com/google/gopacket/tcpassembly" "github.com/google/gopacket/tcpassembly"
"github.com/google/gopacket/tcpassembly/tcpreader" "github.com/google/gopacket/tcpassembly/tcpreader"
"time"
) )
type Dispatch struct { type Dispatch struct {
@ -41,7 +42,7 @@ func (d *Dispatch) Capture() {
// Capture // Capture
src := gopacket.NewPacketSource(handle, handle.LinkType()) src := gopacket.NewPacketSource(handle, handle.LinkType())
packets := src.Packets() packets := src.Packets() // get packet chan
// Set up assembly // Set up assembly
streamFactory := &ProtocolStreamFactory{ streamFactory := &ProtocolStreamFactory{

BIN
go-sniffer Executable file

Binary file not shown.

BIN
kafka.pcap Normal file

Binary file not shown.

View File

@ -19,6 +19,27 @@ const (
CreateTopicsReqKind = 19 CreateTopicsReqKind = 19
) )
const ()
var RquestNameMap = map[int16]string{
0: "ProduceRequest",
1: "FetchRequest",
2: "OffsetRequest",
3: "MetadataRequest",
//Non-user facing control APIs = 4-7
8: "OffsetCommitRequest",
9: "OffsetFetchRequest",
10: "GroupCoordinatorRequest",
11: "JoinGroupRequest",
12: "HeartbeatRequest",
13: "LeaveGroupRequest",
14: "SyncGroupRequest",
15: "DescribeGroupsRequest",
16: "ListGroupsRequest",
18: "APIVersionsReqKind",
19: "CreateTopicsReqKind",
}
const ( const (
ApiV0 = 0 ApiV0 = 0
ApiV1 = 1 ApiV1 = 1

View File

@ -2,11 +2,13 @@ package build
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"github.com/google/gopacket"
"io" "io"
"strconv" "strconv"
"sync" "sync"
"github.com/google/gopacket"
) )
const ( const (
@ -23,10 +25,10 @@ type Kafka struct {
type stream struct { type stream struct {
packets chan *packet packets chan *packet
correlationMap map[int32]requestHeader
} }
type packet struct { type packet struct {
isClientFlow bool //客户端->服务器端流 isClientFlow bool //客户端->服务器端流
messageSize int32 messageSize int32
@ -51,9 +53,10 @@ type messageSet struct {
offset int64 offset int64
messageSize int32 messageSize int32
} }
func newMessageSet(r io.Reader) messageSet { func newMessageSet(r io.Reader) messageSet {
messageSet := messageSet{} messageSet := messageSet{}
messageSet.offset = ReadInt64(r) _, messageSet.offset = ReadInt64(r)
messageSet.messageSize = ReadInt32(r) messageSet.messageSize = ReadInt32(r)
return messageSet return messageSet
@ -69,6 +72,7 @@ type message struct {
var kafkaInstance *Kafka var kafkaInstance *Kafka
var once sync.Once var once sync.Once
func NewInstance() *Kafka { func NewInstance() *Kafka {
once.Do(func() { once.Do(func() {
kafkaInstance = &Kafka{ kafkaInstance = &Kafka{
@ -94,7 +98,7 @@ func (m *Kafka) SetFlag(flg []string) {
switch key { switch key {
case CmdPort: case CmdPort:
p, err := strconv.Atoi(val); p, err := strconv.Atoi(val)
if err != nil { if err != nil {
panic("端口数不正确") panic("端口数不正确")
} }
@ -110,7 +114,7 @@ func (m *Kafka) SetFlag(flg []string) {
} }
func (m *Kafka) BPFFilter() string { func (m *Kafka) BPFFilter() string {
return "tcp and port "+strconv.Itoa(m.port); return "tcp and port " + strconv.Itoa(m.port)
} }
func (m *Kafka) Version() string { func (m *Kafka) Version() string {
@ -127,6 +131,7 @@ func (m *Kafka) ResolveStream(net, transport gopacket.Flow, buf io.Reader) {
var newStream = stream{ var newStream = stream{
packets: make(chan *packet, 100), packets: make(chan *packet, 100),
correlationMap: make(map[int32]requestHeader),
} }
m.source[uuid] = &newStream m.source[uuid] = &newStream
@ -139,7 +144,7 @@ func (m *Kafka) ResolveStream(net, transport gopacket.Flow, buf io.Reader) {
newPacket := m.newPacket(net, transport, buf) newPacket := m.newPacket(net, transport, buf)
if newPacket == nil { if newPacket == nil {
return continue
} }
m.source[uuid].packets <- newPacket m.source[uuid].packets <- newPacket
@ -151,8 +156,22 @@ func (m *Kafka) newPacket(net, transport gopacket.Flow, r io.Reader) *packet {
//read packet //read packet
pk := packet{} pk := packet{}
/*
bs := make([]byte, 0)
count, err := r.Read(bs)
if err != nil {
panic(err)
}
fmt.Printf("read: %d, buffer: %b", count, bs)
return nil
*/
//read messageSize //read messageSize
pk.messageSize = ReadInt32(r) pk.messageSize = ReadInt32(r)
if pk.messageSize == 0 {
return nil
}
fmt.Printf("pk.messageSize: %d\n", pk.messageSize)
//set flow direction //set flow direction
if transport.Src().String() == strconv.Itoa(m.port) { if transport.Src().String() == strconv.Itoa(m.port) {
@ -160,6 +179,7 @@ func (m *Kafka) newPacket(net, transport gopacket.Flow, r io.Reader) *packet {
respHeader := responseHeader{} respHeader := responseHeader{}
respHeader.correlationId = ReadInt32(r) respHeader.correlationId = ReadInt32(r)
// TODO extract request
pk.responseHeader = respHeader pk.responseHeader = respHeader
var buf bytes.Buffer var buf bytes.Buffer
@ -204,29 +224,68 @@ func (stm *stream) resolve() {
select { select {
case packet := <-stm.packets: case packet := <-stm.packets:
if packet.isClientFlow { if packet.isClientFlow {
stm.correlationMap[packet.requestHeader.correlationId] = packet.requestHeader
stm.resolveClientPacket(packet) stm.resolveClientPacket(packet)
} else { } else {
stm.resolveServerPacket(packet) if _, ok := stm.correlationMap[packet.responseHeader.correlationId]; ok {
stm.resolveServerPacket(packet, stm.correlationMap[packet.responseHeader.correlationId])
}
} }
} }
} }
} }
func (stm *stream) resolveServerPacket(pk *packet) { func (stm *stream) resolveServerPacket(pk *packet, rh requestHeader) {
return var msg interface{}
payload := pk.payload
action := Action{
Request: GetRquestName(pk.apiKey),
Direction: "isServer",
ApiVersion: pk.apiVersion,
}
switch int(rh.apiKey) {
case ProduceRequest:
msg = ReadProduceResponse(payload, rh.apiVersion)
case MetadataRequest:
msg = ReadMetadataResponse(payload, rh.apiVersion)
default:
fmt.Printf("response ApiKey:%d TODO", rh.apiKey)
}
if msg != nil {
action.Message = msg
}
bs, err := json.Marshal(action)
if err != nil {
fmt.Printf("json marshal action failed, err: %+v\n", err)
}
fmt.Printf("%s\n", string(bs))
} }
func (stm *stream) resolveClientPacket(pk *packet) { func (stm *stream) resolveClientPacket(pk *packet) {
var msg string var msg interface{}
action := Action{
Request: GetRquestName(pk.apiKey),
Direction: "isClient",
ApiVersion: pk.apiVersion,
}
payload := pk.payload payload := pk.payload
fmt.Println("apiKey:")
fmt.Println(pk.apiKey)
switch int(pk.apiKey) { switch int(pk.apiKey) {
case ProduceRequest: case ProduceRequest:
msg = ReadProduceRequest(payload, pk.apiVersion) msg = ReadProduceRequest(payload, pk.apiVersion)
case MetadataRequest:
msg = ReadMetadataRequest(payload, pk.apiVersion)
default:
fmt.Printf("ApiKey:%d TODO", pk.apiKey)
} }
_=msg
//fmt.Println(msg) if msg != nil {
action.Message = msg
}
bs, err := json.Marshal(action)
if err != nil {
fmt.Printf("json marshal action failed, err: %+v\n", err)
}
fmt.Printf("%s\n", string(bs))
} }

View File

@ -6,12 +6,13 @@ import (
"time" "time"
) )
type Message struct { type Message struct {
Key []byte Key []byte
Value []byte Value []byte
Offset int64 Offset int64
Crc uint32 Crc uint32
Magic byte
CompressCode byte
Topic string Topic string
Partition int32 Partition int32
TipOffset int64 TipOffset int64
@ -44,9 +45,8 @@ type ProduceReqPartition struct {
Messages []*Message Messages []*Message
} }
func ReadProduceRequest(r io.Reader, version int16) string { func ReadProduceRequest(r io.Reader, version int16) *ProduceReq {
// version == 1
var msg string
produceReq := ProduceReq{} produceReq := ProduceReq{}
@ -59,21 +59,157 @@ func ReadProduceRequest(r io.Reader, version int16) string {
produceReq.Timeout = time.Duration(ReadInt32(r)) * time.Millisecond produceReq.Timeout = time.Duration(ReadInt32(r)) * time.Millisecond
l := ReadInt32(r) l := ReadInt32(r)
req := ProduceReq{} produceReq.Topics = make([]ProduceReqTopic, l)
req.Topics = make([]ProduceReqTopic, l)
for ti := range req.Topics { for ti := range produceReq.Topics {
var topic = &req.Topics[ti] var topic = &produceReq.Topics[ti]
topic.Name, _ = ReadString(r) topic.Name, _ = ReadString(r)
fmt.Println("msg")
fmt.Println(topic.Name)
l := ReadInt32(r) l := ReadInt32(r)
topic.Partitions = make([]ProduceReqPartition, l) topic.Partitions = make([]ProduceReqPartition, l)
for idx := 0; idx < int(l); idx++ {
topic.Partitions[idx].ID = ReadInt32(r)
_ = ReadInt32(r) // partitions size
topic.Partitions[idx].Messages = ReadMessages(r, version)
}
} }
return msg return &produceReq
} }
type ProduceRspPartitions struct {
PartitionID int32
Error int16
Offset int64
}
type ProduceRspTopic struct {
TopicName string
Partitions []ProduceRspPartitions
ThrottleTime int32
}
type ProduceRsp struct {
Topics []ProduceRspTopic
}
func ReadProduceResponse(r io.Reader, version int16) *ProduceRsp {
// version == 1
produceRsp := ProduceRsp{}
l := ReadInt32(r)
produceRsp.Topics = make([]ProduceRspTopic, 0)
for i := 0; i < int(l); i++ {
topic := ProduceRspTopic{}
topic.TopicName, _ = ReadString(r)
pl := ReadInt32(r)
topic.Partitions = make([]ProduceRspPartitions, 0)
for j := 0; j < int(pl); j++ {
pt := ProduceRspPartitions{}
pt.PartitionID = ReadInt32(r)
pt.Error = ReadInt16(r)
_, pt.Offset = ReadInt64(r)
topic.Partitions = append(topic.Partitions, pt)
}
produceRsp.Topics = append(produceRsp.Topics, topic)
}
return &produceRsp
}
type MetadataReq struct {
TopicNames []string
}
func ReadMetadataRequest(r io.Reader, version int16) *MetadataReq {
// version == 0
metadataReq := MetadataReq{}
l := ReadInt32(r)
for i := 0; i < int(l); i++ {
topicName, _ := ReadString(r)
metadataReq.TopicNames = append(metadataReq.TopicNames, topicName)
}
return &metadataReq
}
type Broker struct {
NodeID int32
Host string
Port int32
}
type PartitionMetada struct {
ErrorCode int16
PartitionIndex int32
LeaderID int32
ReplicaNodes []int32
IsrNodes []int32
}
type TopicMetadata struct {
ErrorCode int16
Name string
Partitions []PartitionMetada
}
type MetadataRsp struct {
Brokers []Broker
Topics []TopicMetadata
}
func ReadMetadataResponse(r io.Reader, version int16) *MetadataRsp {
// version == 0
metadataRsp := MetadataRsp{}
// read brokers
metadataRsp.Brokers = make([]Broker, 0)
l := ReadInt32(r)
for i := 0; i < int(l); i++ {
broker := Broker{}
broker.NodeID = ReadInt32(r)
broker.Host, _ = ReadString(r)
broker.Port = ReadInt32(r)
metadataRsp.Brokers = append(metadataRsp.Brokers, broker)
}
// read topics
metadataRsp.Topics = make([]TopicMetadata, 0)
l = ReadInt32(r)
for i := 0; i < int(l); i++ {
topicMetadata := TopicMetadata{}
topicMetadata.ErrorCode = ReadInt16(r)
topicMetadata.Name, _ = ReadString(r)
pl := ReadInt32(r)
topicMetadata.Partitions = make([]PartitionMetada, 0)
for j := 0; j < int(pl); j++ {
pm := PartitionMetada{}
pm.ErrorCode = ReadInt16(r)
pm.PartitionIndex = ReadInt32(r)
pm.LeaderID = ReadInt32(r)
pm.ReplicaNodes = make([]int32, 0)
replicaLen := ReadInt32(r)
for ri := 0; ri < int(replicaLen); ri++ {
pm.ReplicaNodes = append(pm.ReplicaNodes, ReadInt32(r))
}
pm.IsrNodes = make([]int32, 0)
isrLen := ReadInt32(r)
for ri := 0; ri < int(isrLen); ri++ {
pm.IsrNodes = append(pm.IsrNodes, ReadInt32(r))
}
topicMetadata.Partitions = append(topicMetadata.Partitions, pm)
}
metadataRsp.Topics = append(metadataRsp.Topics, topicMetadata)
}
return &metadataRsp
}
type Action struct {
Request string
Direction string
ApiVersion int16
Message interface{}
}

View File

@ -2,7 +2,9 @@ package build
import ( import (
"encoding/binary" "encoding/binary"
"fmt"
"io" "io"
"strconv"
"time" "time"
) )
@ -31,6 +33,11 @@ func ReadOnce() {
} }
func ReadByte(r io.Reader) (n byte) {
binary.Read(r, binary.BigEndian, &n)
return
}
func ReadInt16(r io.Reader) (n int16) { func ReadInt16(r io.Reader) (n int16) {
binary.Read(r, binary.BigEndian, &n) binary.Read(r, binary.BigEndian, &n)
return return
@ -41,11 +48,16 @@ func ReadInt32(r io.Reader) (n int32) {
return return
} }
func ReadInt64(r io.Reader) (n int64) { func ReadUint32(r io.Reader) (n uint32) {
binary.Read(r, binary.BigEndian, &n) binary.Read(r, binary.BigEndian, &n)
return return
} }
func ReadInt64(r io.Reader) (err error, n int64) {
err = binary.Read(r, binary.BigEndian, &n)
return
}
func ReadString(r io.Reader) (string, int) { func ReadString(r io.Reader) (string, int) {
l := int(ReadInt16(r)) l := int(ReadInt16(r))
@ -62,6 +74,7 @@ func ReadString(r io.Reader) (string, int) {
return string(str), l return string(str), l
} }
// //
//func TryReadInt16(r io.Reader) (n int16, err error) { //func TryReadInt16(r io.Reader) (n int16, err error) {
// //
@ -76,19 +89,70 @@ func ReadString(r io.Reader) (string, int) {
func ReadBytes(r io.Reader) []byte { func ReadBytes(r io.Reader) []byte {
l := int(ReadInt32(r)) l := int(ReadInt32(r))
result := make([]byte, 0)
if l <= 0 {
return result
}
var result []byte
var b = make([]byte, l) var b = make([]byte, l)
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
_, err := r.Read(b) _, err := r.Read(b)
if err != nil { if err != nil {
if err == io.EOF {
break
}
panic(err) panic(err)
} }
result = append(result, b[0]) result = append(result, b...)
} }
return result return result
} }
func ReadMessages(r io.Reader, version int16) []*Message {
switch version {
case 0:
return ReadMessagesV1(r)
case 1:
return ReadMessagesV1(r)
}
return make([]*Message, 0)
}
func ReadMessagesV1(r io.Reader) []*Message {
var err error
messages := make([]*Message, 0)
for {
message := Message{}
err, message.Offset = ReadInt64(r)
if err != nil {
if err == io.EOF {
break
}
if err != io.ErrUnexpectedEOF {
fmt.Printf("read message offset , err: %+v\n", err)
}
break
}
_ = ReadInt32(r) // message size
message.Crc = ReadUint32(r)
message.Magic = ReadByte(r)
message.CompressCode = ReadByte(r)
message.Key = ReadBytes(r)
message.Value = ReadBytes(r)
messages = append(messages, &message)
}
return messages
}
func GetRquestName(apiKey int16) string {
if name, ok := RquestNameMap[apiKey]; ok {
return name
}
return strconv.Itoa(int(apiKey))
}

4
start.sh Normal file
View File

@ -0,0 +1,4 @@
#!/bin/bash
go build -gcflags "all=-N -l"
dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./go-sniffer