Documentation
¶
Overview ¶
Package netstack runs a TCP/IP stack -- gVisor's netstack -- over any packetio device, so a program that takes packets off the wire at line rate can also speak TCP on them, in userspace, with the kernel nowhere on the path. Listen and Dial give net.Listener and net.Conn; what runs on top is ordinary Go.
dev, _ := afpacket.Open("eth1")
st, _ := netstack.New(dev, netstack.Config{Addr: netip.MustParsePrefix("10.0.0.2/24"), MAC: mac})
ln, _ := st.Listen("tcp", ":8080")
for { c, _ := ln.Accept(); go io.Copy(c, c) }
The same program runs on afxdp, dpdk, mlx5 and afpacket; the backend is the Open call. The stack does ARP, IPv4 and TCP; the endpoint underneath does Ethernet and 802.1Q, segments large packets into frames in place (GSO), optionally merges arriving segments (GRO), and finishes checksums in software.
Where the bytes go ¶
gVisor owns every byte the stack touches: a received frame is copied once into a pooled view and goes straight back to the receive ring; on transmit a packet's views are copied once, straight into one of the device's frames. One copy each way is the floor gVisor's API sets. Everything else the backend buys is kept: no per-packet syscalls, batched rings, no sk_buff.
The address, and who answers ARP ¶
The stack answers ARP for its own address and asks for its peers'. Whether it ever sees an ARP frame depends on the backend, and so does which address it may use:
- afpacket has no steering: the kernel sees every packet too. Give the stack an address the kernel does not own (an unnumbered interface, or a second address on the subnet not configured on the interface); the kernel then answers neither ARP nor TCP for it. A kernel-owned address gets a kernel RST for every SYN.
- afxdp and mlx5 keep the kernel's interface. With a steering filter for the TCP port alone, the kernel goes on answering ARP for its own address and the stack may share it -- the endpoint learns each peer's MAC from its first frame. To give the stack an address of its own, steer ARP to it as well: on afxdp afxdp.WithXDP(xdp.WithFilter( xdp.MatchEtherType(0x0806), xdp.MatchTCPPort(port))); on mlx5 a whole VLAN, MatchVLAN(id) with MatchDstMAC for the port's own MAC and for broadcast, which the kernel has no interface on.
- dpdk on a device bound to vfio-pci (EC2's ENA among them) has no kernel: every frame arrives here and the stack does ARP itself. On a bifurcated card or a vdev it behaves like afxdp or afpacket respectively.
A stack that dials needs the replies steered to it: by the peer's port, or all of TCP.
The endpoint learns a peer's MAC from the frames the peer sends to the stack, which is what lets the kernel answer ARP on the stack's behalf. That learning is as trustworthy as ARP: unauthenticated. It is bounded -- only frames to the stack's own MAC and address, from its own prefix, with good checksums; never over an entry from Config.Neighbors, which are pinned -- but on a link shared with hosts that are not trusted, the gateway belongs in Config.Neighbors.
Queues ¶
One receive goroutine per receive queue delivers into the stack; one transmit goroutine per transmit queue (gVisor's fifo queueing discipline) writes out of it.
The receive goroutine does more than deliver. The patched gVisor this module carries processes an established connection's segments on the goroutine that delivered them, rather than waking a worker per segment, and runs the work it cannot do there -- handshakes, closes -- after each batch on that same goroutine. So a receive goroutine runs TCP, and with Config.DirectTx it transmits the replies too: for one connection's traffic it is the whole stack. That is where the speed comes from, and it is why anything expensive in a caller's handler belongs on a goroutine of the caller's own, not inline in the read loop. Each packetio queue is driven by exactly one goroutine, as packetio requires, and the caller's own goroutines never touch the device. On a backend that cannot sleep in Poll (dpdk, mlx5) a quiet receive goroutine backs off to short sleeps; Config.BusyPoll keeps it spinning. Config.DirectTx does without the transmit goroutines: whatever goroutine produced a packet transmits it, under a per-queue lock.
One stack per device. Close resets the connections still open, then stops the goroutines; when it returns nothing is inside a queue, and the device may be closed. A queue that fails is reported by Err and Done.
Index ¶
- type Config
- type Stack
- func (st *Stack) AddNeighbor(ip netip.Addr, mac net.HardwareAddr) error
- func (st *Stack) Close() error
- func (st *Stack) Dial(ctx context.Context, network, address string) (net.Conn, error)
- func (st *Stack) Done() <-chan struct{}
- func (st *Stack) Err() error
- func (st *Stack) Listen(network, address string) (net.Listener, error)
- func (st *Stack) Stats() Stats
- func (st *Stack) TCPIP() *stack.Stack
- type Stats
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// Addr is the stack's IPv4 address with its on-link prefix. See the
// package doc for which address to use on each backend: on a device the
// kernel still owns it must be one the kernel does not answer for.
Addr netip.Prefix
// MAC is the address frames are sent from and that inbound frames are
// classified against. Ordinarily the interface's own; the example takes
// it from net.InterfaceByName, and dpdk/mlx5 report it in their Info.
MAC net.HardwareAddr
// VLAN, when nonzero, is the 802.1Q tag every frame must carry and every
// transmitted frame gets.
VLAN uint16
// MTU is the IP MTU. Zero means 1500, or less where a frame cannot hold
// that much (packetio.Capabilities.MaxFrameSize is the ceiling).
MTU int
// Gateway, when set, is the next hop for every destination off the
// prefix. Unset, everything is treated as on-link and resolved by ARP.
Gateway netip.Addr
// Neighbors seeds the MAC table with peers this stack must send to
// before it has heard from them, such as the gateway on a backend where
// the kernel answers ARP and the stack never sees a reply. An entry given
// here is pinned: nothing heard on the wire changes it. Entries the stack
// learns on its own, from frames addressed to it, are as trustworthy as
// ARP is -- a host on the link can claim another's address -- so on a
// shared link the gateway belongs here.
Neighbors map[netip.Addr]net.HardwareAddr
// NoGSO turns segmentation offload off: TCP then hands the endpoint one
// packet per MSS instead of one of up to 64 KiB that the endpoint splits
// in place. GSO is on unless this is set, because it is a large part of
// the throughput.
NoGSO bool
// NoGRO turns off gVisor's software receive offload, which merges a
// burst of one flow's segments into one packet before the stack sees it.
// GRO is on unless this is set, because a peer at line rate sends a
// segment per stack trip otherwise, and one TCP goroutine cannot keep
// up: measured on an ENA, 2.6 Gbit/s without it and 6.3 with. The
// endpoint verifies every frame's checksums itself before merging.
NoGRO bool
// TxQueueLen is how many packets may wait for each transmit queue's
// goroutine. Zero means 1000.
TxQueueLen int
// DirectTx makes the stack transmit from whatever goroutine produced a
// packet, one packet at a time under a per-queue lock, instead of
// handing it to a transmit goroutine per queue (gVisor's fifo
// discipline) that batches. No handoff, no batching: lower latency for
// request/response, and with the gVisor fork's inline processing the
// reply to a segment leaves on the goroutine that received it (166k
// against 125k req/s on an ENA with the fork).
//
// On dpdk and mlx5, whose queues place the goroutine that first
// transmits on the queue's own CPU and leave it there, that goroutine
// is then whichever one produced the packet -- a timer, or one of the
// caller's. Open those devices with their placement option off when
// setting this.
DirectTx bool
// SendBufferSize and ReceiveBufferSize are the TCP defaults, in bytes.
// Zero means 4 MiB each.
SendBufferSize, ReceiveBufferSize int
// BusyPoll keeps a receive goroutine spinning when the queue is idle, on
// a backend that cannot sleep in Poll (dpdk, mlx5). Off, such a goroutine
// backs off to short sleeps after a quiet spell, which costs latency on
// the first packet after it and saves a core the rest of the time.
BusyPoll bool
// FastClock reads the monotonic clock through the runtime's nanotime
// rather than time.Now, which TCP asks for several times per segment. It
// relies on a go:linkname into the runtime; off by default.
FastClock bool
// Logf, when set, receives the endpoint's own messages: a receive queue
// that failed, and nothing on the packet path. Nil means log.Printf.
Logf func(format string, args ...any)
}
Config configures a Stack. Addr and MAC are required; everything else has a default that works.
type Stack ¶
type Stack struct {
// contains filtered or unexported fields
}
Stack is a TCP/IP stack -- gVisor's netstack -- over one packetio.Device. Listen and Dial give net.Listener and net.Conn, so what runs on top is ordinary Go.
func New ¶
New builds a stack over dev and brings it up: the device's queues are driven from here on, and the address is answering ARP. The device is not closed by Close; it belongs to whoever opened it, and its queues must not be used by anything else while the stack is up. One stack per device.
func (*Stack) AddNeighbor ¶
AddNeighbor tells the stack the MAC for ip, for a peer it must send to before it has heard from it. The entry is pinned, as Config.Neighbors are.
func (*Stack) Close ¶
Close stops the stack. Connections still open are reset, the peers told; then the receive goroutines, the stack's own goroutines and transmit are stopped, in that order, so that when Close returns nothing is inside a queue of the device and the device may be closed. The device is left open.
func (*Stack) Dial ¶
Dial connects to the address on the named network, as net.Dialer.DialContext does. The peer's MAC is resolved by ARP unless the stack already knows it.
func (*Stack) Done ¶
func (st *Stack) Done() <-chan struct{}
Done is closed when a queue of the device fails.
func (*Stack) Err ¶
Err returns the first device failure the stack hit, or nil. A queue that fails (the interface went away, say) is logged and not driven again; the stack goes on, with connections timing out. Done is the same news as a channel.
func (*Stack) Listen ¶
Listen announces on the local network address, as net.Listen does. The network must be "tcp" or "tcp4"; the address is host:port, where an empty host means the stack's own address. Accept on the listener reports net.ErrClosed once the listener or the stack is closed, as net.Listen's does, so an ordinary accept loop ends rather than reporting a failure.
type Stats ¶
type Stats struct {
Rx, Tx uint64
TxDropped uint64 // packets the transmit ring had no room for (TCP retransmits them)
TxWaited uint64 // WritePackets calls that had to wait for ring room
GSOPackets uint64 // packets larger than one MSS that the endpoint split
GSOFrames uint64 // frames those packets became
GROFrames uint64 // frames handed to GRO
GROPackets uint64 // packets GRO delivered to the stack
RxBadCsum uint64 // received IPv4 frames dropped for a bad IPv4 or TCP checksum
Fragments uint64 // IPv4 fragments, dropped: TCP never sends them and the stack does not take them
WrongVLAN uint64 // frame tagged for another VLAN, or tagged/untagged mismatch
NotIP uint64 // EtherType the stack is not given: neither IPv4 nor ARP
Runt uint64 // frame too short for an IPv4 header
OwnEcho uint64 // frames carrying this endpoint's own source MAC (a tap echoing its transmissions)
Chained uint64 // frames that were one piece of a packet spanning several; not accepted
UnknownMAC uint64 // transmit to a peer never heard from and never resolved: sent to broadcast
Neighbors int // size of the MAC table, seeded and learned
// DeviceRxDropped and DeviceTxDropped are the device's own counters:
// what the NIC or the kernel discarded before this stack saw it, and
// what the device refused to send. They are not this package's drops
// and are usually the first sign that a link is offering more than the
// stack is taking.
DeviceRxDropped, DeviceTxDropped uint64
// QDiscDropped is packets the stack's transmit queueing discipline
// dropped for want of room, before the endpoint saw them. It is what
// fills first when the wire cannot keep up; Config.TxQueueLen is its
// depth. Zero with Config.DirectTx, which has no such queue.
QDiscDropped uint64
}
Stats is a snapshot of the endpoint's counters. Rx and Tx count frames delivered to the stack and put on the wire; the rest are drops, each named for its reason.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
tcpecho
command
Command tcpecho is a TCP echo server -- and, with -client, a client -- on gVisor's TCP/IP stack over any packetio backend.
|
Command tcpecho is a TCP echo server -- and, with -client, a client -- on gVisor's TCP/IP stack over any packetio backend. |
|
gvisor
|
|
|
pkg/atomicbitops
Package atomicbitops provides extensions to the sync/atomic package.
|
Package atomicbitops provides extensions to the sync/atomic package. |
|
pkg/bits
Package bits includes all bit related types and operations.
|
Package bits includes all bit related types and operations. |
|
pkg/buffer
Package buffer provides the implementation of a non-contiguous buffer that is reference counted, pooled, and copy-on-write.
|
Package buffer provides the implementation of a non-contiguous buffer that is reference counted, pooled, and copy-on-write. |
|
pkg/context
Package context defines an internal context type.
|
Package context defines an internal context type. |
|
pkg/cpuid
Package cpuid provides basic functionality for creating and adjusting CPU feature sets.
|
Package cpuid provides basic functionality for creating and adjusting CPU feature sets. |
|
pkg/gohacks
Package gohacks contains utilities for subverting the Go compiler.
|
Package gohacks contains utilities for subverting the Go compiler. |
|
pkg/goid
Package goid provides the Get function.
|
Package goid provides the Get function. |
|
pkg/linewriter
Package linewriter provides an io.Writer which calls an emitter on each line.
|
Package linewriter provides an io.Writer which calls an emitter on each line. |
|
pkg/log
Package log implements a library for logging.
|
Package log implements a library for logging. |
|
pkg/rand
Package rand implements a cryptographically secure pseudorandom number generator.
|
Package rand implements a cryptographically secure pseudorandom number generator. |
|
pkg/refs
Package refs defines an interface for reference counted objects.
|
Package refs defines an interface for reference counted objects. |
|
pkg/sleep
Package sleep allows goroutines to efficiently sleep on multiple sources of notifications (wakers).
|
Package sleep allows goroutines to efficiently sleep on multiple sources of notifications (wakers). |
|
pkg/state
Package state provides functionality related to saving and loading object graphs.
|
Package state provides functionality related to saving and loading object graphs. |
|
pkg/state/wire
Package wire contains a few basic types that can be composed to serialize graph information for the state package.
|
Package wire contains a few basic types that can be composed to serialize graph information for the state package. |
|
pkg/sync
Package sync provides synchronization primitives.
|
Package sync provides synchronization primitives. |
|
pkg/sync/locking
Package locking implements lock primitives with the correctness validator.
|
Package locking implements lock primitives with the correctness validator. |
|
pkg/tcpip
Package tcpip provides the interfaces and related types that users of the tcpip stack will use in order to create endpoints used to send and receive data over the network stack.
|
Package tcpip provides the interfaces and related types that users of the tcpip stack will use in order to create endpoints used to send and receive data over the network stack. |
|
pkg/tcpip/adapters/gonet
Package gonet provides a Go net package compatible wrapper for a tcpip stack.
|
Package gonet provides a Go net package compatible wrapper for a tcpip stack. |
|
pkg/tcpip/checksum
Package checksum provides the implementation of the encoding and decoding of network protocol headers.
|
Package checksum provides the implementation of the encoding and decoding of network protocol headers. |
|
pkg/tcpip/hash/jenkins
Package jenkins implements Jenkins's one_at_a_time, non-cryptographic hash functions created by by Bob Jenkins.
|
Package jenkins implements Jenkins's one_at_a_time, non-cryptographic hash functions created by by Bob Jenkins. |
|
pkg/tcpip/header
Package header provides the implementation of the encoding and decoding of network protocol headers.
|
Package header provides the implementation of the encoding and decoding of network protocol headers. |
|
pkg/tcpip/header/parse
Package parse provides utilities to parse packets.
|
Package parse provides utilities to parse packets. |
|
pkg/tcpip/internal/tcp
Package tcp contains internal type definitions that are not expected to be used by anyone else outside pkg/tcpip.
|
Package tcp contains internal type definitions that are not expected to be used by anyone else outside pkg/tcpip. |
|
pkg/tcpip/link/qdisc
Package qdisc provides shared building blocks used by queueing disciplines.
|
Package qdisc provides shared building blocks used by queueing disciplines. |
|
pkg/tcpip/link/qdisc/fifo
Package fifo provides the implementation of FIFO queuing discipline that queues all outbound packets and asynchronously dispatches them to the lower link endpoint in the order that they were queued.
|
Package fifo provides the implementation of FIFO queuing discipline that queues all outbound packets and asynchronously dispatches them to the lower link endpoint in the order that they were queued. |
|
pkg/tcpip/network/arp
Package arp implements the ARP network protocol.
|
Package arp implements the ARP network protocol. |
|
pkg/tcpip/network/hash
Package hash contains utility functions for hashing.
|
Package hash contains utility functions for hashing. |
|
pkg/tcpip/network/internal/fragmentation
Package fragmentation contains the implementation of IP fragmentation.
|
Package fragmentation contains the implementation of IP fragmentation. |
|
pkg/tcpip/network/internal/ip
Package ip holds IPv4/IPv6 common utilities.
|
Package ip holds IPv4/IPv6 common utilities. |
|
pkg/tcpip/network/internal/multicast
Package multicast contains utilities for supporting multicast routing.
|
Package multicast contains utilities for supporting multicast routing. |
|
pkg/tcpip/network/ipv4
Package ipv4 contains the implementation of the ipv4 network protocol.
|
Package ipv4 contains the implementation of the ipv4 network protocol. |
|
pkg/tcpip/ports
Package ports provides PortManager that manages allocating, reserving and releasing ports.
|
Package ports provides PortManager that manages allocating, reserving and releasing ports. |
|
pkg/tcpip/seqnum
Package seqnum defines the types and methods for TCP sequence numbers such that they fit in 32-bit words and work properly when overflows occur.
|
Package seqnum defines the types and methods for TCP sequence numbers such that they fit in 32-bit words and work properly when overflows occur. |
|
pkg/tcpip/stack
Package stack provides the glue between networking protocols and the consumers of the networking stack.
|
Package stack provides the glue between networking protocols and the consumers of the networking stack. |
|
pkg/tcpip/stack/gro
Package gro implements generic receive offload.
|
Package gro implements generic receive offload. |
|
pkg/tcpip/transport
Package transport supports transport protocols.
|
Package transport supports transport protocols. |
|
pkg/tcpip/transport/internal/network
Package network provides facilities to support tcpip.Endpoints that operate at the network layer or above.
|
Package network provides facilities to support tcpip.Endpoints that operate at the network layer or above. |
|
pkg/tcpip/transport/internal/noop
Package noop contains an endpoint that implements all tcpip.Endpoint functions as noops.
|
Package noop contains an endpoint that implements all tcpip.Endpoint functions as noops. |
|
pkg/tcpip/transport/packet
Package packet provides the implementation of packet sockets (see packet(7)).
|
Package packet provides the implementation of packet sockets (see packet(7)). |
|
pkg/tcpip/transport/raw
Package raw provides the implementation of raw sockets (see raw(7)).
|
Package raw provides the implementation of raw sockets (see raw(7)). |
|
pkg/tcpip/transport/tcp
Package tcp contains the implementation of the TCP transport protocol.
|
Package tcp contains the implementation of the TCP transport protocol. |
|
pkg/tcpip/transport/tcpconntrack
Package tcpconntrack implements a TCP connection tracking object.
|
Package tcpconntrack implements a TCP connection tracking object. |
|
pkg/tcpip/transport/udp
Package udp contains the implementation of the UDP transport protocol.
|
Package udp contains the implementation of the UDP transport protocol. |
|
pkg/waiter
Package waiter provides the implementation of a wait queue, where waiters can be enqueued to be notified when an event of interest happens.
|
Package waiter provides the implementation of a wait queue, where waiters can be enqueued to be notified when an event of interest happens. |