-
-
Notifications
You must be signed in to change notification settings - Fork 274
/
icmp_listener.go
100 lines (81 loc) Β· 2.75 KB
/
icmp_listener.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package netenv
import (
"sync"
"github.com/tevino/abool"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/network/packet"
)
/*
This ICMP listening system is a simple system for components to listen to ICMP
packets via the firewall.
The main use case for this is to receive ICMP packets that are not always
delivered correctly, or need special permissions and or sockets to receive
them. This is the case when doing a traceroute.
In order to keep it simple, the system is only designed to be used by one
"user" at at time. Further calls to ListenToICMP will wait for the previous
operation to complete.
*/
var (
// listenICMPLock locks the ICMP listening system for one user at a time.
listenICMPLock sync.Mutex
// listenICMPEnabled defines whether or not the firewall should submit ICMP
// packets to this interface.
listenICMPEnabled = abool.New()
// listenICMPInput is created for every use of the ICMP listenting system.
listenICMPInput chan packet.Packet
listenICMPInputLock sync.Mutex
)
// ListenToICMP returns a new channel for listenting to icmp packets. Please
// note that any icmp packet will be passed and filtering must be done on
// the side of the caller. The caller must call the returned done function when
// done with the listener.
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, 100)
listenICMPEnabled.Set()
listenICMPInputLock.Unlock()
return listenICMPInput, func() {
// Release for someone else to use.
defer listenICMPLock.Unlock()
// Close input channel.
listenICMPInputLock.Lock()
listenICMPEnabled.UnSet()
close(listenICMPInput)
listenICMPInputLock.Unlock()
}
}
// SubmitPacketToICMPListener checks if an ICMP packet should be submitted to
// the listener. If so, it is submitted right away. The function returns
// whether or not the packet should be submitted, not if it was successful.
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
}
// Send to channel, if possible.
select {
case listenICMPInput <- pkt:
default:
log.Warning("netenv: failed to send packet payload to ICMP listener: channel full")
}
}