Skip to content

Commit

Permalink
Merge pull request #294 from safing/feature/os-integration-fast-tracking
Browse files Browse the repository at this point in the history
Add support for fast-tracking connections within the OS integration
  • Loading branch information
dhaavi authored Apr 19, 2021
2 parents 995e924 + c3d94ef commit 80508a1
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 16 deletions.
6 changes: 6 additions & 0 deletions firewall/interception.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
6 changes: 3 additions & 3 deletions firewall/interception/nfq/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}

Expand Down
18 changes: 15 additions & 3 deletions firewall/interception/windowskext/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 14 additions & 2 deletions firewall/interception/windowskext/kext.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -178,29 +179,40 @@ 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
}

// 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() {
Expand Down
26 changes: 18 additions & 8 deletions firewall/interception/windowskext/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -53,63 +63,63 @@ 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
}

// 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
}

// 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
}

// 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
}

// 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
}

// 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
}

// 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
}

// 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
}
7 changes: 7 additions & 0 deletions network/packet/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -222,6 +228,7 @@ type Packet interface {
PermanentDrop() error
RerouteToNameserver() error
RerouteToTunnel() error
FastTrackedByIntegration() bool

// INFO
SetCtx(context.Context)
Expand Down

0 comments on commit 80508a1

Please sign in to comment.