Merge pull request #2403 from thaJeztah/carry_2370_sctp_update

[carry 2370] Update sctp package
This commit is contained in:
Euan Harris 2019-06-25 15:11:50 +01:00 committed by GitHub
commit c3d402cef3
11 changed files with 391 additions and 101 deletions

View file

@ -51,8 +51,8 @@ func parseHostContainerAddrs() (host net.Addr, container net.Addr) {
host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
case "sctp":
host = &sctp.SCTPAddr{IP: []net.IP{net.ParseIP(*hostIP)}, Port: *hostPort}
container = &sctp.SCTPAddr{IP: []net.IP{net.ParseIP(*containerIP)}, Port: *containerPort}
host = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*hostIP)}}, Port: *hostPort}
container = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*containerIP)}}, Port: *containerPort}
default:
log.Fatalf("unsupported protocol %s", *proto)
}

View file

@ -285,7 +285,7 @@ func TestSCTP4Proxy(t *testing.T) {
backend := NewEchoServer(t, "sctp", "127.0.0.1:0", EchoServerOptions{})
defer backend.Close()
backend.Run()
frontendAddr := &sctp.SCTPAddr{IP: []net.IP{net.IPv4(127, 0, 0, 1)}, Port: 0}
frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv4(127, 0, 0, 1)}}, Port: 0}
proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
if err != nil {
t.Fatal(err)
@ -298,7 +298,7 @@ func TestSCTP6Proxy(t *testing.T) {
backend := NewEchoServer(t, "sctp", "[::1]:0", EchoServerOptions{})
defer backend.Close()
backend.Run()
frontendAddr := &sctp.SCTPAddr{IP: []net.IP{net.IPv6loopback}, Port: 0}
frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv6loopback}}, Port: 0}
proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
if err != nil {
t.Fatal(err)

View file

@ -115,16 +115,16 @@ func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart,
m = &mapping{
proto: proto,
host: &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: allocatedHostPort},
host: &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: allocatedHostPort},
container: container,
}
if useProxy {
sctpAddr := container.(*sctp.SCTPAddr)
if len(sctpAddr.IP) == 0 {
if len(sctpAddr.IPAddrs) == 0 {
return nil, ErrSCTPAddrNoIP
}
m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IP[0], sctpAddr.Port, pm.proxyPath)
m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IPAddrs[0].IP, sctpAddr.Port, pm.proxyPath)
if err != nil {
return nil, err
}
@ -210,10 +210,10 @@ func (pm *PortMapper) Unmap(host net.Addr) error {
case *net.UDPAddr:
return pm.Allocator.ReleasePort(a.IP, "udp", a.Port)
case *sctp.SCTPAddr:
if len(a.IP) == 0 {
if len(a.IPAddrs) == 0 {
return ErrSCTPAddrNoIP
}
return pm.Allocator.ReleasePort(a.IP[0], "sctp", a.Port)
return pm.Allocator.ReleasePort(a.IPAddrs[0].IP, "sctp", a.Port)
}
return ErrUnknownBackendAddressType
}
@ -239,11 +239,11 @@ func getKey(a net.Addr) string {
case *net.UDPAddr:
return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
case *sctp.SCTPAddr:
if len(t.IP) == 0 {
if len(t.IPAddrs) == 0 {
logrus.Error(ErrSCTPAddrNoIP)
return ""
}
return fmt.Sprintf("%s:%d/%s", t.IP[0].String(), t.Port, "sctp")
return fmt.Sprintf("%s:%d/%s", t.IPAddrs[0].IP.String(), t.Port, "sctp")
}
return ""
}
@ -255,11 +255,11 @@ func getIPAndPort(a net.Addr) (net.IP, int) {
case *net.UDPAddr:
return t.IP, t.Port
case *sctp.SCTPAddr:
if len(t.IP) == 0 {
if len(t.IPAddrs) == 0 {
logrus.Error(ErrSCTPAddrNoIP)
return nil, 0
}
return t.IP[0], t.Port
return t.IPAddrs[0].IP, t.Port
}
return nil, 0
}

View file

@ -90,7 +90,7 @@ func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, er
addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
return &dummyProxy{addr: addr}, nil
case "sctp":
addr := &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: hostPort}
addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort}
return &dummyProxy{addr: addr}, nil
default:
return nil, fmt.Errorf("Unknown addr type: %s", proto)

View file

@ -99,7 +99,7 @@ func (p PortBinding) HostAddr() (net.Addr, error) {
case TCP:
return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
case SCTP:
return &sctp.SCTPAddr{IP: []net.IP{p.HostIP}, Port: int(p.HostPort)}, nil
return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.HostIP}}, Port: int(p.HostPort)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
@ -113,7 +113,7 @@ func (p PortBinding) ContainerAddr() (net.Addr, error) {
case TCP:
return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
case SCTP:
return &sctp.SCTPAddr{IP: []net.IP{p.IP}, Port: int(p.Port)}, nil
return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.IP}}, Port: int(p.Port)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}

View file

@ -48,7 +48,7 @@ golang.org/x/net a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1
golang.org/x/sys d455e41777fca6e8a5a79e34a14b8368bc11d9ba
golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca
github.com/pkg/errors ba968bfe8b2f7e042a574c888954fccecfa385b4 # v0.8.1
github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
github.com/ishidawataru/sctp 6e2cb1366111dcf547c13531e3a263a067715847
gotest.tools b6e20af1ed078cd01a6413b734051a292450b4cb # v2.1.0
github.com/google/go-cmp 3af367b6b30c263d47e8895973edcca9a49cf029 # v0.2.0

View file

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,218 @@
package sctp
import (
"net"
"os"
"sync"
"syscall"
)
//from https://github.com/golang/go
// Boolean to int.
func boolint(b bool) int {
if b {
return 1
}
return 0
}
//from https://github.com/golang/go
func ipToSockaddr(family int, ip net.IP, port int, zone string) (syscall.Sockaddr, error) {
switch family {
case syscall.AF_INET:
if len(ip) == 0 {
ip = net.IPv4zero
}
ip4 := ip.To4()
if ip4 == nil {
return nil, &net.AddrError{Err: "non-IPv4 address", Addr: ip.String()}
}
sa := &syscall.SockaddrInet4{Port: port}
copy(sa.Addr[:], ip4)
return sa, nil
case syscall.AF_INET6:
// In general, an IP wildcard address, which is either
// "0.0.0.0" or "::", means the entire IP addressing
// space. For some historical reason, it is used to
// specify "any available address" on some operations
// of IP node.
//
// When the IP node supports IPv4-mapped IPv6 address,
// we allow an listener to listen to the wildcard
// address of both IP addressing spaces by specifying
// IPv6 wildcard address.
if len(ip) == 0 || ip.Equal(net.IPv4zero) {
ip = net.IPv6zero
}
// We accept any IPv6 address including IPv4-mapped
// IPv6 address.
ip6 := ip.To16()
if ip6 == nil {
return nil, &net.AddrError{Err: "non-IPv6 address", Addr: ip.String()}
}
//we set ZoneId to 0, as currently we use this functon only to probe the IP capabilities of the host
//if real Zone handling is required, the zone cache implementation in golang/net should be pulled here
sa := &syscall.SockaddrInet6{Port: port, ZoneId: 0}
copy(sa.Addr[:], ip6)
return sa, nil
}
return nil, &net.AddrError{Err: "invalid address family", Addr: ip.String()}
}
//from https://github.com/golang/go
func sockaddr(a *net.TCPAddr, family int) (syscall.Sockaddr, error) {
if a == nil {
return nil, nil
}
return ipToSockaddr(family, a.IP, a.Port, a.Zone)
}
//from https://github.com/golang/go
type ipStackCapabilities struct {
sync.Once // guards following
ipv4Enabled bool
ipv6Enabled bool
ipv4MappedIPv6Enabled bool
}
//from https://github.com/golang/go
var ipStackCaps ipStackCapabilities
//from https://github.com/golang/go
// supportsIPv4 reports whether the platform supports IPv4 networking
// functionality.
func supportsIPv4() bool {
ipStackCaps.Once.Do(ipStackCaps.probe)
return ipStackCaps.ipv4Enabled
}
//from https://github.com/golang/go
// supportsIPv6 reports whether the platform supports IPv6 networking
// functionality.
func supportsIPv6() bool {
ipStackCaps.Once.Do(ipStackCaps.probe)
return ipStackCaps.ipv6Enabled
}
//from https://github.com/golang/go
// supportsIPv4map reports whether the platform supports mapping an
// IPv4 address inside an IPv6 address at transport layer
// protocols. See RFC 4291, RFC 4038 and RFC 3493.
func supportsIPv4map() bool {
ipStackCaps.Once.Do(ipStackCaps.probe)
return ipStackCaps.ipv4MappedIPv6Enabled
}
//from https://github.com/golang/go
// Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
// capabilities which are controlled by the IPV6_V6ONLY socket option
// and kernel configuration.
//
// Should we try to use the IPv4 socket interface if we're only
// dealing with IPv4 sockets? As long as the host system understands
// IPv4-mapped IPv6, it's okay to pass IPv4-mapeed IPv6 addresses to
// the IPv6 interface. That simplifies our code and is most
// general. Unfortunately, we need to run on kernels built without
// IPv6 support too. So probe the kernel to figure it out.
func (p *ipStackCapabilities) probe() {
s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
switch err {
case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
case nil:
syscall.Close(s)
p.ipv4Enabled = true
}
var probes = []struct {
laddr net.TCPAddr
value int
}{
// IPv6 communication capability
{laddr: net.TCPAddr{IP: net.IPv6loopback}, value: 1},
// IPv4-mapped IPv6 address communication capability
{laddr: net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)}, value: 0},
}
for i := range probes {
s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if err != nil {
continue
}
defer syscall.Close(s)
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value)
sa, err := sockaddr(&(probes[i].laddr), syscall.AF_INET6)
if err != nil {
continue
}
if err := syscall.Bind(s, sa); err != nil {
continue
}
if i == 0 {
p.ipv6Enabled = true
} else {
p.ipv4MappedIPv6Enabled = true
}
}
}
//from https://github.com/golang/go
//Change: we check the first IP address in the list of candidate SCTP IP addresses
func (a *SCTPAddr) isWildcard() bool {
if a == nil {
return true
}
if 0 == len(a.IPAddrs) {
return true
}
return a.IPAddrs[0].IP.IsUnspecified()
}
func (a *SCTPAddr) family() int {
if a != nil {
for _, ip := range a.IPAddrs {
if ip.IP.To4() == nil {
return syscall.AF_INET6
}
}
}
return syscall.AF_INET
}
//from https://github.com/golang/go
func favoriteAddrFamily(network string, laddr *SCTPAddr, raddr *SCTPAddr, mode string) (family int, ipv6only bool) {
switch network[len(network)-1] {
case '4':
return syscall.AF_INET, false
case '6':
return syscall.AF_INET6, true
}
if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
if supportsIPv4map() || !supportsIPv4() {
return syscall.AF_INET6, false
}
if laddr == nil {
return syscall.AF_INET, false
}
return laddr.family(), false
}
if (laddr == nil || laddr.family() == syscall.AF_INET) &&
(raddr == nil || raddr.family() == syscall.AF_INET) {
return syscall.AF_INET, false
}
return syscall.AF_INET6, false
}
//from https://github.com/golang/go
//Changes: it is for SCTP only
func setDefaultSockopts(s int, family int, ipv6only bool) error {
if family == syscall.AF_INET6 {
// Allow both IP versions even if the OS default
// is otherwise. Note that some operating systems
// never admit this option.
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
}
// Allow broadcast.
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
}

View file

@ -197,37 +197,58 @@ func htons(h uint16) uint16 {
var ntohs = htons
func setNumOstreams(fd, num int) error {
param := InitMsg{
NumOstreams: uint16(num),
}
optlen := unsafe.Sizeof(param)
_, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&param)), uintptr(optlen))
// setInitOpts sets options for an SCTP association initialization
// see https://tools.ietf.org/html/rfc4960#page-25
func setInitOpts(fd int, options InitMsg) error {
optlen := unsafe.Sizeof(options)
_, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&options)), uintptr(optlen))
return err
}
func setNumOstreams(fd, num int) error {
return setInitOpts(fd, InitMsg{NumOstreams: uint16(num)})
}
type SCTPAddr struct {
IP []net.IP
Port int
IPAddrs []net.IPAddr
Port int
}
func (a *SCTPAddr) ToRawSockAddrBuf() []byte {
buf := []byte{}
p := htons(uint16(a.Port))
for _, ip := range a.IP {
if ip.To4() != nil {
if len(a.IPAddrs) == 0 { // if a.IPAddrs list is empty - fall back to IPv4 zero addr
s := syscall.RawSockaddrInet4{
Family: syscall.AF_INET,
Port: p,
}
copy(s.Addr[:], net.IPv4zero)
return toBuf(s)
}
buf := []byte{}
for _, ip := range a.IPAddrs {
ipBytes := ip.IP
if len(ipBytes) == 0 {
ipBytes = net.IPv4zero
}
if ip4 := ipBytes.To4(); ip4 != nil {
s := syscall.RawSockaddrInet4{
Family: syscall.AF_INET,
Port: p,
}
copy(s.Addr[:], ip.To4())
copy(s.Addr[:], ip4)
buf = append(buf, toBuf(s)...)
} else {
s := syscall.RawSockaddrInet6{
Family: syscall.AF_INET6,
Port: p,
var scopeid uint32
ifi, err := net.InterfaceByName(ip.Zone)
if err == nil {
scopeid = uint32(ifi.Index)
}
copy(s.Addr[:], ip)
s := syscall.RawSockaddrInet6{
Family: syscall.AF_INET6,
Port: p,
Scope_id: scopeid,
}
copy(s.Addr[:], ipBytes)
buf = append(buf, toBuf(s)...)
}
}
@ -237,15 +258,15 @@ func (a *SCTPAddr) ToRawSockAddrBuf() []byte {
func (a *SCTPAddr) String() string {
var b bytes.Buffer
for n, i := range a.IP {
if a.IP[n].To4() != nil {
for n, i := range a.IPAddrs {
if i.IP.To4() != nil {
b.WriteString(i.String())
} else if a.IP[n].To16() != nil {
} else if i.IP.To16() != nil {
b.WriteRune('[')
b.WriteString(i.String())
b.WriteRune(']')
}
if n < len(a.IP)-1 {
if n < len(a.IPAddrs)-1 {
b.WriteRune('/')
}
}
@ -260,6 +281,7 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) {
tcpnet := ""
switch network {
case "", "sctp":
tcpnet = "tcp"
case "sctp4":
tcpnet = "tcp4"
case "sctp6":
@ -271,26 +293,26 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) {
if len(elems) == 0 {
return nil, fmt.Errorf("invalid input: %s", addrs)
}
ipaddrs := make([]net.IP, 0, len(elems))
ipaddrs := make([]net.IPAddr, 0, len(elems))
for _, e := range elems[:len(elems)-1] {
tcpa, err := net.ResolveTCPAddr(tcpnet, e+":")
if err != nil {
return nil, err
}
ipaddrs = append(ipaddrs, tcpa.IP)
ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone})
}
tcpa, err := net.ResolveTCPAddr(tcpnet, elems[len(elems)-1])
if err != nil {
return nil, err
}
if tcpa.IP != nil {
ipaddrs = append(ipaddrs, tcpa.IP)
ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone})
} else {
ipaddrs = nil
}
return &SCTPAddr{
IP: ipaddrs,
Port: tcpa.Port,
IPAddrs: ipaddrs,
Port: tcpa.Port,
}, nil
}
@ -357,15 +379,12 @@ func (c *SCTPConn) Read(b []byte) (int, error) {
}
func (c *SCTPConn) SetInitMsg(numOstreams, maxInstreams, maxAttempts, maxInitTimeout int) error {
param := InitMsg{
return setInitOpts(c.fd(), InitMsg{
NumOstreams: uint16(numOstreams),
MaxInstreams: uint16(maxInstreams),
MaxAttempts: uint16(maxAttempts),
MaxInitTimeout: uint16(maxInitTimeout),
}
optlen := unsafe.Sizeof(param)
_, _, err := setsockopt(c.fd(), SCTP_INITMSG, uintptr(unsafe.Pointer(&param)), uintptr(optlen))
return err
})
}
func (c *SCTPConn) SubscribeEvents(flags int) error {
@ -473,7 +492,7 @@ func (c *SCTPConn) GetDefaultSentParam() (*SndRcvInfo, error) {
func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
addr := &SCTPAddr{
IP: make([]net.IP, n),
IPAddrs: make([]net.IPAddr, n),
}
switch family := (*(*syscall.RawSockaddrAny)(ptr)).Addr.Family; family {
@ -484,7 +503,7 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
for i := 0; i < n; i++ {
a := *(*syscall.RawSockaddrInet4)(unsafe.Pointer(
uintptr(ptr) + size*uintptr(i)))
addr.IP[i] = a.Addr[:]
addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:]}
}
case syscall.AF_INET6:
addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port)))
@ -493,7 +512,12 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
for i := 0; i < n; i++ {
a := *(*syscall.RawSockaddrInet6)(unsafe.Pointer(
uintptr(ptr) + size*uintptr(i)))
addr.IP[i] = a.Addr[:]
var zone string
ifi, err := net.InterfaceByIndex(int(a.Scope_id))
if err == nil {
zone = ifi.Name
}
addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:], Zone: zone}
}
default:
return nil, fmt.Errorf("unknown address family: %d", family)

View file

@ -3,7 +3,6 @@
package sctp
import (
"fmt"
"io"
"net"
"sync/atomic"
@ -115,31 +114,14 @@ func (c *SCTPConn) Close() error {
return syscall.EBADF
}
// ListenSCTP - start listener on specified address/port
func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
af := syscall.AF_INET
switch net {
case "sctp":
hasv6 := func(addr *SCTPAddr) bool {
if addr == nil {
return false
}
for _, ip := range addr.IP {
if ip.To4() == nil {
return true
}
}
return false
}
if hasv6(laddr) {
af = syscall.AF_INET6
}
case "sctp4":
case "sctp6":
af = syscall.AF_INET6
default:
return nil, fmt.Errorf("invalid net: %s", net)
}
return ListenSCTPExt(net, laddr, InitMsg{NumOstreams: SCTP_MAX_STREAM})
}
// ListenSCTPExt - start listener on specified address/port with given SCTP options
func ListenSCTPExt(network string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) {
af, ipv6only := favoriteAddrFamily(network, laddr, nil, "listen")
sock, err := syscall.Socket(
af,
syscall.SOCK_STREAM,
@ -148,11 +130,30 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
if err != nil {
return nil, err
}
err = setNumOstreams(sock, SCTP_MAX_STREAM)
// close socket on error
defer func() {
if err != nil {
syscall.Close(sock)
}
}()
if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
return nil, err
}
err = setInitOpts(sock, options)
if err != nil {
return nil, err
}
if laddr != nil && len(laddr.IP) != 0 {
if laddr != nil {
// 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})
}
}
err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
if err != nil {
return nil, err
@ -167,40 +168,30 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
}, nil
}
func (ln *SCTPListener) Accept() (net.Conn, error) {
// AcceptSCTP waits for and returns the next SCTP connection to the listener.
func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) {
fd, _, err := syscall.Accept4(ln.fd, 0)
return NewSCTPConn(fd, nil), err
}
// Accept waits for and returns the next connection connection to the listener.
func (ln *SCTPListener) Accept() (net.Conn, error) {
return ln.AcceptSCTP()
}
func (ln *SCTPListener) Close() error {
syscall.Shutdown(ln.fd, syscall.SHUT_RDWR)
return syscall.Close(ln.fd)
}
// DialSCTP - bind socket to laddr (if given) and connect to raddr
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
af := syscall.AF_INET
switch net {
case "sctp":
hasv6 := func(addr *SCTPAddr) bool {
if addr == nil {
return false
}
for _, ip := range addr.IP {
if ip.To4() == nil {
return true
}
}
return false
}
if hasv6(laddr) || hasv6(raddr) {
af = syscall.AF_INET6
}
case "sctp4":
case "sctp6":
af = syscall.AF_INET6
default:
return nil, fmt.Errorf("invalid net: %s", net)
}
return DialSCTPExt(net, laddr, raddr, InitMsg{NumOstreams: SCTP_MAX_STREAM})
}
// DialSCTPExt - same as DialSCTP but with given SCTP options
func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) {
af, ipv6only := favoriteAddrFamily(network, laddr, raddr, "dial")
sock, err := syscall.Socket(
af,
syscall.SOCK_STREAM,
@ -209,11 +200,29 @@ func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
if err != nil {
return nil, err
}
err = setNumOstreams(sock, SCTP_MAX_STREAM)
// close socket on error
defer func() {
if err != nil {
syscall.Close(sock)
}
}()
if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
return nil, err
}
err = setInitOpts(sock, options)
if err != nil {
return nil, err
}
if laddr != nil {
// 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})
}
}
err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
if err != nil {
return nil, err

View file

@ -34,10 +34,18 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
return nil, ErrUnsupported
}
func ListenSCTPExt(net string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) {
return nil, ErrUnsupported
}
func (ln *SCTPListener) Accept() (net.Conn, error) {
return nil, ErrUnsupported
}
func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) {
return nil, ErrUnsupported
}
func (ln *SCTPListener) Close() error {
return ErrUnsupported
}
@ -45,3 +53,7 @@ func (ln *SCTPListener) Close() error {
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
return nil, ErrUnsupported
}
func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) {
return nil, ErrUnsupported
}