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,25 +2,26 @@ 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 {
device string device string
payload []byte payload []byte
Plug *Plug Plug *Plug
} }
func NewDispatch(plug *Plug, cmd *Cmd) *Dispatch { func NewDispatch(plug *Plug, cmd *Cmd) *Dispatch {
return &Dispatch { return &Dispatch{
Plug: plug, Plug: plug,
device:cmd.Device, device: cmd.Device,
} }
} }
@ -40,16 +41,16 @@ 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{
dispatch:d, dispatch: d,
} }
streamPool := NewStreamPool(streamFactory) streamPool := NewStreamPool(streamFactory)
assembler := NewAssembler(streamPool) assembler := NewAssembler(streamPool)
ticker := time.Tick(time.Minute) ticker := time.Tick(time.Minute)
// Loop until ctrl+z // Loop until ctrl+z
for { for {
@ -84,7 +85,7 @@ type ProtocolStream struct {
func (m *ProtocolStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream { func (m *ProtocolStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream {
//init stream struct //init stream struct
stm := &ProtocolStream { stm := &ProtocolStream{
net: net, net: net,
transport: transport, transport: transport,
r: tcpreader.NewReaderStream(), r: tcpreader.NewReaderStream(),
@ -97,4 +98,4 @@ func (m *ProtocolStreamFactory) New(net, transport gopacket.Flow) tcpassembly.St
go m.dispatch.Plug.ResolveStream(net, transport, &(stm.r)) go m.dispatch.Plug.ResolveStream(net, transport, &(stm.r))
return &(stm.r) return &(stm.r)
} }

BIN
go-sniffer Executable file

Binary file not shown.

BIN
kafka.pcap Normal file

Binary file not shown.

View File

@ -1,14 +1,14 @@
package build package build
const ( const (
ProduceRequest = 0 ProduceRequest = 0
FetchRequest = 1 FetchRequest = 1
OffsetRequest = 2 OffsetRequest = 2
MetadataRequest = 3 MetadataRequest = 3
//Non-user facing control APIs = 4-7 //Non-user facing control APIs = 4-7
OffsetCommitRequest = 8 OffsetCommitRequest = 8
OffsetFetchRequest = 9 OffsetFetchRequest = 9
GroupCoordinatorRequest = 10 GroupCoordinatorRequest = 10
JoinGroupRequest = 11 JoinGroupRequest = 11
HeartbeatRequest = 12 HeartbeatRequest = 12
LeaveGroupRequest = 13 LeaveGroupRequest = 13
@ -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,15 +2,17 @@ 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 (
Port = 9092 Port = 9092
Version = "0.1" Version = "0.1"
CmdPort = "-p" CmdPort = "-p"
) )
@ -22,18 +24,18 @@ 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
requestHeader requestHeader
responseHeader responseHeader
payload io.Reader payload io.Reader
} }
type requestHeader struct { type requestHeader struct {
@ -51,50 +53,52 @@ 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
} }
type message struct { type message struct {
crc int32 crc int32
magicByte int8 magicByte int8
attributes int8 attributes int8
key []byte key []byte
value []byte value []byte
} }
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{
port :Port, port: Port,
version:Version, version: Version,
source: make(map[string]*stream), source: make(map[string]*stream),
} }
}) })
return kafkaInstance return kafkaInstance
} }
func (m *Kafka) SetFlag(flg []string) { func (m *Kafka) SetFlag(flg []string) {
c := len(flg) c := len(flg)
if c == 0 { if c == 0 {
return return
} }
if c >> 1 != 1 { if c>>1 != 1 {
panic("Mongodb参数数量不正确!") panic("Mongodb参数数量不正确!")
} }
for i:=0;i<c;i=i+2 { for i := 0; i < c; i = i + 2 {
key := flg[i] key := flg[i]
val := flg[i+1] val := flg[i+1]
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 {
@ -125,8 +129,9 @@ func (m *Kafka) ResolveStream(net, transport gopacket.Flow, buf io.Reader) {
//resolve packet //resolve packet
if _, ok := m.source[uuid]; !ok { if _, ok := m.source[uuid]; !ok {
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
@ -174,18 +194,18 @@ func (m *Kafka) newPacket(net, transport gopacket.Flow, r io.Reader) *packet {
pk.payload = &buf pk.payload = &buf
}else{ } else {
pk.isClientFlow = true pk.isClientFlow = true
var clientIdLen = 0 var clientIdLen = 0
reqHeader := requestHeader{} reqHeader := requestHeader{}
reqHeader.apiKey = ReadInt16(r) reqHeader.apiKey = ReadInt16(r)
reqHeader.apiVersion = ReadInt16(r) reqHeader.apiVersion = ReadInt16(r)
reqHeader.correlationId = ReadInt32(r) reqHeader.correlationId = ReadInt32(r)
reqHeader.clientId, clientIdLen = ReadString(r) reqHeader.clientId, clientIdLen = ReadString(r)
pk.requestHeader = reqHeader pk.requestHeader = reqHeader
var buf bytes.Buffer var buf bytes.Buffer
if _, err := io.CopyN(&buf, r, int64(pk.messageSize-10) - int64(clientIdLen)); err != nil { if _, err := io.CopyN(&buf, r, int64(pk.messageSize-10)-int64(clientIdLen)); err != nil {
if err == io.EOF { if err == io.EOF {
fmt.Println(net, transport, " 关闭") fmt.Println(net, transport, " 关闭")
return nil return nil
@ -202,31 +222,70 @@ func (m *Kafka) newPacket(net, transport gopacket.Flow, r io.Reader) *packet {
func (stm *stream) resolve() { func (stm *stream) resolve() {
for { for {
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,27 +6,28 @@ 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
Topic string Magic byte
Partition int32 CompressCode byte
TipOffset int64 Topic string
Partition int32
TipOffset int64
} }
/** /**
Produce request Protocol Produce request Protocol
v0, v1 (supported in 0.9.0 or later) and v2 (supported in 0.10.0 or later) v0, v1 (supported in 0.9.0 or later) and v2 (supported in 0.10.0 or later)
ProduceRequest => RequiredAcks Timeout [TopicName [Partition MessageSetSize MessageSet]] ProduceRequest => RequiredAcks Timeout [TopicName [Partition MessageSetSize MessageSet]]
RequiredAcks => int16 RequiredAcks => int16
Timeout => int32 Timeout => int32
Partition => int32 Partition => int32
MessageSetSize => int32 MessageSetSize => int32
*/ */
type ProduceReq struct { type ProduceReq struct {
TransactionalID string TransactionalID string
RequiredAcks int16 RequiredAcks int16
@ -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{}
@ -55,25 +55,161 @@ func ReadProduceRequest(r io.Reader, version int16) string {
fmt.Println(produceReq.TransactionalID) fmt.Println(produceReq.TransactionalID)
} }
produceReq.RequiredAcks = ReadInt16(r) produceReq.RequiredAcks = ReadInt16(r)
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"
) )
@ -12,7 +14,7 @@ func GetNowStr(isClient bool) string {
msg += time.Now().Format(layout) msg += time.Now().Format(layout)
if isClient { if isClient {
msg += "| cli -> ser |" msg += "| cli -> ser |"
}else{ } else {
msg += "| ser -> cli |" msg += "| ser -> cli |"
} }
return msg return msg
@ -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,18 +48,23 @@ 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))
//-1 => null //-1 => null
if l == -1 { if l == -1 {
return " ",1 return " ", 1
} }
str := make([]byte, l) str := make([]byte, l)
@ -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