diff --git a/firewall/interception.go b/firewall/interception.go index f68f20a9..296eefba 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -161,6 +161,12 @@ func getConnection(pkt packet.Packet) (*network.Connection, error) { func fastTrackedPermit(pkt packet.Packet) (handled bool) { meta := pkt.Info() + // Check if packed was already fast-tracked by the OS integration. + if pkt.FastTrackedByIntegration() { + log.Debugf("filter: fast-tracked by OS integration: %s", pkt) + return true + } + // Check if connection was already blocked. if meta.Dst.Equal(blockedIPv4) || meta.Dst.Equal(blockedIPv6) { _ = pkt.PermanentBlock() diff --git a/firewall/interception/nfq/packet.go b/firewall/interception/nfq/packet.go index f8c7ed96..5fdfe473 100644 --- a/firewall/interception/nfq/packet.go +++ b/firewall/interception/nfq/packet.go @@ -86,7 +86,7 @@ func (pkt *packet) mark(mark int) (err error) { return pkt.setMark(mark) } - return errors.New("verdict set") + return errors.New("verdict already set") } func (pkt *packet) setMark(mark int) error { @@ -113,12 +113,12 @@ func (pkt *packet) setMark(mark int) error { } } - log.Errorf("nfqueue: failed to set verdict %s for %s (%s -> %s): %s", markToString(mark), pkt.ID(), pkt.Info().Src, pkt.Info().Dst, err) + log.Tracer(pkt.Ctx()).Errorf("nfqueue: failed to set verdict %s for %s (%s -> %s): %s", markToString(mark), pkt.ID(), pkt.Info().Src, pkt.Info().Dst, err) return err } break } - log.Tracef("nfqueue: marking packet %s (%s -> %s) on queue %d with %s after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, pkt.queue.id, markToString(mark), time.Since(pkt.received)) + log.Tracer(pkt.Ctx()).Tracef("nfqueue: marking packet %s (%s -> %s) on queue %d with %s after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, pkt.queue.id, markToString(mark), time.Since(pkt.received)) return nil } diff --git a/firewall/interception/windowskext/handler.go b/firewall/interception/windowskext/handler.go index dec83cbc..623c49a3 100644 --- a/firewall/interception/windowskext/handler.go +++ b/firewall/interception/windowskext/handler.go @@ -13,14 +13,26 @@ import ( "github.com/safing/portmaster/network/packet" ) +const ( + // VerdictRequestFlagFastTrackPermitted is set on packets that have been + // already permitted by the kernel extension and the verdict request is only + // informational. + VerdictRequestFlagFastTrackPermitted = 1 + + // VerdictRequestFlagSocketAuth indicates that the verdict request is for a + // connection that was intercepted on an ALE layer instead of in the network + // stack itself. Thus, no packet data is available. + VerdictRequestFlagSocketAuth = 2 +) + // VerdictRequest is the request structure from the Kext. type VerdictRequest struct { id uint32 // ID from RegisterPacket _ uint64 // Process ID - does not yet work direction uint8 - ipV6 uint8 // True: IPv6, False: IPv4 - protocol uint8 // Protocol - _ uint8 + ipV6 uint8 // True: IPv6, False: IPv4 + protocol uint8 // Protocol + flags uint8 // Flags localIP [4]uint32 // Source Address remoteIP [4]uint32 // Destination Address localPort uint16 // Source Port diff --git a/firewall/interception/windowskext/kext.go b/firewall/interception/windowskext/kext.go index 76057342..1767667b 100644 --- a/firewall/interception/windowskext/kext.go +++ b/firewall/interception/windowskext/kext.go @@ -20,6 +20,7 @@ import ( // Package errors var ( ErrKextNotReady = errors.New("the windows kernel extension (driver) is not ready to accept commands") + ErrNoPacketID = errors.New("the packet has no ID, possibly because it was fast-tracked by the kernel extension") winErrInvalidData = uintptr(windows.ERROR_INVALID_DATA) @@ -178,22 +179,29 @@ func RecvVerdictRequest() (*VerdictRequest, error) { } // SetVerdict sets the verdict for a packet and/or connection. -func SetVerdict(packetID uint32, verdict network.Verdict) error { +func SetVerdict(pkt *Packet, verdict network.Verdict) error { + if pkt.verdictRequest.id == 0 { + log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s: no packet ID", verdict) + return ErrNoPacketID + } + kextLock.RLock() defer kextLock.RUnlock() if !ready.IsSet() { + log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s: kext not ready", verdict) return ErrKextNotReady } atomic.AddInt32(urgentRequests, 1) // timestamp := time.Now() rc, _, lastErr := kext.setVerdict.Call( - uintptr(packetID), + uintptr(pkt.verdictRequest.id), uintptr(verdict), ) // log.Tracef("winkext: settings verdict for packetID %d took %s", packetID, time.Now().Sub(timestamp)) atomic.AddInt32(urgentRequests, -1) if rc != windows.NO_ERROR { + log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s on packet %d", verdict, pkt.verdictRequest.id) return formatErr(lastErr, rc) } return nil @@ -201,6 +209,10 @@ func SetVerdict(packetID uint32, verdict network.Verdict) error { // GetPayload returns the payload of a packet. func GetPayload(packetID uint32, packetSize uint32) ([]byte, error) { + if packetID == 0 { + return nil, ErrNoPacketID + } + kextLock.RLock() defer kextLock.RUnlock() if !ready.IsSet() { diff --git a/firewall/interception/windowskext/packet.go b/firewall/interception/windowskext/packet.go index 35790a5c..2a8186f6 100644 --- a/firewall/interception/windowskext/packet.go +++ b/firewall/interception/windowskext/packet.go @@ -23,11 +23,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 +} + // GetPayload returns the full raw packet. func (pkt *Packet) LoadPacketData() error { pkt.lock.Lock() defer pkt.lock.Unlock() + if pkt.verdictRequest.id == 0 { + return packet.ErrNoPacketID + } + if !pkt.payloadLoaded { pkt.payloadLoaded = true @@ -53,7 +63,7 @@ func (pkt *Packet) LoadPacketData() error { // Accept accepts the packet. func (pkt *Packet) Accept() error { if pkt.verdictSet.SetToIf(false, true) { - return SetVerdict(pkt.verdictRequest.id, -network.VerdictAccept) + return SetVerdict(pkt, -network.VerdictAccept) } return nil } @@ -61,7 +71,7 @@ func (pkt *Packet) Accept() error { // Block blocks the packet. func (pkt *Packet) Block() error { if pkt.verdictSet.SetToIf(false, true) { - return SetVerdict(pkt.verdictRequest.id, -network.VerdictBlock) + return SetVerdict(pkt, -network.VerdictBlock) } return nil } @@ -69,7 +79,7 @@ func (pkt *Packet) Block() error { // Drop drops the packet. func (pkt *Packet) Drop() error { if pkt.verdictSet.SetToIf(false, true) { - return SetVerdict(pkt.verdictRequest.id, -network.VerdictDrop) + return SetVerdict(pkt, -network.VerdictDrop) } return nil } @@ -77,7 +87,7 @@ func (pkt *Packet) Drop() error { // PermanentAccept permanently accepts connection (and the current packet). func (pkt *Packet) PermanentAccept() error { if pkt.verdictSet.SetToIf(false, true) { - return SetVerdict(pkt.verdictRequest.id, network.VerdictAccept) + return SetVerdict(pkt, network.VerdictAccept) } return nil } @@ -85,7 +95,7 @@ func (pkt *Packet) PermanentAccept() error { // PermanentBlock permanently blocks connection (and the current packet). func (pkt *Packet) PermanentBlock() error { if pkt.verdictSet.SetToIf(false, true) { - return SetVerdict(pkt.verdictRequest.id, network.VerdictBlock) + return SetVerdict(pkt, network.VerdictBlock) } return nil } @@ -93,7 +103,7 @@ func (pkt *Packet) PermanentBlock() error { // PermanentDrop permanently drops connection (and the current packet). func (pkt *Packet) PermanentDrop() error { if pkt.verdictSet.SetToIf(false, true) { - return SetVerdict(pkt.verdictRequest.id, network.VerdictDrop) + return SetVerdict(pkt, network.VerdictDrop) } return nil } @@ -101,7 +111,7 @@ func (pkt *Packet) PermanentDrop() error { // RerouteToNameserver permanently reroutes the connection to the local nameserver (and the current packet). func (pkt *Packet) RerouteToNameserver() error { if pkt.verdictSet.SetToIf(false, true) { - return SetVerdict(pkt.verdictRequest.id, network.VerdictRerouteToNameserver) + return SetVerdict(pkt, network.VerdictRerouteToNameserver) } return nil } @@ -109,7 +119,7 @@ func (pkt *Packet) RerouteToNameserver() error { // RerouteToTunnel permanently reroutes the connection to the local tunnel entrypoint (and the current packet). func (pkt *Packet) RerouteToTunnel() error { if pkt.verdictSet.SetToIf(false, true) { - return SetVerdict(pkt.verdictRequest.id, network.VerdictRerouteToTunnel) + return SetVerdict(pkt, network.VerdictRerouteToTunnel) } return nil } diff --git a/network/packet/packet.go b/network/packet/packet.go index 81d0f626..c735e38f 100644 --- a/network/packet/packet.go +++ b/network/packet/packet.go @@ -18,6 +18,12 @@ type Base struct { layer5Data []byte } +// FastTrackedByIntegration returns whether the packet has been fast-track +// accepted by the OS integration. +func (pkt *Base) FastTrackedByIntegration() bool { + return false +} + // SetCtx sets the packet context. func (pkt *Base) SetCtx(ctx context.Context) { pkt.ctx = ctx @@ -222,6 +228,7 @@ type Packet interface { PermanentDrop() error RerouteToNameserver() error RerouteToTunnel() error + FastTrackedByIntegration() bool // INFO SetCtx(context.Context)