Skip to content

Commit fb734d3

Browse files
authored
Merge pull request #58 from gomaja/feature-negotiated-stream-id-usage
feat: features usage of the exact negotiated stream id limit
2 parents eb4a888 + f45b98e commit fb734d3

7 files changed

Lines changed: 67 additions & 9 deletions

File tree

client.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import (
1414
)
1515

1616
// Dial establishes a M3UA connection as a client.
17-
//
18-
// After successfully established the connection with peer, state-changing
17+
// After successfully establishing the connection with peer, state-changing
1918
// signals and heartbeats are automatically handled background in another goroutine.
2019
func Dial(ctx context.Context, net string, laddr, raddr *sctp.SCTPAddr, cfg *Config) (*Conn, error) {
2120
var err error
@@ -42,6 +41,12 @@ func Dial(ctx context.Context, net string, laddr, raddr *sctp.SCTPAddr, cfg *Con
4241
return nil, err
4342
}
4443

44+
r, err := conn.sctpConn.GetStatus()
45+
if err != nil {
46+
return nil, fmt.Errorf("failed to get sctpConn status: %w", err)
47+
}
48+
conn.maxMessageStreamID = r.Ostreams - 1 // removing 1 for management messages of stream ID 0
49+
4550
go func() {
4651
conn.stateChan <- StateAspDown
4752
}()
@@ -50,10 +55,12 @@ func Dial(ctx context.Context, net string, laddr, raddr *sctp.SCTPAddr, cfg *Con
5055
select {
5156
case _, ok := <-conn.established:
5257
if !ok {
58+
conn.sctpConn.Close()
5359
return nil, ErrFailedToEstablish
5460
}
5561
return conn, nil
5662
case <-time.After(10 * time.Second):
63+
conn.sctpConn.Close()
5764
return nil, ErrTimeout
5865
}
5966
}

config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func NewHeartbeatInfo(interval, timer time.Duration, data []byte) *HeartbeatInfo
2525
}
2626
}
2727

28-
// Config is a configration that defines a M3UA server.
28+
// Config is a configuration that defines a M3UA server.
2929
type Config struct {
3030
*HeartbeatInfo
3131
AspIdentifier *params.Param

conn.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package m3ua
66

77
import (
88
"fmt"
9+
"math/rand"
910
"net"
1011
"sync"
1112
"time"
@@ -24,6 +25,9 @@ const (
2425

2526
// Conn represents a M3UA connection, which satisfies standard net.Conn interface.
2627
type Conn struct {
28+
// maxMessageStreamID is the maximum negotiated sctp stream ID used,
29+
// must not be zero, must vary from 1 to maxMessageStreamID
30+
maxMessageStreamID uint16
2731
// muState is to Lock when updating state
2832
muState *sync.RWMutex
2933
// mode represents the endpoint works as client or server
@@ -100,7 +104,9 @@ func (c *Conn) ReadPD() (pd *params.ProtocolDataPayload, err error) {
100104

101105
// Write writes data to the connection.
102106
func (c *Conn) Write(b []byte) (n int, err error) {
103-
return c.WriteToStream(b, c.StreamID())
107+
stream := c.chooseStreamID()
108+
109+
return c.WriteToStream(b, stream)
104110
}
105111

106112
// WriteToStream writes data to the connection and specific stream
@@ -133,7 +139,9 @@ func (c *Conn) WriteToStream(b []byte, streamID uint16) (n int, err error) {
133139

134140
// WritePD writes data with a specific mtp3 protocol data to the connection.
135141
func (c *Conn) WritePD(protocolData *params.Param) (n int, err error) {
136-
return c.WritePDToStream(protocolData, c.StreamID())
142+
stream := c.chooseStreamID()
143+
144+
return c.WritePDToStream(protocolData, stream)
137145
}
138146

139147
// WritePDToStream writes data with a specific mtp3 protocol data to the connection and specific stream
@@ -238,3 +246,19 @@ func (c *Conn) State() State {
238246
func (c *Conn) StreamID() uint16 {
239247
return c.sctpInfo.Stream
240248
}
249+
250+
// MaxMessageStreamID returns the maximum negotiated sctp stream ID
251+
// The streamID for sending a message must start from 1 up to maxMessageStreamID, 0 is reserved for management messages
252+
func (c *Conn) MaxMessageStreamID() uint16 {
253+
return c.maxMessageStreamID
254+
}
255+
256+
// chooseStreamID generates a random uint16 from 1 to max (inclusive)
257+
func (c *Conn) chooseStreamID() uint16 {
258+
if c.maxMessageStreamID == 1 {
259+
return 1
260+
}
261+
r := rand.New(rand.NewSource(time.Now().UnixNano()))
262+
randomNum := uint16(r.Intn(int(c.maxMessageStreamID)))
263+
return randomNum + 1
264+
}

fsm.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ const (
2525
StateSCTPRI
2626
)
2727

28+
func (s State) String() string {
29+
switch s {
30+
case StateAspDown:
31+
return "AspDown"
32+
case StateAspInactive:
33+
return "AspInactive"
34+
case StateAspActive:
35+
return "AspActive"
36+
case StateSCTPCDI:
37+
return "SCTPCDI"
38+
case StateSCTPRI:
39+
return "SCTPRI"
40+
default:
41+
return "Unknown"
42+
}
43+
}
44+
2845
func (c *Conn) handleStateUpdate(current State) error {
2946
c.muState.Lock()
3047
defer c.muState.Unlock()

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ go 1.23
44

55
require (
66
github.com/google/go-cmp v0.7.0
7-
github.com/ishidawataru/sctp v0.0.0-20250425063137-c91d9fd4afaf
7+
github.com/ishidawataru/sctp v0.0.0-20250427101207-53eab83c1cf6
88
github.com/pascaldekloe/goe v0.1.1
99
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
22
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
33
github.com/ishidawataru/sctp v0.0.0-20250425063137-c91d9fd4afaf h1:Yk0+ELKOCwbQ3ysGeTYMQYZJMb1rEj7VPTRXHqaEasM=
44
github.com/ishidawataru/sctp v0.0.0-20250425063137-c91d9fd4afaf/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
5+
github.com/ishidawataru/sctp v0.0.0-20250427101207-53eab83c1cf6 h1:BcV9jRUmgOhP6dWHo1awB1QQQjGMRUuS9E4/lmwcbQY=
6+
github.com/ishidawataru/sctp v0.0.0-20250427101207-53eab83c1cf6/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
57
github.com/pascaldekloe/goe v0.1.1 h1:Ah6WQ56rZONR3RW3qWa2NCZ6JAVvSpUcoLBaOmYFt9Q=
68
github.com/pascaldekloe/goe v0.1.1/go.mod h1:KSyfaxQOh0HZPjDP1FL/kFtbqYqrALJTaMafFUIccqU=

server.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package m3ua
66

77
import (
88
"context"
9-
"errors"
109
"fmt"
1110
"net"
1211
"sync"
@@ -39,7 +38,7 @@ func Listen(net string, laddr *sctp.SCTPAddr, cfg *Config) (*Listener, error) {
3938
}
4039

4140
// Accept waits for and returns the next connection to the listener.
42-
// After successfully established the association with peer, Payload can be read with Read() func.
41+
// After successfully establishing the association with peer, Payload can be read with Read() func.
4342
// Other signals are automatically handled background in another goroutine.
4443
func (l *Listener) Accept(ctx context.Context) (*Conn, error) {
4544
conn := &Conn{
@@ -63,9 +62,16 @@ func (l *Listener) Accept(ctx context.Context) (*Conn, error) {
6362
var ok bool
6463
conn.sctpConn, ok = c.(*sctp.SCTPConn)
6564
if !ok {
66-
return nil, errors.New("failed to assert conn")
65+
c.Close()
66+
return nil, fmt.Errorf("failed to assert server connection")
6767
}
6868

69+
r, err := conn.sctpConn.GetStatus()
70+
if err != nil {
71+
return nil, fmt.Errorf("failed to get sctpConn status: %w", err)
72+
}
73+
conn.maxMessageStreamID = r.Ostreams - 1 // removing 1 for management messages of stream ID 0
74+
6975
go func() {
7076
conn.stateChan <- StateAspDown
7177
}()
@@ -74,10 +80,12 @@ func (l *Listener) Accept(ctx context.Context) (*Conn, error) {
7480
select {
7581
case _, ok := <-conn.established:
7682
if !ok {
83+
conn.sctpConn.Close()
7784
return nil, ErrFailedToEstablish
7885
}
7986
return conn, nil
8087
case <-time.After(10 * time.Second):
88+
conn.sctpConn.Close()
8189
return nil, ErrTimeout
8290
}
8391
}

0 commit comments

Comments
 (0)