fix(pcap): add pcap filters
This commit is contained in:
@@ -38,6 +38,10 @@ type Client struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func New(config Config) (*Client, error) {
|
func New(config Config) (*Client, error) {
|
||||||
|
if config.HTTP3 {
|
||||||
|
config.KeepAlive = true
|
||||||
|
}
|
||||||
|
|
||||||
logger.Debug("Creating DoH client: %s:%s%s (KeepAlive: %v)", config.Host, config.Port, config.Path, config.KeepAlive)
|
logger.Debug("Creating DoH client: %s:%s%s (KeepAlive: %v)", config.Host, config.Port, config.Path, config.KeepAlive)
|
||||||
|
|
||||||
if config.Host == "" || config.Port == "" || config.Path == "" {
|
if config.Host == "" || config.Port == "" || config.Path == "" {
|
||||||
@@ -76,10 +80,6 @@ func New(config Config) (*Client, error) {
|
|||||||
if config.KeepAlive {
|
if config.KeepAlive {
|
||||||
transport.MaxIdleConnsPerHost = 10
|
transport.MaxIdleConnsPerHost = 10
|
||||||
transport.MaxConnsPerHost = 10
|
transport.MaxConnsPerHost = 10
|
||||||
} else {
|
|
||||||
transport.MaxIdleConns = 0
|
|
||||||
transport.MaxIdleConnsPerHost = 0
|
|
||||||
transport.DisableKeepAlives = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
@@ -93,11 +93,6 @@ func New(config Config) (*Client, error) {
|
|||||||
TLSClientConfig: tlsConfig,
|
TLSClientConfig: tlsConfig,
|
||||||
AllowHTTP: true,
|
AllowHTTP: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !config.KeepAlive {
|
|
||||||
http2Transport.DisableCompression = true
|
|
||||||
}
|
|
||||||
|
|
||||||
httpClient.Transport = http2Transport
|
httpClient.Transport = http2Transport
|
||||||
transportType = "HTTP/2"
|
transportType = "HTTP/2"
|
||||||
} else if config.HTTP3 {
|
} else if config.HTTP3 {
|
||||||
@@ -106,11 +101,6 @@ func New(config Config) (*Client, error) {
|
|||||||
TLSClientConfig: quicTlsConfig,
|
TLSClientConfig: quicTlsConfig,
|
||||||
QUICConfig: quicConfig,
|
QUICConfig: quicConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !config.KeepAlive {
|
|
||||||
http3Transport.DisableCompression = true
|
|
||||||
}
|
|
||||||
|
|
||||||
httpClient.Transport = http3Transport
|
httpClient.Transport = http3Transport
|
||||||
transportType = "HTTP/3"
|
transportType = "HTTP/3"
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package capture
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
"github.com/google/gopacket"
|
||||||
@@ -19,12 +21,91 @@ type PacketCapture struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPacketCapture(iface, outputPath string) (*PacketCapture, error) {
|
func getLocalIPs() ([]string, error) {
|
||||||
|
var localIPs []string
|
||||||
|
|
||||||
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get network interfaces: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
var ip net.IP
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = v.IP
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = v.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip loopback
|
||||||
|
if ip == nil || ip.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
localIPs = append(localIPs, ip.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(localIPs) == 0 {
|
||||||
|
return nil, fmt.Errorf("no non-loopback IPs found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return localIPs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBPFFilter(protocol string, localIPs []string) string {
|
||||||
|
// Build filter for this machine's IPs
|
||||||
|
var hostFilters []string
|
||||||
|
for _, ip := range localIPs {
|
||||||
|
hostFilters = append(hostFilters, fmt.Sprintf("host %s", ip))
|
||||||
|
}
|
||||||
|
testMachineFilter := "(" + strings.Join(hostFilters, " or ") + ")"
|
||||||
|
|
||||||
|
// Protocol-specific ports
|
||||||
|
var portFilter string
|
||||||
|
switch strings.ToLower(protocol) {
|
||||||
|
case "udp":
|
||||||
|
portFilter = "(port 53)"
|
||||||
|
case "tls", "dot":
|
||||||
|
portFilter = "(port 53 or port 853)"
|
||||||
|
case "https", "doh":
|
||||||
|
portFilter = "(port 53 or port 443)"
|
||||||
|
case "doq":
|
||||||
|
portFilter = "(port 53 or port 853)"
|
||||||
|
case "doh3":
|
||||||
|
portFilter = "(port 53 or port 443)"
|
||||||
|
default:
|
||||||
|
portFilter = "(port 53 or port 443 or port 853)"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude private-to-private traffic (LAN-to-LAN, includes Docker ranges)
|
||||||
|
privateExclude := "not (src net (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and dst net (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16))"
|
||||||
|
|
||||||
|
// Combine: test machine AND protocol ports AND NOT (private to private)
|
||||||
|
return testMachineFilter + " and " + portFilter + " and " + privateExclude
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPacketCapture(iface, outputPath, protocol string) (*PacketCapture, error) {
|
||||||
handle, err := pcap.OpenLive(iface, 65535, true, pcap.BlockForever)
|
handle, err := pcap.OpenLive(iface, 65535, true, pcap.BlockForever)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("pcap open (try running as root): %w", err)
|
return nil, fmt.Errorf("pcap open (try running as root): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get local IPs dynamically
|
||||||
|
localIPs, err := getLocalIPs()
|
||||||
|
if err != nil {
|
||||||
|
handle.Close()
|
||||||
|
return nil, fmt.Errorf("failed to get local IPs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build and apply BPF filter
|
||||||
|
bpfFilter := buildBPFFilter(protocol, localIPs)
|
||||||
|
|
||||||
|
if err := handle.SetBPFFilter(bpfFilter); err != nil {
|
||||||
|
handle.Close()
|
||||||
|
return nil, fmt.Errorf("failed to set BPF filter '%s': %w", bpfFilter, err)
|
||||||
|
}
|
||||||
|
|
||||||
file, err := os.Create(outputPath)
|
file, err := os.Create(outputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handle.Close()
|
handle.Close()
|
||||||
|
|||||||
@@ -94,7 +94,10 @@ func (r *MeasurementRunner) runMeasurement(upstream string, domains []string, qT
|
|||||||
fmt.Printf(">>> Measuring %s (dnssec=%v, auth=%v%s) → %s\n", upstream, r.config.DNSSEC, r.config.AuthoritativeDNSSEC, keepAliveStr, relPath)
|
fmt.Printf(">>> Measuring %s (dnssec=%v, auth=%v%s) → %s\n", upstream, r.config.DNSSEC, r.config.AuthoritativeDNSSEC, keepAliveStr, relPath)
|
||||||
|
|
||||||
// Setup packet capture
|
// Setup packet capture
|
||||||
packetCapture, err := capture.NewPacketCapture(r.config.Interface, pcapPath)
|
proto := DetectProtocol(upstream)
|
||||||
|
|
||||||
|
// Setup packet capture with protocol-aware filtering
|
||||||
|
packetCapture, err := capture.NewPacketCapture(r.config.Interface, pcapPath, proto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -107,6 +110,7 @@ func (r *MeasurementRunner) runMeasurement(upstream string, domains []string, qT
|
|||||||
}
|
}
|
||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
// Run measurements
|
// Run measurements
|
||||||
return r.runQueries(dnsClient, upstream, domains, qType, writer, packetCapture)
|
return r.runQueries(dnsClient, upstream, domains, qType, writer, packetCapture)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user