diff --git a/input_raw.go b/input_raw.go index e809c33..5339ae3 100644 --- a/input_raw.go +++ b/input_raw.go @@ -18,6 +18,7 @@ type RAWInput struct { realIPHeader []byte trackResponse bool listener *raw.Listener + bpfFilter string } // Available engines for intercepting traffic @@ -28,7 +29,7 @@ const ( ) // NewRAWInput constructor for RAWInput. Accepts address with port as argument. -func NewRAWInput(address string, engine int, trackResponse bool, expire time.Duration, realIPHeader string) (i *RAWInput) { +func NewRAWInput(address string, engine int, trackResponse bool, expire time.Duration, realIPHeader string, bpfFilter string) (i *RAWInput) { i = new(RAWInput) i.data = make(chan *raw.TCPMessage) i.address = address @@ -80,7 +81,7 @@ func (i *RAWInput) listen(address string) { log.Fatal("input-raw: error while parsing address", err) } - i.listener = raw.NewListener(host, port, i.engine, i.trackResponse, i.expire) + i.listener = raw.NewListener(host, port, i.engine, i.trackResponse, i.expire, i.bpfFilter) ch := i.listener.Receiver() diff --git a/input_raw_test.go b/input_raw_test.go index 30d3425..c11a27c 100644 --- a/input_raw_test.go +++ b/input_raw_test.go @@ -43,7 +43,7 @@ func TestRAWInputIPv4(t *testing.T) { var respCounter, reqCounter int64 - input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "X-Real-IP") + input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "X-Real-IP", "") defer input.Close() output := NewTestOutput(func(data []byte) { @@ -105,7 +105,7 @@ func TestRAWInputNoKeepAlive(t *testing.T) { originAddr := listener.Addr().String() - input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "") + input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "", "") defer input.Close() output := NewTestOutput(func(data []byte) { @@ -151,7 +151,7 @@ func TestRAWInputIPv6(t *testing.T) { var respCounter, reqCounter int64 - input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "") + input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "", "") defer input.Close() output := NewTestOutput(func(data []byte) { @@ -202,7 +202,7 @@ func TestInputRAW100Expect(t *testing.T) { originAddr := strings.Replace(origin.Listener.Addr().String(), "[::]", "127.0.0.1", -1) - input := NewRAWInput(originAddr, EnginePcap, true, time.Second, "") + input := NewRAWInput(originAddr, EnginePcap, true, time.Second, "", "") defer input.Close() // We will use it to get content of raw HTTP request @@ -265,7 +265,7 @@ func TestInputRAWChunkedEncoding(t *testing.T) { })) originAddr := strings.Replace(origin.Listener.Addr().String(), "[::]", "127.0.0.1", -1) - input := NewRAWInput(originAddr, EnginePcap, true, time.Second, "") + input := NewRAWInput(originAddr, EnginePcap, true, time.Second, "", "") defer input.Close() replay := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -329,7 +329,7 @@ func TestInputRAWLargePayload(t *testing.T) { })) originAddr := strings.Replace(origin.Listener.Addr().String(), "[::]", "127.0.0.1", -1) - input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "") + input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "", "") defer input.Close() replay := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { @@ -374,7 +374,7 @@ func BenchmarkRAWInput(b *testing.B) { var respCounter, reqCounter int64 - input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "") + input := NewRAWInput(originAddr, EnginePcap, true, testRawExpire, "", "") defer input.Close() output := NewTestOutput(func(data []byte) { diff --git a/middleware_test.go b/middleware_test.go index 6e65ee2..b135d86 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -117,7 +117,7 @@ func TestEchoMiddleware(t *testing.T) { // Catch traffic from one service fromAddr := strings.Replace(from.Listener.Addr().String(), "[::]", "127.0.0.1", -1) - input := NewRAWInput(fromAddr, EnginePcap, true, testRawExpire, "") + input := NewRAWInput(fromAddr, EnginePcap, true, testRawExpire, "", "") defer input.Close() // And redirect to another @@ -179,7 +179,7 @@ func TestTokenMiddleware(t *testing.T) { fromAddr := strings.Replace(from.Listener.Addr().String(), "[::]", "127.0.0.1", -1) // Catch traffic from one service - input := NewRAWInput(fromAddr, EnginePcap, true, testRawExpire, "") + input := NewRAWInput(fromAddr, EnginePcap, true, testRawExpire, "", "") defer input.Close() // And redirect to another diff --git a/plugins.go b/plugins.go index 3685313..504abb7 100644 --- a/plugins.go +++ b/plugins.go @@ -106,7 +106,7 @@ func InitPlugins() { } for _, options := range Settings.inputRAW { - registerPlugin(NewRAWInput, options, engine, Settings.inputRAWTrackResponse, Settings.inputRAWExpire, Settings.inputRAWRealIPHeader) + registerPlugin(NewRAWInput, options, engine, Settings.inputRAWTrackResponse, Settings.inputRAWExpire, Settings.inputRAWRealIPHeader, Settings.inputRAWBpfFilter) } for _, options := range Settings.inputTCP { diff --git a/raw_socket_listener/listener.go b/raw_socket_listener/listener.go index 63050c1..f32e043 100644 --- a/raw_socket_listener/listener.go +++ b/raw_socket_listener/listener.go @@ -70,6 +70,8 @@ type Listener struct { trackResponse bool messageExpire time.Duration + bpfFilter string + conn net.PacketConn pcapHandles []*pcap.Handle @@ -91,7 +93,7 @@ const ( ) // NewListener creates and initializes new Listener object -func NewListener(addr string, port string, engine int, trackResponse bool, expire time.Duration) (l *Listener) { +func NewListener(addr string, port string, engine int, trackResponse bool, expire time.Duration, bpfFilter string) (l *Listener) { l = &Listener{} l.packetsChan = make(chan *packet, 10000) @@ -105,6 +107,7 @@ func NewListener(addr string, port string, engine int, trackResponse bool, expir l.respAliases = make(map[uint32]*TCPMessage) l.respWithoutReq = make(map[uint32]tcpID) l.trackResponse = trackResponse + l.bpfFilter = bpfFilter l.addr = addr _port, _ := strconv.Atoi(port) @@ -368,6 +371,10 @@ func (t *Listener) readPcap() { bpf = "tcp dst port " + strconv.Itoa(int(t.port)) + " and (" + bpfDstHost + ")" } + if t.bpfFilter != "" { + bpf = t.bpfFilter + } + if err := handle.SetBPFFilter(bpf); err != nil { log.Println("BPF filter error:", err, "Device:", device.Name, bpf) wg.Done() diff --git a/raw_socket_listener/listener_test.go b/raw_socket_listener/listener_test.go index 70271a7..fc0408a 100644 --- a/raw_socket_listener/listener_test.go +++ b/raw_socket_listener/listener_test.go @@ -12,7 +12,7 @@ import ( func TestRawListenerInput(t *testing.T) { var req, resp *TCPMessage - listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond) + listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond, "") defer listener.Close() reqPacket := buildPacket(true, 1, 1, []byte("GET / HTTP/1.1\r\n\r\n"), time.Now()) @@ -49,7 +49,7 @@ func TestRawListenerInput(t *testing.T) { func TestRawListenerInputResponseByClose(t *testing.T) { var req, resp *TCPMessage - listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond) + listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond, "") defer listener.Close() reqPacket := buildPacket(true, 1, 1, []byte("GET / HTTP/1.1\r\n\r\n"), time.Now()) @@ -89,7 +89,7 @@ func TestRawListenerInputResponseByClose(t *testing.T) { func TestRawListenerInputWithoutResponse(t *testing.T) { var req *TCPMessage - listener := NewListener("", "0", EnginePcap, false, 10*time.Millisecond) + listener := NewListener("", "0", EnginePcap, false, 10*time.Millisecond, "") defer listener.Close() reqPacket := buildPacket(true, 1, 1, []byte("GET / HTTP/1.1\r\n\r\n"), time.Now()) @@ -111,7 +111,7 @@ func TestRawListenerInputWithoutResponse(t *testing.T) { func TestRawListenerResponse(t *testing.T) { var req, resp *TCPMessage - listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond) + listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond, "") defer listener.Close() reqPacket := buildPacket(true, 1, 1, []byte("GET / HTTP/1.1\r\n\r\n"), time.Now()) @@ -149,7 +149,7 @@ func TestRawListenerResponse(t *testing.T) { } func TestShort100Continue(t *testing.T) { - listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond) + listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond, "") defer listener.Close() reqPacket1 := buildPacket(true, 1, 1, []byte("POST / HTTP/1.1\r\nContent-Length: 2\r\nExpect: 100-continue\r\n\r\n"), time.Now()) @@ -169,7 +169,7 @@ func TestShort100Continue(t *testing.T) { // Response comes before Request func Test100ContinueWrongOrder(t *testing.T) { - listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond) + listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond, "") defer listener.Close() reqPacket1 := buildPacket(true, 1, 1, []byte("POST / HTTP/1.1\r\nContent-Length: 2\r\nExpect: 100-continue\r\n\r\n"), time.Now()) @@ -188,7 +188,7 @@ func Test100ContinueWrongOrder(t *testing.T) { } func TestAlt100ContinueHeaderOrder(t *testing.T) { - listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond) + listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond, "") defer listener.Close() reqPacket1 := buildPacket(true, 1, 1, []byte("POST / HTTP/1.1\r\nExpect: 100-continue\r\nContent-Length: 2\r\n\r\n"), time.Now()) @@ -349,7 +349,7 @@ func permutation(n int, list []*TCPPacket) []*TCPPacket { // Response comes before Request func TestRawListenerChunkedWrongOrder(t *testing.T) { - listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond) + listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond, "") defer listener.Close() reqPacket1 := buildPacket(true, 1, 1, []byte("POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\nExpect: 100-continue\r\n\r\n"), time.Now()) @@ -427,7 +427,7 @@ func getMessage() []*TCPPacket { // Response comes before Request func TestRawListenerBench(t *testing.T) { - l := NewListener("", "0", EnginePcap, true, 200*time.Millisecond) + l := NewListener("", "0", EnginePcap, true, 200*time.Millisecond, "") defer l.Close() // Should re-construct message from all possible combinations diff --git a/settings.go b/settings.go index a55ca42..d7be435 100644 --- a/settings.go +++ b/settings.go @@ -53,6 +53,7 @@ type AppSettings struct { inputRAWTrackResponse bool inputRAWRealIPHeader string inputRAWExpire time.Duration + inputRAWBpfFilter string middleware string @@ -128,6 +129,8 @@ func init() { flag.DurationVar(&Settings.inputRAWExpire, "input-raw-expire", time.Second*2, "How much it should wait for the last TCP packet, till consider that TCP message complete.") + flag.StringVar(&Settings.inputRAWBpfFilter, "input-raw-bpf-filter", "", "BPF filter to write custom expressions. Can be useful in case of non standard network interfaces like tunneling or SPAN port. Example: --input-raw-bpf-filter 'dst port 80'") + flag.StringVar(&Settings.middleware, "middleware", "", "Used for modifying traffic using external command") // flag.Var(&Settings.inputHTTP, "input-http", "Read requests from HTTP, should be explicitly sent from your application:\n\t# Listen for http on 9000\n\tgor --input-http :9000 --output-http staging.com")