From 259a0a8b2208794206b20ee900dc564c89f900e7 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 29 Mar 2021 13:41:17 +0200 Subject: [PATCH] Add ICMP listener --- firewall/interception.go | 18 ++++++++- netenv/icmp_listener.go | 82 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 netenv/icmp_listener.go diff --git a/firewall/interception.go b/firewall/interception.go index 30e16b7e..5fa1eb80 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -139,15 +139,29 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) { switch meta.Protocol { case packet.ICMP: + // Submit to ICMP listener. + submitted := netenv.SubmitPacketToICMPListener(pkt) + // Always permit ICMP. log.Debugf("filter: fast-track accepting ICMP: %s", pkt) - _ = pkt.PermanentAccept() + if submitted { + _ = pkt.Accept() + } else { + _ = pkt.PermanentAccept() + } return true case packet.ICMPv6: + // Submit to ICMP listener. + submitted := netenv.SubmitPacketToICMPListener(pkt) + // Always permit ICMPv6. log.Debugf("filter: fast-track accepting ICMPv6: %s", pkt) - _ = pkt.PermanentAccept() + if submitted { + _ = pkt.Accept() + } else { + _ = pkt.PermanentAccept() + } return true case packet.UDP, packet.TCP: diff --git a/netenv/icmp_listener.go b/netenv/icmp_listener.go new file mode 100644 index 00000000..6b8de2e5 --- /dev/null +++ b/netenv/icmp_listener.go @@ -0,0 +1,82 @@ +package netenv + +import ( + "sync" + + "github.com/tevino/abool" + + "github.com/safing/portbase/log" + "github.com/safing/portmaster/network/packet" +) + +var ( + // listenICMPLock locks the ICMP listening system for one user at a time. + listenICMPLock sync.Mutex + + // listenICMPEnabled defines whether or not the firewall should send ICMP + // packets through this interface. + listenICMPEnabled = abool.New() + + // listenICMPInput is created for every use of the ICMP listenting system. + listenICMPInput chan packet.Packet + listenICMPInputLock sync.Mutex +) + +func ListenToICMP() (packets chan packet.Packet, done func()) { + // Lock for single use. + listenICMPLock.Lock() + + // Create new input channel. + listenICMPInputLock.Lock() + listenICMPInput = make(chan packet.Packet, 10) + listenICMPEnabled.Set() + listenICMPInputLock.Unlock() + + return listenICMPInput, func() { + // Close input channel. + listenICMPInputLock.Lock() + listenICMPEnabled.UnSet() + close(listenICMPInput) + listenICMPInputLock.Unlock() + + // Release for someone else to use. + listenICMPLock.Unlock() + } +} + +func SubmitPacketToICMPListener(pkt packet.Packet) (submitted bool) { + // Hot path. + if !listenICMPEnabled.IsSet() { + return false + } + + // Slow path. + submitPacketToICMPListenerSlow(pkt) + return true +} + +func submitPacketToICMPListenerSlow(pkt packet.Packet) { + // Make sure the payload is available. + if err := pkt.LoadPacketData(); err != nil { + log.Warningf("netenv: failed to get payload for ICMP listener: %s", err) + return + } + + // Send to input channel. + listenICMPInputLock.Lock() + defer listenICMPInputLock.Unlock() + + // Check if still enabled. + if !listenICMPEnabled.IsSet() { + return + } + + log.Criticalf("netenv: recvd ICMP packet: %s", pkt) + + // Send to channel, if possible. + select { + case listenICMPInput <- pkt: + default: + log.Warning("netenv: failed to send packet payload to ICMP listener: channel full") + } +}