Documentation
¶
Overview ¶
Package vpn is the top-level go-openvpn package.
It provides an OpenVPN client that runs the whole connection lifecycle: the control channel and its TLS session, authentication, the data channel, and the tunnel device and routes. A profile authenticates with a client certificate, with a username and password, or — for an AWS Client VPN endpoint — with a federated SAML assertion.
Typical usage. ParsePath rather than ParseFile, because a profile may name its CA in a file beside it and only the path form can resolve that:
p, err := profile.ParsePath(path)
if err != nil {
return err
}
c := vpn.New(p)
c.EventFn = vpn.StderrEvents // or your own sink; nil is silent
c.CredentialsFn = func(ctx context.Context) (vpn.Credentials, error) {
return vpn.Credentials{Username: user, Password: pass}, nil
}
if err := c.Connect(ctx); err != nil {
return err
}
// tunnel is now up; data flows through the TUN device
defer c.WaitForDisconnect() //nolint:errcheck
defer c.Disconnect() //nolint:errcheck
A caller with no file to parse builds a *profile.Profile from profile.Spec. A cert-only profile needs no callback; a federated one needs SAMLTokenFn, which is called with the identity provider's URL and returns the assertion.
Index ¶
- Constants
- Variables
- func BuildVersion() string
- func StderrEvents(e Event)
- type Attempt
- type Client
- func (c *Client) Attempts() []Attempt
- func (c *Client) Connect(ctx context.Context) error
- func (c *Client) ConnectPhase2(ctx context.Context, samlToken string) error
- func (c *Client) ConnectedAt() time.Time
- func (c *Client) Disconnect() error
- func (c *Client) Done() <-chan struct{}
- func (c *Client) LastTransientError() error
- func (c *Client) LocalIP() string
- func (c *Client) Phase1ForTest(ctx context.Context) (*SAMLChallenge, error)
- func (c *Client) Phase1IP() string
- func (c *Client) Preflight() error
- func (c *Client) Reconnect(ctx context.Context) error
- func (c *Client) Reconnects() int
- func (c *Client) Report() *diag.SessionReport
- func (c *Client) SetRelayPhase2(remoteIP, stateID string)
- func (c *Client) Stats() Stats
- func (c *Client) TunnelDevice() device.Device
- func (c *Client) WaitForDisconnect() error
- type ClientState
- type Credentials
- type DataV2Advertisement
- type Event
- type EventFn
- type EventType
- type SAMLChallenge
- type Stats
Constants ¶
const AWSSAMLUnsupportedNotice = "This client is not AWS-supported for SAML Client VPN authentication. For AWS-supported operation, use the AWS VPN Client."
AWSSAMLUnsupportedNotice describes the AWS support boundary for this independent CRV1 implementation.
Variables ¶
var ErrNoUsableCA = tlsverify.ErrNoUsableCA
ErrNoUsableCA is returned for a profile that carries no certificate authority the server can be verified against. It is a configuration fault, not a TLS fault: knowable before a socket opens, and refused at the StageParse boundary so that no unverified tunnel is ever built.
var ErrReauthRequired = fmt.Errorf("vpn: SAML re-authentication required: token rejected by server")
ErrReauthRequired is returned by Reconnect when a federated session cannot be resumed: the cached assertion has expired, or the server refused it. An assertion is cryptographically bound to the AuthnRequest that obtained it and cannot be presented to a new session, so the caller's next move is the browser flow — which Connect runs, from the state Reconnect leaves behind. No other authentication method returns it.
var Version string
Version is the release this build came from, stamped at link time:
-ldflags "-X github.com/buengese/go-openvpn.Version=v1.2.3"
It is empty in a plain `go build`, where BuildVersion falls back to what the toolchain recorded.
Functions ¶
func BuildVersion ¶
func BuildVersion() string
BuildVersion reports the release this binary was built from: the link-time stamp, else the version the module was built as, else "unknown" — a `go build` from a source archive has no VCS information at all.
func StderrEvents ¶
func StderrEvents(e Event)
StderrEvents is an EventFn that writes log lines and state changes to standard error.
Types ¶
type Attempt ¶
type Attempt struct {
// N is the 1-based attempt number.
N int
// StartedAt is when the dial began.
StartedAt time.Time
// Report is the attempt's own session report, complete and independent
// of every other attempt's.
Report *diag.SessionReport
}
Attempt is one connection attempt within a reconnect sequence.
type Client ¶
type Client struct {
// SAMLTokenFn is called during Connect when the server issues a SAML/CRV1
// challenge: it must open challenge.URL in a browser, wait for the
// SAMLResponse via the ACS server on 127.0.0.1:35001, and return the
// base64-encoded token. Required for AWS Client VPN profiles.
SAMLTokenFn func(ctx context.Context, challenge SAMLChallenge) (string, error)
// CredentialsFn supplies the username and password for a profile that
// requires them. It is called at most once per attempt, before the
// key-method-2 packet is sent, and may block on a UI or a keychain.
// Without it a profile.FlowUserPass attempt ends at StageParse with
// diag.ClassConfig; every other flow ignores it. Set it before Connect;
// the client stores nothing it returns beyond the session.
CredentialsFn func(ctx context.Context) (Credentials, error)
// ProtectFn, if set, is called with the raw file descriptor of every
// transport socket before it is used. On Android it must call
// VpnService.protect(fd), so the socket reaches the real network.
ProtectFn func(fd int) error
// Device selects the tunnel backend. Nil selects the platform default: the
// kernel backend on Linux and macOS, and an error on Android and iOS,
// where the mobile wrapper sets this field itself. Set it to a netstack
// backend for an unprivileged tunnel that touches no host state. It is
// read once, when the tunnel comes up.
Device device.Backend
// EventFn, if set, is called for every notable lifecycle event: state
// transitions, log lines and periodic stats. It is called concurrently
// from several goroutines, must not block, and is set before Connect.
//
// Nil is silent. The one exception is the SSLKEYLOGFILE notice, which the
// TLS layer writes to stderr as the caller's own opt-in.
EventFn EventFn
// PreflightMode selects what the StageParse capability preflight does with
// a diag.SeverityFatal gap. The zero value, diag.PreflightFailFast, ends
// the attempt there with diag.ClassUnsupported before a socket is opened;
// diag.PreflightAdvisory records the same gaps and lets the attempt fail
// wherever it really fails. The mode is recorded in the session report, so
// aggregated reports cannot silently mix the two. Read at StageParse.
PreflightMode diag.PreflightMode
// DataV2 selects whether the IV_PROTO advertisement claims
// IV_PROTO_DATA_V2. The zero value claims it, which is what every
// production connection wants: P_DATA_V2 is this client's preferred
// data-channel format.
//
// The format is not a client choice — see datachannel.WireFormat — and a
// server decides it from this advertisement alone, so withholding it is
// the only way to put a P_DATA_V1 server in front of this client (see
// WithholdDataV2). It is read at each key-method-2 packet.
DataV2 DataV2Advertisement
// reconnect
// MaxReconnects is the maximum number of reconnect attempts before giving
// up. A value of 0 means unlimited retries. Default is 0 (unlimited).
MaxReconnects int
// contains filtered or unexported fields
}
Client is a go-openvpn VPN client. It is not safe for concurrent use by multiple goroutines except where noted — Stats and WaitForDisconnect may be called concurrently with the data channel.
func New ¶
New creates a new Client from the given profile. The Client is idle until Connect is called.
func (*Client) Attempts ¶
Attempts returns every attempt made by the current or most recent Reconnect, in order; a single Connect yields exactly one. Every Report is a snapshot and safe to retain, so this is safe to call from another goroutine while a Reconnect runs, and like Report the reports carry unredacted credential material: marshal SessionReport.Redacted. MaxReconnects bounds the slice and defaults to unlimited.
func (*Client) Connect ¶
Connect dials, authenticates, and brings up the VPN tunnel.
The authentication method is taken from the profile: a client certificate, a username and password from CredentialsFn, or — for an AWS Client VPN endpoint — a federated SAML assertion from SAMLTokenFn. All three take the same route through this function; only the method knows how many exchanges it needs.
Connect is not safe for concurrent use. However it ends, Report describes the attempt: the stages it reached and the class of the failure.
func (*Client) ConnectPhase2 ¶
ConnectPhase2 completes the connection with a SAML assertion the caller already holds: it runs the CRV1 second exchange and brings the tunnel up. samlToken is the base64-encoded SAMLResponse, and where it came from is not this method's business. What is required is the first exchange's state — a state id, a backend address and a client in stateConnecting — put there by SetRelayPhase2 or by the client's own first exchange.
func (*Client) ConnectedAt ¶
ConnectedAt returns when the current tunnel was established, or the zero time if none is up. It is what Stats.Uptime is measured from, exposed separately because the instant does not go stale the moment it is read.
func (*Client) Disconnect ¶
Disconnect initiates a graceful teardown of the VPN tunnel. It signals the background goroutines to stop and begins cleaning up; call WaitForDisconnect to block until it completes. It is the client's only deliberate teardown, and so the only one that sends an explicit-exit-notify.
func (*Client) Done ¶
func (c *Client) Done() <-chan struct{}
Done returns a channel that is closed when the client disconnects (for any reason — graceful, keepalive timeout, server-initiated, etc.). The disconnect reason is available via WaitForDisconnect after it closes.
func (*Client) LastTransientError ¶
LastTransientError returns the most recent failure this session recovered from and went on running, or nil if there has not been one.
It is narrower than "the last error": a failure that ended the session is the session's outcome and belongs in the report, while this records the kind nothing else would mention — a renegotiation that failed while the old key epoch stayed installed, which is this client's one recoverable failure.
func (*Client) LocalIP ¶
LocalIP returns the tunnel-side IP address assigned by the server after a successful Phase 2, or "" if the tunnel is not up.
func (*Client) Phase1ForTest ¶
func (c *Client) Phase1ForTest(ctx context.Context) (*SAMLChallenge, error)
Phase1ForTest runs dialAndAuthenticate and returns the SAML challenge (or nil). Only for integration tests — do not use in production code.
func (*Client) Phase1IP ¶
Phase1IP returns the sticky backend IP the handshake reached, or "" before the handshake completes. It is what a relay agent must dial for the second exchange, because AWS binds a CRV1 state id to one backend instance and re-resolving the hostname may route elsewhere.
func (*Client) Preflight ¶
Preflight runs the StageParse boundary — profile fingerprint, capability preflight, endpoint and credential registration — without opening a socket, and returns the error the attempt would fail with, or nil.
Under the default diag.PreflightFailFast a profile with a fatal capability gap yields a *diag.Error of diag.ClassUnsupported at diag.StageParse whose Feature names the directive; under diag.PreflightAdvisory it returns nil and the gaps are readable from Report.
The boundary is shared with Connect and is idempotent: calling Preflight and then Connect on the same Client runs it once and replays its result.
func (*Client) Reconnect ¶
Reconnect tears down the current connection and re-establishes the tunnel. It is not safe for concurrent use.
Every method reconnects from its own resumption state, and this loop asks the method rather than deciding for it. A certificate or a cached password redials and runs the whole handshake again, including remote failover; a SAML session still inside its NotOnOrAfter skips the first exchange and runs the second against the backend that issued it. ErrReauthRequired is that method's answer and no other's, from either of the two ways its state runs out, and it leaves the client in stateNew because Connect's browser flow starts nowhere else.
What is retried follows the class of the failure, not how many there have been: see retryClass. A rejected credential ends the loop unless the method has something else to present — a password from CredentialsFn, once, because retrying a rejected password locks an account. Backoff is 1 s, 2 s, 4 s, … capped at 30 s; MaxReconnects limits total attempts (0 = unlimited).
func (*Client) Reconnects ¶
Reconnects returns how many times Reconnect has re-established this tunnel. It counts successes, not attempts: a sequence that came up on its fourth try adds one, and Attempts holds the other three, each with its own report.
func (*Client) Report ¶
func (c *Client) Report() *diag.SessionReport
Report returns the diagnostics record of the most recent connection attempt. It is valid as soon as New returns and stays valid after Connect returns, whether Connect succeeded or failed, and equally after the two-phase Phase1ForTest / ConnectPhase2 and SetRelayPhase2 / ConnectPhase2 paths. The furthest stage reached is the last entry in Stages; a stage with a zero Duration is one the attempt did not come back out of.
The returned report is a snapshot and is safe to retain while the session continues. It carries unredacted credential material: marshal SessionReport.Redacted, never the report itself.
Reconnect starts a fresh report for each attempt, so this describes the last attempt only; Attempts keeps the others.
func (*Client) SetRelayPhase2 ¶
SetRelayPhase2 pre-seeds the Phase 1 state obtained by the mobile/desktop app so that ConnectPhase2 can skip Phase 1 and dial the sticky backend IP with the SAML credentials the relay delivered. It must be called before ConnectPhase2 and only in relay mode: remoteIP is the backend server IP from the CRV1 challenge, stateID the opaque CRV1 state token.
func (*Client) Stats ¶
Stats returns a snapshot of current traffic counters and uptime. Safe to call concurrently while the tunnel is up.
func (*Client) TunnelDevice ¶
TunnelDevice returns the device carrying the tunnel, or nil when none is up. It lets a wrapper that supplied the backend recover its own concrete device; the core itself only ever uses the device.Device interface.
func (*Client) WaitForDisconnect ¶
WaitForDisconnect blocks until the client is fully disconnected and returns the disconnect reason (nil for a clean Disconnect call). It is safe to call concurrently. Called while Reconnect is running, it waits for whichever attempt is current when it reads the channel — each attempt gets its own channel from reset — so it returns when the client has stopped for good.
type ClientState ¶
type ClientState int
ClientState is the connection lifecycle state reported via events.
const ( // StateIdle means no active connection. StateIdle ClientState = iota // StateConnecting covers everything from the first dial to the tunnel // coming up, including a second authentication exchange where one happens. StateConnecting // StateWaitingSAML means the server asked for a federated assertion and // the caller must open the identity provider's URL. StateWaitingSAML // StateConnected means the TUN interface is up and data flows. StateConnected // StateDisconnecting means teardown is in progress. StateDisconnecting // StateError means connection failed; Message carries the reason. StateError )
func (ClientState) String ¶
func (s ClientState) String() string
String returns a lowercase D-Bus-friendly representation of the state.
type Credentials ¶
type Credentials struct {
// Username is the user name to present in the key-method-2 packet.
Username string
// Password is the password to present alongside it.
Password string
}
Credentials is one username and password. Both are secrets and are registered with the session report's redaction before use.
type DataV2Advertisement ¶
type DataV2Advertisement int
DataV2Advertisement selects whether this client's IV_PROTO advertisement claims IV_PROTO_DATA_V2.
const ( // AdvertiseDataV2 claims the bit. It is the zero value and the default: // P_DATA_V2 is the format this client prefers, and a server that hears // the bit assigns a peer-id and speaks it. AdvertiseDataV2 DataV2Advertisement = iota // WithholdDataV2 advertises IV_PROTO=0 — no protocol extensions at // all — so the server assigns no peer-id and speaks P_DATA_V1. Clearing // the DATA_V2 bit alone does not reach P_DATA_V1: // // IV_PROTO pushed data opcode // 30 peer-id 0 P_DATA_V2 // 28 peer-id 0 P_DATA_V2 (every bit but DATA_V2) // 4 peer-id 0 P_DATA_V2 (no DATA_V2 bit at all) // 0 nothing P_DATA_V1 // // OpenVPN 2.4 and 2.5 decide in push.c:prepare_push_reply on a numeric // comparison rather than a bit test — // // int r = sscanf(optstr, "IV_PROTO=%d", &proto); // if ((r == 1) && (proto >= 2)) { push "peer-id"; use_peer_id = true; } // // — so any bit above the lowest keeps the peer-id coming, while 2.6 uses // the bit test (multi.c:multi_client_connect_late_setup via // extract_iv_proto) and 0 satisfies both. The three remaining bits go with // it, and against a 2.4 peer they cost nothing: the keying-material // exporter and auth-pending are 2.6 features. // // Withholding a bit can only make a server offer less than the client // implements, so it does not breach the rule the advertisement is built // on, and both wire formats are implemented // (internal/datachannel.WireFormat). It is not a fallback and not a // preference: a connection that withholds it still takes its format from // what the server pushes. The vehicle is the matrix isolate // v24-cbc256-sha256-plain-udp-datav1, there being no server-side switch // for P_DATA_V1 on any pinned version. WithholdDataV2 )
type Event ¶
type Event struct {
// Type identifies the event category.
Type EventType
// State is set when Type == EventStateChanged.
State ClientState
// Message carries a log line (EventLog), an error description
// (StateError), or the assigned tunnel IP (StateConnected). It never
// carries the SAML URL, which consumers would log.
Message string
// ServerIP is the VPN server IP (set when State == StateConnected).
ServerIP string
// Stats is set when Type == EventStatsUpdate.
Stats Stats
// Stage is set when Type == EventStage: the connection stage just
// entered. It is the zero value (diag.StageParse) for every other event
// type, so consumers must gate on Type before reading it.
Stage diag.Stage
// At is the wall-clock time of the event.
At time.Time
}
Event is emitted by the Client for every notable lifecycle transition or log line.
type EventFn ¶
type EventFn func(Event)
EventFn is a callback invoked for every Event emitted by the Client.
It is called from several internal goroutines — the two data-path pumps, the keepalive and inactivity timers, and a renegotiation — which can emit at the same time, so an implementation must be safe for concurrent calls as well as quick to return. Set Client.EventFn before calling Connect.
type EventType ¶
type EventType int
EventType identifies the kind of event emitted by the Client.
const ( // EventLog is an informational log line. EventLog EventType = iota // EventStateChanged signals a connection state transition. EventStateChanged // EventStatsUpdate carries a traffic statistics snapshot. EventStatsUpdate // EventStage signals that the connection entered a new stage of the // handshake. Stage carries the stage and Message its name. It is last on // purpose: consumers switch on EventType, so the values above cannot move. EventStage )
type SAMLChallenge ¶
type SAMLChallenge struct {
// URL is the identity-provider URL the user must visit.
URL string
// StateID is the opaque session token that must be returned in Phase 2.
StateID string
}
SAMLChallenge holds the parsed fields from a CRV1 SAML challenge. The caller must open URL in a browser; the IdP will POST the SAMLResponse to 127.0.0.1:35001, which the caller can capture via auth/saml.NewACSServer.
type Stats ¶
type Stats struct {
// BytesSent is the total number of plaintext bytes sent through the tunnel.
BytesSent uint64
// BytesRecv is the total number of plaintext bytes received through the tunnel.
BytesRecv uint64
// Uptime is the duration since the tunnel was established (zero if not up yet).
Uptime time.Duration
}
Stats is a snapshot of per-session traffic counters.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
auth
|
|
|
saml
Package saml handles the AWS Client VPN CRV1 SAML challenge-response flow.
|
Package saml handles the AWS Client VPN CRV1 SAML challenge-response flow. |
|
Package caps answers one question about a parsed profile: what does it ask for that this client cannot honour?
|
Package caps answers one question about a parsed profile: what does it ask for that this client cannot honour? |
|
cmd
|
|
|
cli
command
Command go-openvpn-cli is a minimal CLI for the go-openvpn VPN client.
|
Command go-openvpn-cli is a minimal CLI for the go-openvpn VPN client. |
|
relay-server
command
Command relay-server is a zero-dependency, in-memory relay server for local testing.
|
Command relay-server is a zero-dependency, in-memory relay server for local testing. |
|
Package device is the tunnel device seam.
|
Package device is the tunnel device seam. |
|
fd
Package fd is the supplied-descriptor tunnel backend.
|
Package fd is the supplied-descriptor tunnel backend. |
|
kernel
Package kernel is the privileged tunnel backend.
|
Package kernel is the privileged tunnel backend. |
|
Package diag defines the diagnostics vocabulary shared by the client, the capability registry and the measurement harness: the connection stages, the error taxonomy, capability gaps, and the per-attempt session report.
|
Package diag defines the diagnostics vocabulary shared by the client, the capability registry and the measurement harness: the connection stages, the error taxonomy, capability gaps, and the per-attempt session report. |
|
Package dns handles DNS configuration pushed by an OpenVPN server.
|
Package dns handles DNS configuration pushed by an OpenVPN server. |
|
Package e2e holds the end-to-end tests of the OpenVPN client: the ones that stand up a real peer and drive a whole connection through it.
|
Package e2e holds the end-to-end tests of the OpenVPN client: the ones that stand up a real peer and drive a whole connection through it. |
|
internal
|
|
|
compress
Package compress implements OpenVPN's data-channel compression framing and LZO decompression.
|
Package compress implements OpenVPN's data-channel compression framing and LZO decompression. |
|
control
Package control reads and classifies the plaintext messages the server sends down the OpenVPN control channel once TLS is up: PUSH_REPLY, AUTH_FAILED in its plain, temporary and challenge forms, and the RESTART and HALT pushes that end a session.
|
Package control reads and classifies the plaintext messages the server sends down the OpenVPN control channel once TLS is up: PUSH_REPLY, AUTH_FAILED in its plain, temporary and challenge forms, and the RESTART and HALT pushes that end a session. |
|
crypto
CBC cipher implementation for the OpenVPN3 data channel.
|
CBC cipher implementation for the OpenVPN3 data channel. |
|
ctls
Package ctls implements TLS-over-control-channel for OpenVPN3.
|
Package ctls implements TLS-over-control-channel for OpenVPN3. |
|
datachannel
Package datachannel implements the OpenVPN3 data channel: the P_DATA_V2 and P_DATA_V1 packet framings, the encrypt/decrypt pipeline, and the replay-protection sliding window.
|
Package datachannel implements the OpenVPN3 data channel: the P_DATA_V2 and P_DATA_V1 packet framings, the encrypt/decrypt pipeline, and the replay-protection sliding window. |
|
framing
Package framing implements the OpenVPN3 wire format: packet opcodes, the 2-byte length-prefixed TCP framing, and raw UDP framing.
|
Package framing implements the OpenVPN3 wire format: packet opcodes, the 2-byte length-prefixed TCP framing, and raw UDP framing. |
|
keymethod2
Package keymethod2 implements the OpenVPN key-method-2 auth exchange in both framings it is spoken in: the stock uint16_be one, and the AWS Client VPN patched uint32 one that exists because SAML assertions outgrow a uint16 length field.
|
Package keymethod2 implements the OpenVPN key-method-2 auth exchange in both framings it is spoken in: the stock uint16_be one, and the AWS Client VPN patched uint32 one that exists because SAML assertions outgrow a uint16 length field. |
|
mssfix
Package mssfix clamps the TCP MSS option in SYN and SYN-ACK packets.
|
Package mssfix clamps the TCP MSS option in SYN and SYN-ACK packets. |
|
occ
Package occ holds the in-band control plaintexts of the OpenVPN data channel: the keepalive magic and the OCC (OpenVPN Configuration Control) messages.
|
Package occ holds the in-band control plaintexts of the OpenVPN data channel: the keepalive magic and the OCC (OpenVPN Configuration Control) messages. |
|
prf
Package prf implements the classic OpenVPN key-method-2 key derivation: the two-stage expansion that turns the key material both peers exchange inside the control channel into the 256-byte data-channel key block.
|
Package prf implements the classic OpenVPN key-method-2 key derivation: the two-stage expansion that turns the key material both peers exchange inside the control channel into the 256-byte data-channel key block. |
|
reliable
Package reliable implements the OpenVPN3 reliable control-channel transport: sequence numbers, ACK handling, retransmit queue, and a sliding receive window.
|
Package reliable implements the OpenVPN3 reliable control-channel transport: sequence numbers, ACK handling, retransmit queue, and a sliding receive window. |
|
tlsverify
Package tlsverify builds the control channel's TLS config and performs the server verification OpenVPN performs, in place of the one crypto/tls performs.
|
Package tlsverify builds the control channel's TLS config and performs the server verification OpenVPN performs, in place of the one crypto/tls performs. |
|
wrap
Package wrap implements the OpenVPN control-channel wrappings — the transform tls-auth and tls-crypt apply to every control packet on its way to the wire, and undo on the way back.
|
Package wrap implements the OpenVPN control-channel wrappings — the transform tls-auth and tls-crypt apply to every control packet on its way to the wire, and undo on the way back. |
|
Package netstack is the userspace tunnel backend: one gVisor network stack per tunnel, inside this process, needing no privilege and touching no host state.
|
Package netstack is the userspace tunnel backend: one gVisor network stack per tunnel, inside this process, needing no privilege and touching no host state. |
|
Package profile parses OpenVPN .ovpn configuration files, and builds profiles programmatically from a Spec.
|
Package profile parses OpenVPN .ovpn configuration files, and builds profiles programmatically from a Spec. |
|
Package relay implements the CLI agent side of the go-openvpn SAML relay protocol.
|
Package relay implements the CLI agent side of the go-openvpn SAML relay protocol. |
|
Netlink route management for the routing package.
|
Netlink route management for the routing package. |
|
Package testenv manages the mock openvpn3-core server for integration tests.
|
Package testenv manages the mock openvpn3-core server for integration tests. |
|
mockserver
command
Command mockserver is the go-openvpn mock OpenVPN server.
|
Command mockserver is the go-openvpn mock OpenVPN server. |
|
Package tun provides a TUN network interface for Linux.
|
Package tun provides a TUN network interface for Linux. |