Add support for fast-tracking connections within the OS integration

This commit is contained in:
Daniel 2021-04-19 23:13:06 +02:00
parent 995e924b15
commit c3d94efab9
6 changed files with 63 additions and 16 deletions

View file

@ -161,6 +161,12 @@ func getConnection(pkt packet.Packet) (*network.Connection, error) {
func fastTrackedPermit(pkt packet.Packet) (handled bool) { func fastTrackedPermit(pkt packet.Packet) (handled bool) {
meta := pkt.Info() 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. // Check if connection was already blocked.
if meta.Dst.Equal(blockedIPv4) || meta.Dst.Equal(blockedIPv6) { if meta.Dst.Equal(blockedIPv4) || meta.Dst.Equal(blockedIPv6) {
_ = pkt.PermanentBlock() _ = pkt.PermanentBlock()

View file

@ -86,7 +86,7 @@ func (pkt *packet) mark(mark int) (err error) {
return pkt.setMark(mark) return pkt.setMark(mark)
} }
return errors.New("verdict set") return errors.New("verdict already set")
} }
func (pkt *packet) setMark(mark int) error { 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 return err
} }
break 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 return nil
} }

View file

@ -13,6 +13,18 @@ import (
"github.com/safing/portmaster/network/packet" "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. // VerdictRequest is the request structure from the Kext.
type VerdictRequest struct { type VerdictRequest struct {
id uint32 // ID from RegisterPacket id uint32 // ID from RegisterPacket
@ -20,7 +32,7 @@ type VerdictRequest struct {
direction uint8 direction uint8
ipV6 uint8 // True: IPv6, False: IPv4 ipV6 uint8 // True: IPv6, False: IPv4
protocol uint8 // Protocol protocol uint8 // Protocol
_ uint8 flags uint8 // Flags
localIP [4]uint32 // Source Address localIP [4]uint32 // Source Address
remoteIP [4]uint32 // Destination Address remoteIP [4]uint32 // Destination Address
localPort uint16 // Source Port localPort uint16 // Source Port

View file

@ -20,6 +20,7 @@ import (
// Package errors // Package errors
var ( var (
ErrKextNotReady = errors.New("the windows kernel extension (driver) is not ready to accept commands") 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) winErrInvalidData = uintptr(windows.ERROR_INVALID_DATA)
@ -178,22 +179,29 @@ func RecvVerdictRequest() (*VerdictRequest, error) {
} }
// SetVerdict sets the verdict for a packet and/or connection. // 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() kextLock.RLock()
defer kextLock.RUnlock() defer kextLock.RUnlock()
if !ready.IsSet() { if !ready.IsSet() {
log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s: kext not ready", verdict)
return ErrKextNotReady return ErrKextNotReady
} }
atomic.AddInt32(urgentRequests, 1) atomic.AddInt32(urgentRequests, 1)
// timestamp := time.Now() // timestamp := time.Now()
rc, _, lastErr := kext.setVerdict.Call( rc, _, lastErr := kext.setVerdict.Call(
uintptr(packetID), uintptr(pkt.verdictRequest.id),
uintptr(verdict), uintptr(verdict),
) )
// log.Tracef("winkext: settings verdict for packetID %d took %s", packetID, time.Now().Sub(timestamp)) // log.Tracef("winkext: settings verdict for packetID %d took %s", packetID, time.Now().Sub(timestamp))
atomic.AddInt32(urgentRequests, -1) atomic.AddInt32(urgentRequests, -1)
if rc != windows.NO_ERROR { 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 formatErr(lastErr, rc)
} }
return nil return nil
@ -201,6 +209,10 @@ func SetVerdict(packetID uint32, verdict network.Verdict) error {
// GetPayload returns the payload of a packet. // GetPayload returns the payload of a packet.
func GetPayload(packetID uint32, packetSize uint32) ([]byte, error) { func GetPayload(packetID uint32, packetSize uint32) ([]byte, error) {
if packetID == 0 {
return nil, ErrNoPacketID
}
kextLock.RLock() kextLock.RLock()
defer kextLock.RUnlock() defer kextLock.RUnlock()
if !ready.IsSet() { if !ready.IsSet() {

View file

@ -23,11 +23,21 @@ type Packet struct {
lock sync.Mutex 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. // GetPayload returns the full raw packet.
func (pkt *Packet) LoadPacketData() error { func (pkt *Packet) LoadPacketData() error {
pkt.lock.Lock() pkt.lock.Lock()
defer pkt.lock.Unlock() defer pkt.lock.Unlock()
if pkt.verdictRequest.id == 0 {
return packet.ErrNoPacketID
}
if !pkt.payloadLoaded { if !pkt.payloadLoaded {
pkt.payloadLoaded = true pkt.payloadLoaded = true
@ -53,7 +63,7 @@ func (pkt *Packet) LoadPacketData() error {
// Accept accepts the packet. // Accept accepts the packet.
func (pkt *Packet) Accept() error { func (pkt *Packet) Accept() error {
if pkt.verdictSet.SetToIf(false, true) { if pkt.verdictSet.SetToIf(false, true) {
return SetVerdict(pkt.verdictRequest.id, -network.VerdictAccept) return SetVerdict(pkt, -network.VerdictAccept)
} }
return nil return nil
} }
@ -61,7 +71,7 @@ func (pkt *Packet) Accept() error {
// Block blocks the packet. // Block blocks the packet.
func (pkt *Packet) Block() error { func (pkt *Packet) Block() error {
if pkt.verdictSet.SetToIf(false, true) { if pkt.verdictSet.SetToIf(false, true) {
return SetVerdict(pkt.verdictRequest.id, -network.VerdictBlock) return SetVerdict(pkt, -network.VerdictBlock)
} }
return nil return nil
} }
@ -69,7 +79,7 @@ func (pkt *Packet) Block() error {
// Drop drops the packet. // Drop drops the packet.
func (pkt *Packet) Drop() error { func (pkt *Packet) Drop() error {
if pkt.verdictSet.SetToIf(false, true) { if pkt.verdictSet.SetToIf(false, true) {
return SetVerdict(pkt.verdictRequest.id, -network.VerdictDrop) return SetVerdict(pkt, -network.VerdictDrop)
} }
return nil return nil
} }
@ -77,7 +87,7 @@ func (pkt *Packet) Drop() error {
// PermanentAccept permanently accepts connection (and the current packet). // PermanentAccept permanently accepts connection (and the current packet).
func (pkt *Packet) PermanentAccept() error { func (pkt *Packet) PermanentAccept() error {
if pkt.verdictSet.SetToIf(false, true) { if pkt.verdictSet.SetToIf(false, true) {
return SetVerdict(pkt.verdictRequest.id, network.VerdictAccept) return SetVerdict(pkt, network.VerdictAccept)
} }
return nil return nil
} }
@ -85,7 +95,7 @@ func (pkt *Packet) PermanentAccept() error {
// PermanentBlock permanently blocks connection (and the current packet). // PermanentBlock permanently blocks connection (and the current packet).
func (pkt *Packet) PermanentBlock() error { func (pkt *Packet) PermanentBlock() error {
if pkt.verdictSet.SetToIf(false, true) { if pkt.verdictSet.SetToIf(false, true) {
return SetVerdict(pkt.verdictRequest.id, network.VerdictBlock) return SetVerdict(pkt, network.VerdictBlock)
} }
return nil return nil
} }
@ -93,7 +103,7 @@ func (pkt *Packet) PermanentBlock() error {
// PermanentDrop permanently drops connection (and the current packet). // PermanentDrop permanently drops connection (and the current packet).
func (pkt *Packet) PermanentDrop() error { func (pkt *Packet) PermanentDrop() error {
if pkt.verdictSet.SetToIf(false, true) { if pkt.verdictSet.SetToIf(false, true) {
return SetVerdict(pkt.verdictRequest.id, network.VerdictDrop) return SetVerdict(pkt, network.VerdictDrop)
} }
return nil return nil
} }
@ -101,7 +111,7 @@ func (pkt *Packet) PermanentDrop() error {
// RerouteToNameserver permanently reroutes the connection to the local nameserver (and the current packet). // RerouteToNameserver permanently reroutes the connection to the local nameserver (and the current packet).
func (pkt *Packet) RerouteToNameserver() error { func (pkt *Packet) RerouteToNameserver() error {
if pkt.verdictSet.SetToIf(false, true) { if pkt.verdictSet.SetToIf(false, true) {
return SetVerdict(pkt.verdictRequest.id, network.VerdictRerouteToNameserver) return SetVerdict(pkt, network.VerdictRerouteToNameserver)
} }
return nil 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). // RerouteToTunnel permanently reroutes the connection to the local tunnel entrypoint (and the current packet).
func (pkt *Packet) RerouteToTunnel() error { func (pkt *Packet) RerouteToTunnel() error {
if pkt.verdictSet.SetToIf(false, true) { if pkt.verdictSet.SetToIf(false, true) {
return SetVerdict(pkt.verdictRequest.id, network.VerdictRerouteToTunnel) return SetVerdict(pkt, network.VerdictRerouteToTunnel)
} }
return nil return nil
} }

View file

@ -18,6 +18,12 @@ type Base struct {
layer5Data []byte 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. // SetCtx sets the packet context.
func (pkt *Base) SetCtx(ctx context.Context) { func (pkt *Base) SetCtx(ctx context.Context) {
pkt.ctx = ctx pkt.ctx = ctx
@ -222,6 +228,7 @@ type Packet interface {
PermanentDrop() error PermanentDrop() error
RerouteToNameserver() error RerouteToNameserver() error
RerouteToTunnel() error RerouteToTunnel() error
FastTrackedByIntegration() bool
// INFO // INFO
SetCtx(context.Context) SetCtx(context.Context)