From 28d3d2498892b4f088797eea2473b2f75347d4dc Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 18 Jul 2023 16:16:41 +0200 Subject: [PATCH] Add and improve InfoOnly and ExpectInfo packet flags --- firewall/interception.go | 11 +++++- .../ebpf/connection_listener/packet.go | 6 ++++ .../windowskext/bandwidth_stats.go | 3 ++ firewall/interception/windowskext/handler.go | 5 +++ firewall/interception/windowskext/kext.go | 6 ++-- firewall/interception/windowskext/packet.go | 13 ++++--- network/connection.go | 36 ++++++++++++++++++- network/packet/packet.go | 6 ++++ 8 files changed, 78 insertions(+), 8 deletions(-) diff --git a/firewall/interception.go b/firewall/interception.go index 775b30b3..5c6c5db8 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -173,9 +173,13 @@ func interceptionStart() error { getConfig() startAPIAuth() - interceptionModule.StartServiceWorker("stat logger", 0, statLogger) interceptionModule.StartServiceWorker("packet handler", 0, packetHandler) + // Start stat logger if logging is set to trace. + if log.GetLogLevel() == log.TraceLevel { + interceptionModule.StartServiceWorker("stat logger", 0, statLogger) + } + return interception.Start() } @@ -543,6 +547,11 @@ func inspectAndVerdictHandler(conn *network.Connection, pkt packet.Packet) { } func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.Verdict, allowPermanent bool) { + // Check if packed was already fast-tracked by the OS integration. + if pkt.FastTrackedByIntegration() { + return + } + // enable permanent verdict if allowPermanent && !conn.VerdictPermanent { conn.VerdictPermanent = permanentVerdicts() diff --git a/firewall/interception/ebpf/connection_listener/packet.go b/firewall/interception/ebpf/connection_listener/packet.go index fffd5558..19895646 100644 --- a/firewall/interception/ebpf/connection_listener/packet.go +++ b/firewall/interception/ebpf/connection_listener/packet.go @@ -13,6 +13,12 @@ type infoPacket struct { pmpacket.Base } +// InfoOnly returns whether the packet is informational only and does not +// represent an actual packet. +func (pkt *infoPacket) InfoOnly() bool { + return true +} + // LoadPacketData does nothing on Linux, as data is always fully parsed. func (pkt *infoPacket) LoadPacketData() error { return fmt.Errorf("can't load data in info only packet") diff --git a/firewall/interception/windowskext/bandwidth_stats.go b/firewall/interception/windowskext/bandwidth_stats.go index d10a66f5..b013623c 100644 --- a/firewall/interception/windowskext/bandwidth_stats.go +++ b/firewall/interception/windowskext/bandwidth_stats.go @@ -1,3 +1,6 @@ +//go:build windows +// +build windows + package windowskext // This file contains example code how to read bandwidth stats from the kext. Its not ment to be used in production. diff --git a/firewall/interception/windowskext/handler.go b/firewall/interception/windowskext/handler.go index a81d28d9..d943fccc 100644 --- a/firewall/interception/windowskext/handler.go +++ b/firewall/interception/windowskext/handler.go @@ -30,6 +30,11 @@ const ( // connection that was intercepted on an ALE layer instead of in the network // stack itself. Thus, no packet data is available. VerdictRequestFlagSocketAuth = 2 + + // VerdictRequestFlagExpectSocketAuth indicates that the next verdict + // requests is expected to be an informational socket auth request from + // the ALE layer. + VerdictRequestFlagExpectSocketAuth = 4 ) // Do not change the order of the members! The structure is used to communicate with the kernel extension. diff --git a/firewall/interception/windowskext/kext.go b/firewall/interception/windowskext/kext.go index fe1daf64..7766a786 100644 --- a/firewall/interception/windowskext/kext.go +++ b/firewall/interception/windowskext/kext.go @@ -127,8 +127,10 @@ func RecvVerdictRequest() (*VerdictRequest, error) { return nil, ErrKextNotReady } - timestamp := time.Now() - defer log.Tracef("winkext: getting verdict request took %s", time.Since(timestamp)) + // DEBUG: + // timestamp := time.Now() + // defer log.Tracef("winkext: getting verdict request took %s", time.Since(timestamp)) + // Initialize struct for the output data var new VerdictRequest diff --git a/firewall/interception/windowskext/packet.go b/firewall/interception/windowskext/packet.go index 282a85a7..6c7b24da 100644 --- a/firewall/interception/windowskext/packet.go +++ b/firewall/interception/windowskext/packet.go @@ -24,16 +24,21 @@ type Packet struct { lock sync.Mutex } +// FastTrackedByIntegration returns whether the packet has been fast-track +// accepted by the OS integration. +func (pkt *Packet) FastTrackedByIntegration() bool { + return pkt.verdictRequest.flags&VerdictRequestFlagFastTrackPermitted > 0 +} + // InfoOnly returns whether the packet is informational only and does not // represent an actual packet. func (pkt *Packet) InfoOnly() bool { return pkt.verdictRequest.flags&VerdictRequestFlagSocketAuth > 0 } -// FastTrackedByIntegration returns whether the packet has been fast-track -// accepted by the OS integration. -func (pkt *Packet) FastTrackedByIntegration() bool { - return pkt.verdictRequest.flags&VerdictRequestFlagFastTrackPermitted > 0 +// ExpectInfo returns whether the next packet is expected to be informational only. +func (pkt *Packet) ExpectInfo() bool { + return pkt.verdictRequest.flags&VerdictRequestFlagExpectSocketAuth > 0 } // GetPayload returns the full raw packet. diff --git a/network/connection.go b/network/connection.go index fe3e92fe..eae2617c 100644 --- a/network/connection.go +++ b/network/connection.go @@ -760,12 +760,46 @@ func (conn *Connection) packetHandlerWorker(ctx context.Context) error { pktQueue = conn.pktQueue }() + // pktSeq counts the seen packets. + var pktSeq int + for { select { case pkt := <-pktQueue: if pkt == nil { return nil } + pktSeq++ + + // Check if we should expect an(other) info only packet. + // Only wait if this is the first packet and is not an info packet itself. + if pktSeq == 1 && pkt.ExpectInfo() && !pkt.InfoOnly() { + // Debug: FIXME + // log.Debugf("filter: waiting for info only packet in order to pull forward: %s", pkt) + select { + case infoPkt := <-pktQueue: + if infoPkt != nil { + // Debug: FIXME + // log.Debugf("filter: packet #%d [pulled forward] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt) + packetHandlerHandleConn(ctx, conn, pkt) + pktSeq++ + } + case <-time.After(5 * time.Millisecond): + } + } + + // Debug: FIXME + // switch { + // case pkt.Info().Inbound: + // log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt) + // case pktSeq == 1 && !pkt.InfoOnly(): + // log.Warningf("filter: packet #%d [should be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt) + // case pktSeq >= 2 && pkt.InfoOnly(): + // log.Errorf("filter: packet #%d [should not be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt) + // default: + // log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt) + // } + packetHandlerHandleConn(ctx, conn, pkt) case <-ctx.Done(): @@ -802,7 +836,7 @@ func packetHandlerHandleConn(ctx context.Context, conn *Connection, pkt packet.P case conn.DataIsComplete(): tracer.Infof("filter: connection %s %s: %s", conn, conn.VerdictVerb(), conn.Reason.Msg) case conn.Verdict.Firewall != VerdictUndecided: - tracer.Debugf("filter: connection %s fast-tracked", conn) + tracer.Debugf("filter: connection %s fast-tracked", pkt) default: tracer.Infof("filter: gathered data on connection %s", conn) } diff --git a/network/packet/packet.go b/network/packet/packet.go index 6034f46f..a4c9d780 100644 --- a/network/packet/packet.go +++ b/network/packet/packet.go @@ -30,6 +30,11 @@ func (pkt *Base) InfoOnly() bool { return false } +// ExpectInfo returns whether the next packet is expected to be informational only. +func (pkt *Base) ExpectInfo() bool { + return false +} + // SetCtx sets the packet context. func (pkt *Base) SetCtx(ctx context.Context) { pkt.ctx = ctx @@ -244,6 +249,7 @@ type Packet interface { RerouteToTunnel() error FastTrackedByIntegration() bool InfoOnly() bool + ExpectInfo() bool // Info. SetCtx(context.Context)