first commit

master v0.0.1
兔子 1 month ago
commit 5cb3c6d651

1
.gitignore vendored

@ -0,0 +1 @@
.idea

@ -0,0 +1,280 @@
package bcap
import (
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"net"
"sync"
)
type Packets struct {
sync.RWMutex
cu map[string]PacketInfo
}
type PacketInfo struct {
Key string
ReverseKey string
Type string
SrcMac net.HardwareAddr
DstMac net.HardwareAddr
SrcIP string
SrcPort string
DstIP string
DstPort string
comment string
packet gopacket.Packet
tcpSeq uint32
tcpAck uint32
tcpWindow uint16
tcpPayloads int
finState bool
synState bool
isFirst bool
//stateDescript 0=unknown 1=tcp_connect_1 2=tcp_connect_2 3=tcp_connect_3
// 4=tcp_disconnect_1 5=tcp_disconnect_2 6=tcp_disconnect_2+3 7=tcp_disconnect_3
// 8=tcp_disconnect_4 9=tcp_ack_ok 10=tcp_retransmit 11=tcp_ece 12=tcp_cwr 13=tcp_rst
// 14=tcp_keepalived 20=udp
stateDescript uint8
}
func (p *Packets) Key(key string) PacketInfo {
return p.cu[key]
}
func (p *Packets) SetComment(key, comment string) {
p.Lock()
c := p.cu[key]
c.comment = comment
p.cu[key] = c
p.Unlock()
}
func (p *PacketInfo) SetComment(comment string) {
p.comment = comment
}
func (p PacketInfo) StateDescript() uint8 {
return p.stateDescript
}
func (p PacketInfo) TcpPayloads() int {
return p.tcpPayloads
}
func (p PacketInfo) FinState() bool {
return p.finState
}
func (p PacketInfo) SynState() bool {
return p.synState
}
func (p PacketInfo) TcpWindow() uint16 {
return p.tcpWindow
}
func (p PacketInfo) TcpAck() uint32 {
return p.tcpAck
}
func (p PacketInfo) TcpSeq() uint32 {
return p.tcpSeq
}
func (p PacketInfo) Packet() gopacket.Packet {
return p.packet
}
func (p PacketInfo) Comment() string {
return p.comment
}
func NewPackets() *Packets {
return &Packets{
cu: make(map[string]PacketInfo),
}
}
func (p *Packets) ParsePacket(packet gopacket.Packet, opts ...interface{}) (PacketInfo, error) {
var info PacketInfo
//firstOpts is macaddr(nfqueue)
if ew := packet.Layer(layers.LayerTypeEthernet); ew != nil {
eth := ew.(*layers.Ethernet)
info.SrcMac = eth.SrcMAC
info.DstMac = eth.DstMAC
}
for k, v := range opts {
switch tp := v.(type) {
case *[]byte:
if tp != nil && k == 0 {
//nfqueue src mac addr
info.SrcMac = net.HardwareAddr(*tp)
}
}
}
if nw := packet.NetworkLayer(); nw != nil {
srcp, dstp := nw.NetworkFlow().Endpoints()
info.SrcIP = srcp.String()
info.DstIP = dstp.String()
} else {
return info, fmt.Errorf("Failed to parse packet,not a valid network info")
}
{
//tcp valid
layer := packet.Layer(layers.LayerTypeTCP)
if layer != nil {
if tcp, ok := layer.(*layers.TCP); ok {
return p.parseTcp(info, packet, layer, tcp)
}
}
}
{
layer := packet.Layer(layers.LayerTypeUDP)
if layer != nil {
if udp, ok := layer.(*layers.UDP); ok {
return p.parseUdp(info, packet, layer, udp)
}
}
}
//icmp
return info, fmt.Errorf("not support packet")
}
func (p *Packets) parseTcp(info PacketInfo, packet gopacket.Packet, layer gopacket.Layer, tcp *layers.TCP) (PacketInfo, error) {
info.Key = fmt.Sprintf("tcp://%s:%d=%s:%d", info.SrcIP, tcp.SrcPort, info.DstIP, tcp.DstPort)
info.ReverseKey = fmt.Sprintf("tcp://%s:%d=%s:%d", info.DstIP, tcp.DstPort, info.SrcIP, tcp.SrcPort)
info.Type = "tcp"
info.SrcPort = fmt.Sprintf("%d", tcp.SrcPort)
info.DstPort = fmt.Sprintf("%d", tcp.DstPort)
info.packet = packet
info.tcpSeq = tcp.Seq
info.tcpAck = tcp.Ack
info.tcpPayloads = len(layer.LayerPayload())
info.finState = tcp.FIN
info.synState = tcp.SYN
info.tcpWindow = tcp.Window
lastPacket := p.cu[info.Key]
if lastPacket.Key != info.Key {
lastPacket = PacketInfo{
Key: info.Key,
ReverseKey: info.ReverseKey,
Type: "tcp",
SrcIP: info.SrcIP,
SrcPort: info.SrcPort,
DstIP: info.DstIP,
DstPort: info.DstPort,
comment: "",
packet: packet,
tcpSeq: tcp.Seq,
tcpAck: tcp.Ack,
tcpWindow: tcp.Window,
tcpPayloads: len(layer.LayerPayload()),
finState: tcp.FIN,
synState: tcp.SYN,
isFirst: true,
stateDescript: 0,
}
p.Lock()
p.cu[info.Key] = lastPacket
p.Unlock()
}
lastReverse := p.cu[info.ReverseKey]
if !lastPacket.isFirst {
info.comment = lastPacket.comment
if lastPacket.SrcMac != nil && len(info.SrcMac) == 0 {
info.SrcMac = lastPacket.SrcMac
}
if lastPacket.SrcMac != nil && len(info.SrcMac) == 0 {
info.SrcMac = lastPacket.SrcMac
}
}
if lastReverse.SrcMac != nil && len(info.DstMac) == 0 {
info.DstMac = lastReverse.SrcMac
}
{
//state judge
if tcp.RST {
info.stateDescript = 13
p.Lock()
delete(p.cu, info.Key)
delete(p.cu, info.ReverseKey)
p.Unlock()
return info, nil
}
if tcp.SYN && !tcp.ACK {
info.stateDescript = 1
} else if tcp.SYN && tcp.ACK {
info.stateDescript = 2
} else if tcp.ACK {
if !tcp.FIN {
if lastReverse.tcpSeq+1 == tcp.Ack && lastReverse.stateDescript == 2 {
info.stateDescript = 3
} else if tcp.CWR {
info.stateDescript = 12
} else if tcp.ECE {
info.stateDescript = 11
}
if info.stateDescript != 0 {
goto savereturn
}
if info.tcpSeq == lastReverse.tcpAck-1 && info.tcpSeq == lastPacket.tcpSeq+uint32(lastPacket.tcpPayloads)-1 {
//keepalive
info.stateDescript = 14
goto savereturn
}
if !lastPacket.isFirst {
if info.tcpSeq < lastPacket.tcpSeq+uint32(lastPacket.tcpPayloads) {
//retransmit
info.stateDescript = 10
goto savereturn
}
}
if lastReverse.finState && lastPacket.finState {
info.stateDescript = 8
p.Lock()
delete(p.cu, info.Key)
delete(p.cu, info.ReverseKey)
p.Unlock()
return info, nil
}
if lastReverse.finState && lastReverse.tcpSeq+1 == info.tcpAck {
info.stateDescript = 5
goto savereturn
}
info.stateDescript = 9
} else {
if !lastReverse.finState {
info.stateDescript = 4
} else {
if lastReverse.finState && lastReverse.tcpSeq+1 == info.tcpAck && lastPacket.tcpAck == info.tcpAck && lastPacket.tcpSeq == info.tcpSeq {
info.stateDescript = 7
} else {
info.stateDescript = 6
}
}
}
}
}
savereturn:
p.Lock()
p.cu[info.Key] = info
p.Unlock()
return info, nil
}
func (p *Packets) parseUdp(info PacketInfo, packet gopacket.Packet, layer gopacket.Layer, udp *layers.UDP) (PacketInfo, error) {
info.Key = fmt.Sprintf("udp://%s:%d=%s:%d", info.SrcIP, udp.SrcPort, info.DstIP, udp.DstPort)
info.ReverseKey = fmt.Sprintf("udp://%s:%d=%s:%d", info.DstIP, udp.DstPort, info.SrcIP, udp.SrcPort)
info.Type = "udp"
info.SrcPort = fmt.Sprintf("%d", udp.SrcPort)
info.DstPort = fmt.Sprintf("%d", udp.DstPort)
info.packet = packet
info.tcpPayloads = len(layer.LayerPayload())
p.Lock()
p.cu[info.Key] = info
p.Unlock()
return info, nil
}

@ -0,0 +1,18 @@
module b612.me/bcap
go 1.22.4
require (
github.com/florianl/go-nfqueue/v2 v2.0.0
github.com/google/gopacket v1.1.19
)
require (
github.com/google/go-cmp v0.6.0 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.20.0 // indirect
)

@ -0,0 +1,30 @@
github.com/florianl/go-nfqueue/v2 v2.0.0 h1:NTCxS9b0GSbHkWv1a7oOvZn679fsyDkaSkRvOYpQ9Oo=
github.com/florianl/go-nfqueue/v2 v2.0.0/go.mod h1:M2tBLIj62QpwqjwV0qfcjqGOqP3qiTuXr2uSRBXH9Qk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

@ -0,0 +1,77 @@
package libpcap
import (
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
)
type NetCatch struct {
eth string
host string
sentence string
fn func(gopacket.Packet)
*pcap.Handle
}
func (n *NetCatch) SetRecall(fn func(p gopacket.Packet)) {
n.fn = fn
}
func FindAllDevs() ([]pcap.Interface, error) {
return pcap.FindAllDevs()
}
func NewCatch(host string, sentence string) (*NetCatch, error) {
ifs, err := pcap.FindAllDevs()
if err != nil {
return nil, err
}
eth := ""
for _, v := range ifs {
if host == "127.0.0.1" && v.Name == "\\Device\\NPF_Loopback" {
eth = v.Name
}
for _, addr := range v.Addresses {
if addr.IP.String() == host {
eth = v.Name
}
}
}
if len(eth) == 0 {
return nil, fmt.Errorf("cannot found eth")
}
nc := new(NetCatch)
nc.host = host
nc.eth = eth
nc.sentence = sentence
return nc, nil
}
func NewCatchEth(eth string, sentence string) (*NetCatch, error) {
nc := new(NetCatch)
nc.eth = eth
nc.sentence = sentence
return nc, nil
}
func (n *NetCatch) Run() error {
if n.eth == "" {
return fmt.Errorf("no pcap device")
}
handle, err := pcap.OpenLive(n.eth, 65535, true, pcap.BlockForever)
if err != nil {
return err
}
n.Handle = handle
if err = handle.SetBPFFilter(n.sentence); err != nil {
return err
}
pks := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range pks.Packets() {
if n.fn != nil {
n.fn(packet)
}
}
return nil
}

@ -0,0 +1,85 @@
package nfq
import (
"context"
"fmt"
"github.com/florianl/go-nfqueue/v2"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"time"
)
type NfQueue struct {
queid uint16
maxqueue uint32
ctx context.Context
stopFn context.CancelFunc
recallFn func(id uint32, q *nfqueue.Nfqueue, p Packet)
}
type Packet struct {
Packet gopacket.Packet
Attr nfqueue.Attribute
}
func NewNfQueue(ctx context.Context, queid uint16, maxqueue uint32) *NfQueue {
var q = new(NfQueue)
if ctx == nil {
q.ctx, q.stopFn = context.WithCancel(context.Background())
} else {
q.ctx, q.stopFn = context.WithCancel(ctx)
}
q.queid = queid
q.maxqueue = maxqueue
return q
}
func (n *NfQueue) SetRecall(fn func(id uint32, q *nfqueue.Nfqueue, p Packet)) {
n.recallFn = fn
}
func (n *NfQueue) Run() error {
cfg := nfqueue.Config{
NfQueue: n.queid,
MaxQueueLen: n.maxqueue,
Copymode: nfqueue.NfQnlCopyPacket,
WriteTimeout: time.Second * 10,
}
nfq, err := nfqueue.Open(&cfg)
if err != nil {
return fmt.Errorf("failed to open nfqueue, err:", err)
}
if err := nfq.RegisterWithErrorFunc(n.ctx, func(a nfqueue.Attribute) int {
return n.handlePacket(nfq, a)
}, func(e error) int {
return 0
}); err != nil {
return fmt.Errorf("failed to register handlers, err:", err)
}
<-n.ctx.Done()
return nil
}
func (n *NfQueue) handlePacket(q *nfqueue.Nfqueue, a nfqueue.Attribute) int {
if a.Payload != nil && len(*a.Payload) != 0 {
var packet gopacket.Packet
data := *a.Payload
if data[0]&0xf0 == 0x40 {
packet = gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.DecodeOptions{Lazy: true, NoCopy: true})
} else {
packet = gopacket.NewPacket(data, layers.LayerTypeIPv6, gopacket.DecodeOptions{Lazy: true, NoCopy: true})
}
if n.recallFn != nil {
n.recallFn(*a.PacketID, q, Packet{
Packet: packet,
Attr: a,
})
} else {
q.SetVerdict(*a.PacketID, nfqueue.NfAccept)
}
return 0
}
q.SetVerdict(*a.PacketID, nfqueue.NfAccept)
return 0
}
Loading…
Cancel
Save