|
|
|
@ -7,6 +7,7 @@ import (
|
|
|
|
|
"b612.me/bcap/nfq"
|
|
|
|
|
"b612.me/stario"
|
|
|
|
|
"b612.me/starlog"
|
|
|
|
|
"b612.me/starmap"
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"fmt"
|
|
|
|
@ -63,18 +64,21 @@ type NfCap struct {
|
|
|
|
|
// 触发封禁词后,使用RST重置链路
|
|
|
|
|
useRST bool
|
|
|
|
|
// RST模式,target=目标端单向RST,reverse=对端反向RST,both=双向RST
|
|
|
|
|
rstMode string
|
|
|
|
|
fastMode bool //自探测
|
|
|
|
|
printColor []*starlog.Color
|
|
|
|
|
logCache chan loged
|
|
|
|
|
monitorPort int
|
|
|
|
|
cache []string
|
|
|
|
|
NFQNums int
|
|
|
|
|
ctx context.Context
|
|
|
|
|
fn context.CancelFunc
|
|
|
|
|
requests chan *handler
|
|
|
|
|
allowRandomAck bool
|
|
|
|
|
onlyDropblackwordPacket bool
|
|
|
|
|
rstMode string
|
|
|
|
|
fastMode bool //自探测
|
|
|
|
|
printColor []*starlog.Color
|
|
|
|
|
logCache chan loged
|
|
|
|
|
monitorPort int
|
|
|
|
|
cache []string
|
|
|
|
|
NFQNums int
|
|
|
|
|
ctx context.Context
|
|
|
|
|
fn context.CancelFunc
|
|
|
|
|
requests chan *handler
|
|
|
|
|
allowRandomAck bool
|
|
|
|
|
singlePacketMode bool
|
|
|
|
|
cuePktMethod []string
|
|
|
|
|
CapFileCacheNum int
|
|
|
|
|
Flags []string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type loged struct {
|
|
|
|
@ -205,7 +209,7 @@ func (t *NfCap) Run() error {
|
|
|
|
|
|
|
|
|
|
func NewNfCap() *NfCap {
|
|
|
|
|
var nf = new(NfCap)
|
|
|
|
|
nf.packetCache = make(chan gopacket.Packet, 2048)
|
|
|
|
|
nf.packetCache = make(chan gopacket.Packet, 8192)
|
|
|
|
|
nf.logCache = make(chan loged, 2048)
|
|
|
|
|
nf.printColor = []*starlog.Color{
|
|
|
|
|
starlog.NewColor(starlog.FgWhite), //0=unknown
|
|
|
|
@ -233,7 +237,7 @@ func NewNfCap() *NfCap {
|
|
|
|
|
}
|
|
|
|
|
nf.cap = bcap.NewPackets()
|
|
|
|
|
nf.ctx, nf.fn = context.WithCancel(context.Background())
|
|
|
|
|
nf.requests = make(chan *handler, 2048)
|
|
|
|
|
nf.requests = make(chan *handler, 8192)
|
|
|
|
|
return nf
|
|
|
|
|
}
|
|
|
|
|
func (t *NfCap) handleNfResult() {
|
|
|
|
@ -246,8 +250,10 @@ func (t *NfCap) handleNfResult() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if t.allowRandomAck {
|
|
|
|
|
go info.p.SetVerdict(info.id, <-info.fin)
|
|
|
|
|
continue
|
|
|
|
|
go func() {
|
|
|
|
|
info.p.SetVerdict(info.id, <-info.fin)
|
|
|
|
|
}()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
info.p.SetVerdict(info.id, <-info.fin)
|
|
|
|
|
}
|
|
|
|
@ -344,6 +350,21 @@ func (n *NfCap) strInRange(str string) bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NfCap) strInRangeIdx(str string) int {
|
|
|
|
|
if len(n.targetCmd) == 0 {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
for k, v := range n.targetCmd {
|
|
|
|
|
if v == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if strings.Contains(str, v) {
|
|
|
|
|
return k
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NfCap) handlePacket(info bcap.PacketInfo, nfp nfq.Packet) int {
|
|
|
|
|
p := nfp.Packet
|
|
|
|
|
n.count++
|
|
|
|
@ -427,6 +448,9 @@ func (n *NfCap) handlePacket(info bcap.PacketInfo, nfp nfq.Packet) int {
|
|
|
|
|
case 14:
|
|
|
|
|
dec = "TCP Keepalive"
|
|
|
|
|
}
|
|
|
|
|
if len(n.Flags) > 0 && !n.writerMatch(p, true) {
|
|
|
|
|
return nfqueue.NfAccept
|
|
|
|
|
}
|
|
|
|
|
if n.showAll || n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP) {
|
|
|
|
|
n.logCache <- loged{
|
|
|
|
|
str: fmt.Sprintf("%s %v:%v -> %v:%v %s seq=%v ack=%v win=%v len=%v\n", time.Now().Format("2006-01-02 15:04:05.000000"), info.SrcIP, info.SrcPort,
|
|
|
|
@ -451,6 +475,21 @@ func (n *NfCap) handlePacket(info bcap.PacketInfo, nfp nfq.Packet) int {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if res := n.cuePacket(info, layer); res != -1 {
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
if shouldDisallow {
|
|
|
|
|
n.logCache <- loged{
|
|
|
|
|
str: fmt.Sprintf("Block(loss) TCP %v:%v -> %v:%v,LEN=%d\n", info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpPayloads()),
|
|
|
|
|
stateLevel: 0,
|
|
|
|
|
logLevel: "warning",
|
|
|
|
|
}
|
|
|
|
|
return nfqueue.NfDrop
|
|
|
|
|
}
|
|
|
|
|
return nfqueue.NfAccept
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NfCap) cuePacket(info bcap.PacketInfo, layer gopacket.Layer) int {
|
|
|
|
|
if info.Comment() != "" || n.cap.Key(info.ReverseKey).Comment() != "" {
|
|
|
|
|
tmp := info.Comment()
|
|
|
|
|
if tmp == "" {
|
|
|
|
@ -478,39 +517,32 @@ func (n *NfCap) handlePacket(info bcap.PacketInfo, nfp nfq.Packet) int {
|
|
|
|
|
n.cap.SetComment(info.ReverseKey, strconv.Itoa(pkg-1))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(n.targetCmd) > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) && n.strInRange(string(layer.LayerPayload())) {
|
|
|
|
|
n.logCache <- loged{
|
|
|
|
|
str: fmt.Sprintf("%s:%s -> %s:%s !!Match Keyword,will block!!\n", info.SrcIP, info.SrcPort,
|
|
|
|
|
info.DstIP, info.DstPort),
|
|
|
|
|
stateLevel: 0,
|
|
|
|
|
logLevel: "warning",
|
|
|
|
|
}
|
|
|
|
|
if n.onlyDropblackwordPacket {
|
|
|
|
|
if n.useRST && info.StateDescript() == 13 {
|
|
|
|
|
return nfqueue.NfAccept
|
|
|
|
|
}
|
|
|
|
|
if len(n.targetCmd) > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) {
|
|
|
|
|
if idx := n.strInRangeIdx(string(layer.LayerPayload())); idx >= 0 {
|
|
|
|
|
n.logCache <- loged{
|
|
|
|
|
str: fmt.Sprintf("Block TCP %v:%v -> %v:%v,LEN=%d\n", info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpPayloads()),
|
|
|
|
|
str: fmt.Sprintf("%s:%s -> %s:%s !!Match Keyword,will block!!\n", info.SrcIP, info.SrcPort,
|
|
|
|
|
info.DstIP, info.DstPort),
|
|
|
|
|
stateLevel: 0,
|
|
|
|
|
logLevel: "warning",
|
|
|
|
|
}
|
|
|
|
|
return nfqueue.NfDrop
|
|
|
|
|
}
|
|
|
|
|
if n.packetDelay > 0 && info.Comment() == "" {
|
|
|
|
|
n.cap.SetComment(info.Key, strconv.Itoa(n.packetDelay))
|
|
|
|
|
n.cap.SetComment(info.ReverseKey, strconv.Itoa(n.packetDelay))
|
|
|
|
|
} else {
|
|
|
|
|
if n.useRST {
|
|
|
|
|
RealSendRST(info, n.rstMode, 3)
|
|
|
|
|
if n.singlePacketMode {
|
|
|
|
|
return n.singlePacket(info, idx)
|
|
|
|
|
}
|
|
|
|
|
if n.ipInRange(info.SrcIP) {
|
|
|
|
|
n.blockMap.Store(info.SrcIP, true)
|
|
|
|
|
if n.packetDelay > 0 && info.Comment() == "" {
|
|
|
|
|
n.cap.SetComment(info.Key, strconv.Itoa(n.packetDelay))
|
|
|
|
|
n.cap.SetComment(info.ReverseKey, strconv.Itoa(n.packetDelay))
|
|
|
|
|
} else {
|
|
|
|
|
n.blockMap.Store(info.DstIP, true)
|
|
|
|
|
if n.useRST {
|
|
|
|
|
RealSendRST(info, n.rstMode, 3)
|
|
|
|
|
}
|
|
|
|
|
if n.ipInRange(info.SrcIP) {
|
|
|
|
|
n.blockMap.Store(info.SrcIP, true)
|
|
|
|
|
} else {
|
|
|
|
|
n.blockMap.Store(info.DstIP, true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, ok1 := n.blockMap.Load(info.DstIP)
|
|
|
|
|
_, ok2 := n.blockMap.Load(info.SrcIP)
|
|
|
|
|
if ok1 || ok2 {
|
|
|
|
@ -524,35 +556,176 @@ func (n *NfCap) handlePacket(info bcap.PacketInfo, nfp nfq.Packet) int {
|
|
|
|
|
}
|
|
|
|
|
return nfqueue.NfDrop
|
|
|
|
|
}
|
|
|
|
|
if shouldDisallow {
|
|
|
|
|
n.logCache <- loged{
|
|
|
|
|
str: fmt.Sprintf("Block(loss) TCP %v:%v -> %v:%v,LEN=%d\n", info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpPayloads()),
|
|
|
|
|
stateLevel: 0,
|
|
|
|
|
logLevel: "warning",
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NfCap) singlePacket(info bcap.PacketInfo, idx int) int {
|
|
|
|
|
n.logCache <- loged{
|
|
|
|
|
str: fmt.Sprintf("Handle TCP %v:%v -> %v:%v,LEN=%d\n", info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpPayloads()),
|
|
|
|
|
stateLevel: 0,
|
|
|
|
|
logLevel: "warning",
|
|
|
|
|
}
|
|
|
|
|
if len(n.cuePktMethod) == 0 {
|
|
|
|
|
return nfqueue.NfDrop
|
|
|
|
|
}
|
|
|
|
|
task := n.cuePktMethod[idx]
|
|
|
|
|
for _, v := range strings.Split(task, ";") {
|
|
|
|
|
v = strings.TrimSpace(v)
|
|
|
|
|
tasks := strings.Fields(v)
|
|
|
|
|
switch strings.ToLower(tasks[0]) {
|
|
|
|
|
case "delay":
|
|
|
|
|
if len(tasks) < 2 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
tmp, err := strconv.Atoi(tasks[1])
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("输入延迟无效:%v\n", err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(time.Millisecond * time.Duration(tmp))
|
|
|
|
|
case "drop":
|
|
|
|
|
return nfqueue.NfDrop
|
|
|
|
|
case "allow":
|
|
|
|
|
return nfqueue.NfAccept
|
|
|
|
|
case "reset":
|
|
|
|
|
RealSendRST(info, n.rstMode, 3)
|
|
|
|
|
default:
|
|
|
|
|
starlog.Warningf("未知命令:%s\n", v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nfqueue.NfAccept
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NfCap) pcapWriter(stopCtx context.Context, fp *os.File) error {
|
|
|
|
|
w := pcapgo.NewWriter(fp)
|
|
|
|
|
err := w.WriteFileHeader(65535, layers.LinkTypeRaw)
|
|
|
|
|
err := w.WriteFileHeader(65535, layers.LinkTypeEthernet)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
buf := starmap.NewStarChanStack(uint64(n.CapFileCacheNum))
|
|
|
|
|
avail := 0
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-stopCtx.Done():
|
|
|
|
|
return nil
|
|
|
|
|
case p := <-n.packetCache:
|
|
|
|
|
w.WritePacket(gopacket.CaptureInfo{
|
|
|
|
|
Timestamp: time.Now(),
|
|
|
|
|
CaptureLength: len(p.Data()),
|
|
|
|
|
Length: len(p.Data()),
|
|
|
|
|
}, p.Data())
|
|
|
|
|
if n.CapFileCacheNum == 0 {
|
|
|
|
|
w.WritePacket(p.Metadata().CaptureInfo, p.Data())
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if avail > 0 {
|
|
|
|
|
avail--
|
|
|
|
|
if n.writerMatch(p, false) {
|
|
|
|
|
avail = n.CapFileCacheNum
|
|
|
|
|
}
|
|
|
|
|
w.WritePacket(p.Metadata().CaptureInfo, p.Data())
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if buf.Free() == 0 {
|
|
|
|
|
buf.Pop()
|
|
|
|
|
}
|
|
|
|
|
if !n.writerMatch(p, false) {
|
|
|
|
|
buf.Push(p)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for buf.Len() > 0 {
|
|
|
|
|
hp, err := buf.Pop()
|
|
|
|
|
if err == nil {
|
|
|
|
|
w.WritePacket(hp.(gopacket.Packet).Metadata().CaptureInfo, hp.(gopacket.Packet).Data())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
w.WritePacket(p.Metadata().CaptureInfo, p.Data())
|
|
|
|
|
avail = n.CapFileCacheNum
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NfCap) writerMatch(pkt gopacket.Packet, onlyFlags bool) bool {
|
|
|
|
|
tcpLayer := pkt.Layer(layers.LayerTypeTCP)
|
|
|
|
|
if !onlyFlags {
|
|
|
|
|
var src, dst string
|
|
|
|
|
if nw := pkt.NetworkLayer(); nw != nil {
|
|
|
|
|
srcp, dstp := nw.NetworkFlow().Endpoints()
|
|
|
|
|
src = srcp.String()
|
|
|
|
|
dst = dstp.String()
|
|
|
|
|
}
|
|
|
|
|
if !(n.ipInRange(src) || n.ipInRange(dst)) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if tcpLayer == nil {
|
|
|
|
|
if len(n.targetCmd) != 0 && !n.strInRange(string(pkt.TransportLayer().LayerPayload())) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if len(n.targetCmd) != 0 && !n.strInRange(string(tcpLayer.LayerPayload())) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(n.Flags) == 0 {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if tcpLayer == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
tl := tcpLayer.(*layers.TCP)
|
|
|
|
|
for _, seq := range n.Flags {
|
|
|
|
|
notMatch := false
|
|
|
|
|
bkfor:
|
|
|
|
|
for _, v := range strings.Split(strings.ToUpper(seq), ",") {
|
|
|
|
|
switch strings.TrimSpace(v) {
|
|
|
|
|
case "SYN":
|
|
|
|
|
if !tl.SYN {
|
|
|
|
|
notMatch = true
|
|
|
|
|
break bkfor
|
|
|
|
|
}
|
|
|
|
|
case "ACK":
|
|
|
|
|
if !tl.ACK {
|
|
|
|
|
notMatch = true
|
|
|
|
|
break bkfor
|
|
|
|
|
}
|
|
|
|
|
case "FIN":
|
|
|
|
|
if !tl.FIN {
|
|
|
|
|
notMatch = true
|
|
|
|
|
break bkfor
|
|
|
|
|
}
|
|
|
|
|
case "RST":
|
|
|
|
|
if !tl.RST {
|
|
|
|
|
notMatch = true
|
|
|
|
|
break bkfor
|
|
|
|
|
}
|
|
|
|
|
case "CWR":
|
|
|
|
|
if !tl.CWR {
|
|
|
|
|
notMatch = true
|
|
|
|
|
break bkfor
|
|
|
|
|
}
|
|
|
|
|
case "ECE":
|
|
|
|
|
if !tl.ECE {
|
|
|
|
|
notMatch = true
|
|
|
|
|
break bkfor
|
|
|
|
|
}
|
|
|
|
|
case "NS":
|
|
|
|
|
if !tl.NS {
|
|
|
|
|
notMatch = true
|
|
|
|
|
break bkfor
|
|
|
|
|
}
|
|
|
|
|
case "PSH":
|
|
|
|
|
if !tl.PSH {
|
|
|
|
|
notMatch = true
|
|
|
|
|
break bkfor
|
|
|
|
|
}
|
|
|
|
|
case "URG":
|
|
|
|
|
if !tl.URG {
|
|
|
|
|
notMatch = true
|
|
|
|
|
break bkfor
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !notMatch {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RealSendRST(info bcap.PacketInfo, target string, number int) {
|
|
|
|
|