ddosmonit/ddosmonit/ddosmonit.go

273 lines
7.6 KiB
Go

package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"log"
"net"
"regexp"
"strings"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)
var (
deviceMonitor = flag.String("d", "eth0", "Device to monitor")
packetThreshold = flag.Int("c", 5000, "Packets per second threshold to start logging")
excludeList = flag.String("x", "", "Comma-separated list of IPs and ports to exclude")
includeList = flag.String("i", "", "Comma-separated list of IPs and ports to include")
)
const (
ColorReset = "\033[0m"
ColorDarkGrey = "\033[90m"
ColorYellow = "\033[33m"
ColorRed = "\033[31m"
ColorGreen = "\033[32m"
ColorPurple = "\033[35m"
ColorCyan = "\033[36m"
ColorPink = "\033[95m"
)
type PacketInfo struct {
Timestamp string `json:"timestamp"`
Protocol string `json:"protocol"`
SourceIP net.IP `json:"source_ip"`
SourcePort int `json:"source_port"`
DestIP net.IP `json:"dest_ip"`
DestPort int `json:"dest_port"`
Length int `json:"length,omitempty"`
TTL int `json:"ttl,omitempty"`
WindowSize int `json:"window_size,omitempty"`
Checksum int `json:"checksum,omitempty"`
TCPFlags string `json:"tcp_flags,omitempty"`
ICMPData string `json:"icmp_data,omitempty"`
PayloadData string `json:"payload_data,omitempty"`
}
func main() {
flag.Parse()
deviceMAC, err := getInterfaceMAC(*deviceMonitor)
if err != nil {
log.Fatalf("Error getting MAC address of %s: %v", *deviceMonitor, err)
}
excludeIPs, excludePorts := parseAndValidateIPsAndPorts(*excludeList)
includeIPs, includePorts := parseAndValidateIPsAndPorts(*includeList)
snaplen := int32(1600)
promiscuous := false
timeout := pcap.BlockForever
handle, err := pcap.OpenLive(*deviceMonitor, snaplen, promiscuous, timeout)
if err != nil {
log.Fatal(err)
}
defer handle.Close()
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
var totalBytes int64
var packetCount int
startTime := time.Now()
ticker := time.NewTicker(1 * time.Second)
go func() {
for range ticker.C {
elapsed := time.Since(startTime).Seconds()
pps := int(float64(packetCount) / elapsed)
mbps := (float64(totalBytes) / 1e6) / elapsed
fmt.Print("\033[A\033[K") // Move up one line and clear it.
fmt.Println(strings.Repeat(" ", 100)) // Clear the line with 50 spaces (or enough to cover the previous text).
fmt.Print("\033[A") // Move up one line again to overwrite the cleared line.
fmt.Printf("PP/s: %-7d %.2f MB/s\n", pps, mbps)
packetCount = 0
totalBytes = 0
startTime = time.Now()
}
}()
for packet := range packetSource.Packets() {
ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
if ethernetLayer != nil {
ethernet, _ := ethernetLayer.(*layers.Ethernet)
if !bytes.Equal(ethernet.DstMAC, deviceMAC) {
continue
}
}
if shouldProcessPacket(packet, excludeIPs, excludePorts, includeIPs, includePorts) {
ipv4Layer := packet.Layer(layers.LayerTypeIPv4)
tcpLayer := packet.Layer(layers.LayerTypeTCP)
udpLayer := packet.Layer(layers.LayerTypeUDP)
icmpLayer := packet.Layer(layers.LayerTypeICMPv4)
if ipv4Layer != nil && (tcpLayer != nil || udpLayer != nil || icmpLayer != nil) {
fmt.Print("\033[A\033[K")
printPacketInfo(packet)
packetCount++
totalBytes += int64(packet.Metadata().Length)
ppsColor := ""
switch {
case packetCount > *packetThreshold:
ppsColor = ColorRed
case packetCount > *packetThreshold/2:
ppsColor = ColorYellow
default:
ppsColor = ColorReset
}
elapsed := time.Since(startTime).Seconds()
if elapsed > 0 {
pps := int(float64(packetCount) / elapsed)
mbps := (float64(totalBytes) * 8) / 1e6 / elapsed
fmt.Printf("%sPP/s: %-7d %.2f%s MB/s\n", ppsColor, pps, mbps, ColorReset)
}
}
}
}
}
func printPacketInfo(packet gopacket.Packet) {
var srcIP, dstIP net.IP
var srcPort, dstPort int
var length, ttl, winSize, checksum, icmpCode, icmpType int
var protocol, tcpFlags, payloadData, icmpData string
timestamp := time.Now().Format("03:04:05")
ipv4Layer := packet.Layer(layers.LayerTypeIPv4)
if ipv4Layer != nil {
ipv4, _ := ipv4Layer.(*layers.IPv4)
srcIP = ipv4.SrcIP
dstIP = ipv4.DstIP
length = int(ipv4.Length)
ttl = int(ipv4.TTL)
}
tcpLayer := packet.Layer(layers.LayerTypeTCP)
udpLayer := packet.Layer(layers.LayerTypeUDP)
icmpLayer := packet.Layer(layers.LayerTypeICMPv4)
if tcpLayer != nil {
tcp, _ := tcpLayer.(*layers.TCP)
srcPort = int(tcp.SrcPort)
dstPort = int(tcp.DstPort)
protocol = "TCP"
checksum = int(tcp.Checksum)
payloadData = string(tcp.Payload)
tcpFlags = getTCPFlags(tcp)
winSize = int(tcp.Window)
} else if udpLayer != nil {
udp, _ := udpLayer.(*layers.UDP)
srcPort = int(udp.SrcPort)
dstPort = int(udp.DstPort)
protocol = "UDP"
checksum = int(udp.Checksum)
payloadData = string(udp.Payload)
} else if icmpLayer != nil {
icmp, _ := icmpLayer.(*layers.ICMPv4)
protocol = "ICMP"
checksum = int(icmp.Checksum)
payloadData = string(icmp.Payload)
icmpType = int(icmp.TypeCode >> 8)
icmpCode = int(icmp.TypeCode & 0xFF)
icmpData = fmt.Sprintf("%d-%d", icmpType, icmpCode)
}
packetInfo := PacketInfo{
Timestamp: timestamp,
Protocol: protocol,
SourceIP: srcIP,
SourcePort: srcPort,
DestIP: dstIP,
DestPort: dstPort,
Length: length,
TTL: ttl,
WindowSize: winSize,
TCPFlags: tcpFlags,
Checksum: checksum,
PayloadData: payloadData,
ICMPData: icmpData,
}
jsonData, err := json.Marshal(packetInfo)
if err != nil {
fmt.Println("Error marshaling JSON:", err)
return
}
writeToFile(jsonData)
printWithColors(packetInfo)
}
func printWithColors(info PacketInfo) {
payloadDisplay := info.PayloadData
if len(payloadDisplay) != 0 {
if isLikelyPlainText([]byte(payloadDisplay)) {
reg := regexp.MustCompile(`[\s\r\n\v\f]+`)
payloadDisplay = strings.TrimSpace(reg.ReplaceAllString(payloadDisplay, " "))
format := "%sPayload: %s%s%s"
if len(payloadDisplay) > 100 {
payloadDisplay = fmt.Sprintf(format, ColorCyan, ColorPurple, payloadDisplay[:100]+"... "+fmt.Sprintf("%s(%d)%s", ColorDarkGrey, len(payloadDisplay), ColorReset), ColorReset)
} else {
payloadDisplay = fmt.Sprintf(format, ColorCyan, ColorPurple, payloadDisplay, ColorReset)
}
} else {
payloadDisplay = fmt.Sprintf("%sPayload: %sNon-printable data %s(%d)%s", ColorCyan, ColorPurple, ColorDarkGrey, len(payloadDisplay), ColorReset)
}
}
srcPortDisplay := fmt.Sprintf("%d", info.SourcePort)
dstPortDisplay := fmt.Sprintf("%d", info.DestPort)
if info.SourcePort == 0 {
srcPortDisplay = ""
}
if info.DestPort == 0 {
dstPortDisplay = ""
}
protocolColorMap := map[string]string{
"TCP": ColorGreen,
"UDP": ColorYellow,
"ICMP": ColorPurple,
}
protocolColor := protocolColorMap[info.Protocol]
extraData := " "
if info.Protocol == "ICMP" {
extraData = fmt.Sprintf("%3s", info.ICMPData)
} else if info.Protocol == "TCP" && info.TCPFlags != "" {
extraData = info.TCPFlags
}
SEP := ColorDarkGrey + "│" + ColorReset
fmt.Printf("%s %s %s %s %15s %-5s -> %15s %-5s %s %s %5d %s %s %3d %s %s %5d %s %s %5d %s %s %s %s\n",
ColorDarkGrey+info.Timestamp+ColorReset,
SEP,
protocolColor+fmt.Sprintf("%4s", info.Protocol)+ColorReset,
SEP,
info.SourceIP,
srcPortDisplay,
info.DestIP,
dstPortDisplay,
SEP,
ColorCyan+"Len:"+ColorReset, info.Length,
SEP,
ColorCyan+"TTL:"+ColorReset, info.TTL,
SEP,
ColorCyan+"Window:"+ColorReset, info.WindowSize,
SEP,
ColorCyan+"Checksum:"+ColorReset, info.Checksum,
SEP,
extraData,
SEP,
payloadDisplay,
)
}