m3ua

package module
v0.0.0-...-70391ff Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 1, 2026 License: MIT Imports: 18 Imported by: 0

README

go-m3ua

Simple M3UA protocol implementation in the Go programming language.

CI status golangci-lint Go Reference GitHub

Quickstart

Installation

Run go mod tidy in your project's directory to collect the required packages automatically.

This project follows the Release Policy of Go.

Full SCTP socket validation runs on Linux. Non-Linux systems can build and run non-socket tests, but production M3UA associations require OS SCTP support.

Trying Examples

Working examples are available in examples directory. Just executing the following commands, you can see the client and server setting up M3UA connection.

# Run Server first
cd examples/server
go run m3ua-server.go

// Run Client then
cd examples/client
go run m3ua-client.go

There is also an example for Point Code format conversion, which works like this;

$ ./pc-conv -raw 1234 -variant 3-8-3
2023/04/05 06:07:08 PC successfully converted.
        Raw: 1234, Formatted: 0-154-2, Variant: 3-8-3
$ 
$ ./pc-conv -str 1-234-5 -variant 4-3-7
2023/04/05 06:07:08 PC successfully converted.
        Raw: 29957, Formatted: 1-234-5, Variant: 4-3-7
For Developers

The API design is kept as similar as possible to other protocols in standard net package. To establish M3UA connection as client/server, you can use Dial() and Listen()/Accept() without caring about the underlying SCTP association, as go-m3ua handles it together with M3UA ASPSM & ASPTM procedures.

Here is an example to develop your own M3UA client using go-m3ua.

First, you need to create *Config used to setup/maintain M3UA connection.

config := m3ua.NewClientConfig(
    &m3ua.HeartbeatInfo{
        Enabled:  true,
        Interval: time.Duration(3 * time.Second),
        Timer:    time.Duration(10 * time.Second),
    },
    0x11111111, // OriginatingPointCode
    0x22222222, // DestinationPointCode
    1,          // AspIdentifier
    params.TrafficModeLoadshare, // TrafficModeType
    0,                     // NetworkAppearance
    0,                     // CorrelationID
    []uint32{1, 2},        // RoutingContexts
    params.ServiceIndSCCP, // ServiceIndicator
    0, // NetworkIndicator
    0, // MessagePriority
    1, // SignalingLinkSelection
)
// set nil on unnecessary paramters.
config.CorrelationID = nil

Then, prepare network addresses and context and try to connect with Dial().

// setup SCTP peer on the specified IPs and Port.
raddr, err := sctp.ResolveSCTPAddr("sctp", SERVER_IPS)
if err != nil {
    log.Fatal(err)
}

ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()

conn, err := m3ua.Dial(ctx, "m3ua", nil, raddr, config)
if err != nil {
    log.Fatalf("Failed to dial M3UA: %s", err)
}
defer conn.Close()

Now you can Read() / Write() data from/to the remote endpoint.

if _, err := conn.Write(d); err != nil {
    log.Fatalf("Failed to write M3UA data: %s", err)
}
log.Printf("Successfully sent M3UA data: %x", d)

buf := make([]byte, 1500)
n, err := conn.Read(buf)
if err != nil {
    log.Fatal(err)
}

log.Printf("Successfully read M3UA data: %x", buf[:n])

See example/server directory for server example.

Supported Features

Messages
Class Message Supported Notes
Transfer Payload Data Message (DATA) Yes RFC4666#3.3
SSNM Destination Unavailable (DUNA) Yes RFC4666#3.4
Destination Available (DAVA) Yes
Destination State Audit (DAUD) Yes
Signalling Congestion (SCON) Yes
Destination User Part Unavailable (DUPU) Yes
Destination Restricted (DRST) Yes
ASPSM ASP Up Yes RFC4666#3.5
ASP Up Acknowledgement (ASP Up Ack) Yes
ASP Down Yes
ASP Down Acknowledgement (ASP Down Ack) Yes
Heartbeat (BEAT) Yes
Heartbeat Acknowledgement (BEAT Ack) Yes
RKM Registration Request (REG REQ) No Dynamic RKM is not implemented; RKM is answered with Unsupported Message Class per RFC4666#4.4.1.
Registration Response (REG RSP) No Same as above.
Deregistration Request (DEREG REQ) No Same as above.
Deregistration Response (DEREG RSP) No Same as above.
ASPTM ASP Active Yes RFC4666#3.7
ASP Active Acknowledgement (ASP Active Ack) Yes
ASP Inactive Yes
ASP Inactive Acknowledgement (ASP Inactive Ack) Yes
MGMT Error Yes RFC4666#3.8
Notify Yes
Parameters
Type Parameters Supported Notes
Common INFO String Yes
Routing Context Yes
Diagnostic Information Yes
Heartbeat Data Yes
Traffic Mode Type Yes
Error Code Yes
Status Yes
ASP Identifier Yes
M3UA-specific Network Appearance Yes
User/Cause Yes
Congestion Indications Yes
Concerned Destination Yes
Routing Key Yes
Registration Result Yes
Deregistration Result Yes
Local Routing Key Identifier Yes
Destination Point Code Yes
Service Indicators Yes
Originating Point Code List Yes
Protocol Data Yes
Registration Status Yes
Deregistration Status Yes

Compliance

This project targets RFC 4666 with current IANA SIGTRAN/SCTP assignments and SCTP behavior from RFC 9260 where it affects the M3UA transport. See docs/compliance.md for the current audit notes.

The module is still pre-v1. Some exported APIs may change before v1.0.0.

LICENSE

MIT

Documentation

Overview

Package m3ua provides easy and painless handling of M3UA protocol in pure Golang.

The API design is kept as similar as possible to other protocols in standard net package. To establish M3UA connection as client/server, you can use Dial() and Listen() / Accept() without caring about the underlying SCTP association, as go-m3ua handles it together with M3UA ASPSM & ASPTM procedures.

This package relies much on github.com/gomaja/go-sctp, as M3UA requires underlying SCTP connection,

Specification: https://www.rfc-editor.org/rfc/rfc4666.html

Security

RFC 4666 Section 6 carries one normative requirement, and it points elsewhere:

Implementations MUST follow the normative guidance of RFC3788 [11] on
the integration and usage of security mechanisms in SIGTRAN protocols.

RFC 3788 Section 7 states it plainly: "A SIGTRAN node MUST support IPsec and MAY support TLS." A library that only implements M3UA over SCTP cannot satisfy that node-level requirement by itself. IPsec ESP and IKE normally live in the host network stack, outside this package; the SCTP association opened here is an ordinary one to which host IPsec policy can apply.

Concretely, so that a deployment is not left to guess:

  • This package does NOT provide confidentiality, integrity, or peer authentication of its own. What it sends is exactly as protected as the IP path underneath it.

  • A conformant SIGTRAN node must provide the IPsec and IKE capabilities RFC 3788 requires. This package does not provide them, so applications claiming node-level conformance must supply that support through the host or another layer. RFC 3788 does not require every association to have IPsec enabled; enabling protection is a deployment-policy decision based on the threats to that association.

  • TLS is permitted by RFC 3788 as an alternative but is not usable here: RFC 3788 Section 6 requires TLS "on all bi-directional streams" of the SCTP association, which the underlying SCTP package does not implement. IPsec is the practical answer.

  • Nothing in M3UA authenticates a peer. An association that completes the SCTP handshake and sends ASP Up is treated as the ASP it claims to be, so network reachability is the authentication boundary: the M3UA port should be reachable only from the peers intended to use it.

Two related choices this package does make are documented where they are implemented, and a security review will want to know about them: an ASP's SCON is recorded apart from the SGP's own destination state, so a peer cannot inject SS7 congestion into what other ASPs are told when they audit (see Conn.PeerCongestionLevel); and an ASP Active from a peer in ASP-DOWN is refused rather than acknowledged, so an association that has not completed ASPSM cannot be driven into carrying traffic.

Index

Constants

View Source
const DefaultBroadcastFlowCacheEntries = 4096

DefaultBroadcastFlowCacheEntries bounds synchronization markers retained for one Broadcast AS. Eviction can only cause an extra Correlation ID to be sent.

View Source
const DefaultBroadcastFlowIdentifierBytes = 256

DefaultBroadcastFlowIdentifierBytes bounds the application-defined portion of a retained Broadcast flow key.

View Source
const DefaultDataQueueSize = 1024

DefaultDataQueueSize is the number of inbound DATA messages one association retains while its application is not reading. It is large enough to absorb ordinary traffic bursts without the 65,535-slot allocation every Conn used to make; sustained overflow remains non-blocking and reports local congestion.

View Source
const DefaultEstablishTimeout = 10 * time.Second

DefaultEstablishTimeout bounds the M3UA handshake once the association is up: how long Dial and Accept wait to reach ASP-ACTIVE.

View Source
const DefaultInitTimeout = 5 * time.Second

DefaultInitTimeout bounds one SCTP association attempt: how long Dial waits for the INIT-ACK before giving up and releasing the socket.

Dial makes at most one attempt and never retries; a caller that wants to keep trying loops over Dial and chooses its own cadence. Left to the kernel, the attempt runs to net.sctp.max_init_retransmits with the RTO doubling from net.sctp.rto_initial to net.sctp.rto_max — measured at nine INIT chunks over 342 seconds on Linux defaults, which is far too long for an application to react to anything.

Dial enforces the one-shot contract per socket by raising SCTP_RTOINFO's Initial and Max values beyond this budget before connecting. If no INIT-ACK arrives before the timeout, Dial returns ErrInitTimeout; if ctx is cancelled first, Dial aborts the in-flight socket promptly and returns ctx.Err().

View Source
const DefaultReadBufferSize = 65535

DefaultReadBufferSize is the read buffer size used when SCTPConfig leaves ReadBufferSize unset.

64 KiB accommodates any M3UA message in practical use — SS7 traffic interworked at an SG is capped at a 272-octet SIF, and even the larger blocks M3UA permits natively stay far below this — while costing one allocation per association rather than per read.

View Source
const DefaultRecoveryQueueBytes = 4 << 20

DefaultRecoveryQueueBytes bounds the encoded size of the DATA messages held for one AS during T(r). The count limit alone is insufficient because M3UA deliberately permits payloads larger than an SS7 MTP2 SIF.

View Source
const DefaultRecoveryQueueMessages = 1024

DefaultRecoveryQueueMessages bounds how many outbound DATA messages an SGP retains while an Application Server is AS-PENDING. RFC 4666 Section 4.3.2 recommends queuing during T(r), but an unbounded queue lets a failed AS exhaust the process before recovery can complete.

View Source
const DefaultRecoveryQueueTotalBytes = 16 << 20

DefaultRecoveryQueueTotalBytes is the listener-wide encoded DATA budget.

View Source
const DefaultRecoveryQueueTotalMessages = 4096

DefaultRecoveryQueueTotalMessages bounds recovery DATA retained across all Application Servers owned by one Listener.

View Source
const DefaultRecoveryTimer = 2 * time.Second

DefaultRecoveryTimer is the default for T(r), the AS-PENDING recovery timer of RFC 4666 Section 4.3.2.

The RFC gives no value for T(r) as it does for T(ack), leaving it to configuration; two seconds matches T(ack)'s default and keeps a failed changeover short.

View Source
const DefaultTAck = 2 * time.Second

DefaultTAck is the T(ack) timer's default value.

RFC 4666 Sections 4.3.4.1 to 4.3.4.4 each specify the same timer for the request they describe: "T(ack) is provisionable, with a default of 2 seconds."

View Source
const DefaultTAckRetries = 5

DefaultTAckRetries is how many times an unacknowledged ASPSM/ASPTM request is resent before the attempt is reported as failed.

RFC 4666 says to resend "until it receives the Ack", which is unbounded; a bound turns a peer that will never answer into a reportable error instead of an indefinite silent retry against a network already in trouble.

View Source
const M3UAPPID uint32 = 3

M3UAPPID is the SCTP Payload Protocol Identifier IANA assigned to M3UA.

RFC 4666 Section 7.1 says value 3 SHOULD be included in each SCTP DATA chunk. The value is in host order here, as expected by sctp.SCTPWrite; that method performs the conversion required for the SCTP ancillary data on the wire.

Variables

View Source
var (
	ErrSCTPNotAlive        = errors.New("SCTP is no longer alive")
	ErrInvalidState        = errors.New("invalid state")
	ErrNotEstablished      = errors.New("M3UA Conn not established")
	ErrFailedToEstablish   = errors.New("failed to establish M3UA Conn")
	ErrTimeout             = errors.New("timed out")
	ErrHeartbeatExpired    = errors.New("heartbeat timer expired")
	ErrFailedToPeelOff     = errors.New("failed to peel off Protocol Data")
	ErrFailedToWriteSignal = errors.New("failed to write signal")

	// ErrManagementBlocking reports that this SGP cannot service the request
	// because it is isolated from its nodal interworking function.
	//
	// RFC 4666 Section 4.7: "Upon receiving an ASP Up message while isolated
	// from the NIF, the SGP should respond with an Error ("Refused -
	// Management Blocking")", and the same for an ASP Active naming an
	// Application Server the SGP can no longer service.
	ErrManagementBlocking = errors.New("refused: isolated from the nodal interworking function")

	// ErrRoutingContextNotActive is returned when DATA is written for a Routing
	// Context that is inactive for this association.
	//
	// RFC 4666 Section 4.3.4.3 has the SGP answer "For the Application Servers
	// for which the ASP can be activated", so a partial acknowledgement leaves
	// the rest inactive and traffic for them has nowhere to go.
	ErrRoutingContextNotActive = errors.New("routing context is not active for this association")

	// ErrInvalidParameterValue is used when a parameter is well formed but
	// carries a value the message does not permit.
	//
	// RFC 4666 Section 3.8.1 gives the DUPU case as its own example: "The
	// 'Invalid Parameter Value' error is sent if a message is received with an
	// invalid parameter value (e.g., a DUPU message was received with a Mask
	// value other than \"0\"."
	ErrInvalidParameterValue = errors.New("parameter carries a value this message does not permit")

	// ErrAmbiguousRoutingContext is returned when DATA is written on an
	// association that carries several Routing Contexts and none has been
	// chosen with SelectRoutingContext.
	//
	// RFC 4666 Section 3.3.1 requires the Routing Context "to identify the
	// traffic flow" in exactly this case, and which flow a payload belongs to
	// is the caller's knowledge. Sending every configured context instead
	// identifies none of them.
	ErrAmbiguousRoutingContext = errors.New("several Routing Contexts configured and none selected")

	// ErrMissingProtocolData is used when a DATA message arrives without the
	// Protocol Data parameter, which RFC 4666 Section 3.3.1 lists as Mandatory.
	ErrMissingProtocolData = errors.New("DATA without Protocol Data parameter")

	// ErrMissingRoutingContext is used when DATA or SSNM omits its Routing
	// Context on an association carrying multiple Routing Keys. Their RFC 4666
	// message formats make the otherwise Conditional parameter mandatory in
	// that situation so the receiver can identify the concerned traffic flow.
	ErrMissingRoutingContext = errors.New("routing context required on a multi-flow association")

	// ErrMissingStatus is used when a Notify arrives without the Status
	// parameter, which RFC 4666 Section 3.8.2 lists as Mandatory.
	ErrMissingStatus = errors.New("notify without Status parameter")

	// ErrAspIDRequired is used by an SGP in response to an ASP Up message that
	// does not contain an ASP Identifier parameter when the SGP requires one.
	ErrAspIDRequired = errors.New("ASP Identifier required")

	// ErrInvalidAspIdentifier is returned when another ASP supporting the same
	// Application Server already claimed the identifier supplied in ASP Up.
	ErrInvalidAspIdentifier = errors.New("non-unique ASP identifier")

	// ErrUnsupportedTrafficMode is used by an SGP whose configured traffic
	// handling mode is incompatible with the one a peer requested in ASP
	// Active, per RFC 4666 Section 4.3.4.3.
	ErrUnsupportedTrafficMode = errors.New("unsupported traffic handling mode")

	// ErrMessageTooLarge is used when a peer sends a message that does not fit
	// in the read buffer. Raise SCTPConfig.ReadBufferSize to accept it.
	ErrMessageTooLarge = errors.New("received message larger than the read buffer")

	// ErrMissingAffectedPointCode is used when an SSNM message arrives without
	// the Affected Point Code parameter, which RFC 4666 Sections 3.4.1 to
	// 3.4.6 list as Mandatory in every SSNM message.
	ErrMissingAffectedPointCode = errors.New("SSNM message without Affected Point Code parameter")

	// ErrInvalidRoutingContext is used when a peer names a Routing Context
	// this connection is not configured for, per RFC 4666 Section 3.8.1.
	// RoutingContextError matches it through errors.Is.
	ErrInvalidRoutingContext = errors.New("invalid routing context")

	// ErrInvalidNetworkAppearance is used when an ASP names a network the SGP
	// has not configured, per RFC 4666 Section 3.8.1. NetworkAppearanceError
	// matches it through errors.Is and carries the value that must be returned
	// to the ASP.
	ErrInvalidNetworkAppearance = errors.New("invalid network appearance")

	// ErrInitTimeout is used when one SCTP association attempt did not complete
	// within Config.InitTimeout. Dial does not retry; the caller decides
	// whether to attempt again.
	ErrInitTimeout = errors.New("SCTP association attempt timed out")

	// ErrConnClosed is reported by Conn.Err when the association ended because
	// the owner closed it, rather than through any failure.
	ErrConnClosed = errors.New("connection closed by the local endpoint")

	// ErrMissingUserCause is used when a DUPU arrives without the User/Cause
	// parameter, which RFC 4666 Section 3.4.5 lists as Mandatory.
	ErrMissingUserCause = errors.New("DUPU message without User/Cause parameter")

	// ErrDataQueueFull is used when a received DATA payload cannot be queued
	// because the application is not reading. It is reported, never fatal: the
	// association is healthy and the peer is answerable, which is precisely
	// what would be lost by blocking instead.
	ErrDataQueueFull = errors.New("inbound DATA queue full: payload discarded")

	// ErrRecoveryQueueFull reports that an AS-PENDING recovery queue reached
	// its configured message or byte bound. Existing queued traffic is kept;
	// the new message is not partially retained.
	ErrRecoveryQueueFull = errors.New("AS recovery queue full")

	// ErrBroadcastFlowIdentifierTooLong reports an application classifier result
	// that would make a retained Broadcast flow key exceed its configured cap.
	ErrBroadcastFlowIdentifierTooLong = errors.New("broadcast flow identifier too long")

	// ErrNotificationQueueFull reports a peer whose blocked control stream has
	// exhausted the bounded mandatory-control queue. The association is closed.
	ErrNotificationQueueFull = errors.New("mandatory control queue full")

	// ErrIndicationQueueFull reports an association whose application stopped
	// consuming lossless Layer Management indications. The association is
	// closed rather than silently losing a state or management event.
	ErrIndicationQueueFull = errors.New("layer management indication queue full")

	// ErrNoActiveASP reports that an Application Server is neither able to
	// deliver DATA now nor in AS-PENDING where T(r) permits it to be queued.
	ErrNoActiveASP = errors.New("application server has no active ASP")

	// ErrSackDelayTooLarge is used when a delayed-SACK timer above the ceiling
	// RFC 9260 Section 6.2 sets is asked for:
	//
	//	An implementation MUST NOT allow the maximum delay (protocol
	//	parameter 'SACK.Delay') to be configured to be more than 500 ms.
	//	In other words, an implementation MAY lower the value of
	//	'SACK.Delay' below 500 ms but MUST NOT raise it above 500 ms.
	//
	// The value used to go straight to setsockopt. Linux enforces this ceiling
	// itself — measured on 6.x, sack_delay up to 500 is accepted and 501 and
	// above return EINVAL — so the association was never actually configured
	// beyond it there. What the caller got was "failed to set sack timer:
	// invalid argument", indistinguishable from every other setsockopt failure
	// and impossible to match on, and the conformance depended on the host
	// stack rather than on this package. A stack that does not enforce it would
	// have applied the value.
	ErrSackDelayTooLarge = errors.New("SACK delay above the 500 ms ceiling of RFC 9260 Section 6.2")

	// ErrNoDataStream is used when there is no SCTP stream DATA may travel on.
	// RFC 4666 Section 1.4.7 rule 1: "The DATA message MUST NOT be sent on
	// stream 0", so a peer that negotiates a single outbound stream leaves the
	// association with nowhere legal to carry traffic.
	ErrNoDataStream = errors.New("no SCTP stream available for DATA: stream 0 is reserved")

	// ErrNoConfiguredAS is used when a peer asks to activate a Routing Context
	// for which no Routing Key is defined. RFC 4666 Section 4.3.4.3: "If the RC
	// parameter is included in the ASP Active message and a corresponding RK
	// has not been previously defined [...] the peer MUST respond with an ERROR
	// message with the Error Code 'No configured AS for ASP'."
	// RoutingContextError matches it through errors.Is.
	ErrNoConfiguredAS = errors.New("no configured AS for ASP")

	// ErrTAckExpired is used when an ASPSM/ASPTM request went unacknowledged
	// through every T(ack) retry, so the peer is not completing the handshake.
	ErrTAckExpired = errors.New("T(ack) expired: peer never acknowledged the request")

	// ErrUnsupportedMode is used when a Conn holds a mode the state machine has
	// no procedures for. Only client and server exist today; IPSP (RFC 4666
	// Section 1.4.3.4) would add a third.
	ErrUnsupportedMode = errors.New("unsupported M3UA mode")
)

Error definitions.

View Source
var (
	// ErrMTP3RestartInProgress reports an affected scope that overlaps another
	// restart procedure which has not completed yet.
	ErrMTP3RestartInProgress = errors.New("overlapping MTP3 restart already in progress")
	// ErrMTP3RestartScope reports an update outside the affected destinations
	// atomically declared when the restart began.
	ErrMTP3RestartScope = errors.New("destination is outside the MTP3 restart scope")
	// ErrStaleMTP3Restart reports a handle whose restart has already completed
	// or no longer belongs to this Listener generation.
	ErrStaleMTP3Restart = errors.New("stale MTP3 restart handle")
	// ErrEmptyMTP3Restart reports a restart declaration with no affected scope.
	ErrEmptyMTP3Restart = errors.New("MTP3 restart has no affected destinations")
)

Functions

func DisableLogging

func DisableLogging()

DisableLogging disables the logging from the package. Logging is enabled by default. Records already queued may still complete on the logger selected when they were emitted; DisableLogging never waits for an application Writer.

func EnableLogging

func EnableLogging(l *log.Logger)

EnableLogging enables the logging from the package. If l is nil, it uses default logger provided by the package. Logging is enabled by default. The asynchronous, best-effort behaviour described by SetLogger applies.

See also: SetLogger.

func SetLogger

func SetLogger(l *log.Logger)

SetLogger replaces the standard logger with arbitrary *log.Logger.

This package prints just informational logs from goroutines working background that might help developers test the program but can be ignored safely. More important ones that needs any action by caller would be returned as errors. Logging is asynchronous and best-effort: a slow Writer never blocks protocol handling, and records are discarded when the bounded internal queue is full.

Types

type ASPAuthorizer

type ASPAuthorizer func(ASPIdentity) []uint32

ASPAuthorizer returns the Routing Contexts an accepted ASP is configured to serve. The result is copied and must be a subset of Config.RoutingContexts. Returning an empty slice authorizes no Application Server. The decision is resolved once at ASP Up and remains immutable for that association.

type ASPIdentity

type ASPIdentity struct {
	ASPIdentifier    uint32
	ASPIdentifierSet bool
	RemoteAddr       net.Addr
}

ASPIdentity identifies an ASP at the point its ASP Up is received. The ASP Identifier is optional in RFC 4666, so ASPIdentifierSet distinguishes an omitted identifier from the valid value zero. RemoteAddr is nil for a Conn constructed without a live SCTP association.

type ASState

type ASState uint8

ASState is the state of an Application Server, as distinct from the state of any one ASP serving it.

RFC 4666 Section 4.3.2: "The state of the AS is maintained in the M3UA layer on the SGPs. The state of an AS changes due to events. These events include: * ASP state transitions * Recovery timer triggers".

const (
	// ASDown is "The Application Server is unavailable.  This state implies
	// that all related ASPs are in ASP-DOWN state for this AS.  Initially the
	// AS will be in this state."
	ASDown ASState = iota
	// ASInactive is "The Application Server is available, but no application
	// traffic is active.  One or more related ASPs are in ASP-INACTIVE state,
	// and/or the number of related ASPs in ASP-ACTIVE state has not reached n".
	ASInactive
	// ASActive is "The Application Server is available and application traffic
	// is active."
	ASActive
	// ASPending is "An active ASP has transitioned to ASP-INACTIVE or ASP DOWN
	// and it was the last remaining active ASP in the AS.  A recovery timer
	// T(r) SHOULD be started, and all incoming signalling messages SHOULD be
	// queued by the SGP."
	ASPending
)

The Application Server states of RFC 4666 Section 4.3.2.

func (ASState) String

func (s ASState) String() string

type AffectedDestination

type AffectedDestination struct {
	NetworkAppearance    uint32
	NetworkAppearanceSet bool
	RoutingContext       uint32
	RoutingContextSet    bool
	PointCode            uint32
	Mask                 uint8
}

AffectedDestination identifies one destination range affected by an MTP3 restart. Mask is the number of wildcarded low-order point-code bits.

type AssociationStatus

type AssociationStatus struct {
	// State is the association's SCTP state, as a name (for example
	// "ESTABLISHED"). RFC 9260 Section 4 defines the state machine.
	State string
	// ReceiverWindow is the peer's advertised receiver window, in octets.
	ReceiverWindow uint32
	// UnackedDataChunks is how many DATA chunks this end has sent and not yet
	// had acknowledged. A number that stays high is the peer, or the path, not
	// keeping up.
	UnackedDataChunks uint16
	// PendingDataChunks is how many DATA chunks are queued here awaiting a
	// send, which is this end's own backlog rather than the peer's.
	PendingDataChunks uint16
	// InboundStreams and OutboundStreams are the stream counts negotiated at
	// association setup. OutboundStreams bounds the stream a message may be
	// sent on; see MaxMessageStreamID.
	InboundStreams  uint16
	OutboundStreams uint16
	// FragmentationPoint is the largest payload that will be sent without
	// being fragmented across DATA chunks, in octets.
	FragmentationPoint uint32
	// PrimaryCongestionWindow is the primary path's congestion window, in
	// octets.
	PrimaryCongestionWindow uint32
	// PrimarySmoothedRTT is the primary path's smoothed round-trip time.
	PrimarySmoothedRTT time.Duration
	// PrimaryRetransmissionTimeout is the primary path's current RTO. It is the
	// delay before an unacknowledged chunk is resent, so it bounds how long a
	// lost message can go unnoticed.
	PrimaryRetransmissionTimeout time.Duration
	// PrimaryMTU is the primary path's maximum transmission unit, in octets.
	PrimaryMTU uint32
}

AssociationStatus is the SCTP-level status of the association underneath an M3UA Conn.

RFC 4666 Section 4.2 describes M-SCTP_STATUS as a Layer Management query that "supports a Layer Management query of the local status of a particular SCTP association", answered by mapping the SCTP layer's own status into a confirm primitive; no peer protocol is involved. The values below are that status, read from the association at the moment of the call.

Round-trip time and retransmission timeout describe the primary path only. SCTP associations are multi-homed, and a secondary path can be in a quite different condition; this reports the path traffic is actually taking.

type BroadcastFlowIdentifier

type BroadcastFlowIdentifier func(*params.ProtocolDataPayload) (string, error)

BroadcastFlowIdentifier adds application semantics to the default Broadcast traffic-flow key. The library already keys by Network Appearance, Routing Context, and the complete MTP3 routing label. Applications may return, for example, an ISUP CIC or SCCP subsystem discriminator when independently sequenced flows share that label. The callback receives an owned copy and must return a stable identifier for equivalent flows.

type Config

type Config struct {
	*HeartbeatInfo
	*SCTPConfig
	// TAck is the T(ack) timer: how long to wait for an ASPSM/ASPTM
	// acknowledgement before resending the request. Zero selects DefaultTAck
	// (2 seconds, per RFC 4666 Sections 4.3.4.1 to 4.3.4.4).
	TAck time.Duration
	// TAckRetries bounds how many times a request is resent before the attempt
	// is reported as failed. Zero selects DefaultTAckRetries.
	TAckRetries int
	// EstablishTimeout bounds the M3UA handshake once the association is up.
	// Zero selects DefaultEstablishTimeout.
	EstablishTimeout time.Duration
	// DataQueueSize is the maximum number of inbound DATA messages retained for
	// Read, ReadPD, or ReadData. Values less than or equal to zero select
	// DefaultDataQueueSize. Once full, further DATA is discarded and local
	// congestion is reported without blocking the association dispatcher.
	DataQueueSize int
	AspIdentifier *params.Param
	// TrafficModeType is the default traffic-handling mode. Its value is copied
	// when a Conn or Listener is constructed; later mutations do not alter
	// established protocol policy.
	TrafficModeType *params.Param
	// AuthorizeASP optionally narrows the listener-wide Routing Context inventory
	// to the immutable set this peer may serve. Without it, accepted ASPs retain
	// the legacy policy of being configured for every Routing Context.
	AuthorizeASP ASPAuthorizer
	// TrafficModes optionally configures Traffic Mode per Routing Context. It
	// takes precedence over TrafficModeType and permits one association to serve
	// ASes with different modes. Values must be Override, Loadshare, or Broadcast.
	// The map is copied when a Conn or Listener is constructed.
	TrafficModes      map[uint32]uint32
	NetworkAppearance *params.Param
	// RecoveryTimer is T(r), the AS-PENDING recovery timer of RFC 4666
	// Section 4.3.2: "A recovery timer T(r) SHOULD be started, and all incoming
	// signalling messages SHOULD be queued by the SGP.  If an ASP becomes
	// ASP-ACTIVE before T(r) expires, the AS is moved to the AS-ACTIVE state".
	//
	// Zero selects DefaultRecoveryTimer. Server-side only: the AS state machine
	// is maintained on the SGP.
	RecoveryTimer time.Duration
	// RecoveryQueueMessages and RecoveryQueueBytes bound the DATA retained for
	// an AS while T(r) runs. Values less than or equal to zero select the
	// corresponding defaults. Both limits apply; reaching either refuses the
	// new message without disturbing traffic already queued.
	RecoveryQueueMessages int
	RecoveryQueueBytes    int
	// RecoveryQueueTotalMessages and RecoveryQueueTotalBytes cap the aggregate
	// queued plus in-flight recovery traffic across every AS on the Listener.
	// Values less than or equal to zero select the corresponding defaults.
	RecoveryQueueTotalMessages int
	RecoveryQueueTotalBytes    int
	// BroadcastFlowIdentifier optionally refines the default routing-label flow
	// key used for RFC 4666 Broadcast synchronization Correlation IDs. It is
	// snapshotted when the Listener first accepts an association.
	BroadcastFlowIdentifier BroadcastFlowIdentifier
	// BroadcastFlowCacheEntries bounds remembered flows per AS. Values less than
	// or equal to zero select DefaultBroadcastFlowCacheEntries. On overflow the
	// cache is cleared, which safely produces extra synchronization markers.
	BroadcastFlowCacheEntries int
	// BroadcastFlowIdentifierBytes bounds the identifier returned by
	// BroadcastFlowIdentifier. Values less than or equal to zero select the
	// default. Oversized identifiers are rejected rather than truncated.
	BroadcastFlowIdentifierBytes int

	RoutingContexts        *params.Param
	CorrelationID          *params.Param
	OriginatingPointCode   uint32
	DestinationPointCode   uint32
	ServiceIndicator       uint8
	NetworkIndicator       uint8
	MessagePriority        uint8
	SignalingLinkSelection uint8
}

Config is a configuration that defines a M3UA server.

func NewClientConfig

func NewClientConfig(hbInfo *HeartbeatInfo, opc, dpc, aspID, tmt, nwApr, corrID uint32, rtCtxs []uint32, si, ni, mp, sls uint8) *Config

NewClientConfig creates a new Config for Client.

The optional parameters that is not required (like CorrelationID) can be omitted by setting it to nil after created *Config.

func NewConfig

func NewConfig(opc, dpc uint32, si, ni, mp, sls uint8) *Config

NewConfig creates a new Config.

To set additional parameters, use constructors in param package or setters defined in this package. Note that the params left nil won't appear in the packets but the initialized params will, with zero values.

func NewServerConfig

func NewServerConfig(hbInfo *HeartbeatInfo, opc, dpc, aspID, tmt, nwApr, corrID uint32, rtCtxs []uint32, si, ni, mp, sls uint8) *Config

NewServerConfig creates a new Config for Server.

The optional parameters that is not required (like CorrelationID) can be omitted by setting it to nil after created *Config.

func (*Config) EnableHeartbeat

func (c *Config) EnableHeartbeat(interval, timer time.Duration) *Config

EnableHeartbeat enables M3UA BEAT with interval and expiration timer given.

Each BEAT carries freshly generated random Heartbeat Data, and the BEAT Ack's echo is validated against it, so BEAT/BEAT Ack pairs identify themselves; the HeartbeatInfo.Data field does not influence the exchange.

func (*Config) SetAspIdentifier

func (c *Config) SetAspIdentifier(id uint32) *Config

SetAspIdentifier sets AspIdentifier in Config.

func (*Config) SetCorrelationID

func (c *Config) SetCorrelationID(id uint32) *Config

SetCorrelationID sets CorrelationID in Config.

func (*Config) SetNetworkAppearance

func (c *Config) SetNetworkAppearance(nwApr uint32) *Config

SetNetworkAppearance sets NetworkAppearance in Config.

func (*Config) SetNoDelayConfig

func (c *Config) SetNoDelayConfig(noDelay bool) *Config

SetNoDelayConfig sets the SCTP_NODELAY option.

When noDelay is true, the Nagle-like bundling algorithm is disabled and user messages are sent as soon as possible. When false, small messages may be bundled to improve throughput.

func (*Config) SetRoutingContexts

func (c *Config) SetRoutingContexts(rtCtxs ...uint32) *Config

SetRoutingContexts sets RoutingContexts in Config.

func (*Config) SetSackConfig

func (c *Config) SetSackConfig(sackDelay, sackFrequency uint32) *Config

SetSackConfig sets the SCTP SACK timer configuration.

sackDelay is the number of milliseconds for the delayed SACK timer. RFC 9260 Section 6.2 allows any value up to 500 ms and forbids more. This setter is chainable and has no way to report that, so a larger value is refused when the association is set up, and Dial or Accept fails with ErrSackDelayTooLarge.

sackFrequency is the number of packets to receive before sending a SACK without waiting for the delay timer. Setting to 1 disables the delayed SACK algorithm.

Note: sackDelay=0, sackFrequency=1 (disables delayed SACK)

func (*Config) SetTrafficModeType

func (c *Config) SetTrafficModeType(tmType uint32) *Config

SetTrafficModeType sets TrafficModeType in Config.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn represents a M3UA connection, which satisfies the standard net.Conn interface.

func Dial

func Dial(ctx context.Context, net string, laddr, raddr *sctp.SCTPAddr, cfg *Config) (*Conn, error)

Dial establishes a M3UA connection as a client. After successfully establishing the connection with peer, state-changing signals and heartbeats are automatically handled background in another goroutine.

Dial makes at most one SCTP association attempt — one INIT if ctx is live, none if ctx is already done — bounded by Config.InitTimeout, and never retries. A caller that wants to keep trying loops over Dial and chooses its own cadence, which is the point: left to the kernel's defaults an unanswered attempt runs to nine INIT chunks over 342 seconds, far too long for an application to react to anything.

Every path releases the socket before returning. On a timeout or a cancelled context the association is aborted at the deadline, so nothing further is put on the wire and the descriptor and kernel-side association are gone by the time Dial returns.

The M3UA handshake that follows has its own budget, Config.EstablishTimeout, and observes ctx as well.

func (*Conn) ActivateRoutingContexts

func (c *Conn) ActivateRoutingContexts(routingContexts ...uint32) error

ActivateRoutingContexts starts the ASP Active procedure for the named Application Servers. With no arguments it requests every configured Routing Context. The request remains protected by T(ack) until all scoped Acks arrive.

func (*Conn) AssociationStatus

func (c *Conn) AssociationStatus() (*AssociationStatus, error)

AssociationStatus reports the SCTP association's own status, which is what RFC 4666 Section 4.2 calls an M-SCTP_STATUS request and its confirm. No M3UA peer protocol is invoked: the answer comes from the local SCTP layer.

setUpSocket has always read this at construction and kept only the negotiated outbound stream count, discarding the rest, so an operator had no way to see the round-trip time, the retransmission timeout, or the queue depths of an association they were running. Those are exactly the numbers that distinguish a link that is slow from one that is failing.

It returns an error once the association is gone.

func (*Conn) Close

func (c *Conn) Close() error

Close closes the connection.

Err then reports ErrConnClosed, unless the association had already ended for some other reason, in which case that reason is kept.

func (*Conn) DeactivateRoutingContexts

func (c *Conn) DeactivateRoutingContexts(routingContexts ...uint32) error

DeactivateRoutingContexts starts the ASP Inactive procedure for the named Application Servers. With no arguments it requests every configured Routing Context. Local traffic eligibility changes only as the scoped Acks arrive.

func (*Conn) DestinationRanges

func (c *Conn) DestinationRanges() []DestinationRange

DestinationRanges returns every range visible through DestinationState in update order. On a multi-Routing-Context association it contains only all-context baselines.

func (*Conn) DestinationRangesForNetwork

func (c *Conn) DestinationRangesForNetwork(networkAppearance uint32) []DestinationRange

DestinationRangesForNetwork returns every range visible for an explicit Network Appearance in update order. On a multi-Routing-Context association it contains only all-context baselines.

func (*Conn) DestinationRangesForNetworkAndRoutingContext

func (c *Conn) DestinationRangesForNetworkAndRoutingContext(networkAppearance, routingContext uint32) []DestinationRange

DestinationRangesForNetworkAndRoutingContext returns every all-context and per-context range visible in one Network Appearance and Routing Context, in update order.

func (*Conn) DestinationState

func (c *Conn) DestinationState(pointCode uint32) DestinationState

DestinationState reports the availability of an SS7 destination as most recently advertised by the peer. On a single-Routing-Context association it resolves that flow. On a multi-Routing-Context association the legacy API is ambiguous and therefore sees only all-context baselines; use DestinationStateForNetworkAndRoutingContext for a per-flow answer. Destinations the peer has not reported on are Available, since SSNM carries changes rather than a full inventory.

func (*Conn) DestinationStateForNetwork

func (c *Conn) DestinationStateForNetwork(networkAppearance, pointCode uint32) DestinationState

DestinationStateForNetwork reports a destination's state within an explicit Network Appearance. On a multi-Routing-Context association it sees only all-context baselines; use DestinationStateForNetworkAndRoutingContext for a per-flow answer.

func (*Conn) DestinationStateForNetworkAndRoutingContext

func (c *Conn) DestinationStateForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32) DestinationState

DestinationStateForNetworkAndRoutingContext reports a destination's state in one explicit Network Appearance and Routing Context. All-context baseline ranges participate, and the newest covering update wins.

func (*Conn) DestinationStates

func (c *Conn) DestinationStates() map[uint32]DestinationState

DestinationStates returns the exact, Mask-zero destination records visible through DestinationState. The legacy map cannot represent ranges; use DestinationRanges for a lossless snapshot.

func (*Conn) DestinationStatesForNetwork

func (c *Conn) DestinationStatesForNetwork(networkAppearance uint32) map[uint32]DestinationState

DestinationStatesForNetwork returns the exact, Mask-zero records visible for an explicit Network Appearance. On a multi-Routing-Context association it is limited to all-context baselines. Use DestinationRangesForNetworkAndRoutingContext for a lossless per-flow view.

func (*Conn) Done

func (c *Conn) Done() <-chan struct{}

Done returns a channel closed when the association ends, for whatever reason.

It follows context.Context's shape: select on Done, then ask Err what happened. Without it the only way to notice an association had gone was to poll State until it read ASP-DOWN, or to wait for a Read or Write to start failing — neither of which says why.

func (*Conn) Err

func (c *Conn) Err() error

Err reports why the association ended, or nil while it is still up.

It distinguishes the cases an application has to tell apart: ErrConnClosed for its own shutdown, ErrHeartbeatExpired for a peer that stopped answering, a context error for a cancelled owner, and the underlying read or protocol error otherwise. Read and Write report ErrNotEstablished for all of them.

func (*Conn) LocalAddr

func (c *Conn) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Conn) ManagementIndications

func (c *Conn) ManagementIndications() <-chan *ManagementIndication

ManagementIndications reports what RFC 4666 Section 1.6.3 calls the M3UA to Layer Management indications, and is closed when the association ends.

Section 4.2: "M-NOTIFY indication and M-ERROR indication primitives indicate to Layer Management the notification or error information contained in a received M3UA Notify or Error message, respectively." Both messages were decoded correctly and then written to a log line and dropped, so the information reached an operator reading logs and nothing else: an application could not see that the peer had reported AS-INACTIVE, or Insufficient ASP Resources, or refused something with an error code.

Delivery never stalls the dispatcher. A reader that allows the bounded queue to fill closes the association with ErrIndicationQueueFull, rather than silently losing M-NOTIFY or M-ERROR.

go func() {
	for ind := range conn.ManagementIndications() {
		log.Printf("%v: %s", ind.Kind, ind.Description)
	}
}()

func (*Conn) MaxMessageStreamID

func (c *Conn) MaxMessageStreamID() uint16

MaxMessageStreamID returns the highest negotiated SCTP stream ID DATA may use. Valid explicit DATA stream IDs run from 1 through this value; stream 0 is reserved for management, and a zero result means no DATA stream is available.

func (*Conn) PeerASPIdentifier

func (c *Conn) PeerASPIdentifier() (uint32, bool)

PeerASPIdentifier returns the ASP Identifier the peer supplied in ASP Up. The boolean is false when the peer omitted the optional parameter.

func (*Conn) PeerCongestionLevel

func (c *Conn) PeerCongestionLevel() uint8

PeerCongestionLevel returns the congestion level the peer last reported about itself, or 0 if it has reported none.

This is the ASP-to-peer direction of SCON, which RFC 4666 Section 3.4.4 describes as "indicating that the congestion level of the M3UA layer or the ASP has changed". It says nothing about whether any SS7 destination is reachable, which is why it is reported separately from DestinationState: an SGP that folded the two together would answer another ASP's DAUD with congestion this ASP invented.

The values are those of Section 3.4.4: 0 "No Congestion or Undefined", and 1 to 3 for the national congestion levels.

func (*Conn) Read

func (c *Conn) Read(b []byte) (n int, err error)

Read reads data from the connection.

M3UA is message-oriented, so one Read yields the payload of one DATA message and never joins two together. If b is too small for it, the payload is truncated to fit and io.ErrShortBuffer is returned alongside the number of bytes actually written: the remainder cannot be recovered, because the message has already left the queue. Use ReadPD to take the payload whole without sizing a buffer for it, or ReadData when Network Appearance or Routing Context matters.

Read used to return len(pd.Data) regardless of how much it had copied, so a caller with a short buffer was told it had received more bytes than exist in b — and the idiomatic b[:n] then panicked with a slice-bounds error, on data chosen by the peer.

func (*Conn) ReadData

func (c *Conn) ReadData() (*DataMessage, error)

ReadData reads the next DATA message from the connection, reporting the payload together with its Network Appearance and Routing Context.

It is the read-side counterpart of the WithRoutingContext writes: an application serving several networks or Routing Contexts over one association needs this to distribute an inbound message to the right network and flow, and to answer in the scope the request arrived on. Read and ReadPD take from the same queue, so a caller that does not care about that scope can keep using either.

func (*Conn) ReadPD

func (c *Conn) ReadPD() (pd *params.ProtocolDataPayload, err error)

ReadPD reads the next ProtocolDataPayload from the connection.

The Network Appearance and Routing Context the message named are not reported here; use ReadData when the association carries more than one SS7 network or traffic flow.

func (*Conn) RemoteAddr

func (c *Conn) RemoteAddr() net.Addr

RemoteAddr returns the remote network address.

func (*Conn) ReportDestinationRange

func (c *Conn) ReportDestinationRange(pointCode uint32, mask uint8, state DestinationState) error

ReportDestinationRange records and synchronously reports a destination range.

func (*Conn) ReportDestinationRangeForNetwork

func (c *Conn) ReportDestinationRangeForNetwork(networkAppearance, pointCode uint32, mask uint8, state DestinationState) error

ReportDestinationRangeForNetwork records and synchronously reports a range in an explicit Network Appearance.

func (*Conn) ReportDestinationRangeForNetworkAndRoutingContext

func (c *Conn) ReportDestinationRangeForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32, mask uint8, state DestinationState) error

ReportDestinationRangeForNetworkAndRoutingContext records and synchronously reports a destination range in one explicit traffic scope.

func (*Conn) ReportDestinationState

func (c *Conn) ReportDestinationState(pointCode uint32, state DestinationState) error

ReportDestinationState records and synchronously reports a destination.

func (*Conn) ReportDestinationStateForNetwork

func (c *Conn) ReportDestinationStateForNetwork(networkAppearance, pointCode uint32, state DestinationState) error

ReportDestinationStateForNetwork records and synchronously reports a destination in an explicit Network Appearance.

func (*Conn) ReportDestinationStateForNetworkAndRoutingContext

func (c *Conn) ReportDestinationStateForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32, state DestinationState) error

ReportDestinationStateForNetworkAndRoutingContext records and synchronously reports one destination in one explicit traffic scope.

func (*Conn) SelectRoutingContext

func (c *Conn) SelectRoutingContext(rtCtx uint32) error

SelectRoutingContext chooses which Routing Context outbound DATA names by default.

It sets association state, so it suits a caller that carries one traffic flow per association. It is NOT a way to switch flows from message to message: where several goroutines write to one Conn, a second goroutine's selection can land between this goroutine's selection and its write, and the payload goes out naming the other flow — a silent mis-identification of exactly the thing Section 3.3.1 requires the parameter to get right. Callers sending for more than one flow use the WithRoutingContext writes, which carry the context on the message it describes.

It is needed only when several Routing Contexts are configured for the association. RFC 4666 Section 3.3.1 declares the parameter singular — "Routing Context: 32 bits (unsigned integer)" — and requires it to pick one flow out of several: "Where multiple Routing Keys and Routing Contexts are used across a common association, the Routing Context MUST be sent to identify the traffic flow, assisting in the internal distribution of Data messages." Sending the whole configured set instead identifies nothing.

With one Routing Context configured there is nothing to choose and DATA uses it; with none, the parameter is omitted, which the same section permits: "Where a Routing Key has not been coordinated between the SGP and ASP, sending of Routing Context is not required."

The context must be one of those configured for this association.

func (*Conn) SetDeadline

func (c *Conn) SetDeadline(t time.Time) error

SetDeadline sets the read and write deadlines associated.

func (*Conn) SetDestinationRange

func (c *Conn) SetDestinationRange(pointCode uint32, mask uint8, state DestinationState)

SetDestinationRange records an all-Routing-Context destination range in the configured Network Appearance. Mask wildcards that many low-order bits.

func (*Conn) SetDestinationRangeForNetwork

func (c *Conn) SetDestinationRangeForNetwork(networkAppearance, pointCode uint32, mask uint8, state DestinationState)

SetDestinationRangeForNetwork records an all-Routing-Context range within an explicit Network Appearance.

func (*Conn) SetDestinationRangeForNetworkAndRoutingContext

func (c *Conn) SetDestinationRangeForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32, mask uint8, state DestinationState)

SetDestinationRangeForNetworkAndRoutingContext records a destination range in one explicit Network Appearance and Routing Context.

func (*Conn) SetDestinationState

func (c *Conn) SetDestinationState(pointCode uint32, state DestinationState)

SetDestinationState records a destination's availability at an SGP so that DAUD audits can be answered from it. An SG learns this state from the SS7 network, which is outside M3UA, so the application supplies it.

On an accepted association this writes the listener's node-wide view, which is shared by every ASP it serves and outlives any one of them. Prefer Listener.SetDestinationState, which says so; this form remains because an operator holding a Conn should not have to find the Listener to record what the SS7 network just told it.

func (*Conn) SetDestinationStateForNetwork

func (c *Conn) SetDestinationStateForNetwork(networkAppearance, pointCode uint32, state DestinationState)

SetDestinationStateForNetwork records an SGP destination state within an explicit Network Appearance.

func (*Conn) SetDestinationStateForNetworkAndRoutingContext

func (c *Conn) SetDestinationStateForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32, state DestinationState)

SetDestinationStateForNetworkAndRoutingContext records one exact destination in an explicit Network Appearance and Routing Context.

func (*Conn) SetReadDeadline

func (c *Conn) SetReadDeadline(t time.Time) error

SetReadDeadline sets the deadline for future Read, ReadPD and ReadData calls. A zero time removes it. An expired deadline makes those calls return os.ErrDeadlineExceeded, which reports Timeout() true, and leaves the association usable so the caller can read again.

The deadline is kept here rather than pushed down to the SCTP socket. The only reader of that socket is this package's own receive loop, which is meant to wait indefinitely for the peer; a deadline reaching it did not bound the caller's Read at all — Read has never consulted one — and instead expired the receive loop, whose error path closes the association. Setting a read deadline on an idle, healthy, ASP-ACTIVE association therefore tore it down, took the state to ASP-DOWN, and turned every subsequent Read and Write into ErrNotEstablished. That is the opposite of what net.Conn promises, where a read timeout is recoverable.

func (*Conn) SetSctpNoDelayConfig

func (c *Conn) SetSctpNoDelayConfig(noDelay bool) error

SetSctpNoDelayConfig sets the SCTP_NODELAY option on an active connection.

When noDelay is true, the Nagle-like bundling algorithm is disabled and user messages are sent as soon as possible. When false, small messages may be bundled to improve throughput.

func (*Conn) SetSctpSackConfig

func (c *Conn) SetSctpSackConfig(sackDelay, sackFrequency uint32) error

SetSctpSackConfig sets the SCTP SACK timer configuration on an active connection.

sackDelay is the number of milliseconds for the delayed SACK timer. RFC 9260 Section 6.2 allows any value up to 500 ms and forbids more; anything above that is refused with ErrSackDelayTooLarge rather than passed to the kernel.

sackFrequency is the number of packets to receive before sending a SACK without waiting for the delay timer. Setting to 1 disables the delayed SACK algorithm.

Note: sackDelay=0, sackFrequency=1 (disables delayed SACK)

func (*Conn) SetWriteDeadline

func (c *Conn) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets the deadline for future Write calls.

func (*Conn) Shutdown

func (c *Conn) Shutdown() error

Shutdown ends the association the orderly way, then closes it.

RFC 4666 Section 4.9 offers a node two ways to stop communicating:

a) Send the sequence of ASP-INACTIVE, DEREG (optionally whenever
   dynamic registration is used), and ASP-DOWN messages and perform
   the SCTP Shutdown procedure after that.

b) Just do the SCTP Shutdown procedure.

Close is (b), which is why it stays abrupt. Shutdown is (a): the peer is told traffic is stopping and that this ASP is going down before the association disappears underneath it, so it can move traffic rather than discover the loss from a vanished socket. DEREG is not sent, since this library does not support dynamic registration and the RFC makes it optional in any case.

Each request completes its T(ack) procedure before the next is sent. Use ShutdownContext to bound the operation more tightly than the configured retry budget.

func (*Conn) ShutdownContext

func (c *Conn) ShutdownContext(ctx context.Context) error

ShutdownContext is Shutdown with caller-controlled cancellation. Cancellation stops the outstanding T(ack) request and still releases SCTP.

func (*Conn) SignallingStatus

func (c *Conn) SignallingStatus() <-chan *DestinationStatus

SignallingStatus returns the channel on which SSNM status changes are delivered. An MTP3-User reads it to stop, restart, or throttle traffic to a point code as RFC 4666 Section 4.5 requires.

The channel is bounded so a peer cannot block the dispatcher. If the reader falls behind, the oldest queued status is replaced with a ResyncRequired marker; callers must then query destination state and treat peer-only SCON and DUPU information as unknown until the peer reports it again.

Closing the Conn closes the channel, so

for st := range conn.SignallingStatus() { ... }

terminates with the association rather than parking forever. Anything already buffered is still delivered before the range ends.

func (*Conn) State

func (c *Conn) State() State

State returns current state of Conn.

func (*Conn) StateChanges

func (c *Conn) StateChanges() <-chan State

StateChanges reports every ASP state transition this association makes, in order, and is closed when the association ends.

RFC 4666 Section 1.6.3 gives Layer Management an indication for each of these transitions -- M-ASP_UP, M-ASP_DOWN, M-ASP_ACTIVE and M-ASP_INACTIVE, in their confirm and indication forms -- and Section 4.2.1 has the M3UA layer invoke them "upon successful state changes". State() could only answer the question when asked, so a management layer had to poll for edges the library already knew about, and a transition that came and went between two polls was simply lost.

Delivery never stalls the dispatcher. A reader that allows the bounded queue to fill closes the association with ErrIndicationQueueFull, rather than receiving a partial transition history. Read it in a dedicated goroutine if every edge matters.

go func() {
	for st := range conn.StateChanges() {
		log.Printf("ASP state: %v", st)
	}
	// the channel closes with the association
}()

func (*Conn) StreamID

func (c *Conn) StreamID() uint16

StreamID returns sctpInfo.Stream of Conn.

This is the outbound template, not the stream any received message arrived on; see Conn.recvStream for that.

func (*Conn) Write

func (c *Conn) Write(b []byte) (n int, err error)

Write writes data to the connection.

A successful call returns len(b), as io.Writer requires: the payload is wrapped in an M3UA DATA message before it goes out, but the count reported is the caller's, not the message's.

func (*Conn) WritePD

func (c *Conn) WritePD(protocolData *params.Param) (n int, err error)

WritePD writes data with a specific mtp3 protocol data to the connection.

A successful call reports the number of SS7 user octets carried inside protocolData, so the count means the same thing as Write's.

func (*Conn) WritePDToStream

func (c *Conn) WritePDToStream(protocolData *params.Param, streamID uint16) (n int, err error)

WritePDToStream writes data with a specific mtp3 protocol data to the connection and specific stream.

The stream range and errors are the same as WriteToStream.

As with WritePD, a successful call reports the SS7 user octets carried.

func (*Conn) WritePDToStreamWithRoutingContext

func (c *Conn) WritePDToStreamWithRoutingContext(protocolData *params.Param, streamID uint16, rtCtx uint32) (n int, err error)

WritePDToStreamWithRoutingContext writes data with a specific mtp3 protocol data on a specific stream, naming the traffic flow it belongs to.

See WriteWithRoutingContext for why the flow belongs on the message and WriteToStream for the permitted stream range.

func (*Conn) WritePDWithRoutingContext

func (c *Conn) WritePDWithRoutingContext(protocolData *params.Param, rtCtx uint32) (n int, err error)

WritePDWithRoutingContext writes data with a specific mtp3 protocol data, naming the traffic flow it belongs to.

See WriteWithRoutingContext for why the flow belongs on the message. This is the form to use when one association carries several Routing Contexts and more than one goroutine writes to it.

func (*Conn) WriteSignal

func (c *Conn) WriteSignal(m3 messages.M3UA) (n int, err error)

WriteSignal writes any type of M3UA signals on top of SCTP Connection.

It takes a message rather than a buffer, so a successful call reports the encoded length of that message.

func (*Conn) WriteToStream

func (c *Conn) WriteToStream(b []byte, streamID uint16) (n int, err error)

WriteToStream writes data to the connection and specific stream.

streamID must be between 1 and MaxMessageStreamID, inclusive. Stream 0 returns ErrNoDataStream; a value above the negotiated maximum returns an *InvalidSCTPStreamIDError.

As with Write, a successful call returns len(b): the count is the payload the caller handed over, not the size of the M3UA message it was wrapped in.

func (*Conn) WriteToStreamWithRoutingContext

func (c *Conn) WriteToStreamWithRoutingContext(b []byte, streamID uint16, rtCtx uint32) (n int, err error)

WriteToStreamWithRoutingContext writes data on a specific stream, naming the traffic flow it belongs to.

See WriteWithRoutingContext for why the flow belongs on the message and WriteToStream for the permitted stream range.

func (*Conn) WriteWithRoutingContext

func (c *Conn) WriteWithRoutingContext(b []byte, rtCtx uint32) (n int, err error)

WriteWithRoutingContext writes data to the connection, naming the traffic flow it belongs to.

This is the concurrency-safe form of SelectRoutingContext followed by Write. RFC 4666 Section 3.3.1 makes the Routing Context a property of the message — it identifies "the traffic flow" the payload belongs to — so on an association carrying several flows it has to travel with the payload. Held on the Conn instead, a second goroutine's selection can land between this goroutine's selection and its write, and the payload goes out naming the other flow.

As with Write, a successful call returns len(b).

type DataMessage

type DataMessage struct {
	// ProtocolData is the MTP3 payload and its routing label.
	ProtocolData *params.ProtocolDataPayload

	// NetworkAppearance is the SS7 network the DATA belongs to, valid only
	// when NetworkAppearanceSet is true. Presence is separate because zero is
	// a legitimate configured value and omission is permitted on a dedicated
	// association.
	NetworkAppearance    uint32
	NetworkAppearanceSet bool

	// RoutingContext is the traffic flow the DATA named, valid only when
	// RoutingContextSet is true.
	//
	// The parameter is Conditional, so its absence is not a fault: "Where a
	// Routing Key has not been coordinated between the SGP and ASP, sending of
	// Routing Context is not required." A separate bool rather than a zero
	// value, because 0 is a Routing Context a peer may legitimately use.
	RoutingContext    uint32
	RoutingContextSet bool
}

DataMessage is one received DATA message: the MTP3 payload together with the SS7 network and traffic flow the sending peer named for it.

RFC 4666 Section 3.3.1 gives Network Appearance and Routing Context exactly these jobs: Network Appearance identifies the SS7 network, while "Where multiple Routing Keys and Routing Contexts are used across a common association, the Routing Context MUST be sent to identify the traffic flow, assisting in the internal distribution of Data messages" — so a receiver that is handed the payload alone cannot perform the distribution those parameters exist for, and cannot tell which network or flow to answer on either.

type DestinationRange

type DestinationRange struct {
	// NetworkAppearance identifies the SS7 network, valid only when
	// NetworkAppearanceSet is true.
	NetworkAppearance    uint32
	NetworkAppearanceSet bool
	// RoutingContext identifies one Application Server flow, valid only when
	// RoutingContextSet is true. An absent scope is an all-context baseline.
	RoutingContext    uint32
	RoutingContextSet bool
	// PointCode is the affected 24-bit SS7 destination or range member.
	PointCode uint32
	// Mask is the number of wildcarded low-order point-code bits.
	Mask uint8
	// State is the availability installed by this update.
	State DestinationState
}

DestinationRange is one recorded SS7 destination-state update. Mask names how many low-order bits of PointCode are wildcarded; values of 24 or greater cover the entire Network Appearance. A range without RoutingContextSet is an all-Routing-Context baseline and is considered alongside a scoped range.

type DestinationState

type DestinationState uint8

DestinationState is the availability of an SS7 destination as most recently reported by the peer through the SSNM procedures of RFC 4666 Section 4.5.

const (
	// DestinationAvailable is the initial assumption for any destination the
	// peer has not reported on, and the state a DAVA restores.
	DestinationAvailable DestinationState = iota
	// DestinationUnavailable is set by DUNA: the SG cannot reach the
	// destination and the MTP3-User is expected to stop traffic to it.
	DestinationUnavailable
	// DestinationRestricted is set by DRST: the destination is reachable but
	// the SG would prefer traffic went elsewhere.
	DestinationRestricted
	// DestinationCongested is set by SCON: traffic should be reduced.
	DestinationCongested
)

Destination state definitions.

func (DestinationState) String

func (s DestinationState) String() string

type DestinationStatus

type DestinationStatus struct {
	// ResyncRequired means one or more preceding status indications were
	// evicted because the receiver did not keep up. Query destination state and
	// treat peer-only SCON or DUPU information as unknown until the peer reports
	// it again.
	ResyncRequired bool
	// PointCode is the affected SS7 destination.
	PointCode uint32
	// Mask is the Affected Point Code mask. It names how many low-order bits
	// of PointCode are wildcarded; values of 24 or greater cover the entire
	// Network Appearance.
	Mask uint8
	// NetworkAppearance is the SS7 network containing PointCode, valid only
	// when NetworkAppearanceSet is true. The parameter is optional, and zero is
	// a legitimate explicit value, so presence cannot be inferred from it.
	NetworkAppearance    uint32
	NetworkAppearanceSet bool
	// RoutingContexts identify the Application Server traffic flows this status
	// concerns, valid only when RoutingContextSet is true. SSNM permits a list,
	// and zero is a legitimate value, so neither length nor value substitutes
	// for an explicit presence bit.
	RoutingContexts   []uint32
	RoutingContextSet bool
	// State is the destination's availability after this message.
	State DestinationState
	// CongestionLevel is the congestion level reported by SCON, if the peer
	// included the Congestion Indications parameter. Zero otherwise.
	CongestionLevel uint8
	// UserCause carries the MTP3-User identity and unavailability cause from
	// DUPU, which reports that a user part — not the destination itself — is
	// unavailable. Zero for other messages.
	UserCause uint32
	// UserPartUnavailable is true when this status came from DUPU. The
	// destination itself remains reachable, so State is left Available and the
	// MTP3-User is expected to act on UserCause instead.
	UserPartUnavailable bool
	// PeerReported is true when this status describes the peer rather than the
	// SS7 network.
	//
	// An SGP receiving a SCON is the case that matters. RFC 4666 Section 3.4.4
	// allows an ASP to send one "indicating that the congestion level of the
	// M3UA layer or the ASP has changed", which says nothing about whether the
	// named destination is reachable through this SG. Such a report is passed
	// on but deliberately kept out of this node's own destination state, so it
	// cannot reach the answer another ASP gets from a DAUD.
	PeerReported bool
	// ConcernedDestination is the originator of the message that triggered an
	// ASP-to-SGP SCON, valid only when ConcernedDestinationSet is true. RFC 4666
	// Section 3.4.4 permits this parameter only in that direction.
	ConcernedDestination    uint32
	ConcernedDestinationSet bool
}

DestinationStatus is a change in an SS7 destination's availability, reported through SSNM. It is what an MTP3-User needs in order to stop, restart, or throttle traffic to a point code.

type HeartbeatInfo

type HeartbeatInfo struct {
	Enabled  bool
	Interval time.Duration
	Timer    time.Duration
	Data     []byte
}

HeartbeatInfo is a set of information for M3UA BEAT.

func NewHeartbeatInfo

func NewHeartbeatInfo(interval, timer time.Duration, data []byte) *HeartbeatInfo

NewHeartbeatInfo creates a new HeartbeatInfo.

type InvalidSCTPStreamIDError

type InvalidSCTPStreamIDError struct {
	ID uint16
}

InvalidSCTPStreamIDError reports an SCTP stream outside the range a message may use, whether selected locally or received from the peer.

func NewInvalidSCTPStreamIDError

func NewInvalidSCTPStreamIDError(id uint16) *InvalidSCTPStreamIDError

NewInvalidSCTPStreamIDError creates InvalidSCTPStreamIDError

func (*InvalidSCTPStreamIDError) Error

func (e *InvalidSCTPStreamIDError) Error() string

Error returns error string with violating stream ID.

type InvalidVersionError

type InvalidVersionError struct {
	Ver uint8
}

InvalidVersionError is used if a message with an unsupported version is received.

func NewInvalidVersionError

func NewInvalidVersionError(ver uint8) *InvalidVersionError

NewInvalidVersionError creates InvalidVersionError.

func (*InvalidVersionError) Error

func (e *InvalidVersionError) Error() string

Error returns error string with violating version.

type Listener

type Listener struct {
	*Config
	// contains filtered or unexported fields
}

Listener is a M3UA listener.

func Listen

func Listen(net string, laddr *sctp.SCTPAddr, cfg *Config) (*Listener, error)

Listen returns a M3UA listener.

func (*Listener) ASPsForTraffic

func (l *Listener) ASPsForTraffic(rtCtx uint32, sls uint8) []*Conn

ASPsForTraffic returns the associations a message for this Routing Context should be sent to, applying the AS's traffic mode.

RFC 4666 Section 4.3.4.3 gives one rule per mode:

  • Override: "receipt of an ASP Active message at an SGP causes the (re)direction of all traffic for the AS to the ASP that sent the ASP Active message", so exactly one ASP carries traffic.
  • Loadshare: traffic goes to all active ASPs, and "The algorithm at the SGP for loadsharing traffic within an AS to all the active ASPs is implementation dependent. The algorithm could, for example, be round-robin or based on information in the Data message (e.g., the SLS ...)". This uses the SLS, because Section 1.4.7 requires that "Traffic that requires sequencing SHOULD be assigned to the same stream" and the same reasoning applies to the choice of ASP: consecutive messages sharing an SLS must not be split across ASPs.
  • Broadcast: "a simple broadcast algorithm, where every message is sent to each of the active ASPs", so all of them.

An empty result means the AS has no ASP able to take traffic, which for an AS-PENDING AS is the point: Section 4.3.2 has the SGP queue rather than discard while T(r) runs.

func (*Listener) Accept

func (l *Listener) Accept(ctx context.Context) (*Conn, error)

Accept waits for and returns the next connection to the listener. After successfully establishing the association with peer, Payload can be read with Read() func. Other signals are automatically handled background in another goroutine.

Accept does not return until the M3UA handshake for that peer has completed, or until it gives up on it after ten seconds. A single accept loop therefore serves peers strictly one at a time, and one silent peer holds up every other ASP waiting behind it for the whole of that budget.

Accept is safe for concurrent use, so a server expecting several ASPs should run several Accepts rather than one loop:

for i := 0; i < concurrency; i++ {
	go func() {
		for {
			conn, err := l.Accept(ctx)
			if err != nil {
				return
			}
			go serve(conn)
		}
	}()
}

Nothing in Accept writes to the shared Config, and each accepted Conn owns its own association; TestConcurrentAcceptsAreIndependent covers this.

Cancelling ctx does not interrupt an Accept that is blocked waiting for a peer to connect — only Close does. Once a peer has connected, ctx bounds the handshake, alongside Config.EstablishTimeout.

func (*Listener) ActiveASPs

func (l *Listener) ActiveASPs(rtCtx uint32) []*Conn

ActiveASPs returns the associations currently able to carry traffic for a Routing Context, in a stable order.

It is the raw form of the distribution function RFC 4666 Section 1.4.2.4 describes: "To direct messages received from the SS7 MTP3 network to the appropriate IP destination, the SGP must perform a message distribution function using information from the received MTP3-User message." Which Routing Key an inbound MTP3 message matches is decided above this library, which has no MTP3 ingress of its own; what the library can answer, and could not before, is which ASPs are presently serving the AS that Routing Key maps to.

func (*Listener) Addr

func (l *Listener) Addr() net.Addr

Addr returns the listener's network address.

func (*Listener) ApplicationServerState

func (l *Listener) ApplicationServerState(rtCtx uint32) ASState

ApplicationServerState returns the AS state for a Routing Context, as maintained by RFC 4666 Section 4.3.2: "The state of the AS is maintained in the M3UA layer on the SGPs."

func (*Listener) BeginMTP3Restart

func (l *Listener) BeginMTP3Restart(affected ...AffectedDestination) (*MTP3Restart, error)

BeginMTP3Restart starts the MTP3 restart procedure in RFC 4666 Section 4.6. Validation and isolation-state publication are atomic. A non-nil handle is returned even when one or more ASP writes fail, so the procedure can still be updated and completed.

func (*Listener) Close

func (l *Listener) Close() error

Close closes the listener.

func (*Listener) DestinationRanges

func (l *Listener) DestinationRanges() []DestinationRange

DestinationRanges returns every range visible through DestinationState in update order. With several configured Routing Contexts it contains only all-context baselines.

func (*Listener) DestinationRangesForNetwork

func (l *Listener) DestinationRangesForNetwork(networkAppearance uint32) []DestinationRange

DestinationRangesForNetwork returns every range visible for an explicit Network Appearance in update order. With several configured Routing Contexts it contains only all-context baselines.

func (*Listener) DestinationRangesForNetworkAndRoutingContext

func (l *Listener) DestinationRangesForNetworkAndRoutingContext(networkAppearance, routingContext uint32) []DestinationRange

DestinationRangesForNetworkAndRoutingContext returns every all-context and per-context range visible in one Network Appearance and Routing Context, in update order.

func (*Listener) DestinationState

func (l *Listener) DestinationState(pointCode uint32) (DestinationState, bool)

DestinationState reports what this SG last recorded for a destination, and whether anything was recorded at all. With several configured Routing Contexts the legacy API sees only all-context baselines; use DestinationStateForNetworkAndRoutingContext for a per-flow answer.

func (*Listener) DestinationStateForNetwork

func (l *Listener) DestinationStateForNetwork(networkAppearance, pointCode uint32) (DestinationState, bool)

DestinationStateForNetwork reports this SG's state for a destination in an explicit Network Appearance, and whether it has been recorded. With several configured Routing Contexts the legacy API sees only all-context baselines.

func (*Listener) DestinationStateForNetworkAndRoutingContext

func (l *Listener) DestinationStateForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32) (DestinationState, bool)

DestinationStateForNetworkAndRoutingContext reports this SG's state for one destination in an explicit Network Appearance and Routing Context.

func (*Listener) DistributeData

func (l *Listener) DistributeData(data *messages.Data) (TrafficDistribution, error)

DistributeData sends one DATA message to the ASPs serving its Application Server. It applies Override, Loadshare, and Broadcast traffic modes, retains traffic while the AS is AS-PENDING, and adds the Broadcast synchronization marker RFC 4666 Section 4.3.4.3 requires after an ASP becomes active.

The message is copied before the call returns. The caller may reuse or mutate it immediately, including when the result says it was queued.

func (*Listener) ReportDestinationRange

func (l *Listener) ReportDestinationRange(pointCode uint32, mask uint8, state DestinationState) error

ReportDestinationRange records and synchronously reports a destination range.

func (*Listener) ReportDestinationRangeForNetwork

func (l *Listener) ReportDestinationRangeForNetwork(networkAppearance, pointCode uint32, mask uint8, state DestinationState) error

ReportDestinationRangeForNetwork records and synchronously reports a range in an explicit Network Appearance.

func (*Listener) ReportDestinationRangeForNetworkAndRoutingContext

func (l *Listener) ReportDestinationRangeForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32, mask uint8, state DestinationState) error

ReportDestinationRangeForNetworkAndRoutingContext records and synchronously reports a destination range to the currently concerned active ASPs.

func (*Listener) ReportDestinationState

func (l *Listener) ReportDestinationState(pointCode uint32, state DestinationState) error

ReportDestinationState records and synchronously reports a destination.

func (*Listener) ReportDestinationStateForNetwork

func (l *Listener) ReportDestinationStateForNetwork(networkAppearance, pointCode uint32, state DestinationState) error

ReportDestinationStateForNetwork records and synchronously reports a destination in an explicit Network Appearance.

func (*Listener) ReportDestinationStateForNetworkAndRoutingContext

func (l *Listener) ReportDestinationStateForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32, state DestinationState) error

ReportDestinationStateForNetworkAndRoutingContext records and synchronously reports one destination in one explicit traffic scope.

func (*Listener) SetASAvailable

func (l *Listener) SetASAvailable(rtCtx uint32, available bool)

SetASAvailable declares whether this SGP can still service one Application Server, for the partial-failure case of RFC 4666 Section 4.7:

If an SGP suffers a partial failure (where an SGP can continue to
service one or more active AS but due to a partial failure it is
unable to service one or more other active AS), the SGP should send
ASP Inactive Ack to all its connected ASPs for the affected AS.
Upon receiving an ASP Active message for an affected AS while still
partially isolated from the NIF, the SGP should respond with an
Error ("Refused - Management Blocking").

func (*Listener) SetDestinationRange

func (l *Listener) SetDestinationRange(pointCode uint32, mask uint8, state DestinationState)

SetDestinationRange records an all-Routing-Context destination range in the configured Network Appearance. Mask wildcards that many low-order bits.

func (*Listener) SetDestinationRangeForNetwork

func (l *Listener) SetDestinationRangeForNetwork(networkAppearance, pointCode uint32, mask uint8, state DestinationState)

SetDestinationRangeForNetwork records an all-Routing-Context destination range in an explicit Network Appearance.

func (*Listener) SetDestinationRangeForNetworkAndRoutingContext

func (l *Listener) SetDestinationRangeForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32, mask uint8, state DestinationState)

SetDestinationRangeForNetworkAndRoutingContext records a destination range in one explicit Network Appearance and Routing Context.

func (*Listener) SetDestinationState

func (l *Listener) SetDestinationState(pointCode uint32, state DestinationState)

SetDestinationState records a destination's availability at this SG, for every association it serves and every one it will serve.

RFC 4666 Section 4.5.3 has an SG answer a DAUD from what it knows of the SS7 network, and that knowledge is a property of the node: it does not arrive over any ASP's association and does not leave with one. Recording it per Conn meant an ASP that reconnected — which Section 4.4.2 has it do precisely so it can resynchronise — was answered DUNA for destinations this SG knew were reachable, until an operator happened to set them again on the new association.

It may be called before any association exists.

func (*Listener) SetDestinationStateForNetwork

func (l *Listener) SetDestinationStateForNetwork(networkAppearance, pointCode uint32, state DestinationState)

SetDestinationStateForNetwork records this SG's state for a destination in an explicit Network Appearance.

func (*Listener) SetDestinationStateForNetworkAndRoutingContext

func (l *Listener) SetDestinationStateForNetworkAndRoutingContext(networkAppearance, routingContext, pointCode uint32, state DestinationState)

SetDestinationStateForNetworkAndRoutingContext records one exact destination in an explicit Network Appearance and Routing Context.

func (*Listener) SetNIFAvailable

func (l *Listener) SetNIFAvailable(available bool)

SetNIFAvailable declares whether this SGP can still reach the SS7 network through its nodal interworking function.

RFC 4666 Section 4.7 describes what an SGP should do when it cannot: "If an SGP is isolated entirely from the NIF, the SGP should send ASP Down Ack to all its connected ASPs. Upon receiving an ASP Up message while isolated from the NIF, the SGP should respond with an Error ("Refused - Management Blocking")."

The NIF is above this library — it is where MTP3 meets M3UA, and this package has no MTP3 side — so only the application knows when it has gone. Declaring it here is what lets the library produce the behaviour the section describes; without it, error code 0x0d could never be sent at all.

type MTP3Restart

type MTP3Restart struct {
	// contains filtered or unexported fields
}

MTP3Restart is an opaque generation handle for one Listener restart procedure. Its methods are safe to call concurrently.

func (*MTP3Restart) Complete

func (r *MTP3Restart) Complete() error

Complete atomically publishes all staged final states, then sends recovery SSNM to the currently active and concerned ASPs. Calling it again is a no-op.

func (*MTP3Restart) Update

func (r *MTP3Restart) Update(destination AffectedDestination, state DestinationState) error

Update stages a destination's final state. No recovery SSNM is emitted until Complete, and DAUD continues to report DUNA for the affected scope meanwhile.

type ManagementIndication

type ManagementIndication struct {
	Kind ManagementIndicationKind

	// StatusType and StatusInfo are the two halves of the Status parameter of
	// a received Notify (RFC 4666 Section 3.8.2). Set for ManagementNotify.
	StatusType uint16
	StatusInfo uint16

	// ErrorCode is the Error Code of a received Error message (Section 3.8.1).
	// Set for ManagementError.
	ErrorCode uint32

	// RoutingContexts is the complete Application Server scope of the
	// indication. For ManagementNotify it contains every explicitly named
	// Routing Context, or the configured AS memberships inferred under RFC 4666
	// Section 4.3.4.5 when NTFY omitted the parameter. For ManagementError it
	// contains every Routing Context the peer explicitly named. The slice is
	// owned by the indication and may be retained or modified by the caller.
	RoutingContexts []uint32

	// RoutingContext is the first explicitly named traffic flow, valid only
	// when RoutingContextSet is true. It is retained for source compatibility;
	// RoutingContexts carries the complete explicit or inferred scope.
	//
	// It is what makes the indication actionable on an association carrying
	// more than one Application Server. RFC 4666 Errata ID 2065 asks for the
	// parameter to be Conditional rather than Optional in Notify for exactly
	// this reason: when a second ASP becomes active for one of several
	// Application Servers, the Notify names which one, "allow[ing] the first
	// ASP to become inactive only for that particular Application Server,
	// rather than all of them". Section 3.8.1 goes further for Error and makes
	// it Mandatory for specific codes — an "Invalid Routing Context" error
	// carries the context that was invalid.
	RoutingContext    uint32
	RoutingContextSet bool

	// AspIdentifier is the ASP the indication concerns, valid only when
	// AspIdentifierSet is true. Set for ManagementNotify: Section 3.8.2 lists
	// it Conditional, and an "Alternate ASP Active" notification uses it to name
	// the ASP that took the traffic over.
	AspIdentifier    uint32
	AspIdentifierSet bool

	// NetworkAppearance is the network the indication concerns, valid only when
	// NetworkAppearanceSet is true. Section 3.8.1 makes it Mandatory for the
	// "Invalid Network Appearance" error, which "MUST be included in the
	// Network Appearance parameter".
	NetworkAppearance    uint32
	NetworkAppearanceSet bool

	// AffectedPointCodes are the destinations an Error concerns, empty when it
	// named none. Section 3.8.1 on "Destination Status Unknown": "the invalid
	// or unauthorized Point Code(s) MUST be included along with the Network
	// Appearance and/or Routing Context associated with the Point Code(s)."
	AffectedPointCodes []uint32

	// Description names the status or the error code in the RFC's own words.
	Description string
}

ManagementIndication is one report from the M3UA layer to Layer Management.

Which fields carry meaning depends on Kind; the rest are zero. Description is always set, and names the value as the RFC names it, so an indication can be logged without a lookup table.

type ManagementIndicationKind

type ManagementIndicationKind uint8

ManagementIndicationKind identifies which of RFC 4666's Layer Management indications a ManagementIndication carries.

const (
	// ManagementNotify is the M-NOTIFY indication of RFC 4666 Section 1.6.3:
	// "M3UA reports that it has received a Notify message from its peer."
	ManagementNotify ManagementIndicationKind = iota
	// ManagementError is the M-ERROR indication: "M3UA reports that it has
	// received an Error message from its peer or that a local operation has
	// been unsuccessful."
	ManagementError
	// ManagementSCTPRestart is the M-SCTP_RESTART indication: "M3UA informs LM
	// that an SCTP restart indication has been received."
	ManagementSCTPRestart
	// ManagementSCTPRelease is the M-SCTP_RELEASE indication/confirm emitted
	// when the association is released locally, remotely, or by failure.
	ManagementSCTPRelease
)

func (ManagementIndicationKind) String

func (k ManagementIndicationKind) String() string

String names the indication as Section 1.6.3 does.

type NetworkAppearanceError

type NetworkAppearanceError struct {
	Appearance uint32
}

NetworkAppearanceError reports the Network Appearance an ASP named that the SGP has not configured. RFC 4666 Section 3.8.1 requires that exact value in the Error response, so a sentinel alone cannot represent the fault.

func NewInvalidNetworkAppearanceError

func NewInvalidNetworkAppearanceError(appearance uint32) *NetworkAppearanceError

NewInvalidNetworkAppearanceError reports an unconfigured Network Appearance named by an ASP.

func (*NetworkAppearanceError) Error

func (e *NetworkAppearanceError) Error() string

func (*NetworkAppearanceError) Is

func (e *NetworkAppearanceError) Is(target error) bool

Is lets callers match ErrInvalidNetworkAppearance while retaining the offending value through errors.As.

type ParameterFaultError

type ParameterFaultError struct {
	// Raw is the message as received; it could not be parsed, so there is no
	// decoded form to carry.
	Raw []byte
	// Code is the RFC 4666 Section 3.8.1 error code to report.
	Code uint32
	// Cause is the decode failure this was derived from.
	Cause error
}

ParameterFaultError is used when a message this package implements cannot be decoded because of its parameters rather than its class or type.

RFC 4666 Section 3.8.1 keeps the two apart. An unsupported type tells the peer to stop sending that message altogether; a parameter fault complains about one message. The section names the codes:

The "Parameter Field Error" would be sent if a message is received
with a parameter having a wrong length field.

The "Unexpected Parameter" error would be sent if a message contains
an invalid parameter.

func NewParameterFaultErrorFor

func NewParameterFaultErrorFor(raw []byte, cause error) *ParameterFaultError

NewParameterFaultErrorFor reports a parameter-level decode failure for a message whose class and type this package implements, choosing the error code the fault calls for.

func (*ParameterFaultError) Error

func (e *ParameterFaultError) Error() string

Error returns the error string with the offending message's class and type.

func (*ParameterFaultError) Unwrap

func (e *ParameterFaultError) Unwrap() error

Unwrap exposes the decode failure this was derived from.

type RoutingContextError

type RoutingContextError struct {
	// Code is the RFC 4666 Section 3.8.1 error code to report: either
	// params.ErrInvalidRoutingContext or params.ErrNoConfiguredAsForAsp.
	Code uint32
	// Contexts are the offending Routing Contexts, as named by the peer.
	Contexts []uint32
}

RoutingContextError reports Routing Contexts a peer named that this node cannot act on, and carries them.

RFC 4666 Section 3.8.1 is explicit that they must travel with the report: "For this error, the invalid Routing Context(s) MUST be included in the Error message." A bare sentinel could not carry them, so the Error message named this node's own configured contexts instead — telling the peer that our contexts were the invalid ones.

func NewInvalidRoutingContextError

func NewInvalidRoutingContextError(rcs ...uint32) *RoutingContextError

NewInvalidRoutingContextError reports contexts a peer named that this connection is not configured for.

func NewNoConfiguredASError

func NewNoConfiguredASError(rcs ...uint32) *RoutingContextError

NewNoConfiguredASError reports contexts a peer asked to activate for which no Routing Key is defined.

func (*RoutingContextError) Error

func (e *RoutingContextError) Error() string

func (*RoutingContextError) Is

func (e *RoutingContextError) Is(target error) bool

Is lets callers keep testing against the sentinels.

type SCTPConfig

type SCTPConfig struct {
	*SctpSackInfo
	*SctpNoDelayInfo

	// InitTimeout bounds Dial's one-shot SCTP association attempt. Zero
	// selects DefaultInitTimeout.
	InitTimeout time.Duration

	// ReadBufferSize is the size of the buffer each read from the association
	// is made into. Zero selects DefaultReadBufferSize.
	//
	// SCTP reassembles a fragmented message before delivering it, so this is
	// not an MTU: it must accommodate the largest single M3UA message a peer
	// may send. RFC 4666 Section 1.3.2.1 is explicit that "The M3UA layer does
	// not impose a 272-octet signalling information field (SIF) length limit
	// as specified by the SS7 MTP Level 2 protocol. Larger information blocks
	// can be accommodated directly by M3UA/SCTP", and the message length field
	// is 32-bit, so there is no protocol-level maximum to size against. Raise
	// this if a peer is known to send larger blocks than the default allows.
	ReadBufferSize int
}

SCTPConfig holds all SCTP-related configuration parameters. This separates SCTP layer configuration from M3UA layer configuration.

A Config — and so this SCTPConfig — may be shared by every Conn a Listener accepts, and by repeated Dials. It therefore holds settings only: the association itself and everything derived from it belong to the Conn.

type SctpNoDelayInfo

type SctpNoDelayInfo struct {
	Enabled bool
	NoDelay bool
}

SctpNoDelayInfo is a set of information for SCTP NODELAY configuration.

NoDelay, when true, disables the Nagle-like bundling algorithm so that user messages are sent as soon as possible (SCTP_NODELAY = 1). When false, small messages may be bundled to improve throughput.

type SctpSackInfo

type SctpSackInfo struct {
	Enabled       bool
	SackDelay     uint32
	SackFrequency uint32
}

SctpSackInfo is a set of information for SCTP SACK timer configuration.

SackDelay sack_delay: This parameter contains the number of milliseconds the user is requesting that the delayed SACK timer be set to.

RFC 9260 Section 6.2 — the current SCTP specification, which obsoleted RFC 4960 — bounds it from above only: "An implementation MUST NOT allow the maximum delay (protocol parameter 'SACK.Delay') to be configured to be more than 500 ms. In other words, an implementation MAY lower the value of 'SACK.Delay' below 500 ms but MUST NOT raise it above 500 ms." The 200 ms in the same section is when an acknowledgement SHOULD be generated by, not a floor, so any value up to 500 is permitted and zero disables the delay. A larger value is refused when the association is set up; see ErrSackDelayTooLarge.

SackFrequency sack_freq: This parameter contains the number of packets that must be received before a SACK is sent without waiting for the delay timer to expire. The default value is 2; setting this value to 1 will disable the delayed SACK algorithm.

type State

type State uint8

State represents ASP State.

const (
	StateAspDown State = iota
	StateAspInactive
	StateAspActive
	StateSCTPCDI
	StateSCTPRI
)

M3UA status definitions.

func (State) String

func (s State) String() string

type TrafficDistribution

type TrafficDistribution struct {
	Delivered int
	Queued    bool
}

TrafficDistribution reports what DistributeData did with one MTP3 message. Queued and Delivered are mutually exclusive for a successful call.

type UnexpectedMessageError

type UnexpectedMessageError struct {
	Msg messages.M3UA
}

UnexpectedMessageError is used if a defined and recognized message is received that is not expected in the current state (in some cases, the ASP may optionally silently discard the message and not send an Error message).

func NewUnexpectedMessageError

func NewUnexpectedMessageError(msg messages.M3UA) *UnexpectedMessageError

NewUnexpectedMessageError creates UnexpectedMessageError

func (*UnexpectedMessageError) Error

func (e *UnexpectedMessageError) Error() string

Error returns error string with message class and type.

type UnsupportedClassError

type UnsupportedClassError struct {
	// Raw is the message as received, when it could not be parsed.
	Raw []byte
	Msg messages.M3UA
}

UnsupportedClassError is used if a message with an unexpected or unsupported Message Class is received.

func NewUnsupportedClassError

func NewUnsupportedClassError(msg messages.M3UA) *UnsupportedClassError

NewUnsupportedClassError creates UnsupportedClassError

func NewUnsupportedClassErrorFor

func NewUnsupportedClassErrorFor(raw []byte) *UnsupportedClassError

NewUnsupportedClassErrorFor reports an unsupported class for a message that could not be parsed, carrying the octets received so the Diagnostic Information parameter can quote them (RFC 4666 Section 3.8.1).

func (*UnsupportedClassError) Error

func (e *UnsupportedClassError) Error() string

Error returns error string with message class.

type UnsupportedMessageError

type UnsupportedMessageError struct {
	// Raw is the message as received, when it could not be parsed.
	Raw []byte
	Msg messages.M3UA
}

UnsupportedMessageError is used if a message with an unexpected or unsupported Message Type is received.

func NewUnsupportedMessageError

func NewUnsupportedMessageError(msg messages.M3UA) *UnsupportedMessageError

NewUnsupportedMessageError creates UnsupportedMessageError

func NewUnsupportedMessageErrorFor

func NewUnsupportedMessageErrorFor(raw []byte) *UnsupportedMessageError

NewUnsupportedMessageErrorFor reports an unsupported type for a message that could not be parsed, carrying the octets received.

func (*UnsupportedMessageError) Error

func (e *UnsupportedMessageError) Error() string

Error returns error string with message class and type.

Directories

Path Synopsis
examples
client command
Command m3ua-client works as M3UA client.
Command m3ua-client works as M3UA client.
pc-conv command
server command
Command m3ua-server works as M3UA server.
Command m3ua-server works as M3UA server.
Package messages provides protocol definitions and encoding/decoding feature of M3UA messages.
Package messages provides protocol definitions and encoding/decoding feature of M3UA messages.
params
Package params provides the protocol definition and encoding/decoding feature of M3UA Common Paratemeters and M3UA-specific parameters.
Package params provides the protocol definition and encoding/decoding feature of M3UA Common Paratemeters and M3UA-specific parameters.
Package pc provides Point Code converting from some variants and translation to IP.
Package pc provides Point Code converting from some variants and translation to IP.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL