Documentation
¶
Overview ¶
Package dnp3 is the module root of a pure Go implementation of DNP3 (IEEE 1815-2012), the SCADA protocol between masters and outstations in electric, water and oil and gas utilities.
Applications import the subpackages:
- github.com/otfabric/go-dnp3/client: the master: integrity and event polls, unsolicited responses, controls with select-before- operate, time synchronization, restarts, file transfer, secure authentication, automatic reconnect and retries
- github.com/otfabric/go-dnp3/server: the outstation: a point database with event buffers per class, request handling, unsolicited responses, controls and command events, file transfer, secure authentication with statistics, and the dual end point
- github.com/otfabric/go-dnp3/secauth: Secure Authentication version 5: users and keys, MACs, the AES key wrap, statistics
- github.com/otfabric/go-dnp3/app: the application layer codec and the object library
- github.com/otfabric/go-dnp3/transport: the transport function
- github.com/otfabric/go-dnp3/link: the data link layer codec
This package holds what client and server share: the sentinel and typed errors, the Logger and Metrics interfaces and the channel State.
Docs: README.md, API.md (public API), ARCHITECTURE.md, ERRORS.md (error semantics), OBSERVABILITY.md (logging and metrics), INTEROPERABILITY.md (conformance and interop coverage), SECURITY.md, CONTRIBUTING.md, RELEASE.md (changelog).
Index ¶
- Constants
- Variables
- type AuthError
- type CommandError
- type ContextLogger
- type FieldLogger
- type FileError
- type IINError
- type Logger
- type Metrics
- type NopMetrics
- func (NopMetrics) OnConnect(net.Addr)
- func (NopMetrics) OnDecodeError(net.Addr, error)
- func (NopMetrics) OnDisconnect(net.Addr, error)
- func (NopMetrics) OnFragmentReceived(net.Addr, *app.Fragment, int)
- func (NopMetrics) OnFragmentSent(net.Addr, *app.Fragment, int)
- func (NopMetrics) OnFrameReceived(net.Addr, *link.Frame, int)
- func (NopMetrics) OnFrameSent(net.Addr, *link.Frame, int)
- func (NopMetrics) OnFramingError(net.Addr, error)
- type RequestMetrics
- type ServerMetrics
- type State
Constants ¶
const IINErrorBits = app.IINNoFuncCodeSupport | app.IINObjectUnknown | app.IINParameterError | app.IINAlreadyExecuting
IINErrorBits are the internal indications that make a response an IINError.
Variables ¶
var ( // ErrClosed reports an operation on a client, server or association // that was closed by the application. ErrClosed = errors.New("dnp3: closed") // ErrNotConnected reports a request on a client that has no // established channel. ErrNotConnected = errors.New("dnp3: not connected") // ErrConnectFailed reports that a client could not establish its // channel: the dial, the TLS handshake or the opening of the serial // port failed. The underlying error is wrapped alongside it. ErrConnectFailed = errors.New("dnp3: connect failed") // ErrConnectionLost reports that the peer closed the channel or the // transport failed while a request was in progress. ErrConnectionLost = errors.New("dnp3: connection lost") // ErrTimeout reports that the peer did not answer within the response // timeout (client) or did not confirm within the confirm timeout // (server), or that a link layer confirmation did not arrive. ErrTimeout = errors.New("dnp3: timeout") // ErrProtocol reports a peer that violated the protocol: a response // whose objects do not match the request (a control echo that differs, // a missing or unexpected object, a wrong file block), or a link layer // answer that does not fit. A fragment with an unexpected sequence // number is ignored instead. ErrProtocol = errors.New("dnp3: protocol violation") // ErrBusy reports a request that cannot be issued because the // association has reached its queue limit. ErrBusy = errors.New("dnp3: too many pending requests") // ErrInvalidOption reports an option value a client or server cannot // be constructed with. ErrInvalidOption = errors.New("dnp3: invalid option") // ErrAuthentication reports a failure of secure authentication: a MAC // that does not verify, a key that is not initialized, or a challenge // that was not answered. An *[AuthError] carries the code the peer sent. ErrAuthentication = errors.New("dnp3: authentication failed") )
Sentinel errors shared by the client and the server. They are usually wrapped with detail; test with errors.Is.
Functions ¶
This section is empty.
Types ¶
type AuthError ¶
type AuthError struct {
// Code is the error code of the g120v7 object.
Code app.AuthErrorCode
// User is the user number the error is about.
User uint16
// Text is the optional error text the peer sent.
Text string
}
AuthError reports an authentication error object (g120v7) received from the peer. It matches ErrAuthentication with errors.Is.
type CommandError ¶
type CommandError struct {
// GV is the object of the command, for example app.G12V1.
GV app.GroupVariation
// Index is the point index.
Index uint32
// Status is the status code of the echo.
Status app.CommandStatus
}
CommandError reports a control the outstation did not execute: its echo carried a status other than SUCCESS.
func (*CommandError) Error ¶
func (e *CommandError) Error() string
Error describes the failure, for example "dnp3: g12v1 index 3: command status NO_SELECT".
type ContextLogger ¶
type ContextLogger interface {
Logger
DebugContext(ctx context.Context, msg string, keysAndValues ...any)
InfoContext(ctx context.Context, msg string, keysAndValues ...any)
WarnContext(ctx context.Context, msg string, keysAndValues ...any)
ErrorContext(ctx context.Context, msg string, keysAndValues ...any)
}
ContextLogger extends Logger with context-aware structured logging, so that a handler can pick trace and span identifiers out of the context. When the Logger also implements ContextLogger, entries that belong to a request are logged with the context the request was called with.
type FieldLogger ¶
type FieldLogger interface {
Logger
// With returns a logger that adds the given fields to every entry.
With(keysAndValues ...any) FieldLogger
DebugKV(msg string, keysAndValues ...any)
InfoKV(msg string, keysAndValues ...any)
WarnKV(msg string, keysAndValues ...any)
ErrorKV(msg string, keysAndValues ...any)
}
FieldLogger extends Logger with structured key-value logging. When the Logger given to a client or server also implements FieldLogger, the library logs through it: every entry is a constant message plus fields such as "component", "remote", "outstation", "master" and "error", instead of one formatted string. NewSlogLogger returns such a logger.
keysAndValues alternate string keys and values, as in log/slog.
func NewSlogFieldLogger ¶
func NewSlogFieldLogger(h slog.Handler) FieldLogger
NewSlogFieldLogger is NewSlogLogger with the result typed as a FieldLogger, for callers that want to add fields with With before handing the logger to a client or server. A nil handler returns a no-op logger.
type FileError ¶
type FileError struct {
// Op is the operation: "open", "close", "delete", "read", "write",
// "abort", "info" or "authenticate".
Op string
// Status is the file status the outstation answered.
Status app.FileStatus
// Text is the optional text the outstation sent with the status.
Text string
}
FileError reports a file operation the outstation refused.
type IINError ¶
type IINError struct {
// Function is the function code of the rejected request.
Function app.FunctionCode
// IIN is the internal indications of the response.
IIN app.IIN
}
IINError reports that an outstation rejected a request: the response carried IIN2 bits that report an error (no function code support, object unknown, parameter error, already executing).
type Logger ¶
type Logger interface {
Debugf(format string, args ...any)
Infof(format string, args ...any)
Warnf(format string, args ...any)
Errorf(format string, args ...any)
}
Logger is the logging interface accepted by the client and the server. Implement it to integrate any logging library.
Logging is silent by default. Methods are invoked synchronously on the protocol path and must not block.
func NewSlogLogger ¶
NewSlogLogger wraps a slog.Handler. The result implements FieldLogger and ContextLogger, so the library logs structured fields through it; messages are only built when the handler is enabled for their level. A nil handler returns a no-op logger.
func NewStdLogger ¶
NewStdLogger wraps a standard library logger. Each message is prefixed with its level. A nil l logs to os.Stderr with the standard flags.
type Metrics ¶
type Metrics interface {
// OnConnect is called when a transport connection is established or
// a serial port opened.
OnConnect(remote net.Addr)
// OnDisconnect is called when a channel ends. err is nil when the
// application closed it.
OnDisconnect(remote net.Addr, err error)
// OnFrameSent is called after a link frame was written. size is its
// length on the wire in octets.
OnFrameSent(remote net.Addr, f *link.Frame, size int)
// OnFrameReceived is called for every link frame read from the peer.
OnFrameReceived(remote net.Addr, f *link.Frame, size int)
// OnFramingError is called when octets were skipped or a frame was
// discarded: a bad start sequence, a CRC error, an invalid length.
OnFramingError(remote net.Addr, err error)
// OnFragmentSent is called after an application fragment was handed
// to the transport function. size is its length in octets.
OnFragmentSent(remote net.Addr, f *app.Fragment, size int)
// OnFragmentReceived is called for every fragment that was
// reassembled and decoded.
OnFragmentReceived(remote net.Addr, f *app.Fragment, size int)
// OnDecodeError is called when a reassembled fragment, or a segment,
// could not be decoded and was dropped.
OnDecodeError(remote net.Addr, err error)
}
Metrics is an optional callback interface for observing the channels and the traffic of a client or a server. A nil Metrics disables collection.
The methods are called synchronously on the protocol path, possibly from several goroutines at once; implementations must be safe for concurrent use and must not block (increment a counter, send on a buffered channel). Frames and fragments passed to them must not be retained or modified.
Embed NopMetrics to implement only the callbacks you need and to stay compatible when methods are added.
type NopMetrics ¶
type NopMetrics struct{}
NopMetrics is a Metrics that does nothing. Embed it in your own type.
func (NopMetrics) OnDecodeError ¶
func (NopMetrics) OnDecodeError(net.Addr, error)
OnDecodeError does nothing.
func (NopMetrics) OnDisconnect ¶
func (NopMetrics) OnDisconnect(net.Addr, error)
OnDisconnect does nothing.
func (NopMetrics) OnFragmentReceived ¶
OnFragmentReceived does nothing.
func (NopMetrics) OnFragmentSent ¶
OnFragmentSent does nothing.
func (NopMetrics) OnFrameReceived ¶
OnFrameReceived does nothing.
func (NopMetrics) OnFrameSent ¶
OnFrameSent does nothing.
func (NopMetrics) OnFramingError ¶
func (NopMetrics) OnFramingError(net.Addr, error)
OnFramingError does nothing.
type RequestMetrics ¶
type RequestMetrics interface {
// OnRequest is called before a request is sent.
OnRequest(remote net.Addr, outstation uint16, fc app.FunctionCode)
// OnRequestDone is called with the final outcome. err is nil on
// success; classify failures with errors.Is and errors.As
// (*IINError, *CommandError, ErrTimeout, ...).
OnRequestDone(remote net.Addr, outstation uint16, fc app.FunctionCode, duration time.Duration, err error)
// OnRetry is called when attempt (1 for the first) failed with err and
// the request is about to be tried again.
OnRetry(remote net.Addr, outstation uint16, fc app.FunctionCode, attempt int, err error)
}
RequestMetrics is an optional extension of Metrics for a client. When the value given to client.WithMetrics also implements it, the client reports every request: polls, commands, time synchronization, file operations.
OnRequest fires once before the first attempt and OnRequestDone once with the final outcome, whose duration includes retries and their delays. OnRetry fires for each attempt that failed and will be repeated.
remote is the address of the channel, or nil while the client has never been connected. outstation is the link address of the outstation.
type ServerMetrics ¶
type ServerMetrics interface {
// OnHandled is called after a request was handled, with the time
// from its reception to the end of the handler calls.
OnHandled(remote net.Addr, fc app.FunctionCode, duration time.Duration)
// OnEventsDiscarded is called when an event buffer overflowed and n
// events of the class (1..3) were discarded.
OnEventsDiscarded(class int, n int)
// OnUnsolicited is called after an unsolicited response was confirmed
// (err == nil) or abandoned.
OnUnsolicited(remote net.Addr, events int, err error)
}
ServerMetrics is an optional extension of Metrics for a server. When the value given to server.WithMetrics also implements it, the server reports every request it handled and every event it had to discard.
type State ¶
type State uint8
State is the state of a channel as seen by one station.
const ( // StateDisconnected means there is no transport connection. StateDisconnected State = iota // StateConnecting means the client is establishing or re-establishing // the transport connection. StateConnecting // StateConnected means the transport is open and frames flow. StateConnected )
Channel states.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package app is the DNP3 application layer codec (IEEE 1815-2012 clause 4 and Annex A): fragments with their control octet, function code and internal indications, object headers with their qualifiers, and the object library.
|
Package app is the DNP3 application layer codec (IEEE 1815-2012 clause 4 and Annex A): fragments with their control octet, function code and internal indications, object headers with their qualifiers, and the object library. |
|
Package client is the DNP3 master (IEEE 1815-2012).
|
Package client is the DNP3 master (IEEE 1815-2012). |
|
examples
|
|
|
client
command
Command client is a standalone master for trying the library against an outstation: it connects, runs the usual startup (disable unsolicited, integrity poll, enable unsolicited), synchronizes time when the outstation asks for it (NEED_TIME), optionally operates a binary output, and prints everything the outstation reports until interrupted.
|
Command client is a standalone master for trying the library against an outstation: it connects, runs the usual startup (disable unsolicited, integrity poll, enable unsolicited), synchronizes time when the outstation asks for it (NEED_TIME), optionally operates a binary output, and prints everything the outstation reports until interrupted. |
|
commands
command
Command commands shows controls: select-before-operate and direct operate of a control relay output block, a set-point, a direct operate without acknowledgement, and how a refused control surfaces as a *dnp3.CommandError.
|
Command commands shows controls: select-before-operate and direct operate of a control relay output block, a set-point, a direct operate without acknowledgement, and how a refused control surfaces as a *dnp3.CommandError. |
|
dialout
command
Command dialout shows the dual end point of IEEE 1815-2012: the outstation, behind a firewall that only lets it connect out, establishes the TCP connection to its master and re-establishes it when it is lost.
|
Command dialout shows the dual end point of IEEE 1815-2012: the outstation, behind a firewall that only lets it connect out, establishes the TCP connection to its master and re-establishes it when it is lost. |
|
events
command
Command events shows how changes reach the master: events per class, collected in a batch, reported unsolicited once the master enabled unsolicited responses, and confirmed.
|
Command events shows how changes reach the master: events per class, collected in a batch, reported unsolicited once the master enabled unsolicited responses, and confirmed. |
|
files
command
Command files shows file transfer (group 70): an outstation that serves a directory tree read-only with server.FSFileServer, and a master that lists a directory, reads a file and asks for a file's descriptor.
|
Command files shows file transfer (group 70): an outstation that serves a directory tree read-only with server.FSFileServer, and a master that lists a directory, reads a file and asks for a file's descriptor. |
|
observability
command
Command observability shows what a long-running program wires up: slog logging and metrics on both sides, a state handler, retries for reads, automatic reconnection, and periodic polls.
|
Command observability shows what a long-running program wires up: slog logging and metrics on both sides, a state handler, retries for reads, automatic reconnection, and periodic polls. |
|
quickstart
command
Command quickstart is the smallest complete program: an outstation with a few points and a master that runs an integrity poll against it, in one process, over a real TCP connection on loopback.
|
Command quickstart is the smallest complete program: an outstation with a few points and a master that runs an integrity poll against it, in one process, over a real TCP connection on loopback. |
|
secure
command
Command secure shows Secure Authentication version 5: both ends share an update key; the master wraps fresh session keys with it when it connects; the outstation then challenges every critical request and the master answers with an HMAC-SHA-256 MAC.
|
Command secure shows Secure Authentication version 5: both ends share an update key; the master wraps fresh session keys with it when it connects; the outstation then challenges every critical request and the master answers with an HMAC-SHA-256 MAC. |
|
server
command
Command server is a standalone outstation for trying the library against another master, a simulator or a test tool: binary and analog inputs, a counter, a controllable breaker and periodic updates, with unsolicited responses enabled.
|
Command server is a standalone outstation for trying the library against another master, a simulator or a test tool: binary and analog inputs, a counter, a controllable breaker and periodic updates, with unsolicited responses enabled. |
|
internal
|
|
|
channel
Package channel runs the DNP3 data link layer procedures and the transport function over one byte stream: a TCP connection, a serial port or a UDP socket.
|
Package channel runs the DNP3 data link layer procedures and the transport function over one byte stream: a TCP connection, a serial port or a UDP socket. |
|
cmd/conformance
command
Command conformance is the device under test of the dnp3c conformance run (make conformance): a go-dnp3 outstation with a fixed database, and a control port through which dnp3-interop's dnp3c runner performs the steps a tester would otherwise perform by hand (restarts, configuration changes, input changes) and checks what the outstation reported against what it holds.
|
Command conformance is the device under test of the dnp3c conformance run (make conformance): a go-dnp3 outstation with a fixed database, and a control port through which dnp3-interop's dnp3c runner performs the steps a tester would otherwise perform by hand (restarts, configuration changes, input changes) and checks what the outstation reported against what it holds. |
|
cmd/gencatalog
command
Command gencatalog generates the code and documentation that follow from the DNP3 object catalog, app/catalog.json:
|
Command gencatalog generates the code and documentation that follow from the DNP3 object catalog, app/catalog.json: |
|
cmd/upstreamwatch
command
Command upstreamwatch tells whether the upstreams go-dnp3 follows have moved, and whether they still agree with it:
|
Command upstreamwatch tells whether the upstreams go-dnp3 follows have moved, and whether they still agree with it: |
|
logx
Package logx is how the library writes its log entries: a constant message plus key-value fields.
|
Package logx is how the library writes its log entries: a constant message plus key-value fields. |
|
Package link is the DNP3 data link layer codec (IEEE 1815-2012 clause 9): the FT3 frame format with its 16-bit CRCs, the control octet, the function codes and the addresses, and a Reader that reads frames from a byte stream and resynchronises after damage.
|
Package link is the DNP3 data link layer codec (IEEE 1815-2012 clause 9): the FT3 frame format with its 16-bit CRCs, the control octet, the function codes and the addresses, and a Reader that reads frames from a byte stream and resynchronises after damage. |
|
Package secauth implements the cryptographic core of DNP3 Secure Authentication version 5 (IEEE 1815-2012 clause 7): the message authentication codes and the construction of the data they protect.
|
Package secauth implements the cryptographic core of DNP3 Secure Authentication version 5 (IEEE 1815-2012 clause 7): the message authentication codes and the construction of the data they protect. |
|
Package server is the DNP3 outstation (IEEE 1815-2012).
|
Package server is the DNP3 outstation (IEEE 1815-2012). |
|
Package transport is the DNP3 transport function (IEEE 1815-2012 clause 8): it splits application fragments into segments that fit one link frame and reassembles received segments into fragments.
|
Package transport is the DNP3 transport function (IEEE 1815-2012 clause 8): it splits application fragments into segments that fit one link frame and reassembles received segments into fragments. |