/
Sockets.go
149 lines (126 loc) · 3.89 KB
/
Sockets.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package icmpengine
// Sockets holds the OpenSockets/CloseSockets functions
// IPPROTO_ICMP sockets which are NonPrivilegedPing
// https://lwn.net/Articles/422330/
import (
"fmt"
"log"
"github.com/go-cmd/cmd"
"golang.org/x/net/icmp"
)
const (
SdebugLevel = 111
)
// OpenSockets opens non-privleged ICMP sockets for sending echo requests/replies
// OpenSockets has retry logic, and can use HackSysctl to change the sysctl
// for the non-privleged ICMP sockets if ICMPEngine is running as root
// Hopefully ICMPEngine is not running as root, in which case, if it can't
// open the sockets, it will log fatal
func (ie *ICMPEngine) OpenSockets() {
if ie.Sockets.DebugLevel > 10 {
ie.Log.Info("OpenSockets() acquiring ie.RLock()")
}
ie.RLock()
fakeSuccess := ie.Expirers.FakeSuccess
open := ie.Sockets.Open
ie.RUnlock()
// Assertion - Don't open sockets if we're faking success
if fakeSuccess {
// return
log.Fatal(fmt.Sprintf("OpenSockets fakeSuccess:%t nothing should try to open the sockets", fakeSuccess))
}
// Assertion - don't reopen sockets
if open {
// return
log.Fatal("OpenSockets ie.Sockets.Open sockets are already open")
}
ie.Lock()
defer ie.Unlock()
if ie.Sockets.DebugLevel > 10 {
ie.Log.Info("OpenSockets ie.Lock() acquired")
}
if ie.Sockets.Open {
if ie.Sockets.DebugLevel > 10 {
ie.Log.Info("OpenSockets ie.Sockets.Open sockets are already open wih ie.Lock()")
}
return
}
var sockets int
for _, p := range ie.Protocols {
for retries := 0; retries < OpenSocketsRetriesCst && !ie.Sockets.Opens[p]; retries++ {
if ie.Sockets.Opens[p] {
if ie.Sockets.DebugLevel > 10 {
ie.Log.Info(fmt.Sprintf("OpenSockets ie.Sockets.Opens[%d] sockets are already open. ??!", p))
}
//return
log.Fatal(fmt.Sprintf("OpenSockets ie.Sockets.Opens[%d] sockets are already open. ??!", p))
}
var sockErr error
ie.Sockets.Sockets[p], sockErr = icmp.ListenPacket(ie.Sockets.Networks[p], ie.Sockets.Addresses[p])
if sockErr != nil {
if ie.HackSysctl() {
continue
}
ie.Log.Error("Please run: sudo sysctl -w net.ipv4.ping_group_range=\"0 2147483647\"")
log.Fatal("icmp.ListenPacket sockErr:", sockErr)
}
ie.Sockets.Opens[p] = true
sockets++
if ie.Sockets.DebugLevel > 10 {
ie.Log.Info(fmt.Sprintf("OpenSockets() Socket Open \t protocol:%d \t retries:%d", p, retries))
}
}
}
// Assertion
if sockets != len(ie.Protocols) {
log.Fatal(fmt.Sprintf("OpenSockets() failed to open both IPv4 and IPv6 sockets. !! sockets:%d", sockets))
}
ie.Sockets.Open = true
if ie.Sockets.DebugLevel > 10 {
ie.Log.Info("OpenSockets() ie.SocketsOpen = true")
}
}
// CloseSockets() closes the sockets with some assertion checks
func (ie *ICMPEngine) CloseSockets() {
if ie.Sockets.DebugLevel > 10 {
ie.Log.Info("CloseSockets() acquiring lock")
}
ie.Lock()
defer ie.Unlock()
if ie.Sockets.DebugLevel > 10 {
ie.Log.Info("CloseSockets() lock acquired")
}
var sockets int
for _, p := range ie.Protocols {
(*ie.Sockets.Sockets[p]).Close()
delete(ie.Sockets.Sockets, p)
ie.Sockets.Opens[p] = false
sockets++
}
// Assertion
if sockets != len(ie.Protocols) {
log.Fatal(fmt.Sprintf("Shutdown() closing failed to close both IPv4 and IPv6 sockets. !! sockets:%d", sockets))
}
ie.Sockets.Open = false
if ie.Sockets.DebugLevel > 10 {
ie.Log.Info("CloseSockets() sockets closed")
}
}
// HackSysctl does sysctl -w net.ipv4.ping_group_range=0 2147483647
// This requires root
func (ie *ICMPEngine) HackSysctl() (success bool) {
if ie.EID != 0 {
return success
}
// No need quote the same way as you do from bash
sysctlCmd := cmd.NewCmd(`sysctl`, `-w`, `net.ipv4.ping_group_range=0 2147483647`)
status := <-sysctlCmd.Start()
if ie.DebugLevel > 100 {
ie.Log.Info(fmt.Sprintf("HackSysctl status:%v", status))
for _, line := range status.Stdout {
ie.Log.Info(fmt.Sprintf("HackSysctl line:%s", line))
}
}
success = true
return success
}