This commit updates the vendored ishidawataru/sctp and adapts its used
types.
Signed-off-by: Sascha Grunert <sgrunert@suse.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -51,8 +51,8 @@ func parseHostContainerAddrs() (host net.Addr, container net.Addr) {
|
| 51 | 51 |
host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
| 52 | 52 |
container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
| 53 | 53 |
case "sctp": |
| 54 |
- host = &sctp.SCTPAddr{IP: []net.IP{net.ParseIP(*hostIP)}, Port: *hostPort}
|
|
| 55 |
- container = &sctp.SCTPAddr{IP: []net.IP{net.ParseIP(*containerIP)}, Port: *containerPort}
|
|
| 54 |
+ host = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*hostIP)}}, Port: *hostPort}
|
|
| 55 |
+ container = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*containerIP)}}, Port: *containerPort}
|
|
| 56 | 56 |
default: |
| 57 | 57 |
log.Fatalf("unsupported protocol %s", *proto)
|
| 58 | 58 |
} |
| ... | ... |
@@ -285,7 +285,7 @@ func TestSCTP4Proxy(t *testing.T) {
|
| 285 | 285 |
backend := NewEchoServer(t, "sctp", "127.0.0.1:0", EchoServerOptions{})
|
| 286 | 286 |
defer backend.Close() |
| 287 | 287 |
backend.Run() |
| 288 |
- frontendAddr := &sctp.SCTPAddr{IP: []net.IP{net.IPv4(127, 0, 0, 1)}, Port: 0}
|
|
| 288 |
+ frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv4(127, 0, 0, 1)}}, Port: 0}
|
|
| 289 | 289 |
proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) |
| 290 | 290 |
if err != nil {
|
| 291 | 291 |
t.Fatal(err) |
| ... | ... |
@@ -298,7 +298,7 @@ func TestSCTP6Proxy(t *testing.T) {
|
| 298 | 298 |
backend := NewEchoServer(t, "sctp", "[::1]:0", EchoServerOptions{})
|
| 299 | 299 |
defer backend.Close() |
| 300 | 300 |
backend.Run() |
| 301 |
- frontendAddr := &sctp.SCTPAddr{IP: []net.IP{net.IPv6loopback}, Port: 0}
|
|
| 301 |
+ frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv6loopback}}, Port: 0}
|
|
| 302 | 302 |
proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) |
| 303 | 303 |
if err != nil {
|
| 304 | 304 |
t.Fatal(err) |
| ... | ... |
@@ -115,16 +115,16 @@ func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, |
| 115 | 115 |
|
| 116 | 116 |
m = &mapping{
|
| 117 | 117 |
proto: proto, |
| 118 |
- host: &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: allocatedHostPort},
|
|
| 118 |
+ host: &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: allocatedHostPort},
|
|
| 119 | 119 |
container: container, |
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 | 122 |
if useProxy {
|
| 123 | 123 |
sctpAddr := container.(*sctp.SCTPAddr) |
| 124 |
- if len(sctpAddr.IP) == 0 {
|
|
| 124 |
+ if len(sctpAddr.IPAddrs) == 0 {
|
|
| 125 | 125 |
return nil, ErrSCTPAddrNoIP |
| 126 | 126 |
} |
| 127 |
- m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IP[0], sctpAddr.Port, pm.proxyPath) |
|
| 127 |
+ m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IPAddrs[0].IP, sctpAddr.Port, pm.proxyPath) |
|
| 128 | 128 |
if err != nil {
|
| 129 | 129 |
return nil, err |
| 130 | 130 |
} |
| ... | ... |
@@ -210,10 +210,10 @@ func (pm *PortMapper) Unmap(host net.Addr) error {
|
| 210 | 210 |
case *net.UDPAddr: |
| 211 | 211 |
return pm.Allocator.ReleasePort(a.IP, "udp", a.Port) |
| 212 | 212 |
case *sctp.SCTPAddr: |
| 213 |
- if len(a.IP) == 0 {
|
|
| 213 |
+ if len(a.IPAddrs) == 0 {
|
|
| 214 | 214 |
return ErrSCTPAddrNoIP |
| 215 | 215 |
} |
| 216 |
- return pm.Allocator.ReleasePort(a.IP[0], "sctp", a.Port) |
|
| 216 |
+ return pm.Allocator.ReleasePort(a.IPAddrs[0].IP, "sctp", a.Port) |
|
| 217 | 217 |
} |
| 218 | 218 |
return ErrUnknownBackendAddressType |
| 219 | 219 |
} |
| ... | ... |
@@ -239,11 +239,11 @@ func getKey(a net.Addr) string {
|
| 239 | 239 |
case *net.UDPAddr: |
| 240 | 240 |
return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
|
| 241 | 241 |
case *sctp.SCTPAddr: |
| 242 |
- if len(t.IP) == 0 {
|
|
| 242 |
+ if len(t.IPAddrs) == 0 {
|
|
| 243 | 243 |
logrus.Error(ErrSCTPAddrNoIP) |
| 244 | 244 |
return "" |
| 245 | 245 |
} |
| 246 |
- return fmt.Sprintf("%s:%d/%s", t.IP[0].String(), t.Port, "sctp")
|
|
| 246 |
+ return fmt.Sprintf("%s:%d/%s", t.IPAddrs[0].IP.String(), t.Port, "sctp")
|
|
| 247 | 247 |
} |
| 248 | 248 |
return "" |
| 249 | 249 |
} |
| ... | ... |
@@ -255,11 +255,11 @@ func getIPAndPort(a net.Addr) (net.IP, int) {
|
| 255 | 255 |
case *net.UDPAddr: |
| 256 | 256 |
return t.IP, t.Port |
| 257 | 257 |
case *sctp.SCTPAddr: |
| 258 |
- if len(t.IP) == 0 {
|
|
| 258 |
+ if len(t.IPAddrs) == 0 {
|
|
| 259 | 259 |
logrus.Error(ErrSCTPAddrNoIP) |
| 260 | 260 |
return nil, 0 |
| 261 | 261 |
} |
| 262 |
- return t.IP[0], t.Port |
|
| 262 |
+ return t.IPAddrs[0].IP, t.Port |
|
| 263 | 263 |
} |
| 264 | 264 |
return nil, 0 |
| 265 | 265 |
} |
| ... | ... |
@@ -90,7 +90,7 @@ func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, er |
| 90 | 90 |
addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
|
| 91 | 91 |
return &dummyProxy{addr: addr}, nil
|
| 92 | 92 |
case "sctp": |
| 93 |
- addr := &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: hostPort}
|
|
| 93 |
+ addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort}
|
|
| 94 | 94 |
return &dummyProxy{addr: addr}, nil
|
| 95 | 95 |
default: |
| 96 | 96 |
return nil, fmt.Errorf("Unknown addr type: %s", proto)
|
| ... | ... |
@@ -99,7 +99,7 @@ func (p PortBinding) HostAddr() (net.Addr, error) {
|
| 99 | 99 |
case TCP: |
| 100 | 100 |
return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
|
| 101 | 101 |
case SCTP: |
| 102 |
- return &sctp.SCTPAddr{IP: []net.IP{p.HostIP}, Port: int(p.HostPort)}, nil
|
|
| 102 |
+ return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.HostIP}}, Port: int(p.HostPort)}, nil
|
|
| 103 | 103 |
default: |
| 104 | 104 |
return nil, ErrInvalidProtocolBinding(p.Proto.String()) |
| 105 | 105 |
} |
| ... | ... |
@@ -113,7 +113,7 @@ func (p PortBinding) ContainerAddr() (net.Addr, error) {
|
| 113 | 113 |
case TCP: |
| 114 | 114 |
return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
|
| 115 | 115 |
case SCTP: |
| 116 |
- return &sctp.SCTPAddr{IP: []net.IP{p.IP}, Port: int(p.Port)}, nil
|
|
| 116 |
+ return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.IP}}, Port: int(p.Port)}, nil
|
|
| 117 | 117 |
default: |
| 118 | 118 |
return nil, ErrInvalidProtocolBinding(p.Proto.String()) |
| 119 | 119 |
} |
| ... | ... |
@@ -48,7 +48,7 @@ golang.org/x/net a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1 |
| 48 | 48 |
golang.org/x/sys d455e41777fca6e8a5a79e34a14b8368bc11d9ba |
| 49 | 49 |
golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca |
| 50 | 50 |
github.com/pkg/errors ba968bfe8b2f7e042a574c888954fccecfa385b4 # v0.8.1 |
| 51 |
-github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb |
|
| 51 |
+github.com/ishidawataru/sctp 6e2cb1366111dcf547c13531e3a263a067715847 |
|
| 52 | 52 |
|
| 53 | 53 |
gotest.tools b6e20af1ed078cd01a6413b734051a292450b4cb # v2.1.0 |
| 54 | 54 |
github.com/google/go-cmp 3af367b6b30c263d47e8895973edcca9a49cf029 # v0.2.0 |
| 55 | 55 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+Copyright (c) 2009 The Go Authors. All rights reserved. |
|
| 1 |
+ |
|
| 2 |
+Redistribution and use in source and binary forms, with or without |
|
| 3 |
+modification, are permitted provided that the following conditions are |
|
| 4 |
+met: |
|
| 5 |
+ |
|
| 6 |
+ * Redistributions of source code must retain the above copyright |
|
| 7 |
+notice, this list of conditions and the following disclaimer. |
|
| 8 |
+ * Redistributions in binary form must reproduce the above |
|
| 9 |
+copyright notice, this list of conditions and the following disclaimer |
|
| 10 |
+in the documentation and/or other materials provided with the |
|
| 11 |
+distribution. |
|
| 12 |
+ * Neither the name of Google Inc. nor the names of its |
|
| 13 |
+contributors may be used to endorse or promote products derived from |
|
| 14 |
+this software without specific prior written permission. |
|
| 15 |
+ |
|
| 16 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 17 |
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 18 |
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 19 |
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 20 |
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 21 |
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 22 |
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 23 |
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 24 |
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 25 |
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 26 |
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 0 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,218 @@ |
| 0 |
+package sctp |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net" |
|
| 4 |
+ "os" |
|
| 5 |
+ "sync" |
|
| 6 |
+ "syscall" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+//from https://github.com/golang/go |
|
| 10 |
+// Boolean to int. |
|
| 11 |
+func boolint(b bool) int {
|
|
| 12 |
+ if b {
|
|
| 13 |
+ return 1 |
|
| 14 |
+ } |
|
| 15 |
+ return 0 |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+//from https://github.com/golang/go |
|
| 19 |
+func ipToSockaddr(family int, ip net.IP, port int, zone string) (syscall.Sockaddr, error) {
|
|
| 20 |
+ switch family {
|
|
| 21 |
+ case syscall.AF_INET: |
|
| 22 |
+ if len(ip) == 0 {
|
|
| 23 |
+ ip = net.IPv4zero |
|
| 24 |
+ } |
|
| 25 |
+ ip4 := ip.To4() |
|
| 26 |
+ if ip4 == nil {
|
|
| 27 |
+ return nil, &net.AddrError{Err: "non-IPv4 address", Addr: ip.String()}
|
|
| 28 |
+ } |
|
| 29 |
+ sa := &syscall.SockaddrInet4{Port: port}
|
|
| 30 |
+ copy(sa.Addr[:], ip4) |
|
| 31 |
+ return sa, nil |
|
| 32 |
+ case syscall.AF_INET6: |
|
| 33 |
+ // In general, an IP wildcard address, which is either |
|
| 34 |
+ // "0.0.0.0" or "::", means the entire IP addressing |
|
| 35 |
+ // space. For some historical reason, it is used to |
|
| 36 |
+ // specify "any available address" on some operations |
|
| 37 |
+ // of IP node. |
|
| 38 |
+ // |
|
| 39 |
+ // When the IP node supports IPv4-mapped IPv6 address, |
|
| 40 |
+ // we allow an listener to listen to the wildcard |
|
| 41 |
+ // address of both IP addressing spaces by specifying |
|
| 42 |
+ // IPv6 wildcard address. |
|
| 43 |
+ if len(ip) == 0 || ip.Equal(net.IPv4zero) {
|
|
| 44 |
+ ip = net.IPv6zero |
|
| 45 |
+ } |
|
| 46 |
+ // We accept any IPv6 address including IPv4-mapped |
|
| 47 |
+ // IPv6 address. |
|
| 48 |
+ ip6 := ip.To16() |
|
| 49 |
+ if ip6 == nil {
|
|
| 50 |
+ return nil, &net.AddrError{Err: "non-IPv6 address", Addr: ip.String()}
|
|
| 51 |
+ } |
|
| 52 |
+ //we set ZoneId to 0, as currently we use this functon only to probe the IP capabilities of the host |
|
| 53 |
+ //if real Zone handling is required, the zone cache implementation in golang/net should be pulled here |
|
| 54 |
+ sa := &syscall.SockaddrInet6{Port: port, ZoneId: 0}
|
|
| 55 |
+ copy(sa.Addr[:], ip6) |
|
| 56 |
+ return sa, nil |
|
| 57 |
+ } |
|
| 58 |
+ return nil, &net.AddrError{Err: "invalid address family", Addr: ip.String()}
|
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+//from https://github.com/golang/go |
|
| 62 |
+func sockaddr(a *net.TCPAddr, family int) (syscall.Sockaddr, error) {
|
|
| 63 |
+ if a == nil {
|
|
| 64 |
+ return nil, nil |
|
| 65 |
+ } |
|
| 66 |
+ return ipToSockaddr(family, a.IP, a.Port, a.Zone) |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+//from https://github.com/golang/go |
|
| 70 |
+type ipStackCapabilities struct {
|
|
| 71 |
+ sync.Once // guards following |
|
| 72 |
+ ipv4Enabled bool |
|
| 73 |
+ ipv6Enabled bool |
|
| 74 |
+ ipv4MappedIPv6Enabled bool |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+//from https://github.com/golang/go |
|
| 78 |
+var ipStackCaps ipStackCapabilities |
|
| 79 |
+ |
|
| 80 |
+//from https://github.com/golang/go |
|
| 81 |
+// supportsIPv4 reports whether the platform supports IPv4 networking |
|
| 82 |
+// functionality. |
|
| 83 |
+func supportsIPv4() bool {
|
|
| 84 |
+ ipStackCaps.Once.Do(ipStackCaps.probe) |
|
| 85 |
+ return ipStackCaps.ipv4Enabled |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+//from https://github.com/golang/go |
|
| 89 |
+// supportsIPv6 reports whether the platform supports IPv6 networking |
|
| 90 |
+// functionality. |
|
| 91 |
+func supportsIPv6() bool {
|
|
| 92 |
+ ipStackCaps.Once.Do(ipStackCaps.probe) |
|
| 93 |
+ return ipStackCaps.ipv6Enabled |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+//from https://github.com/golang/go |
|
| 97 |
+// supportsIPv4map reports whether the platform supports mapping an |
|
| 98 |
+// IPv4 address inside an IPv6 address at transport layer |
|
| 99 |
+// protocols. See RFC 4291, RFC 4038 and RFC 3493. |
|
| 100 |
+func supportsIPv4map() bool {
|
|
| 101 |
+ ipStackCaps.Once.Do(ipStackCaps.probe) |
|
| 102 |
+ return ipStackCaps.ipv4MappedIPv6Enabled |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+//from https://github.com/golang/go |
|
| 106 |
+// Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication |
|
| 107 |
+// capabilities which are controlled by the IPV6_V6ONLY socket option |
|
| 108 |
+// and kernel configuration. |
|
| 109 |
+// |
|
| 110 |
+// Should we try to use the IPv4 socket interface if we're only |
|
| 111 |
+// dealing with IPv4 sockets? As long as the host system understands |
|
| 112 |
+// IPv4-mapped IPv6, it's okay to pass IPv4-mapeed IPv6 addresses to |
|
| 113 |
+// the IPv6 interface. That simplifies our code and is most |
|
| 114 |
+// general. Unfortunately, we need to run on kernels built without |
|
| 115 |
+// IPv6 support too. So probe the kernel to figure it out. |
|
| 116 |
+func (p *ipStackCapabilities) probe() {
|
|
| 117 |
+ s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) |
|
| 118 |
+ switch err {
|
|
| 119 |
+ case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT: |
|
| 120 |
+ case nil: |
|
| 121 |
+ syscall.Close(s) |
|
| 122 |
+ p.ipv4Enabled = true |
|
| 123 |
+ } |
|
| 124 |
+ var probes = []struct {
|
|
| 125 |
+ laddr net.TCPAddr |
|
| 126 |
+ value int |
|
| 127 |
+ }{
|
|
| 128 |
+ // IPv6 communication capability |
|
| 129 |
+ {laddr: net.TCPAddr{IP: net.IPv6loopback}, value: 1},
|
|
| 130 |
+ // IPv4-mapped IPv6 address communication capability |
|
| 131 |
+ {laddr: net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)}, value: 0},
|
|
| 132 |
+ } |
|
| 133 |
+ |
|
| 134 |
+ for i := range probes {
|
|
| 135 |
+ s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) |
|
| 136 |
+ if err != nil {
|
|
| 137 |
+ continue |
|
| 138 |
+ } |
|
| 139 |
+ defer syscall.Close(s) |
|
| 140 |
+ syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value) |
|
| 141 |
+ sa, err := sockaddr(&(probes[i].laddr), syscall.AF_INET6) |
|
| 142 |
+ if err != nil {
|
|
| 143 |
+ continue |
|
| 144 |
+ } |
|
| 145 |
+ if err := syscall.Bind(s, sa); err != nil {
|
|
| 146 |
+ continue |
|
| 147 |
+ } |
|
| 148 |
+ if i == 0 {
|
|
| 149 |
+ p.ipv6Enabled = true |
|
| 150 |
+ } else {
|
|
| 151 |
+ p.ipv4MappedIPv6Enabled = true |
|
| 152 |
+ } |
|
| 153 |
+ } |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+//from https://github.com/golang/go |
|
| 157 |
+//Change: we check the first IP address in the list of candidate SCTP IP addresses |
|
| 158 |
+func (a *SCTPAddr) isWildcard() bool {
|
|
| 159 |
+ if a == nil {
|
|
| 160 |
+ return true |
|
| 161 |
+ } |
|
| 162 |
+ if 0 == len(a.IPAddrs) {
|
|
| 163 |
+ return true |
|
| 164 |
+ } |
|
| 165 |
+ |
|
| 166 |
+ return a.IPAddrs[0].IP.IsUnspecified() |
|
| 167 |
+} |
|
| 168 |
+ |
|
| 169 |
+func (a *SCTPAddr) family() int {
|
|
| 170 |
+ if a != nil {
|
|
| 171 |
+ for _, ip := range a.IPAddrs {
|
|
| 172 |
+ if ip.IP.To4() == nil {
|
|
| 173 |
+ return syscall.AF_INET6 |
|
| 174 |
+ } |
|
| 175 |
+ } |
|
| 176 |
+ } |
|
| 177 |
+ return syscall.AF_INET |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 180 |
+//from https://github.com/golang/go |
|
| 181 |
+func favoriteAddrFamily(network string, laddr *SCTPAddr, raddr *SCTPAddr, mode string) (family int, ipv6only bool) {
|
|
| 182 |
+ switch network[len(network)-1] {
|
|
| 183 |
+ case '4': |
|
| 184 |
+ return syscall.AF_INET, false |
|
| 185 |
+ case '6': |
|
| 186 |
+ return syscall.AF_INET6, true |
|
| 187 |
+ } |
|
| 188 |
+ |
|
| 189 |
+ if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
|
|
| 190 |
+ if supportsIPv4map() || !supportsIPv4() {
|
|
| 191 |
+ return syscall.AF_INET6, false |
|
| 192 |
+ } |
|
| 193 |
+ if laddr == nil {
|
|
| 194 |
+ return syscall.AF_INET, false |
|
| 195 |
+ } |
|
| 196 |
+ return laddr.family(), false |
|
| 197 |
+ } |
|
| 198 |
+ |
|
| 199 |
+ if (laddr == nil || laddr.family() == syscall.AF_INET) && |
|
| 200 |
+ (raddr == nil || raddr.family() == syscall.AF_INET) {
|
|
| 201 |
+ return syscall.AF_INET, false |
|
| 202 |
+ } |
|
| 203 |
+ return syscall.AF_INET6, false |
|
| 204 |
+} |
|
| 205 |
+ |
|
| 206 |
+//from https://github.com/golang/go |
|
| 207 |
+//Changes: it is for SCTP only |
|
| 208 |
+func setDefaultSockopts(s int, family int, ipv6only bool) error {
|
|
| 209 |
+ if family == syscall.AF_INET6 {
|
|
| 210 |
+ // Allow both IP versions even if the OS default |
|
| 211 |
+ // is otherwise. Note that some operating systems |
|
| 212 |
+ // never admit this option. |
|
| 213 |
+ syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only)) |
|
| 214 |
+ } |
|
| 215 |
+ // Allow broadcast. |
|
| 216 |
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
|
|
| 217 |
+} |
| ... | ... |
@@ -197,37 +197,58 @@ func htons(h uint16) uint16 {
|
| 197 | 197 |
|
| 198 | 198 |
var ntohs = htons |
| 199 | 199 |
|
| 200 |
-func setNumOstreams(fd, num int) error {
|
|
| 201 |
- param := InitMsg{
|
|
| 202 |
- NumOstreams: uint16(num), |
|
| 203 |
- } |
|
| 204 |
- optlen := unsafe.Sizeof(param) |
|
| 205 |
- _, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(¶m)), uintptr(optlen)) |
|
| 200 |
+// setInitOpts sets options for an SCTP association initialization |
|
| 201 |
+// see https://tools.ietf.org/html/rfc4960#page-25 |
|
| 202 |
+func setInitOpts(fd int, options InitMsg) error {
|
|
| 203 |
+ optlen := unsafe.Sizeof(options) |
|
| 204 |
+ _, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&options)), uintptr(optlen)) |
|
| 206 | 205 |
return err |
| 207 | 206 |
} |
| 208 | 207 |
|
| 208 |
+func setNumOstreams(fd, num int) error {
|
|
| 209 |
+ return setInitOpts(fd, InitMsg{NumOstreams: uint16(num)})
|
|
| 210 |
+} |
|
| 211 |
+ |
|
| 209 | 212 |
type SCTPAddr struct {
|
| 210 |
- IP []net.IP |
|
| 211 |
- Port int |
|
| 213 |
+ IPAddrs []net.IPAddr |
|
| 214 |
+ Port int |
|
| 212 | 215 |
} |
| 213 | 216 |
|
| 214 | 217 |
func (a *SCTPAddr) ToRawSockAddrBuf() []byte {
|
| 215 |
- buf := []byte{}
|
|
| 216 | 218 |
p := htons(uint16(a.Port)) |
| 217 |
- for _, ip := range a.IP {
|
|
| 218 |
- if ip.To4() != nil {
|
|
| 219 |
+ if len(a.IPAddrs) == 0 { // if a.IPAddrs list is empty - fall back to IPv4 zero addr
|
|
| 220 |
+ s := syscall.RawSockaddrInet4{
|
|
| 221 |
+ Family: syscall.AF_INET, |
|
| 222 |
+ Port: p, |
|
| 223 |
+ } |
|
| 224 |
+ copy(s.Addr[:], net.IPv4zero) |
|
| 225 |
+ return toBuf(s) |
|
| 226 |
+ } |
|
| 227 |
+ buf := []byte{}
|
|
| 228 |
+ for _, ip := range a.IPAddrs {
|
|
| 229 |
+ ipBytes := ip.IP |
|
| 230 |
+ if len(ipBytes) == 0 {
|
|
| 231 |
+ ipBytes = net.IPv4zero |
|
| 232 |
+ } |
|
| 233 |
+ if ip4 := ipBytes.To4(); ip4 != nil {
|
|
| 219 | 234 |
s := syscall.RawSockaddrInet4{
|
| 220 | 235 |
Family: syscall.AF_INET, |
| 221 | 236 |
Port: p, |
| 222 | 237 |
} |
| 223 |
- copy(s.Addr[:], ip.To4()) |
|
| 238 |
+ copy(s.Addr[:], ip4) |
|
| 224 | 239 |
buf = append(buf, toBuf(s)...) |
| 225 | 240 |
} else {
|
| 241 |
+ var scopeid uint32 |
|
| 242 |
+ ifi, err := net.InterfaceByName(ip.Zone) |
|
| 243 |
+ if err == nil {
|
|
| 244 |
+ scopeid = uint32(ifi.Index) |
|
| 245 |
+ } |
|
| 226 | 246 |
s := syscall.RawSockaddrInet6{
|
| 227 |
- Family: syscall.AF_INET6, |
|
| 228 |
- Port: p, |
|
| 247 |
+ Family: syscall.AF_INET6, |
|
| 248 |
+ Port: p, |
|
| 249 |
+ Scope_id: scopeid, |
|
| 229 | 250 |
} |
| 230 |
- copy(s.Addr[:], ip) |
|
| 251 |
+ copy(s.Addr[:], ipBytes) |
|
| 231 | 252 |
buf = append(buf, toBuf(s)...) |
| 232 | 253 |
} |
| 233 | 254 |
} |
| ... | ... |
@@ -237,15 +258,15 @@ func (a *SCTPAddr) ToRawSockAddrBuf() []byte {
|
| 237 | 237 |
func (a *SCTPAddr) String() string {
|
| 238 | 238 |
var b bytes.Buffer |
| 239 | 239 |
|
| 240 |
- for n, i := range a.IP {
|
|
| 241 |
- if a.IP[n].To4() != nil {
|
|
| 240 |
+ for n, i := range a.IPAddrs {
|
|
| 241 |
+ if i.IP.To4() != nil {
|
|
| 242 | 242 |
b.WriteString(i.String()) |
| 243 |
- } else if a.IP[n].To16() != nil {
|
|
| 243 |
+ } else if i.IP.To16() != nil {
|
|
| 244 | 244 |
b.WriteRune('[')
|
| 245 | 245 |
b.WriteString(i.String()) |
| 246 | 246 |
b.WriteRune(']')
|
| 247 | 247 |
} |
| 248 |
- if n < len(a.IP)-1 {
|
|
| 248 |
+ if n < len(a.IPAddrs)-1 {
|
|
| 249 | 249 |
b.WriteRune('/')
|
| 250 | 250 |
} |
| 251 | 251 |
} |
| ... | ... |
@@ -260,6 +281,7 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) {
|
| 260 | 260 |
tcpnet := "" |
| 261 | 261 |
switch network {
|
| 262 | 262 |
case "", "sctp": |
| 263 |
+ tcpnet = "tcp" |
|
| 263 | 264 |
case "sctp4": |
| 264 | 265 |
tcpnet = "tcp4" |
| 265 | 266 |
case "sctp6": |
| ... | ... |
@@ -271,26 +293,26 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) {
|
| 271 | 271 |
if len(elems) == 0 {
|
| 272 | 272 |
return nil, fmt.Errorf("invalid input: %s", addrs)
|
| 273 | 273 |
} |
| 274 |
- ipaddrs := make([]net.IP, 0, len(elems)) |
|
| 274 |
+ ipaddrs := make([]net.IPAddr, 0, len(elems)) |
|
| 275 | 275 |
for _, e := range elems[:len(elems)-1] {
|
| 276 | 276 |
tcpa, err := net.ResolveTCPAddr(tcpnet, e+":") |
| 277 | 277 |
if err != nil {
|
| 278 | 278 |
return nil, err |
| 279 | 279 |
} |
| 280 |
- ipaddrs = append(ipaddrs, tcpa.IP) |
|
| 280 |
+ ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone})
|
|
| 281 | 281 |
} |
| 282 | 282 |
tcpa, err := net.ResolveTCPAddr(tcpnet, elems[len(elems)-1]) |
| 283 | 283 |
if err != nil {
|
| 284 | 284 |
return nil, err |
| 285 | 285 |
} |
| 286 | 286 |
if tcpa.IP != nil {
|
| 287 |
- ipaddrs = append(ipaddrs, tcpa.IP) |
|
| 287 |
+ ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone})
|
|
| 288 | 288 |
} else {
|
| 289 | 289 |
ipaddrs = nil |
| 290 | 290 |
} |
| 291 | 291 |
return &SCTPAddr{
|
| 292 |
- IP: ipaddrs, |
|
| 293 |
- Port: tcpa.Port, |
|
| 292 |
+ IPAddrs: ipaddrs, |
|
| 293 |
+ Port: tcpa.Port, |
|
| 294 | 294 |
}, nil |
| 295 | 295 |
} |
| 296 | 296 |
|
| ... | ... |
@@ -357,15 +379,12 @@ func (c *SCTPConn) Read(b []byte) (int, error) {
|
| 357 | 357 |
} |
| 358 | 358 |
|
| 359 | 359 |
func (c *SCTPConn) SetInitMsg(numOstreams, maxInstreams, maxAttempts, maxInitTimeout int) error {
|
| 360 |
- param := InitMsg{
|
|
| 360 |
+ return setInitOpts(c.fd(), InitMsg{
|
|
| 361 | 361 |
NumOstreams: uint16(numOstreams), |
| 362 | 362 |
MaxInstreams: uint16(maxInstreams), |
| 363 | 363 |
MaxAttempts: uint16(maxAttempts), |
| 364 | 364 |
MaxInitTimeout: uint16(maxInitTimeout), |
| 365 |
- } |
|
| 366 |
- optlen := unsafe.Sizeof(param) |
|
| 367 |
- _, _, err := setsockopt(c.fd(), SCTP_INITMSG, uintptr(unsafe.Pointer(¶m)), uintptr(optlen)) |
|
| 368 |
- return err |
|
| 365 |
+ }) |
|
| 369 | 366 |
} |
| 370 | 367 |
|
| 371 | 368 |
func (c *SCTPConn) SubscribeEvents(flags int) error {
|
| ... | ... |
@@ -473,7 +492,7 @@ func (c *SCTPConn) GetDefaultSentParam() (*SndRcvInfo, error) {
|
| 473 | 473 |
|
| 474 | 474 |
func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
|
| 475 | 475 |
addr := &SCTPAddr{
|
| 476 |
- IP: make([]net.IP, n), |
|
| 476 |
+ IPAddrs: make([]net.IPAddr, n), |
|
| 477 | 477 |
} |
| 478 | 478 |
|
| 479 | 479 |
switch family := (*(*syscall.RawSockaddrAny)(ptr)).Addr.Family; family {
|
| ... | ... |
@@ -484,7 +503,7 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
|
| 484 | 484 |
for i := 0; i < n; i++ {
|
| 485 | 485 |
a := *(*syscall.RawSockaddrInet4)(unsafe.Pointer( |
| 486 | 486 |
uintptr(ptr) + size*uintptr(i))) |
| 487 |
- addr.IP[i] = a.Addr[:] |
|
| 487 |
+ addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:]}
|
|
| 488 | 488 |
} |
| 489 | 489 |
case syscall.AF_INET6: |
| 490 | 490 |
addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port))) |
| ... | ... |
@@ -493,7 +512,12 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
|
| 493 | 493 |
for i := 0; i < n; i++ {
|
| 494 | 494 |
a := *(*syscall.RawSockaddrInet6)(unsafe.Pointer( |
| 495 | 495 |
uintptr(ptr) + size*uintptr(i))) |
| 496 |
- addr.IP[i] = a.Addr[:] |
|
| 496 |
+ var zone string |
|
| 497 |
+ ifi, err := net.InterfaceByIndex(int(a.Scope_id)) |
|
| 498 |
+ if err == nil {
|
|
| 499 |
+ zone = ifi.Name |
|
| 500 |
+ } |
|
| 501 |
+ addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:], Zone: zone}
|
|
| 497 | 502 |
} |
| 498 | 503 |
default: |
| 499 | 504 |
return nil, fmt.Errorf("unknown address family: %d", family)
|
| ... | ... |
@@ -3,7 +3,6 @@ |
| 3 | 3 |
package sctp |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
- "fmt" |
|
| 7 | 6 |
"io" |
| 8 | 7 |
"net" |
| 9 | 8 |
"sync/atomic" |
| ... | ... |
@@ -115,31 +114,14 @@ func (c *SCTPConn) Close() error {
|
| 115 | 115 |
return syscall.EBADF |
| 116 | 116 |
} |
| 117 | 117 |
|
| 118 |
+// ListenSCTP - start listener on specified address/port |
|
| 118 | 119 |
func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
|
| 119 |
- af := syscall.AF_INET |
|
| 120 |
- switch net {
|
|
| 121 |
- case "sctp": |
|
| 122 |
- hasv6 := func(addr *SCTPAddr) bool {
|
|
| 123 |
- if addr == nil {
|
|
| 124 |
- return false |
|
| 125 |
- } |
|
| 126 |
- for _, ip := range addr.IP {
|
|
| 127 |
- if ip.To4() == nil {
|
|
| 128 |
- return true |
|
| 129 |
- } |
|
| 130 |
- } |
|
| 131 |
- return false |
|
| 132 |
- } |
|
| 133 |
- if hasv6(laddr) {
|
|
| 134 |
- af = syscall.AF_INET6 |
|
| 135 |
- } |
|
| 136 |
- case "sctp4": |
|
| 137 |
- case "sctp6": |
|
| 138 |
- af = syscall.AF_INET6 |
|
| 139 |
- default: |
|
| 140 |
- return nil, fmt.Errorf("invalid net: %s", net)
|
|
| 141 |
- } |
|
| 120 |
+ return ListenSCTPExt(net, laddr, InitMsg{NumOstreams: SCTP_MAX_STREAM})
|
|
| 121 |
+} |
|
| 142 | 122 |
|
| 123 |
+// ListenSCTPExt - start listener on specified address/port with given SCTP options |
|
| 124 |
+func ListenSCTPExt(network string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) {
|
|
| 125 |
+ af, ipv6only := favoriteAddrFamily(network, laddr, nil, "listen") |
|
| 143 | 126 |
sock, err := syscall.Socket( |
| 144 | 127 |
af, |
| 145 | 128 |
syscall.SOCK_STREAM, |
| ... | ... |
@@ -148,11 +130,30 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
|
| 148 | 148 |
if err != nil {
|
| 149 | 149 |
return nil, err |
| 150 | 150 |
} |
| 151 |
- err = setNumOstreams(sock, SCTP_MAX_STREAM) |
|
| 151 |
+ |
|
| 152 |
+ // close socket on error |
|
| 153 |
+ defer func() {
|
|
| 154 |
+ if err != nil {
|
|
| 155 |
+ syscall.Close(sock) |
|
| 156 |
+ } |
|
| 157 |
+ }() |
|
| 158 |
+ if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
|
|
| 159 |
+ return nil, err |
|
| 160 |
+ } |
|
| 161 |
+ err = setInitOpts(sock, options) |
|
| 152 | 162 |
if err != nil {
|
| 153 | 163 |
return nil, err |
| 154 | 164 |
} |
| 155 |
- if laddr != nil && len(laddr.IP) != 0 {
|
|
| 165 |
+ |
|
| 166 |
+ if laddr != nil {
|
|
| 167 |
+ // If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address |
|
| 168 |
+ if len(laddr.IPAddrs) == 0 {
|
|
| 169 |
+ if af == syscall.AF_INET {
|
|
| 170 |
+ laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero})
|
|
| 171 |
+ } else if af == syscall.AF_INET6 {
|
|
| 172 |
+ laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero})
|
|
| 173 |
+ } |
|
| 174 |
+ } |
|
| 156 | 175 |
err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR) |
| 157 | 176 |
if err != nil {
|
| 158 | 177 |
return nil, err |
| ... | ... |
@@ -167,40 +168,30 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
|
| 167 | 167 |
}, nil |
| 168 | 168 |
} |
| 169 | 169 |
|
| 170 |
-func (ln *SCTPListener) Accept() (net.Conn, error) {
|
|
| 170 |
+// AcceptSCTP waits for and returns the next SCTP connection to the listener. |
|
| 171 |
+func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) {
|
|
| 171 | 172 |
fd, _, err := syscall.Accept4(ln.fd, 0) |
| 172 | 173 |
return NewSCTPConn(fd, nil), err |
| 173 | 174 |
} |
| 174 | 175 |
|
| 176 |
+// Accept waits for and returns the next connection connection to the listener. |
|
| 177 |
+func (ln *SCTPListener) Accept() (net.Conn, error) {
|
|
| 178 |
+ return ln.AcceptSCTP() |
|
| 179 |
+} |
|
| 180 |
+ |
|
| 175 | 181 |
func (ln *SCTPListener) Close() error {
|
| 176 | 182 |
syscall.Shutdown(ln.fd, syscall.SHUT_RDWR) |
| 177 | 183 |
return syscall.Close(ln.fd) |
| 178 | 184 |
} |
| 179 | 185 |
|
| 186 |
+// DialSCTP - bind socket to laddr (if given) and connect to raddr |
|
| 180 | 187 |
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
|
| 181 |
- af := syscall.AF_INET |
|
| 182 |
- switch net {
|
|
| 183 |
- case "sctp": |
|
| 184 |
- hasv6 := func(addr *SCTPAddr) bool {
|
|
| 185 |
- if addr == nil {
|
|
| 186 |
- return false |
|
| 187 |
- } |
|
| 188 |
- for _, ip := range addr.IP {
|
|
| 189 |
- if ip.To4() == nil {
|
|
| 190 |
- return true |
|
| 191 |
- } |
|
| 192 |
- } |
|
| 193 |
- return false |
|
| 194 |
- } |
|
| 195 |
- if hasv6(laddr) || hasv6(raddr) {
|
|
| 196 |
- af = syscall.AF_INET6 |
|
| 197 |
- } |
|
| 198 |
- case "sctp4": |
|
| 199 |
- case "sctp6": |
|
| 200 |
- af = syscall.AF_INET6 |
|
| 201 |
- default: |
|
| 202 |
- return nil, fmt.Errorf("invalid net: %s", net)
|
|
| 203 |
- } |
|
| 188 |
+ return DialSCTPExt(net, laddr, raddr, InitMsg{NumOstreams: SCTP_MAX_STREAM})
|
|
| 189 |
+} |
|
| 190 |
+ |
|
| 191 |
+// DialSCTPExt - same as DialSCTP but with given SCTP options |
|
| 192 |
+func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) {
|
|
| 193 |
+ af, ipv6only := favoriteAddrFamily(network, laddr, raddr, "dial") |
|
| 204 | 194 |
sock, err := syscall.Socket( |
| 205 | 195 |
af, |
| 206 | 196 |
syscall.SOCK_STREAM, |
| ... | ... |
@@ -209,11 +200,29 @@ func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
|
| 209 | 209 |
if err != nil {
|
| 210 | 210 |
return nil, err |
| 211 | 211 |
} |
| 212 |
- err = setNumOstreams(sock, SCTP_MAX_STREAM) |
|
| 212 |
+ |
|
| 213 |
+ // close socket on error |
|
| 214 |
+ defer func() {
|
|
| 215 |
+ if err != nil {
|
|
| 216 |
+ syscall.Close(sock) |
|
| 217 |
+ } |
|
| 218 |
+ }() |
|
| 219 |
+ if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
|
|
| 220 |
+ return nil, err |
|
| 221 |
+ } |
|
| 222 |
+ err = setInitOpts(sock, options) |
|
| 213 | 223 |
if err != nil {
|
| 214 | 224 |
return nil, err |
| 215 | 225 |
} |
| 216 | 226 |
if laddr != nil {
|
| 227 |
+ // If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address |
|
| 228 |
+ if len(laddr.IPAddrs) == 0 {
|
|
| 229 |
+ if af == syscall.AF_INET {
|
|
| 230 |
+ laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero})
|
|
| 231 |
+ } else if af == syscall.AF_INET6 {
|
|
| 232 |
+ laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero})
|
|
| 233 |
+ } |
|
| 234 |
+ } |
|
| 217 | 235 |
err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR) |
| 218 | 236 |
if err != nil {
|
| 219 | 237 |
return nil, err |
| ... | ... |
@@ -34,10 +34,18 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
|
| 34 | 34 |
return nil, ErrUnsupported |
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 |
+func ListenSCTPExt(net string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) {
|
|
| 38 |
+ return nil, ErrUnsupported |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 37 | 41 |
func (ln *SCTPListener) Accept() (net.Conn, error) {
|
| 38 | 42 |
return nil, ErrUnsupported |
| 39 | 43 |
} |
| 40 | 44 |
|
| 45 |
+func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) {
|
|
| 46 |
+ return nil, ErrUnsupported |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 41 | 49 |
func (ln *SCTPListener) Close() error {
|
| 42 | 50 |
return ErrUnsupported |
| 43 | 51 |
} |
| ... | ... |
@@ -45,3 +53,7 @@ func (ln *SCTPListener) Close() error {
|
| 45 | 45 |
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
|
| 46 | 46 |
return nil, ErrUnsupported |
| 47 | 47 |
} |
| 48 |
+ |
|
| 49 |
+func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) {
|
|
| 50 |
+ return nil, ErrUnsupported |
|
| 51 |
+} |