/
socket.go
198 lines (166 loc) · 5.22 KB
/
socket.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package kping
import (
"encoding/binary"
"fmt"
"net"
"os"
"syscall"
"time"
"unsafe"
"golang.org/x/net/ipv4"
)
var (
nativeEndian binary.ByteOrder
)
type rawConn struct {
fd int
readBuffer int64
writeBuffer int64
readTimeout time.Duration
writeTimeout time.Duration
}
func init() {
i := uint32(1)
b := (*[4]byte)(unsafe.Pointer(&i))
if b[0] == 1 {
nativeEndian = binary.LittleEndian
} else {
nativeEndian = binary.BigEndian
}
}
func newRawConn(sourceIP string, readBuffer, writeBuffer int64, readTimeout, writeTimeout time.Duration) (rc *rawConn, err error) {
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, syscall.IPPROTO_ICMP)
if err != nil {
return nil, err
}
sockaddr := &syscall.SockaddrInet4{}
sourceIPAddr, err := net.ResolveIPAddr("ip4", sourceIP)
if err != nil {
return nil, err
}
ip4 := sourceIPAddr.IP.To4()
copy(sockaddr.Addr[:], ip4)
if err := syscall.Bind(fd, sockaddr); err != nil {
return nil, err
}
rc = &rawConn{
fd: fd,
readBuffer: readBuffer,
writeBuffer: writeBuffer,
readTimeout: readTimeout,
writeTimeout: writeTimeout,
}
if readBuffer > 0 {
if err := rc.setReadBuffer(int(readBuffer)); err != nil {
return nil, fmt.Errorf("set read buffer failed: %v", err)
}
}
if writeBuffer > 0 {
if err := rc.setWriteBuffer(int(writeBuffer)); err != nil {
return nil, fmt.Errorf("set write buffer failed: %v", err)
}
}
if readTimeout > 0 {
tv := syscall.Timeval{
Sec: 0,
Usec: int64(readTimeout / time.Microsecond),
}
if err := rc.setReadTimeout(&tv); err != nil {
return nil, fmt.Errorf("set read timeout failed: %v", err)
}
}
if writeTimeout > 0 {
tv := syscall.Timeval{
Sec: 0,
Usec: int64(writeTimeout / time.Microsecond),
}
if err := rc.setWriteTimeout(&tv); err != nil {
return nil, fmt.Errorf("set write timeout failed: %v", err)
}
}
if err := rc.setTOS(0x0); err != nil {
return nil, fmt.Errorf("set TOS failed: %v", err)
}
if err := rc.setTTL(64); err != nil {
return nil, fmt.Errorf("set TTL failed: %v", err)
}
if err := rc.setReuseaddr(); err != nil {
return nil, fmt.Errorf("setReuseaddr failed: %v", err)
}
return rc, nil
}
func (rc *rawConn) setWriteBuffer(size int) (err error) {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(rc.fd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, size))
}
func (rc *rawConn) setReadBuffer(size int) (err error) {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(rc.fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, size))
}
func (rc *rawConn) setTOS(tos int) (err error) {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(rc.fd, syscall.SOL_SOCKET, syscall.IP_TOS, tos))
}
func (rc *rawConn) setTTL(ttl int) (err error) {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(rc.fd, syscall.SOL_SOCKET, syscall.IP_TTL, ttl))
}
func (rc *rawConn) setTimeStampns() (err error) {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(rc.fd, syscall.SOL_SOCKET, syscall.SO_TIMESTAMPNS, 1))
}
func (rc *rawConn) setReuseaddr() (err error) {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(rc.fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1))
}
func (rc *rawConn) setReadTimeout(tv *syscall.Timeval) (err error) {
return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(rc.fd, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, tv))
}
func (rc *rawConn) setWriteTimeout(tv *syscall.Timeval) (err error) {
return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(rc.fd, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, tv))
}
func (rc *rawConn) setICMPFilter(filter *ipv4.ICMPFilter) (err error) {
v := (*[sizeofICMPFilter]byte)(unsafe.Pointer(filter))[:sizeofICMPFilter]
return os.NewSyscallError("setsockopt", setsockopt(uintptr(rc.fd), syscall.SOL_RAW, 0x1, v))
}
func (rc *rawConn) close() (err error) {
return syscall.Close(rc.fd)
}
var hsCache = make([]mmsghdrs, 100) // ReadBatch parallel < 100
func init() {
for i := 0; i < len(hsCache); i++ {
hsCache[i] = make(mmsghdrs, 1024) // buffer size >= UIO_MAXIOV(linux default: 1024)
}
}
func (rc *rawConn) readBatch(ms []message, index int, flags int) (num int, err error) {
hs := hsCache[index][0:len(ms)]
parseFn := parseInetAddr
if err := hs.rPack(ms, parseFn, nil); err != nil {
return 0, err
}
var operr error
num, operr = recvmmsg(uintptr(rc.fd), hs, flags)
if operr != nil {
return num, os.NewSyscallError("recvmmsg", operr)
}
if err := hs[:num].unpack(ms[:num], parseFn, "ipv4"); err != nil {
return num, err
}
return num, nil
}
func (rc *rawConn) writeBatch(ms []message, flags int) (num int, err error) {
hs := make(mmsghdrs, len(ms))
if err := hs.pack(ms, nil, marshalInetAddr); err != nil {
return 0, err
}
var operr error
num, operr = sendmmsg(uintptr(rc.fd), hs, flags)
if operr != nil {
return num, os.NewSyscallError("sendmmsg", operr)
}
if err := hs[:num].unpack(ms[:num], nil, ""); err != nil {
return num, err
}
return num, nil
}
func setsockopt(s uintptr, level, name int, b []byte) error {
_, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
if errno == 0 {
return nil
}
return syscall.Errno(errno)
}