xdp

package
v0.0.0-...-a9890c3 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2020 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package xdp allows to use XDP sockets from Go.

An XDP socket allows to get packets from a network interface driver into a userspace process very fast, bypassing the Linux kernel's network stack, see https://lwn.net/Articles/750845/ for more information.

NOTE:

* If your network link device supports multiple queues - you might not see all of the incoming traffic because it might be e.g. load-balanced between multiple queues. You can use the `ethtool -L` command to control the load-balancing behaviour or even make it so that all of the incoming traffic is put on a single queue.

* On recent versions of Linux kernel, the eBPF map memory counts towards the "locked memory" user limit, so most likely you'll have to increase it. On Fedora Linux, this can be done by running `ulimit -l <new-limit>` command, or to make it permanent, by creating a file at `/etc/security/limits.d/50-lockedmem.conf` with e.g. the following contents (1MiB should be enough for this package):

  • - lockedmem 1048576

logging out and logging back in. When you hit this limit, you'll get an error that looks like this:

error: failed to create an XDP socket: ebpf.NewMap qidconf_map failed: map create: operation not permitted

Here is a minimal example of a program which receives network frames, modifies their destination MAC address in-place to broadcast address and transmits them back out the same network link:

package main

import (
	"github.com/asavie/xdp"
	"github.com/vishvananda/netlink"
)

func main() {
	const LinkName = "enp3s0"
	const QueueID = 0

	link, err := netlink.LinkByName(LinkName)
	if err != nil {
		panic(err)
	}

	xsk, err := xdp.NewSocket(link.Attrs().Index, QueueID)
	if err != nil {
		panic(err)
	}

	for {
		xsk.Fill(xsk.GetDescs(xsk.NumFreeFillSlots()))
		numRx, _, err := xsk.Poll(-1)
		if err != nil {
			panic(err)
		}
		rxDescs := xsk.Receive(numRx)
		for i := 0; i < len(rxDescs); i++ {
			// Set destination MAC address to
			// ff:ff:ff:ff:ff:ff
			frame := xsk.GetFrame(rxDescs[i])
			for i := 0; i < 6; i++ {
				frame[i] = byte(0xff)
			}
		}
		xsk.Transmit(rxDescs)
	}
}

Index

Constants

View Source
const (
	NumFrames              = 128
	FrameSize              = 2048
	FillRingNumDescs       = 64
	CompletionRingNumDescs = 64
	RxRingNumDescs         = 64
	TxRingNumDescs         = 64
	DescBatchSize          = 16
)

Variables

View Source
var DefaultSocketFlags uint16

DefaultSocketFlags are the flags which are passed to bind(2) system call when the XDP socket is bound, possible values include unix.XDP_SHARED_UMEM, unix.XDP_COPY, unix.XDP_ZEROCOPY.

View Source
var DefaultXdpFlags uint32

DefaultXdpFlags are the flags which are passed when the XDP program is attached to the network link, possible values include unix.XDP_FLAGS_DRV_MODE, unix.XDP_FLAGS_HW_MODE, unix.XDP_FLAGS_SKB_MODE, unix.XDP_FLAGS_UPDATE_IF_NOEXIST.

Functions

This section is empty.

Types

type BatchDesc

type BatchDesc struct {
	Len  int
	Data []byte
}

BatchDesc holds descriptors and data for batch reading

type ProgRef

type ProgRef func(xsks_map *ebpf.Map, qidconfMap *ebpf.Map) (*ebpf.Program, error)

ProgRef BPF precompiled program

type Socket

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

Socket XDP socket

func NewSocket

func NewSocket(Ifindex int, QueueID int, ebpfProg ProgRef) (xsk *Socket, err error)

NewSocket returns a new XDP socket attached to the network interface which has the given interface, and attached to the given queue on that network interface.

func (*Socket) Close

func (xsk *Socket) Close() error

Close closes and frees the resources allocated by the Socket.

func (*Socket) Complete

func (xsk *Socket) Complete(n int)

Complete consumes up to n descriptors from the Completion ring queue to which the kernel produces when it has actually transmitted a descriptor it got from Tx ring queue. You should use this method if you are doing polling on the xdp.Socket file descriptor yourself, rather than using the Poll() method.

func (*Socket) FD

func (xsk *Socket) FD() int

FD returns the file descriptor associated with this xdp.Socket which can be used e.g. to do polling.

func (*Socket) Fill

func (xsk *Socket) Fill(descs []unix.XDPDesc) int

Fill submits the given descriptors to be filled (i.e. to receive frames into) it returns how many descriptors where actually put onto Fill ring queue. The descriptors can be acquired either by calling the GetDescs() method or by calling Receive() method.

func (*Socket) GetDescs

func (xsk *Socket) GetDescs(n int) []unix.XDPDesc

GetDescs returns up to n descriptors which are not currently in use.

func (*Socket) GetFrame

func (xsk *Socket) GetFrame(d unix.XDPDesc) []byte

GetFrame returns the buffer containing the frame corresponding to the given descriptor. The returned byte slice points to the actual buffer of the corresponding frame, so modiyfing this slice modifies the frame contents.

func (*Socket) NumCompleted

func (xsk *Socket) NumCompleted() int

NumCompleted returns how many descriptors are there on the Completion ring queue which were produced by the kernel and which we have not yet consumed.

func (*Socket) NumFilled

func (xsk *Socket) NumFilled() int

NumFilled returns how many descriptors are there on the Fill ring queue which have not yet been consumed by the kernel. This method is useful if you're polling the xdp.Socket file descriptor yourself, rather than using the Poll() method - if it returns a number greater than zero it means you should set the unix.POLLIN flag.

func (*Socket) NumFreeFillSlots

func (xsk *Socket) NumFreeFillSlots() int

NumFreeFillSlots returns how many free slots are available on the Fill ring queue, i.e. the queue to which we produce descriptors which should be filled by the kernel with incoming frames.

func (*Socket) NumFreeTxSlots

func (xsk *Socket) NumFreeTxSlots() int

NumFreeTxSlots returns how many free slots are available on the Tx ring queue, i.e. the queue to which we produce descriptors which should be transmitted by the kernel to the wire.

func (*Socket) NumReceived

func (xsk *Socket) NumReceived() int

NumReceived returns how many descriptors are there on the Rx ring queue which were produced by the kernel and which we have not yet consumed.

func (*Socket) NumTransmitted

func (xsk *Socket) NumTransmitted() int

NumTransmitted returns how many descriptors are there on the Tx ring queue which have not yet been consumed by the kernel. Note that even after the descriptors are consumed by the kernel from the Tx ring queue, it doesn't mean that they have actually been sent out on the wire, that can be assumed only after the descriptors have been produced by the kernel to the Completion ring queue. This method is useful if you're polling the xdp.Socket file descriptor yourself, rather than using the Poll() method - if it returns a number greater than zero it means you should set the unix.POLLOUT flag.

func (*Socket) Poll

func (xsk *Socket) Poll(timeout int) (numReceived int, numCompleted int, err error)

Poll blocks until kernel informs us that it has either received or completed (i.e. actually sent) some frames that were previously submitted using Fill() or Transmit() methods. The numReceived return value can be used as the argument for subsequent Receive() method call.

func (*Socket) Receive

func (xsk *Socket) Receive(num int) []unix.XDPDesc

Receive returns the descriptors which were filled, i.e. into which frames were received into.

func (*Socket) Stats

func (xsk *Socket) Stats() (Stats, error)

Stats returns various statistics for this XDP socket.

func (*Socket) Transmit

func (xsk *Socket) Transmit(descs []unix.XDPDesc) (numSubmitted int)

Transmit submits the given descriptors to be sent out, it returns how many descriptors were actually pushed onto the Tx ring queue. The descriptors can be acquired either by calling the GetDescs() method or by calling Receive() method.

type Stats

type Stats struct {
	// Filled is the number of items consumed thus far by the Linux kernel
	// from the Fill ring queue.
	Filled uint64

	// Received is the number of items consumed thus far by the user of
	// this package from the Rx ring queue.
	Received uint64

	// Transmitted is the number of items consumed thus far by the Linux
	// kernel from the Tx ring queue.
	Transmitted uint64

	// Completed is the number of items consumed thus far by the user of
	// this package from the Completion ring queue.
	Completed uint64

	// KernelStats contains the in-kernel statistics of the corresponding
	// XDP socket, such as the number of invalid descriptors that were
	// submitted into Fill or Tx ring queues.
	KernelStats unix.XDPStatistics
}

Stats contains various counters of the XDP socket, such as numbers of sent/received frames.

type Tap

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

Tap provides communication between taps in bridges and other endpoints, via XDP/eBFP redirects implements io.ReadWriteCloser

func NewTap

func NewTap(iface netlink.Link, queue int, prog ProgRef) (*Tap, error)

NewTap creates a new XDP attachment to the given iface queue

func (*Tap) BatchRead

func (xdpt *Tap) BatchRead(ps []BatchDesc) (bytesRead int, nRead int, err error)

BatchRead batch reads from the tap

func (*Tap) BatchWrite

func (xdpt *Tap) BatchWrite(ps []BatchDesc) (int, error)

BatchWrite takes an array of byte arrays to be transmitted

func (*Tap) Close

func (xdpt *Tap) Close() error

Close the underlying xdp socket

func (*Tap) Read

func (xdpt *Tap) Read(p []byte) (int, error)

func (*Tap) Stats

func (xdpt *Tap) Stats() (Stats, error)

Stats provides statistics for the interface (XDP)

func (*Tap) Write

func (xdpt *Tap) Write(p []byte) (int, error)

Jump to

Keyboard shortcuts

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