diff --git a/Gopkg.lock b/Gopkg.lock index f517c08c..0b8474c5 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -49,14 +49,6 @@ revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" -[[projects]] - digest = "1:979299da1a605fc6fddc4d8fcc09ebfc6cfa6a10b05df825a3ec59773d59d0d5" - name = "github.com/florianl/go-nfqueue" - packages = ["."] - pruneopts = "" - revision = "327225dbfdfcc1fc52bb676256bee46e5c8a7a3d" - version = "v2.0.0" - [[projects]] digest = "1:b6581f9180e0f2d5549280d71819ab951db9d511478c87daca95669589d505c0" name = "github.com/go-ole/go-ole" @@ -77,7 +69,7 @@ version = "v5.0.3" [[projects]] - digest = "1:e85e59c4152d8576341daf54f40d96c404c264e04941a4a36b97a0f427eb9e5e" + digest = "1:c18de9c9afca0ab336a29cf356d566abbdc29dd4948547557ed62c0da30d3be3" name = "github.com/google/gopacket" packages = [ ".", @@ -85,8 +77,8 @@ "tcpassembly", ] pruneopts = "" - revision = "6d3e2615da4ed2ed2a349918fe74e7e6d03482fa" - version = "v1.1.17" + revision = "558173e197d46ae52f0f7c58313c96296ee16a9c" + version = "v1.1.18" [[projects]] digest = "1:20dc576ad8f98fe64777c62f090a9b37dd67c62b23fe42b429c2c41936aa8a9c" @@ -113,12 +105,12 @@ version = "v1.1.0" [[projects]] - digest = "1:2f0c811248aeb64978037b357178b1593372439146bda860cb16f2c80785ea93" + digest = "1:ebffb4b4c8ddcf66bb549464183ea2ddbac6c58a803658f67249f83395d17455" name = "github.com/hashicorp/go-version" packages = ["."] pruneopts = "" - revision = "ac23dc3fea5d1a983c43f6a0f6e2c13f0195d8bd" - version = "v1.2.0" + revision = "59da58cfd357de719a4d16dac30481391a56c002" + version = "v1.2.1" [[projects]] digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" @@ -149,19 +141,19 @@ [[projects]] branch = "master" - digest = "1:742865d3c8c267f108f852411bd8385c53c209e96813a3b0e859855cce4a0ed7" + digest = "1:9d781ead5ca35ef02cdf0dc516b239cb387fe73207b0dd01760f7d4a825f4cd3" name = "github.com/miekg/dns" packages = ["."] pruneopts = "" - revision = "0ffcea329570529aedabbc11c1651cba0d46029d" + revision = "da812eed45cba1ce4c978e746039483064b8f92d" [[projects]] - digest = "1:b962a528cbecf7662bee4d84a600f7a0a6a130368666d7d461757ba4d1341906" + digest = "1:3282ac9a9ddf5c2c0eda96693364d34fe0f8d10a0748259082a5c9fbd3e1f7e4" name = "github.com/oschwald/maxminddb-golang" packages = ["."] pruneopts = "" - revision = "6a033e62c03b7dab4c37f7c9eb2ebb3b10e8f13a" - version = "v1.6.0" + revision = "2e4624cc0c4105b1df1d0643ac3aadb53824dc7d" + version = "v1.7.0" [[projects]] digest = "1:c45802472e0c06928cd997661f2af610accd85217023b1d5f6331bebce0671d3" @@ -180,7 +172,7 @@ version = "v1.0.0" [[projects]] - digest = "1:16f319cf21ddf49f27b3a2093d68316840dc25ec5c2a0a431a4a4fc01ea707e2" + digest = "1:70e15b4090e254d1eada6ef156773c0888cf707c43078479114d814761b902c5" name = "github.com/shirou/gopsutil" packages = [ "cpu", @@ -190,8 +182,8 @@ "process", ] pruneopts = "" - revision = "a81cf97fce2300934e6c625b9917103346c26ba3" - version = "v2.20.4" + revision = "7e94bb8bcde053b6d6c98bda5145e9742c913c39" + version = "v2.20.7" [[projects]] digest = "1:bff75d4f1a2d2c4b8f4b46ff5ac230b80b5fa49276f615900cba09fe4c97e66e" @@ -210,20 +202,20 @@ version = "v1.0.5" [[projects]] - digest = "1:cc4eb6813da8d08694e557fcafae8fcc24f47f61a0717f952da130ca9a486dfc" + digest = "1:83fd2513b9f6ae0997bf646db6b74e9e00131e31002116fda597175f25add42d" name = "github.com/stretchr/testify" packages = ["assert"] pruneopts = "" - revision = "3ebf1ddaeb260c4b1ae502a01c7844fa8c1fa0e9" - version = "v1.5.1" + revision = "f654a9112bbeac49ca2cd45bfbe11533c4666cf8" + version = "v1.6.1" [[projects]] - branch = "master" - digest = "1:86e6712cfd4070a2120c03fcec41cfcbbc51813504a74e28d74479edfaf669ee" + digest = "1:1f11a269b089908c141f78c060991ff7bcd16545e95ee48d557e638fa846bde2" name = "github.com/tevino/abool" packages = ["."] pruneopts = "" - revision = "9b9efcf221b50905aab9bbabd3daed56dc10f339" + revision = "8ae5c93531aabf12924a5b78e6dee1216bfff2f8" + version = "v1.2.0" [[projects]] branch = "master" @@ -235,18 +227,26 @@ [[projects]] branch = "master" - digest = "1:90f8aa620559abef3e8222064705e420dcb3498085b20782d128e5fa477b3a89" + digest = "1:df4642a605244e62c69ae335ac3c3cfa1c2b7ec971c3de398e1909592a961923" name = "golang.org/x/crypto" packages = [ "ed25519", "ed25519/internal/edwards25519", ] pruneopts = "" - revision = "06a226fb4e3765ef3f48aa2852b401bc7b98e981" + revision = "123391ffb6de907695e1066dc40c1ff09322aeb6" + +[[projects]] + digest = "1:ba49944a3238ae8f163c85b6d01d2db51cd5b09807105a3cfaacbd414744ca82" + name = "golang.org/x/mod" + packages = ["semver"] + pruneopts = "" + revision = "859b3ef565e237f9f1a0fb6b55385c497545680d" + version = "v0.3.0" [[projects]] branch = "master" - digest = "1:305d718b88fcd3b251b910416367de49af1e7944a9a17efabedab5f0ba7745de" + digest = "1:9ee0e6bc20d85d179d19be321443639dc501a8c0ba1bac173261b57768063e79" name = "golang.org/x/net" packages = [ "bpf", @@ -259,19 +259,19 @@ "publicsuffix", ] pruneopts = "" - revision = "0ba52f642ac2f9371a88bfdde41f4b4e195a37c0" + revision = "3edf25e44fccea9e11b919341e952fca722ef460" [[projects]] branch = "master" - digest = "1:4b0024508290ef8f5d7bd380a1ed9f6ac28255849f8c9da150d150b859c1df7c" + digest = "1:ae1578a64c2b241c13ab243739d05936d83825d2b6e9ff043ea3c7105666493d" name = "golang.org/x/sync" packages = ["errgroup"] pruneopts = "" - revision = "43a5402ce75a95522677f77c619865d66b8c57ab" + revision = "6e8e738ad208923de99951fe0b48239bfd864f28" [[projects]] branch = "master" - digest = "1:bf837d996e7dfe7b819cbe53c8c9733e93228577f0561e43996b9ef0ea8a68a9" + digest = "1:ecfcd51736bf55de713770df4580026a43f01a94c9c077b0ab10239e8a93a589" name = "golang.org/x/sys" packages = [ "internal/unsafeheader", @@ -284,10 +284,10 @@ "windows/svc/mgr", ] pruneopts = "" - revision = "05986578812163b26672dabd9b425240ae2bb0ad" + revision = "3ff754bf58a9922e2b8a1a0bd199be6c9a806123" [[projects]] - digest = "1:740b51a55815493a8d0f2b1e0d0ae48fe48953bf7eaf3fcc4198823bf67768c0" + digest = "1:fccda34e4c58111b1908d8d69bf8d57c41c8e2542bc18ec8cd38c4fa21057f71" name = "golang.org/x/text" packages = [ "collate", @@ -308,12 +308,12 @@ "unicode/rangetable", ] pruneopts = "" - revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475" - version = "v0.3.2" + revision = "23ae387dee1f90d29a23c0e87ee0b46038fbed0e" + version = "v0.3.3" [[projects]] branch = "master" - digest = "1:1c04ddbfd1b1132654a9febab8bdd7a89de852ce0e7a0e1b295eff1718fa26e5" + digest = "1:1f61b0af124800c576e5ccc355d0634413e0b71fe6fbc77694b18bd30d9aa56e" name = "golang.org/x/tools" packages = [ "go/ast/astutil", @@ -328,28 +328,29 @@ "internal/event/label", "internal/gocommand", "internal/packagesinternal", + "internal/typesinternal", ] pruneopts = "" - revision = "cb1345f3a375367f8439bba882e90348348288d9" + revision = "d00afeaade8f1e68fb815705aa42d704c1b6df35" [[projects]] branch = "master" - digest = "1:9d4ac09a835404ae9306c6e1493cf800ecbb0f3f828f4333b3e055de4c962eea" + digest = "1:a5a7a1a9560c0eb1f8b32c40da2e71bd2a05b9ff9e1ea294461c7dbe0d24c6bc" name = "golang.org/x/xerrors" packages = [ ".", "internal", ] pruneopts = "" - revision = "9bdfabe68543c54f90421aeb9a60ef8061b5b544" + revision = "5ec99f83aff198f5fbd629d6c8d8eb38a04218ca" [[projects]] - digest = "1:43eca683d801087f3acacfd036474638bc5e293ff6078758d710d99c40ec3f7c" - name = "gopkg.in/yaml.v2" + branch = "v3" + digest = "1:2e9c4d6def1d36dcd17730e00c06b49a2e97ea5e1e639bcd24fa60fa43e33ad6" + name = "gopkg.in/yaml.v3" packages = ["."] pruneopts = "" - revision = "0b1645d91e851e735d3e23330303ce81f70adbe3" - version = "v2.3.0" + revision = "eeeca48fe7764f320e4870d231902bf9c1be2c08" [solve-meta] analyzer-name = "dep" diff --git a/Gopkg.toml b/Gopkg.toml index dffe63f1..1a26fbfb 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -29,3 +29,7 @@ ignored = ["github.com/safing/portbase/*", "github.com/safing/spn/*"] [[constraint]] name = "github.com/miekg/dns" branch = "master" # switch back to semver releases when https://github.com/miekg/dns/pull/1110 is released + +[[constraint]] + name = "github.com/florianl/go-nfqueue" + branch = "master" # switch back once we migrate to go.mod diff --git a/firewall/interception/nfqexp/nfqexp.go b/firewall/interception/nfqexp/nfqexp.go index 1f17a9f4..c6ae36a7 100644 --- a/firewall/interception/nfqexp/nfqexp.go +++ b/firewall/interception/nfqexp/nfqexp.go @@ -5,10 +5,12 @@ package nfqexp import ( "context" + "sync/atomic" "time" "github.com/safing/portbase/log" pmpacket "github.com/safing/portmaster/network/packet" + "github.com/tevino/abool" "golang.org/x/sys/unix" "github.com/florianl/go-nfqueue" @@ -20,6 +22,9 @@ type Queue struct { nf *nfqueue.Nfqueue packets chan pmpacket.Packet cancelSocketCallback context.CancelFunc + + pendingVerdicts uint64 + verdictCompleted chan struct{} } // New opens a new nfQueue. @@ -30,12 +35,12 @@ func New(qid uint16, v6 bool) (*Queue, error) { } cfg := &nfqueue.Config{ NfQueue: qid, - MaxPacketLen: 0xffff, - MaxQueueLen: 0xff, + MaxPacketLen: 0xff, + MaxQueueLen: 0xffff, AfFamily: uint8(afFamily), Copymode: nfqueue.NfQnlCopyPacket, - ReadTimeout: 50 * time.Millisecond, - WriteTimeout: 50 * time.Millisecond, + ReadTimeout: 5 * time.Millisecond, + WriteTimeout: 100 * time.Millisecond, } nf, err := nfqueue.Open(cfg) @@ -49,6 +54,7 @@ func New(qid uint16, v6 bool) (*Queue, error) { nf: nf, packets: make(chan pmpacket.Packet, 1000), cancelSocketCallback: cancel, + verdictCompleted: make(chan struct{}, 1), } fn := func(attrs nfqueue.Attribute) int { @@ -61,10 +67,11 @@ func New(qid uint16, v6 bool) (*Queue, error) { } pkt := &packet{ - ID: *attrs.PacketID, - queue: q, - received: time.Now(), - verdictSet: make(chan struct{}), + pktID: *attrs.PacketID, + queue: q, + received: time.Now(), + verdictSet: make(chan struct{}), + verdictPending: abool.New(), } if attrs.Payload != nil { @@ -79,7 +86,7 @@ func New(qid uint16, v6 bool) (*Queue, error) { select { case q.packets <- pkt: - log.Tracef("nfqexp: queued packet %d (%s -> %s) after %s", pkt.ID, pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.received)) + log.Tracef("nfqexp: queued packet %s (%s -> %s) after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.received)) case <-ctx.Done(): return 0 case <-time.After(time.Second): @@ -90,10 +97,10 @@ func New(qid uint16, v6 bool) (*Queue, error) { select { case <-pkt.verdictSet: - case <-time.After(5 * time.Second): - log.Warningf("nfqexp: no verdict set for packet %d (%s -> %s) after %s, dropping", pkt.ID, pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.received)) + case <-time.After(20 * time.Second): + log.Warningf("nfqexp: no verdict set for packet %s (%s -> %s) after %s, dropping", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.received)) if err := pkt.Drop(); err != nil { - log.Warningf("nfqexp: failed to apply default-drop to unveridcted packet %d (%s -> %s)", pkt.ID, pkt.Info().Src, pkt.Info().Dst) + log.Warningf("nfqexp: failed to apply default-drop to unveridcted packet %s (%s -> %s)", pkt.ID(), pkt.Info().Src, pkt.Info().Dst) } } }() @@ -101,7 +108,32 @@ func New(qid uint16, v6 bool) (*Queue, error) { return 0 // continue calling this fn } - if err := q.nf.Register(ctx, fn); err != nil { + errorFunc := func(e error) int { + // embedded interface is required to work-around some + // dep-vendoring weirdness + if opError, ok := e.(interface { + Timeout() bool + Temporary() bool + }); ok { + if opError.Timeout() || opError.Temporary() { + c := atomic.LoadUint64(&q.pendingVerdicts) + if c > 0 { + log.Tracef("nfqexp: waiting for %d pending verdicts", c) + + for atomic.LoadUint64(&q.pendingVerdicts) > 0 { // must NOT use c here + <-q.verdictCompleted + } + } + + return 0 + } + } + log.Errorf("nfqexp: encountered error while receiving packets: %s\n", e.Error()) + + return 1 + } + + if err := q.nf.RegisterWithErrorFunc(ctx, fn, errorFunc); err != nil { defer q.nf.Close() return nil, err } diff --git a/firewall/interception/nfqexp/packet.go b/firewall/interception/nfqexp/packet.go index dcb11fa0..620eb52d 100644 --- a/firewall/interception/nfqexp/packet.go +++ b/firewall/interception/nfqexp/packet.go @@ -4,10 +4,13 @@ package nfqexp import ( "errors" + "fmt" + "sync/atomic" "time" + "github.com/tevino/abool" + "github.com/florianl/go-nfqueue" - "github.com/mdlayher/netlink" "github.com/safing/portbase/log" pmpacket "github.com/safing/portmaster/network/packet" ) @@ -51,10 +54,15 @@ func markToString(mark int) string { // packet implements the packet.Packet interface. type packet struct { pmpacket.Base - ID uint32 - received time.Time - queue *Queue - verdictSet chan struct{} + pktID uint32 + received time.Time + queue *Queue + verdictSet chan struct{} + verdictPending *abool.AtomicBool +} + +func (pkt *packet) ID() string { + return fmt.Sprintf("pkt:%d qid:%d", pkt.pktID, pkt.queue.id) } // TODO(ppacher): revisit the following behavior: @@ -68,26 +76,44 @@ type packet struct { // raw-socket. // func (pkt *packet) mark(mark int) (err error) { + if pkt.verdictPending.SetToIf(false, true) { + defer close(pkt.verdictSet) + return pkt.setMark(mark) + } + + return errors.New("verdict set") +} + +func (pkt *packet) setMark(mark int) error { + atomic.AddUint64(&pkt.queue.pendingVerdicts, 1) + defer func() { - if x := recover(); x != nil { - err = errors.New("verdict set") + atomic.AddUint64(&pkt.queue.pendingVerdicts, ^uint64(0)) + select { + case pkt.queue.verdictCompleted <- struct{}{}: + default: } }() + for { - if err := pkt.queue.nf.SetVerdictWithMark(pkt.ID, nfqueue.NfAccept, mark); err != nil { - log.Warningf("nfqexp: failed to set verdict %s for %d (%s -> %s): %s", markToString(mark), pkt.ID, pkt.Info().Src, pkt.Info().Dst, err) - if opErr, ok := err.(*netlink.OpError); ok { + if err := pkt.queue.nf.SetVerdictWithMark(pkt.pktID, nfqueue.NfAccept, mark); err != nil { + // embedded interface is required to work-around some + // dep-vendoring weirdness + if opErr, ok := err.(interface { + Timeout() bool + Temporary() bool + }); ok { if opErr.Timeout() || opErr.Temporary() { continue } } + log.Errorf("nfqexp: 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("nfqexp: marking packet %d (%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)) - close(pkt.verdictSet) + log.Tracef("nfqexp: 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 }