release: version 1.1.6 - Add local IP filtering and SLL support
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
Features: - Add local_ips configuration option for filtering traffic to local machine - Auto-detection of local IP addresses (excludes loopback 127.x.x.x, ::1) - Support interface 'any' for capturing on all network interfaces - Add Linux SLL (cooked capture) support for interface 'any' - Generate BPF filter with 'dst host' for local IP filtering - Add LinkType field to RawPacket for proper packet parsing Testing: - Add unit tests for local IP detection (detectLocalIPs, extractIP) - Add unit tests for SLL packet parsing (IPv4 and IPv6) - Update capture tests for new packetToRawPacket method Configuration: - Update config.yml.example with local_ips documentation - Update RPM spec to version 1.1.6 with changelog Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@ -443,6 +443,7 @@ func buildRawPacket(t *testing.T, srcIP, dstIP string, srcPort, dstPort uint16,
|
||||
return api.RawPacket{
|
||||
Data: buf.Bytes(),
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
LinkType: 1, // Ethernet
|
||||
}
|
||||
}
|
||||
|
||||
@ -1047,3 +1048,262 @@ func TestProcess_EmptyPacketData(t *testing.T) {
|
||||
t.Error("Process() with empty data should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestProcess_SLLPacket tests parsing of Linux SLL (cooked capture) packets
|
||||
func TestProcess_SLLPacket(t *testing.T) {
|
||||
p := NewParser()
|
||||
if p == nil {
|
||||
t.Fatal("NewParser() returned nil")
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
srcIP := "192.168.1.100"
|
||||
dstIP := "10.0.0.1"
|
||||
srcPort := uint16(54321)
|
||||
dstPort := uint16(443)
|
||||
|
||||
// Create a valid ClientHello payload
|
||||
clientHello := createTLSClientHello(0x0303)
|
||||
|
||||
// Build SLL packet instead of Ethernet
|
||||
pkt := buildSLLRawPacket(t, srcIP, dstIP, srcPort, dstPort, clientHello)
|
||||
|
||||
// Debug: try to parse the packet manually
|
||||
packet := gopacket.NewPacket(pkt.Data, layers.LinkTypeLinuxSLL, gopacket.Default)
|
||||
ipLayer := packet.Layer(layers.LayerTypeIPv4)
|
||||
if ipLayer == nil {
|
||||
t.Logf("DEBUG: SLL packet - no IPv4 layer found")
|
||||
t.Logf("DEBUG: Packet data (first 50 bytes): % x", pkt.Data[:min(50, len(pkt.Data))])
|
||||
t.Logf("DEBUG: Packet layers: %v", packet.Layers())
|
||||
}
|
||||
|
||||
result, err := p.Process(pkt)
|
||||
if err != nil {
|
||||
t.Fatalf("Process() with SLL packet error = %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("Process() with SLL packet should return TLSClientHello")
|
||||
}
|
||||
if result.SrcIP != srcIP {
|
||||
t.Errorf("SrcIP = %v, want %v", result.SrcIP, srcIP)
|
||||
}
|
||||
if result.DstIP != dstIP {
|
||||
t.Errorf("DstIP = %v, want %v", result.DstIP, dstIP)
|
||||
}
|
||||
}
|
||||
|
||||
// TestProcess_SLLPacket_IPv6 tests parsing of Linux SLL IPv6 packets
|
||||
func TestProcess_SLLPacket_IPv6(t *testing.T) {
|
||||
p := NewParser()
|
||||
if p == nil {
|
||||
t.Fatal("NewParser() returned nil")
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
srcIP := "2001:db8::1"
|
||||
dstIP := "2001:db8::2"
|
||||
srcPort := uint16(54321)
|
||||
dstPort := uint16(443)
|
||||
|
||||
// Create a valid ClientHello payload
|
||||
clientHello := createTLSClientHello(0x0303)
|
||||
|
||||
// Build SLL IPv6 packet
|
||||
pkt := buildSLLRawPacketIPv6(t, srcIP, dstIP, srcPort, dstPort, clientHello)
|
||||
|
||||
result, err := p.Process(pkt)
|
||||
if err != nil {
|
||||
t.Fatalf("Process() with SLL IPv6 packet error = %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("Process() with SLL IPv6 packet should return TLSClientHello")
|
||||
}
|
||||
if result.SrcIP != srcIP {
|
||||
t.Errorf("SrcIP = %v, want %v", result.SrcIP, srcIP)
|
||||
}
|
||||
if result.DstIP != dstIP {
|
||||
t.Errorf("DstIP = %v, want %v", result.DstIP, dstIP)
|
||||
}
|
||||
}
|
||||
|
||||
// TestProcess_EthernetFallback tests that Ethernet parsing still works
|
||||
func TestProcess_EthernetFallback(t *testing.T) {
|
||||
p := NewParser()
|
||||
if p == nil {
|
||||
t.Fatal("NewParser() returned nil")
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
srcIP := "192.168.1.100"
|
||||
dstIP := "10.0.0.1"
|
||||
srcPort := uint16(54321)
|
||||
dstPort := uint16(443)
|
||||
|
||||
clientHello := createTLSClientHello(0x0303)
|
||||
|
||||
// Build standard Ethernet packet
|
||||
pkt := buildRawPacket(t, srcIP, dstIP, srcPort, dstPort, clientHello)
|
||||
|
||||
result, err := p.Process(pkt)
|
||||
if err != nil {
|
||||
t.Fatalf("Process() with Ethernet packet error = %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("Process() with Ethernet packet should return TLSClientHello")
|
||||
}
|
||||
}
|
||||
|
||||
// buildSLLRawPacket builds a Linux SLL (cooked capture) packet
|
||||
// Manually constructs SLL header since layers.LinuxSLL doesn't implement SerializableLayer
|
||||
func buildSLLRawPacket(t *testing.T, srcIP, dstIP string, srcPort, dstPort uint16, payload []byte) api.RawPacket {
|
||||
t.Helper()
|
||||
|
||||
// Linux SLL header (16 bytes) - manually constructed
|
||||
// See: https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
|
||||
// Packet type (2 bytes): 0x0000 = PACKET_HOST
|
||||
// Address length (2 bytes): 0x0006 = 6 bytes (MAC)
|
||||
// Address (8 bytes): 00:11:22:33:44:55 + 2 padding bytes
|
||||
// Protocol type (2 bytes): 0x0800 = IPv4
|
||||
sllHeader := make([]byte, 16)
|
||||
sllHeader[0] = 0x00 // Packet type: PACKET_HOST (high byte)
|
||||
sllHeader[1] = 0x00 // Packet type: PACKET_HOST (low byte)
|
||||
sllHeader[2] = 0x00 // Address length (high byte)
|
||||
sllHeader[3] = 0x06 // Address length (low byte) = 6
|
||||
// Address (8 bytes, only 6 used)
|
||||
sllHeader[4] = 0x00
|
||||
sllHeader[5] = 0x11
|
||||
sllHeader[6] = 0x22
|
||||
sllHeader[7] = 0x33
|
||||
sllHeader[8] = 0x44
|
||||
sllHeader[9] = 0x55
|
||||
sllHeader[10] = 0x00 // Padding
|
||||
sllHeader[11] = 0x00 // Padding
|
||||
sllHeader[12] = 0x08 // Protocol type: IPv4 (high byte)
|
||||
sllHeader[13] = 0x00 // Protocol type: IPv4 (low byte)
|
||||
|
||||
ip := &layers.IPv4{
|
||||
Version: 4,
|
||||
TTL: 64,
|
||||
SrcIP: net.ParseIP(srcIP).To4(),
|
||||
DstIP: net.ParseIP(dstIP).To4(),
|
||||
Protocol: layers.IPProtocolTCP,
|
||||
}
|
||||
|
||||
tcp := &layers.TCP{
|
||||
SrcPort: layers.TCPPort(srcPort),
|
||||
DstPort: layers.TCPPort(dstPort),
|
||||
Seq: 1,
|
||||
ACK: true,
|
||||
Window: 65535,
|
||||
}
|
||||
if err := tcp.SetNetworkLayerForChecksum(ip); err != nil {
|
||||
t.Fatalf("SetNetworkLayerForChecksum() error = %v", err)
|
||||
}
|
||||
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{
|
||||
FixLengths: true,
|
||||
ComputeChecksums: true,
|
||||
}
|
||||
|
||||
// Serialize IP + TCP + payload (SLL header is prepended manually)
|
||||
if err := gopacket.SerializeLayers(buf, opts, ip, tcp, gopacket.Payload(payload)); err != nil {
|
||||
t.Fatalf("SerializeLayers() error = %v", err)
|
||||
}
|
||||
|
||||
// Prepend SLL header
|
||||
packetData := append(sllHeader, buf.Bytes()...)
|
||||
|
||||
return api.RawPacket{
|
||||
Data: packetData,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
LinkType: 101, // Linux SLL
|
||||
}
|
||||
}
|
||||
|
||||
// buildSLLRawPacketIPv6 builds a Linux SLL IPv6 packet
|
||||
func buildSLLRawPacketIPv6(t *testing.T, srcIP, dstIP string, srcPort, dstPort uint16, payload []byte) api.RawPacket {
|
||||
t.Helper()
|
||||
|
||||
// Linux SLL header for IPv6
|
||||
// Protocol type: 0x86DD = IPv6
|
||||
sllHeader := make([]byte, 16)
|
||||
sllHeader[0] = 0x00 // Packet type: PACKET_HOST (high byte)
|
||||
sllHeader[1] = 0x00 // Packet type: PACKET_HOST (low byte)
|
||||
sllHeader[2] = 0x00 // Address length (high byte)
|
||||
sllHeader[3] = 0x06 // Address length (low byte) = 6
|
||||
// Address (8 bytes, only 6 used)
|
||||
sllHeader[4] = 0x00
|
||||
sllHeader[5] = 0x11
|
||||
sllHeader[6] = 0x22
|
||||
sllHeader[7] = 0x33
|
||||
sllHeader[8] = 0x44
|
||||
sllHeader[9] = 0x55
|
||||
sllHeader[10] = 0x00 // Padding
|
||||
sllHeader[11] = 0x00 // Padding
|
||||
sllHeader[12] = 0x86 // Protocol type: IPv6 (high byte)
|
||||
sllHeader[13] = 0xDD // Protocol type: IPv6 (low byte)
|
||||
|
||||
ip := &layers.IPv6{
|
||||
Version: 6,
|
||||
HopLimit: 64,
|
||||
SrcIP: net.ParseIP(srcIP).To16(),
|
||||
DstIP: net.ParseIP(dstIP).To16(),
|
||||
NextHeader: layers.IPProtocolTCP,
|
||||
}
|
||||
|
||||
tcp := &layers.TCP{
|
||||
SrcPort: layers.TCPPort(srcPort),
|
||||
DstPort: layers.TCPPort(dstPort),
|
||||
Seq: 1,
|
||||
ACK: true,
|
||||
Window: 65535,
|
||||
}
|
||||
if err := tcp.SetNetworkLayerForChecksum(ip); err != nil {
|
||||
t.Fatalf("SetNetworkLayerForChecksum() error = %v", err)
|
||||
}
|
||||
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{
|
||||
FixLengths: true,
|
||||
ComputeChecksums: true,
|
||||
}
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, ip, tcp, gopacket.Payload(payload)); err != nil {
|
||||
t.Fatalf("SerializeLayers() error = %v", err)
|
||||
}
|
||||
|
||||
// Prepend SLL header
|
||||
packetData := append(sllHeader, buf.Bytes()...)
|
||||
|
||||
return api.RawPacket{
|
||||
Data: packetData,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
LinkType: 101, // Linux SLL
|
||||
}
|
||||
}
|
||||
|
||||
// TestParser_SLLPacketType tests different SLL packet types
|
||||
func TestParser_SLLPacketType(t *testing.T) {
|
||||
// Test that the parser handles SLL packets with different packet types
|
||||
p := NewParser()
|
||||
defer p.Close()
|
||||
|
||||
// PACKET_HOST (0) - packet destined for local host
|
||||
srcIP := "192.168.1.100"
|
||||
dstIP := "10.0.0.1"
|
||||
srcPort := uint16(54321)
|
||||
dstPort := uint16(443)
|
||||
|
||||
clientHello := createTLSClientHello(0x0303)
|
||||
|
||||
pkt := buildSLLRawPacket(t, srcIP, dstIP, srcPort, dstPort, clientHello)
|
||||
|
||||
result, err := p.Process(pkt)
|
||||
if err != nil {
|
||||
t.Fatalf("Process() error = %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("Process() should return TLSClientHello for PACKET_HOST")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user