|
|
//go:build !windows
|
|
|
|
|
|
package tcm
|
|
|
|
|
|
import (
|
|
|
"b612.me/bcap"
|
|
|
"b612.me/bcap/nfq"
|
|
|
"b612.me/stario"
|
|
|
"b612.me/starlog"
|
|
|
"b612.me/starmap"
|
|
|
"context"
|
|
|
"encoding/hex"
|
|
|
"fmt"
|
|
|
"github.com/florianl/go-nfqueue/v2"
|
|
|
"github.com/google/gopacket"
|
|
|
"github.com/google/gopacket/layers"
|
|
|
"github.com/google/gopacket/pcapgo"
|
|
|
"math/rand"
|
|
|
"net"
|
|
|
"os"
|
|
|
"os/exec"
|
|
|
"os/signal"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
"syscall"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
type NfCap struct {
|
|
|
count uint64
|
|
|
// 按连接数最后一个历史报文
|
|
|
cap *bcap.Packets
|
|
|
// 监控目标ip地址列表
|
|
|
target []string
|
|
|
// 将军,下达命令吧!
|
|
|
// 监控命令列表
|
|
|
targetCmd []string
|
|
|
targetAsHex bool
|
|
|
//保存为pcap格式的数据文件
|
|
|
saveFile string
|
|
|
// 写缓存
|
|
|
packetCache chan gopacket.Packet
|
|
|
//保存封禁的map
|
|
|
blockMap sync.Map
|
|
|
//交互模式
|
|
|
interactive bool
|
|
|
//展示所有报文信息,包括未在追踪列表的
|
|
|
showAll bool
|
|
|
//展示payload报文(utf-8直接输出)
|
|
|
showPayload bool
|
|
|
//payload展示为hex
|
|
|
showAsHex bool
|
|
|
//payload允许展示的最大字符
|
|
|
maxShowPayloadSize int
|
|
|
//不展示任何信息
|
|
|
noShowMode bool
|
|
|
// 全链路丢包率,百分比
|
|
|
loss float64
|
|
|
// 报文延迟抵达时间,毫秒
|
|
|
delay int
|
|
|
// 触发封禁词后,再过N个包再封禁
|
|
|
packetDelay int
|
|
|
// 触发封禁词后,使用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
|
|
|
singlePacketMode bool
|
|
|
cuePktMethod []string
|
|
|
CapFileCacheNum int
|
|
|
Flags []string
|
|
|
}
|
|
|
|
|
|
type loged struct {
|
|
|
str string
|
|
|
stateLevel int
|
|
|
logLevel string
|
|
|
}
|
|
|
|
|
|
func (n *NfCap) inactve() {
|
|
|
for {
|
|
|
f := strings.Fields(stario.MessageBox("", "").MustString())
|
|
|
if len(f) < 2 {
|
|
|
continue
|
|
|
}
|
|
|
switch f[0] {
|
|
|
case "allow":
|
|
|
for _, v := range f[1:] {
|
|
|
n.blockMap.Delete(v)
|
|
|
starlog.Infof("允许%s报文\n", v)
|
|
|
}
|
|
|
case "drop":
|
|
|
for _, v := range f[1:] {
|
|
|
n.blockMap.Store(v, true)
|
|
|
starlog.Infof("封禁%s报文\n", v)
|
|
|
}
|
|
|
case "delay":
|
|
|
tmp, err := strconv.Atoi(f[1])
|
|
|
if err != nil {
|
|
|
starlog.Errorln("输入延迟无效:%v\n", err)
|
|
|
continue
|
|
|
}
|
|
|
n.delay = tmp
|
|
|
starlog.Infof("延迟生效:%v ms\n", n.delay)
|
|
|
case "loss":
|
|
|
tmp, err := strconv.ParseFloat(f[1], 64)
|
|
|
if err != nil {
|
|
|
starlog.Errorln("输入丢包百分比无效:%v\n", err)
|
|
|
continue
|
|
|
}
|
|
|
n.loss = tmp
|
|
|
starlog.Infof("丢包百分比生效:%v ms\n", n.loss)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (t *NfCap) doIptables() error {
|
|
|
if t.monitorPort != 0 {
|
|
|
if _, err := exec.Command("iptables", "-t", "raw", "-A", "PREROUTING", "-p", "tcp", "--dport", strconv.Itoa(t.monitorPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
t.cache = append(t.cache, "-t raw -D PREROUTING -p tcp --dport "+strconv.Itoa(t.monitorPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums))
|
|
|
if _, err := exec.Command("iptables", "-t", "raw", "-A", "OUTPUT", "-p", "tcp", "--sport", strconv.Itoa(t.monitorPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
t.cache = append(t.cache, "-t raw -D OUTPUT -p tcp --sport "+strconv.Itoa(t.monitorPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums))
|
|
|
if _, err := exec.Command("iptables", "-t", "raw", "-A", "PREROUTING", "-p", "tcp", "--sport", strconv.Itoa(t.monitorPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
t.cache = append(t.cache, "-t raw -D PREROUTING -p tcp --sport "+strconv.Itoa(t.monitorPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums))
|
|
|
if _, err := exec.Command("iptables", "-t", "raw", "-A", "OUTPUT", "-p", "tcp", "--dport", strconv.Itoa(t.monitorPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
t.cache = append(t.cache, "-t raw -D OUTPUT -p tcp --dport "+strconv.Itoa(t.monitorPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums))
|
|
|
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func (t *NfCap) undoIptables() {
|
|
|
for _, cmd := range t.cache {
|
|
|
exec.Command("iptables", strings.Fields(cmd)...).CombinedOutput()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (t *NfCap) Run() error {
|
|
|
stopSignal := make(chan os.Signal)
|
|
|
starlog.Noticef("Starting nfqueue capture\n")
|
|
|
nf := nfq.NewNfQueue(t.ctx, uint16(t.NFQNums), 65535)
|
|
|
nf.SetRecall(t.handleRoute)
|
|
|
if t.targetAsHex {
|
|
|
for k, v := range t.targetCmd {
|
|
|
tmp, _ := hex.DecodeString(v)
|
|
|
t.targetCmd[k] = string(tmp)
|
|
|
}
|
|
|
}
|
|
|
go func() {
|
|
|
if err := nf.Run(); err != nil {
|
|
|
starlog.Errorln(err)
|
|
|
stopSignal <- syscall.SIGKILL
|
|
|
}
|
|
|
}()
|
|
|
if t.saveFile != "" {
|
|
|
f, err := os.Create(t.saveFile)
|
|
|
if err != nil {
|
|
|
starlog.Errorln("创建写入文件失败", err)
|
|
|
os.Exit(4)
|
|
|
}
|
|
|
defer f.Close()
|
|
|
go t.pcapWriter(t.ctx, f)
|
|
|
}
|
|
|
if t.monitorPort != 0 {
|
|
|
if _, err := exec.Command("iptables", "-V").CombinedOutput(); err != nil {
|
|
|
starlog.Warningln("iptables not found, cannot auto set iptables")
|
|
|
return fmt.Errorf("iptables not found")
|
|
|
}
|
|
|
defer t.undoIptables()
|
|
|
if err := t.doIptables(); err != nil {
|
|
|
starlog.Errorf("Failed to set iptables:%v\n", err)
|
|
|
return err
|
|
|
}
|
|
|
starlog.Infof("Set iptables success\n")
|
|
|
}
|
|
|
signal.Notify(stopSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
|
|
|
go t.handleNfResult()
|
|
|
go t.logPrint(t.ctx)
|
|
|
select {
|
|
|
//todo need nf.Stop()
|
|
|
case <-stopSignal:
|
|
|
starlog.Warningf("Received stop signal\n")
|
|
|
|
|
|
case <-t.ctx.Done():
|
|
|
starlog.Infoln("TCPMonitor Task Finished")
|
|
|
|
|
|
}
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
func NewNfCap() *NfCap {
|
|
|
var nf = new(NfCap)
|
|
|
nf.packetCache = make(chan gopacket.Packet, 8192)
|
|
|
nf.logCache = make(chan loged, 2048)
|
|
|
nf.printColor = []*starlog.Color{
|
|
|
starlog.NewColor(starlog.FgWhite), //0=unknown
|
|
|
starlog.NewColor(starlog.FgHiCyan), //1=tcp_connect_1,
|
|
|
starlog.NewColor(starlog.FgHiCyan), //2=tcp_connect_2,
|
|
|
starlog.NewColor(starlog.FgHiCyan), //3=tcp_connect_3,
|
|
|
starlog.NewColor(starlog.FgCyan), //4=tcp_bye_bye
|
|
|
starlog.NewColor(starlog.FgCyan), //5=tcp_bye_bye
|
|
|
starlog.NewColor(starlog.FgCyan), //6=tcp_bye_bye
|
|
|
starlog.NewColor(starlog.FgCyan), //7=tcp_bye_bye
|
|
|
starlog.NewColor(starlog.FgCyan), //8=tcp_bye_bye
|
|
|
starlog.NewColor(starlog.FgGreen), //9=tcp_ok
|
|
|
starlog.NewColor(starlog.BgRed, starlog.FgYellow), //10=tcp_retrans
|
|
|
starlog.NewColor(starlog.FgHiMagenta), //ece
|
|
|
starlog.NewColor(starlog.FgHiMagenta), //cwr
|
|
|
starlog.NewColor(starlog.FgRed), //rst
|
|
|
starlog.NewColor(starlog.FgHiGreen), //keepalive
|
|
|
starlog.NewColor(starlog.FgWhite), //0=unknown
|
|
|
starlog.NewColor(starlog.FgWhite), //0=unknown
|
|
|
starlog.NewColor(starlog.FgWhite), //0=unknown
|
|
|
starlog.NewColor(starlog.FgWhite), //0=unknown
|
|
|
starlog.NewColor(starlog.FgWhite), //0=unknown
|
|
|
starlog.NewColor(starlog.FgWhite), //20=udp
|
|
|
starlog.NewColor(starlog.FgWhite), //0=unknown
|
|
|
}
|
|
|
nf.cap = bcap.NewPackets()
|
|
|
nf.ctx, nf.fn = context.WithCancel(context.Background())
|
|
|
nf.requests = make(chan *handler, 8192)
|
|
|
return nf
|
|
|
}
|
|
|
func (t *NfCap) handleNfResult() {
|
|
|
for {
|
|
|
select {
|
|
|
case <-t.ctx.Done():
|
|
|
return
|
|
|
case info := <-t.requests:
|
|
|
if info == nil {
|
|
|
continue
|
|
|
}
|
|
|
if t.allowRandomAck {
|
|
|
go func() {
|
|
|
info.p.SetVerdict(info.id, <-info.fin)
|
|
|
}()
|
|
|
break
|
|
|
}
|
|
|
info.p.SetVerdict(info.id, <-info.fin)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (t *NfCap) handleRoute(id uint32, q *nfqueue.Nfqueue, p nfq.Packet) {
|
|
|
if t.requests == nil {
|
|
|
q.SetVerdict(id, nfqueue.NfAccept)
|
|
|
return
|
|
|
}
|
|
|
info, err := t.cap.ParsePacket(p.Packet)
|
|
|
if err != nil {
|
|
|
q.SetVerdict(id, nfqueue.NfAccept)
|
|
|
return
|
|
|
}
|
|
|
fin := make(chan int)
|
|
|
t.requests <- &handler{
|
|
|
id: id,
|
|
|
p: q,
|
|
|
packet: p.Packet,
|
|
|
attr: p.Attr,
|
|
|
fin: fin,
|
|
|
}
|
|
|
go func() {
|
|
|
fin <- t.handlePacket(info, p)
|
|
|
}()
|
|
|
}
|
|
|
|
|
|
type handler struct {
|
|
|
id uint32
|
|
|
p *nfqueue.Nfqueue
|
|
|
packet gopacket.Packet
|
|
|
attr nfqueue.Attribute
|
|
|
fin chan int
|
|
|
}
|
|
|
|
|
|
func (n *NfCap) logPrint(ctx context.Context) {
|
|
|
for {
|
|
|
select {
|
|
|
case <-ctx.Done():
|
|
|
return
|
|
|
case l := <-n.logCache:
|
|
|
if n.noShowMode {
|
|
|
fmt.Printf("已捕获报文数量:%v个\r", n.count)
|
|
|
continue
|
|
|
}
|
|
|
switch l.logLevel {
|
|
|
case "info":
|
|
|
starlog.Info(l.str)
|
|
|
case "notice":
|
|
|
starlog.Notice(l.str)
|
|
|
case "warning":
|
|
|
starlog.Warning(l.str)
|
|
|
case "error":
|
|
|
starlog.Error(l.str)
|
|
|
case "critical":
|
|
|
starlog.Critical(l.str)
|
|
|
case "debug":
|
|
|
starlog.Debug(l.str)
|
|
|
case "payload":
|
|
|
fmt.Println(l.str)
|
|
|
default:
|
|
|
n.printColor[l.stateLevel].Print(l.str)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (n *NfCap) ipInRange(ip string) bool {
|
|
|
if len(n.target) == 0 {
|
|
|
return false
|
|
|
}
|
|
|
for _, v := range n.target {
|
|
|
if v == ip {
|
|
|
return true
|
|
|
}
|
|
|
}
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
func (n *NfCap) strInRange(str string) bool {
|
|
|
if len(n.targetCmd) == 0 {
|
|
|
return false
|
|
|
}
|
|
|
for _, v := range n.targetCmd {
|
|
|
if v == "" {
|
|
|
continue
|
|
|
}
|
|
|
if strings.Contains(str, v) {
|
|
|
return true
|
|
|
}
|
|
|
}
|
|
|
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++
|
|
|
if n.saveFile != "" {
|
|
|
n.packetCache <- p
|
|
|
}
|
|
|
var layer gopacket.Layer
|
|
|
for _, layerType := range []gopacket.LayerType{
|
|
|
layers.LayerTypeTCP, layers.LayerTypeUDP, layers.LayerTypeICMPv4, layers.LayerTypeICMPv6,
|
|
|
layers.LayerTypeIPv4, layers.LayerTypeIPv6, layers.LayerTypeARP,
|
|
|
} {
|
|
|
layer = p.Layer(layerType)
|
|
|
if layer == nil {
|
|
|
continue
|
|
|
}
|
|
|
break
|
|
|
}
|
|
|
if layer == nil {
|
|
|
n.logCache <- loged{
|
|
|
str: "无法定义layer类型\n",
|
|
|
stateLevel: 0,
|
|
|
logLevel: "error",
|
|
|
}
|
|
|
return nfqueue.NfAccept
|
|
|
}
|
|
|
var shouldDisallow bool
|
|
|
if n.fastMode {
|
|
|
if n.delay > 0 || n.loss > 0 {
|
|
|
if n.delay > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) {
|
|
|
time.Sleep(time.Millisecond * time.Duration(n.delay))
|
|
|
}
|
|
|
if n.loss > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) {
|
|
|
if rand.Intn(10000) < int(n.loss*100) {
|
|
|
shouldDisallow = true
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if shouldDisallow {
|
|
|
return nfqueue.NfDrop
|
|
|
}
|
|
|
return nfqueue.NfAccept
|
|
|
}
|
|
|
if n.loss > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) {
|
|
|
if rand.Intn(10000) < int(n.loss*100) {
|
|
|
shouldDisallow = true
|
|
|
}
|
|
|
}
|
|
|
if n.delay > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) {
|
|
|
time.Sleep(time.Millisecond * time.Duration(n.delay))
|
|
|
}
|
|
|
var dec string
|
|
|
switch info.StateDescript() {
|
|
|
case 0:
|
|
|
dec = "未知状态"
|
|
|
case 1:
|
|
|
dec = "SYN tcp建立一次握手"
|
|
|
case 2:
|
|
|
dec = "SYN,ACK tcp建立二次握手"
|
|
|
case 3:
|
|
|
dec = "ACK tcp建立三次握手"
|
|
|
case 4:
|
|
|
dec = "FIN tcp断开一次挥手"
|
|
|
case 5:
|
|
|
dec = "ACK tcp断开二次挥手"
|
|
|
case 6:
|
|
|
dec = "FIN,ACK tcp断开二次三次挥手"
|
|
|
case 7:
|
|
|
dec = "FIN tcp断开三次挥手"
|
|
|
case 8:
|
|
|
dec = "ACK tcp断开四次挥手"
|
|
|
case 9:
|
|
|
dec = "tcp报文"
|
|
|
case 10:
|
|
|
dec = "TCP重传"
|
|
|
case 11:
|
|
|
dec = "TCP ece"
|
|
|
case 12:
|
|
|
dec = "TCP cwr"
|
|
|
case 13:
|
|
|
dec = "TCP RST重置"
|
|
|
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,
|
|
|
info.DstIP, info.DstPort, dec, info.TcpSeq(), info.TcpAck(), info.TcpWindow(), info.TcpPayloads()),
|
|
|
stateLevel: int(info.StateDescript()),
|
|
|
logLevel: "",
|
|
|
}
|
|
|
if n.showPayload {
|
|
|
str := string(layer.LayerPayload())
|
|
|
if n.maxShowPayloadSize > 0 {
|
|
|
if len(str) > n.maxShowPayloadSize {
|
|
|
str = str[:n.maxShowPayloadSize]
|
|
|
}
|
|
|
}
|
|
|
if n.showAsHex {
|
|
|
str = hex.EncodeToString([]byte(str))
|
|
|
}
|
|
|
n.logCache <- loged{
|
|
|
str: str,
|
|
|
stateLevel: int(info.StateDescript()),
|
|
|
logLevel: "payload",
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
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 == "" {
|
|
|
tmp = n.cap.Key(info.ReverseKey).Comment()
|
|
|
}
|
|
|
pkg, _ := strconv.Atoi(tmp)
|
|
|
n.logCache <- loged{
|
|
|
str: fmt.Sprintf("current delay count:%v\n", pkg-1),
|
|
|
stateLevel: 0,
|
|
|
logLevel: "warning",
|
|
|
}
|
|
|
if pkg-1 <= 0 {
|
|
|
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)
|
|
|
}
|
|
|
n.cap.SetComment(info.Key, "")
|
|
|
n.cap.SetComment(info.ReverseKey, "")
|
|
|
} else {
|
|
|
n.cap.SetComment(info.Key, strconv.Itoa(pkg-1))
|
|
|
n.cap.SetComment(info.ReverseKey, strconv.Itoa(pkg-1))
|
|
|
}
|
|
|
}
|
|
|
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("%s:%s -> %s:%s !!Match Keyword,will block!!\n", info.SrcIP, info.SrcPort,
|
|
|
info.DstIP, info.DstPort),
|
|
|
stateLevel: 0,
|
|
|
logLevel: "warning",
|
|
|
}
|
|
|
if n.singlePacketMode {
|
|
|
return n.singlePacket(info, idx)
|
|
|
}
|
|
|
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.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 {
|
|
|
if n.useRST && info.StateDescript() == 13 {
|
|
|
return nfqueue.NfAccept
|
|
|
}
|
|
|
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()),
|
|
|
stateLevel: 0,
|
|
|
logLevel: "warning",
|
|
|
}
|
|
|
return nfqueue.NfDrop
|
|
|
}
|
|
|
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.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:
|
|
|
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) {
|
|
|
for i := 0; i < number; i++ {
|
|
|
if target == "both" || target == "target" {
|
|
|
SendRST(info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpSeq()+(uint32(i)*uint32(info.TcpWindow())))
|
|
|
}
|
|
|
if target == "both" || target == "reverse" {
|
|
|
SendRST(info.DstIP, info.DstPort, info.SrcIP, info.SrcPort, info.TcpAck()+(uint32(i)*uint32(info.TcpWindow())))
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func SendRST(srcIP, srcPort, dstIP, dstPort string, seq uint32) error {
|
|
|
if net.ParseIP(dstIP).To4() != nil {
|
|
|
return sendIPv4(srcIP, srcPort, dstIP, dstPort, seq, false)
|
|
|
}
|
|
|
return sendIPv6(srcIP, srcPort, dstIP, dstPort, seq, false)
|
|
|
}
|
|
|
|
|
|
func SendSYN(srcIP, srcPort, dstIP, dstPort string, seq uint32) error {
|
|
|
if net.ParseIP(dstIP).To4() != nil {
|
|
|
return sendIPv4(srcIP, srcPort, dstIP, dstPort, seq, true)
|
|
|
}
|
|
|
return sendIPv6(srcIP, srcPort, dstIP, dstPort, seq, true)
|
|
|
}
|
|
|
|
|
|
func sendIPv4(srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error {
|
|
|
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
defer syscall.Close(fd)
|
|
|
|
|
|
dstNetIP := net.ParseIP(dstIP)
|
|
|
iPv4 := layers.IPv4{
|
|
|
SrcIP: net.ParseIP(srcIP),
|
|
|
DstIP: dstNetIP,
|
|
|
Version: 4,
|
|
|
TTL: 64,
|
|
|
Protocol: layers.IPProtocolTCP,
|
|
|
}
|
|
|
sPort, err := strconv.Atoi(srcPort)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
dPort, err := strconv.Atoi(dstPort)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
tcp := layers.TCP{
|
|
|
SrcPort: layers.TCPPort(sPort),
|
|
|
DstPort: layers.TCPPort(dPort),
|
|
|
Seq: seq,
|
|
|
RST: !isSyn,
|
|
|
SYN: isSyn,
|
|
|
}
|
|
|
|
|
|
if err = tcp.SetNetworkLayerForChecksum(&iPv4); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
buffer := gopacket.NewSerializeBuffer()
|
|
|
options := gopacket.SerializeOptions{
|
|
|
FixLengths: true,
|
|
|
ComputeChecksums: true,
|
|
|
}
|
|
|
if err = gopacket.SerializeLayers(buffer, options, &iPv4, &tcp); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
addr := syscall.SockaddrInet4{
|
|
|
Port: dPort,
|
|
|
Addr: [4]byte{dstNetIP.To4()[0], dstNetIP.To4()[1], dstNetIP.To4()[2], dstNetIP.To4()[3]},
|
|
|
}
|
|
|
return syscall.Sendto(fd, buffer.Bytes(), 0, &addr)
|
|
|
}
|
|
|
|
|
|
func sendIPv6(srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error {
|
|
|
fd, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
defer syscall.Close(fd)
|
|
|
|
|
|
dstNetIP := net.ParseIP(dstIP)
|
|
|
iPv6 := layers.IPv6{
|
|
|
SrcIP: net.ParseIP(srcIP),
|
|
|
DstIP: dstNetIP,
|
|
|
Version: 6,
|
|
|
NextHeader: layers.IPProtocolTCP,
|
|
|
HopLimit: 64,
|
|
|
}
|
|
|
sPort, err := strconv.Atoi(srcPort)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
dPort, err := strconv.Atoi(dstPort)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
tcp := layers.TCP{
|
|
|
SrcPort: layers.TCPPort(sPort),
|
|
|
DstPort: layers.TCPPort(dPort),
|
|
|
Seq: seq,
|
|
|
RST: !isSyn,
|
|
|
SYN: isSyn,
|
|
|
}
|
|
|
|
|
|
if err := tcp.SetNetworkLayerForChecksum(&iPv6); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
buffer := gopacket.NewSerializeBuffer()
|
|
|
options := gopacket.SerializeOptions{
|
|
|
FixLengths: true,
|
|
|
ComputeChecksums: true,
|
|
|
}
|
|
|
if err = gopacket.SerializeLayers(buffer, options, &iPv6, &tcp); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
addr := syscall.SockaddrInet6{
|
|
|
Port: dPort,
|
|
|
Addr: [16]byte(dstNetIP.To16()),
|
|
|
}
|
|
|
return syscall.Sendto(fd, buffer.Bytes(), 0, &addr)
|
|
|
}
|