Help us shape the future of Go! Take the 2026 Go Developer Survey

wireguard

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 27 Imported by: 0

README

WGC

CI Go Reference

WireGuard connections for Go. WGC exchanges authenticated, encrypted messages between peers identified by public keys. It implements WireGuard handshakes, sessions, rekeying, replay protection, and peer roaming over a pluggable transport.

Project page · API reference · Runnable example

Install

Requires Go 1.25 or later.

go get github.com/idy/wgc

The module is github.com/idy/wgc. Its Go package name is wireguard, preserving the original connection API.

import (
    "net"

    wireguard "github.com/idy/wgc"
    "github.com/idy/wgc/transport"
)

Run the example

go run ./examples/echo

The example generates two ephemeral key pairs, opens two local UDP endpoints, authorizes only the other peer, and sends an encrypted echo through a real WireGuard handshake. It requires no TUN device, root privileges, or external server. Expected output includes Received authenticated echo: hello, wgc.

Use a connection

Generate or load the local private key, authorize remote public keys, and provide the endpoint for peers this connection should initiate a handshake with:

socket, err := net.ListenUDP("udp", &net.UDPAddr{Port: 51820})
if err != nil {
    return err
}
conn, err := wireguard.NewConn(transport.FromUDPConn(socket), wireguard.Config{
    StaticPrivateKey: &privateKey,
    MaxPeers:         128,
    PeerConfigLookup: func(key wireguard.Key) (*wireguard.PeerConfig, error) {
        if key != remotePublicKey {
            return nil, nil // Reject unknown peers.
        }
        return &wireguard.PeerConfig{Addr: remoteEndpoint}, nil
    },
})
if err != nil {
    socket.Close()
    return err
}
defer conn.Close()

if _, _, err := conn.WriteTo([]byte("hello"), remotePublicKey); err != nil {
    return err
}
buffer := make([]byte, wireguard.MaximumDataSize)
n, peer, err := conn.ReadFrom(buffer)

privateKey, remotePublicKey, and remoteEndpoint are application-supplied values. See the complete echo example for their setup.

ReadFrom returns the authenticated sender. WriteTo selects a peer by public key and queues a message; encryption and transmission happen asynchronously. A successful return means the message was queued, not delivered. A write can trigger a handshake and be discarded while no session is available. Use application acknowledgements and retries when delivery matters, as the echo example does. PeerWriter provides a reusable writer for a selected peer. The API is message-oriented and does not implement net.Conn or net.PacketConn directly; it does not guarantee delivery or ordering. Keep a read loop running, since WireGuard keepalives can appear as empty messages.

For a receive queue, range over conn.ReadQueue() and call packet.Release() after consuming each packet. Packet bytes are pooled; copy them before retaining them beyond Release. Do not combine queue reads and ReadFrom on one connection.

Peers and resource limits

  • PeerConfigLookup authorizes a new public key. Returning nil denies it; returning an error permits a future lookup retry. A successful result is cached.
  • PeerConfig.PSK adds an optional preshared key. Both peers must use the same key.
  • Authenticated packets update the remote endpoint unless DisableAddrRoaming is set. DisableHandshakeInitiation supports passive peers.
  • Config.MaxPeers bounds live peer state. Zero preserves the original unlimited behavior; set a limit for listeners exposed to untrusted peers.
  • ReadQueueSize and WriteQueueSize default to 1024.
  • SharedWriteQueue shares the connection's write queue across destinations. Explicit PeerWriter values retain their own queues.
  • DisableStats disables peer history and traffic counters. Otherwise, CaptureStats captures and resets the counters.
  • Close closes the underlying transport and unblocks pending I/O.

Transports

The public transport package contains one implementation: FromUDPConn adapts an existing listening *net.UDPConn to the connection API. It sends and receives standard WireGuard messages without additional framing. Closing the adapter closes the supplied socket.

Applications can supply another implementation of wireguard.Transport:

type Transport interface {
    ReadFrom([]byte) (int, netip.AddrPort, error)
    WriteTo([]byte, netip.AddrPort) (int, error)
    Close() error
}

Pass the implementation to wireguard.NewConn(applicationTransport, config). Custom framing, socket policies, and other application-specific transport behavior belong in the application; WGC does not include their implementations. A transport must support a reader and concurrent writers, avoid retaining the supplied buffers after each method returns, and unblock pending I/O on Close.

WGC does not create a VPN interface, manage IP addresses, install routes, or provide a TCP/IP stack. Stock WireGuard peers expect IP packets as payloads; applications exchanging arbitrary messages should use WGC at both ends.

Logging

By default, normal messages, warnings, and errors use Go's standard log package; debug verbosity is suppressed. SetLogHandler installs a concurrent-safe, process-wide callback. Verbosity values are -2 for errors, -1 for warnings, 0 for normal messages, and positive for debug detail. Pass nil to restore the default. Never enable detailed protocol logs where their contents are sensitive.

Development

go test -race ./...
go test -tags BUFPOOL_MINI ./...
go vet ./...
go run ./examples/echo

Tests cover handshake helpers, bounded peer state, concurrent shutdown, authenticated transport messages, replay rejection, UDP I/O, and transport shutdown. BUFPOOL_MINI selects a smaller standard-library buffer pool; the default pool uses gVisor's buffer implementation. This is the existing pool behavior, and the build tag changes MaximumMessageSize and MaximumDataSize.

Key files:

  • conn.go, peer.go, and secure_session.go: connections, peer lifecycle, handshakes, and encrypted sessions.
  • queue_io.go and writer.go: packet ownership and queued writes.
  • transport/: the standard UDP socket adapter.
  • internal/: protocol headers, cryptographic helpers, buffers, timestamps, backoff, and logging.
  • examples/echo/: a self-contained local exchange.
  • site/: the static project page, published to wgc.idy.dev by GitHub Pages.

License

WGC is available under the MIT License. Third-party code and modules retain their own licenses; see third-party notices. WireGuard is a registered trademark of Jason A. Donenfeld. This is an independent project and does not imply endorsement by the WireGuard project.

Documentation

Overview

Package wireguard provides authenticated, encrypted, message-oriented connections using WireGuard handshakes and transport messages. Connections address peers by their static public keys and run over a pluggable Transport.

The module path is github.com/idy/wgc; the package name remains wireguard. It does not configure TUN devices, IP routes, or a userspace TCP/IP stack. Applications decide how to authorize peers and interpret message payloads.

Index

Constants

View Source
const (
	MaximumMessageSize = bufpool.DefaultSize
	MaximumDataSize    = MaximumMessageSize - header.EmptyTransportDataSize
)
View Source
const KeySize = header.KeySize

Variables

View Source
var (
	ErrPeerNotFound  = errors.New("netutils/wireguard: peer not found")
	ErrPeerBlocked   = errors.New("netutils/wireguard: peer blocked")
	ErrPeerNoSession = errors.New("netutils/wireguard: peer has no session")
)
View Source
var ErrInvalidPacket = netutil.ErrInvalidPacket

ErrInvalidPacket identifies malformed or unauthenticated input.

View Source
var GenerateKeys = utils.DHGenerate
View Source
var MustGenerateKeys = utils.MustDHGenerate

Functions

func CheckLocksIgnoreWarning

func CheckLocksIgnoreWarning()

func SetLogHandler

func SetLogHandler(handler func(verbosity int, message string))

SetLogHandler replaces the process-wide log output. Verbosity is -2 for errors, -1 for warnings, 0 for normal messages, and positive for debug detail. The handler must be safe for concurrent calls. A nil handler restores the standard log package and suppresses positive verbosity levels.

Types

type Config

type Config struct {
	// If nil, a random key will be generated. Users can read it with
	// WGConn.PrivateKey method.
	StaticPrivateKey *Key

	// If nil all peer will be block by default. Users can use Peer.Update
	// of Peer.Unblock to resume communication with them.
	PeerConfigLookup func(Key) (*PeerConfig, error)

	// If not specified, the default value is 1024.
	ReadQueueSize int

	// If not specified, the default value is 1024.
	WriteQueueSize int

	// MaxPeers bounds live handshake identities. Zero preserves the legacy
	// unlimited behavior; rejected new peers may retry after eviction.
	MaxPeers int
	// DisableStats disables peer history and traffic counters.
	DisableStats bool
	// SharedWriteQueue avoids a permanent writer/queue per destination when
	// using Conn.WriteTo. Explicit PeerWriter users keep their own queues.
	SharedWriteQueue bool
}

The Config structure uses to config a wireguard connection.

type Conn

type Conn struct {

	// UnderLoad indicates whether the host is busy. Users can set UnderLoad to
	// true or false. If set true, all handshakes require a valid mac2.
	UnderLoad atomicBool
	// contains filtered or unexported fields
}

Conn is a connection based on the WireGuard protocol. It is used to transmit message data to and from the authenticated Peer through UDP. An additional mechanism is needed to ensure the order and continuity of the data. Peer authentication is based on WireGuard's handshake process, which is automatically completed by goroutines running in the background.

func NewConn

func NewConn(transport Transport, config Config) (*Conn, error)

func (*Conn) CaptureStats

func (conn *Conn) CaptureStats() (Stats, time.Time)

CaptureStats captures the status of the current connection, returns the status and the start time of the status counter. After calling this function, the status counter will be reset and the current time will be set as the start time of a new round of counter. The new connection will set the counter start time when ReadFrom is called for the first time.

func (*Conn) CheckLocksIgnoreWarning

func (conn *Conn) CheckLocksIgnoreWarning()

func (*Conn) Close

func (conn *Conn) Close() error

Close closes the underlying udp socket immediately, unblock all pending writes and delete all peers and sessions. Close an closed wireguard have no effect.

func (*Conn) Peer

func (conn *Conn) Peer(peerPubKey Key) (Peer, bool)

Peer returns the currently active peer instance, or nil if it does not exist.

func (*Conn) PeerWriter

func (conn *Conn) PeerWriter(peerPubKey Key) *PeerWriter

PeerWriter returns a peer writer. This peer writer

func (*Conn) PrivateKey

func (conn *Conn) PrivateKey() Key

PrivateKey returns a clone version of the static private key of the WireGuard connection.

func (*Conn) PublicKey

func (conn *Conn) PublicKey() Key

PublicKey returns a clone version of the static public key of the WireGuard connection.

func (*Conn) ReadFrom

func (conn *Conn) ReadFrom(b []byte) (int, Peer, error)

ReadFrom reads up to len(b) bytes into b. It returns the number of bytes read, the corresponding peer and any error encountered. Users can use the returned peer to send data back directly. Writing data in this way is more efficient than calling WriteTo. If err is nil and n is 0, it means that we received an empty packet, e.g. a keep-alive message. Cannot be called concurrently.

func (*Conn) ReadQueue

func (conn *Conn) ReadQueue() <-chan Packet

ReadQueue returns a channel that contains all packets received by the connection. Users can use this channel to read data from the connection.

func (*Conn) WriteTo

func (conn *Conn) WriteTo(b []byte, staticPubKey Key) (int, Peer, error)

WriteTo queues a message for a peer identified by its static public key, consulting PeerConfigLookup when needed. A successful return confirms queue admission, not transmission or delivery. Encryption and sending happen in the background; a message can trigger a handshake and be discarded while no session is available. Applications must implement retries or acknowledgements when delivery matters.

type ConnStats

type ConnStats struct {
	// +checkatomic
	PeersJoin uint64

	// +checkatomic
	PeersEvict uint64

	// +checkatomic
	SessionsCreate uint64

	// +checkatomic
	SessionsDelete uint64
}

ConnStats is a struct that contains WireGuard connection status.

type Key

type Key = header.Key

func KeyFromBase64

func KeyFromBase64(s string) (k Key, err error)

func KeyFromHex

func KeyFromHex(s string) (k Key, err error)

func MustKeyFromBase64

func MustKeyFromBase64(s string) (k Key)

type Packet

type Packet struct {
	Data []byte
	// contains filtered or unexported fields
}

func (*Packet) From

func (pkt *Packet) From() Peer

func (*Packet) Release

func (pkt *Packet) Release()

type Peer

type Peer interface {
	// Block blocks the peer from communicating. All data written and
	// received during the blocking period will not be saved.
	Block() bool

	// IsBlocked returns whether this peer is blocked.
	IsBlocked() bool

	// Unblock resumes peer communication.
	Unblock() bool

	// Update configs peer.
	Update(*PeerConfig)

	// StaticPubKey returns a clone version of the peer's static public key.
	StaticPubKey() Key

	// RemoteAddr returns the peer's remote addr.
	RemoteAddr() netip.AddrPort

	// Writer returns a PeerWriter for more efficient writing.
	Writer() *PeerWriter
}

Peer describes the operations that users can perform on a WireGuard Peer.

type PeerConfig

type PeerConfig struct {
	// The preshared key of the peer.
	PSK *Key

	// The preset public endpoint of the peer.
	Addr netip.AddrPort

	// If true, the peer will not update it's public endpoint when receiving
	// authenticated messages.
	DisableAddrRoaming bool

	// If true, no handshake will be initiated to the peer.
	DisableHandshakeInitiation bool
}

The PeerConfig structure uses to config a Peer.

type PeerStats

type PeerStats struct {
	TxStats
	LastSeen   time.Time
	RemoteAddr netip.AddrPort
	// contains filtered or unexported fields
}

PeerStats is a struct that contains the internal status of a WireGuard Peer.

type PeerWriter

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

A PeerWriter is writer that write data to Peer. It caches and updates the underlying peer for writing data.

func (*PeerWriter) Close

func (w *PeerWriter) Close() error

func (*PeerWriter) Peer

func (w *PeerWriter) Peer() (Peer, bool)

func (*PeerWriter) PeerPublicKey

func (w *PeerWriter) PeerPublicKey() Key

PeerPublicKey returns the public key of the peer writer.

func (*PeerWriter) Write

func (w *PeerWriter) Write(b []byte) (int, error)

Write writes data to a active peer. If no peer is active, returns ErrPeerNotFound error.

func (*PeerWriter) WriteChunks

func (w *PeerWriter) WriteChunks(chunks [][]byte) (int, error)

WriteChunks merges the chunks and writes the merged data to a active peer.

func (*PeerWriter) WriteChunksToPeer

func (w *PeerWriter) WriteChunksToPeer(chunks [][]byte) (int, Peer, error)

func (*PeerWriter) WriteToPeer

func (w *PeerWriter) WriteToPeer(b []byte) (int, Peer, error)

WriteToPeer writes data to a active peer and returns the peer with the write result.

type Stats

type Stats struct {
	ConnStats
	TxStats
	Peers map[Key]PeerStats
}

Stats is a struct that contains the internal status of WireGuard connections.

type Transport

type Transport interface {
	// ReadFrom reads one message into b and returns its sender's endpoint.
	// IPv4 endpoints should use an unmapped IPv4 address.
	ReadFrom(b []byte) (int, netip.AddrPort, error)
	// WriteTo sends one message to addr.
	WriteTo(b []byte, addr netip.AddrPort) (int, error)
	// Close closes the transport and unblocks pending I/O.
	Close() error
}

Transport exchanges WireGuard messages with remote endpoints. Applications supply a Transport to NewConn to configure how each connection sends and receives messages. The transport package provides a standard UDP adapter; application-specific transports and wrappers can implement this interface.

Implementations must support a reader and concurrent writers. They must not retain the supplied buffers after a method returns.

type TxStats

type TxStats struct {
	BytesRecv   uint64
	PacketsRecv uint64

	// +checkatomic
	BytesSent uint64

	// +checkatomic
	PacketsSent uint64

	Handshake struct {
		InitiationsRecv uint64
		ResponsesRecv   uint64
		CookiesRecv     uint64

		// +checkatomic
		InitiationsSent uint64

		// +checkatomic
		ResponsesSent uint64

		// +checkatomic
		CookiesSent uint64
	}
	// contains filtered or unexported fields
}

TxStats is a struct that records the transmission status.

Directories

Path Synopsis
examples
echo command
Echo exchanges a message between two local WireGuard peers.
Echo exchanges a message between two local WireGuard peers.
internal

Jump to

Keyboard shortcuts

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