sctp_linux.go 11.7 KB
Newer Older
1
// +build linux,!386
Wataru Ishida's avatar
Wataru Ishida committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Copyright 2019 Wataru Ishida. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.
16
17
18
19
20
21

package sctp

import (
	"io"
	"net"
Evgeniy Makeev's avatar
Evgeniy Makeev committed
22
	"sync/atomic"
23
24
25
26
27
	"syscall"
	"unsafe"
)

func setsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
28
	// FIXME: syscall.SYS_SETSOCKOPT is undefined on 386
29
30
31
32
33
34
35
36
37
38
39
40
41
42
	r0, r1, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT,
		uintptr(fd),
		SOL_SCTP,
		optname,
		optval,
		optlen,
		0)
	if errno != 0 {
		return r0, r1, errno
	}
	return r0, r1, nil
}

func getsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
43
	// FIXME: syscall.SYS_GETSOCKOPT is undefined on 386
44
45
46
47
48
49
50
51
52
53
54
55
56
	r0, r1, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT,
		uintptr(fd),
		SOL_SCTP,
		optname,
		optval,
		optlen,
		0)
	if errno != 0 {
		return r0, r1, errno
	}
	return r0, r1, nil
}

Spike Curtis's avatar
Spike Curtis committed
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
type rawConn struct {
	sockfd int
}

func (r rawConn) Control(f func(fd uintptr)) error {
	f(uintptr(r.sockfd))
	return nil
}

func (r rawConn) Read(f func(fd uintptr) (done bool)) error {
	panic("not implemented")
}

func (r rawConn) Write(f func(fd uintptr) (done bool)) error {
	panic("not implemented")
}

74
75
76
77
78
79
80
81
func (c *SCTPConn) SCTPWrite(b []byte, info *SndRcvInfo) (int, error) {
	var cbuf []byte
	if info != nil {
		cmsgBuf := toBuf(info)
		hdr := &syscall.Cmsghdr{
			Level: syscall.IPPROTO_SCTP,
			Type:  SCTP_CMSG_SNDRCV,
		}
82
83
84
85

		// bitwidth of hdr.Len is platform-specific,
		// so we use hdr.SetLen() rather than directly setting hdr.Len
		hdr.SetLen(syscall.CmsgSpace(len(cmsgBuf)))
86
87
		cbuf = append(toBuf(hdr), cmsgBuf...)
	}
Evgeniy Makeev's avatar
Evgeniy Makeev committed
88
	return syscall.SendmsgN(c.fd(), b, cbuf, nil, 0)
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
}

func parseSndRcvInfo(b []byte) (*SndRcvInfo, error) {
	msgs, err := syscall.ParseSocketControlMessage(b)
	if err != nil {
		return nil, err
	}
	for _, m := range msgs {
		if m.Header.Level == syscall.IPPROTO_SCTP {
			switch m.Header.Type {
			case SCTP_CMSG_SNDRCV:
				return (*SndRcvInfo)(unsafe.Pointer(&m.Data[0])), nil
			}
		}
	}
	return nil, nil
}

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
func parseNotification(b []byte) Notification {
	snType := SCTPNotificationType(nativeEndian.Uint16(b[:2]))

	switch snType {
	case SCTP_SHUTDOWN_EVENT:
		notification := SCTPShutdownEvent{
			sseType:    nativeEndian.Uint16(b[:2]),
			sseFlags:   nativeEndian.Uint16(b[2:4]),
			sseLength:  nativeEndian.Uint32(b[4:8]),
			sseAssocID: SCTPAssocID(nativeEndian.Uint32(b[8:])),
		}
		return &notification
	case SCTP_ASSOC_CHANGE:
		notification := SCTPAssocChangeEvent{
			sacType:            nativeEndian.Uint16(b[:2]),
			sacFlags:           nativeEndian.Uint16(b[2:4]),
			sacLength:          nativeEndian.Uint32(b[4:8]),
			sacState:           SCTPState(nativeEndian.Uint16(b[8:10])),
			sacError:           nativeEndian.Uint16(b[10:12]),
			sacOutboundStreams: nativeEndian.Uint16(b[12:14]),
			sacInboundStreams:  nativeEndian.Uint16(b[14:16]),
			sacAssocID:         SCTPAssocID(nativeEndian.Uint32(b[16:20])),
			sacInfo:            b[20:],
		}
		return &notification
	default:
		return nil
	}
}

// SCTPRead use syscall.Recvmsg to receive SCTP message and return sctp sndrcvinfo/notification if need
func (c *SCTPConn) SCTPRead(b []byte) (int, *SndRcvInfo, Notification, error) {
139
	oob := make([]byte, 254)
Yan-Jie Lin's avatar
Yan-Jie Lin committed
140
141
	n, oobn, recvflags, _, err := syscall.Recvmsg(c.fd(), b, oob, 0)
	if err != nil {
142
		return n, nil, nil, err
Yan-Jie Lin's avatar
Yan-Jie Lin committed
143
	}
144

Yan-Jie Lin's avatar
Yan-Jie Lin committed
145
	if n == 0 && oobn == 0 {
146
		return 0, nil, nil, io.EOF
Yan-Jie Lin's avatar
Yan-Jie Lin committed
147
	}
148

149
150
151
	if recvflags&MSG_NOTIFICATION > 0 {
		notification := parseNotification(b[:n])
		return n, nil, notification, nil
Yan-Jie Lin's avatar
Yan-Jie Lin committed
152
153
154
155
156
	} else {
		var info *SndRcvInfo
		if oobn > 0 {
			info, err = parseSndRcvInfo(oob[:oobn])
		}
157
		return n, info, nil, err
158
159
160
161
	}
}

func (c *SCTPConn) Close() error {
Evgeniy Makeev's avatar
Evgeniy Makeev committed
162
163
164
165
166
167
168
169
170
171
	if c != nil {
		fd := atomic.SwapInt32(&c._fd, -1)
		if fd > 0 {
			info := &SndRcvInfo{
				Flags: SCTP_EOF,
			}
			c.SCTPWrite(nil, info)
			syscall.Shutdown(int(fd), syscall.SHUT_RDWR)
			return syscall.Close(int(fd))
		}
172
	}
Evgeniy Makeev's avatar
Evgeniy Makeev committed
173
	return syscall.EBADF
174
175
}

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
func (c *SCTPConn) SetWriteBuffer(bytes int) error {
	return syscall.SetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)
}

func (c *SCTPConn) GetWriteBuffer() (int, error) {
	return syscall.GetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_SNDBUF)
}

func (c *SCTPConn) SetReadBuffer(bytes int) error {
	return syscall.SetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)
}

func (c *SCTPConn) GetReadBuffer() (int, error) {
	return syscall.GetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_RCVBUF)
}

192
func (c *SCTPConn) SetWriteTimeout(tv syscall.Timeval) error {
Yan-Jie Lin's avatar
Yan-Jie Lin committed
193
194
195
	return syscall.SetsockoptTimeval(c.fd(), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &tv)
}

196
func (c *SCTPConn) SetReadTimeout(tv syscall.Timeval) error {
Yan-Jie Lin's avatar
Yan-Jie Lin committed
197
198
199
	return syscall.SetsockoptTimeval(c.fd(), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv)
}

Yan-Jie Lin's avatar
Yan-Jie Lin committed
200
201
202
203
func (c *SCTPConn) SetNonBlock(nonBlock bool) error {
	return syscall.SetNonblock(c.fd(), nonBlock)
}

204
func (c *SCTPConn) GetRtoInfo() (*RtoInfo, error) {
205
	return getRtoInfo(c.fd())
206
207
208
}

func (c *SCTPConn) SetRtoInfo(rtoInfo RtoInfo) error {
209
	return setRtoInfo(c.fd(), rtoInfo)
210
211
212
}

func (c *SCTPConn) GetAssocInfo() (*AssocInfo, error) {
213
	return getAssocInfo(c.fd())
214
215
216
}

func (c *SCTPConn) SetAssocInfo(info AssocInfo) error {
217
	return setAssocInfo(c.fd(), info)
218
219
}

220
221
// ListenSCTP - start listener on specified address/port
func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
222
	return ListenSCTPExt(net, laddr, InitMsg{NumOstreams: SCTP_MAX_STREAM}, nil, nil)
223
}
224

225
// ListenSCTPExt - start listener on specified address/port with given SCTP options
226
227
func ListenSCTPExt(network string, laddr *SCTPAddr, options InitMsg, rtoInfo *RtoInfo, assocInfo *AssocInfo) (*SCTPListener, error) {
	return listenSCTPExtConfig(network, laddr, options, rtoInfo, assocInfo, nil)
Spike Curtis's avatar
Spike Curtis committed
228
229
230
}

// listenSCTPExtConfig - start listener on specified address/port with given SCTP options and socket configuration
231
func listenSCTPExtConfig(network string, laddr *SCTPAddr, options InitMsg, rtoInfo *RtoInfo, assocInfo *AssocInfo, control func(network, address string, c syscall.RawConn) error) (*SCTPListener, error) {
232
	af, ipv6only := favoriteAddrFamily(network, laddr, nil, "listen")
233
234
	sock, err := syscall.Socket(
		af,
Yan-Jie Lin's avatar
Yan-Jie Lin committed
235
		syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC,
236
237
238
239
240
		syscall.IPPROTO_SCTP,
	)
	if err != nil {
		return nil, err
	}
241

242
243
244
245
246
247
	// close socket on error
	defer func() {
		if err != nil {
			syscall.Close(sock)
		}
	}()
248
249
250
	if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
		return nil, err
	}
Yan-Jie Lin's avatar
Yan-Jie Lin committed
251
252
253
254
255
256

	// enable REUSEADDR option
	if err = syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
		return nil, err
	}

Spike Curtis's avatar
Spike Curtis committed
257
258
259
260
261
262
	if control != nil {
		rc := rawConn{sockfd: sock}
		if err = control(network, laddr.String(), rc); err != nil {
			return nil, err
		}
	}
Dobie Chiu's avatar
Dobie Chiu committed
263
264

	//RTO
Yan-Jie Lin's avatar
Yan-Jie Lin committed
265
266
267
268
269
	if rtoInfo != nil {
		err = setRtoInfo(sock, *rtoInfo)
		if err != nil {
			return nil, err
		}
Dobie Chiu's avatar
Dobie Chiu committed
270
271
	}

272
273
274
275
276
277
278
279
	// set default association parameters (RFC 6458 8.1.2)
	if assocInfo != nil {
		err = setAssocInfo(sock, *assocInfo)
		if err != nil {
			return nil, err
		}
	}

280
	err = setInitOpts(sock, options)
281
282
283
	if err != nil {
		return nil, err
	}
284
285

	if laddr != nil {
286
		// If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address
287
288
289
290
291
292
293
		if len(laddr.IPAddrs) == 0 {
			if af == syscall.AF_INET {
				laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero})
			} else if af == syscall.AF_INET6 {
				laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero})
			}
		}
294
295
296
297
298
		err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
		if err != nil {
			return nil, err
		}
	}
Wataru Ishida's avatar
Wataru Ishida committed
299
	err = syscall.Listen(sock, syscall.SOMAXCONN)
300
301
302
	if err != nil {
		return nil, err
	}
Yan-Jie Lin's avatar
Yan-Jie Lin committed
303
304
305
306
307
308
309

	// epoll will be used in Accept() to avoid busy waiting because of non-blocking socket
	epfd, err := createEpollForSock(sock)
	if err != nil {
		return nil, err
	}

310
	return &SCTPListener{
Yan-Jie Lin's avatar
Yan-Jie Lin committed
311
312
		fd:   sock,
		epfd: epfd,
313
314
315
	}, nil
}

316
// createEpollForSock - create an epoll for sock; return an epoll fd if no error
Yan-Jie Lin's avatar
Yan-Jie Lin committed
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
func createEpollForSock(sock int) (int, error) {
	epfd, err := syscall.EpollCreate1(syscall.EPOLL_CLOEXEC)
	if err != nil {
		return -1, err
	}

	// close epfd on error
	defer func() {
		if err != nil {
			syscall.Close(epfd)
		}
	}()

	event := syscall.EpollEvent{
		Events: syscall.EPOLLIN,
		Fd:     int32(sock),
	}
	err = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, sock, &event)
	if err != nil {
		return -1, err
	}
	return epfd, nil
}

341
// AcceptSCTP waits for and returns the next SCTP connection to the listener.
Yan-Jie Lin's avatar
Yan-Jie Lin committed
342
// it will use EpollWait to wait for a incoming connection then call syscall.Accept4 to accept
343
func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) {
Yan-Jie Lin's avatar
Yan-Jie Lin committed
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
	var events [1]syscall.EpollEvent
	n, err := syscall.EpollWait(ln.epfd, events[:], -1)
	if err != nil {
		return nil, err
	}

	if n == 0 {
		return nil, err
	}

	if events[0].Fd == int32(ln.fd) {
		fd, _, err := syscall.Accept4(ln.fd, 0)
		return NewSCTPConn(fd, nil), err
	} else {
		return nil, err
	}
360
361
}

362
363
364
365
366
// Accept waits for and returns the next connection connection to the listener.
func (ln *SCTPListener) Accept() (net.Conn, error) {
	return ln.AcceptSCTP()
}

367
368
func (ln *SCTPListener) Close() error {
	syscall.Shutdown(ln.fd, syscall.SHUT_RDWR)
Yan-Jie Lin's avatar
Yan-Jie Lin committed
369
	syscall.Close(ln.epfd)
370
371
372
	return syscall.Close(ln.fd)
}

373
374
// DialSCTP - bind socket to laddr (if given) and connect to raddr
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
375
	return DialSCTPExt(net, laddr, raddr, InitMsg{NumOstreams: SCTP_MAX_STREAM}, nil, nil)
376
}
377

378
// DialSCTPExt - same as DialSCTP but with given SCTP options
379
380
func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg, rtoInfo *RtoInfo, assocInfo *AssocInfo) (*SCTPConn, error) {
	return dialSCTPExtConfig(network, laddr, raddr, options, rtoInfo, assocInfo, nil)
Spike Curtis's avatar
Spike Curtis committed
381
382
383
}

// dialSCTPExtConfig - same as DialSCTP but with given SCTP options and socket configuration
384
func dialSCTPExtConfig(network string, laddr, raddr *SCTPAddr, options InitMsg, rtoInfo *RtoInfo, assocInfo *AssocInfo, control func(network, address string, c syscall.RawConn) error) (*SCTPConn, error) {
385
	af, ipv6only := favoriteAddrFamily(network, laddr, raddr, "dial")
386
387
388
389
390
391
392
393
	sock, err := syscall.Socket(
		af,
		syscall.SOCK_STREAM,
		syscall.IPPROTO_SCTP,
	)
	if err != nil {
		return nil, err
	}
394

395
396
397
398
399
400
	// close socket on error
	defer func() {
		if err != nil {
			syscall.Close(sock)
		}
	}()
401
402
403
	if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
		return nil, err
	}
Spike Curtis's avatar
Spike Curtis committed
404
405
406
407
408
409
	if control != nil {
		rc := rawConn{sockfd: sock}
		if err = control(network, laddr.String(), rc); err != nil {
			return nil, err
		}
	}
410

Dobie Chiu's avatar
Dobie Chiu committed
411
	//RTO
Yan-Jie Lin's avatar
Yan-Jie Lin committed
412
413
	if rtoInfo != nil {
		err = setRtoInfo(sock, *rtoInfo)
414
415
416
417
418
419
420
421
422
423
424
		if err != nil {
			return nil, err
		}
	}

	// AssocInfo
	if assocInfo != nil {
		err = setAssocInfo(sock, *assocInfo)
		if err != nil {
			return nil, err
		}
Yan-Jie Lin's avatar
Yan-Jie Lin committed
425
	}
Dobie Chiu's avatar
Dobie Chiu committed
426

427
	err = setInitOpts(sock, options)
428
429
430
431
	if err != nil {
		return nil, err
	}
	if laddr != nil {
432
433
434
435
436
437
438
439
		// If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address
		if len(laddr.IPAddrs) == 0 {
			if af == syscall.AF_INET {
				laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero})
			} else if af == syscall.AF_INET6 {
				laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero})
			}
		}
440
441
442
443
444
445
446
447
448
449
450
		err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
		if err != nil {
			return nil, err
		}
	}
	_, err = SCTPConnect(sock, raddr)
	if err != nil {
		return nil, err
	}
	return NewSCTPConn(sock, nil), nil
}