libmemif

package
v0.0.0-...-278cd54 Latest Latest
Warning

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

Go to latest
Published: Aug 2, 2022 License: Apache-2.0 Imports: 10 Imported by: 0

README

Go-libmemif

Package libmemif is a Golang adapter for the libmemif library (extras/libmemif in the VPP repository). To differentiate between the adapter and the underlying C-written library, labels Go-libmemif and C-libmemif are used in the documentation.

Requirements

libmemif for Golang is build on the top of the original, C-written libmemif library using cgo. It is therefore necessary to have C-libmemif header files, and the library itself installed in locations known to the compiler.

For example, to install C-libmemif system-wide into the standard locations, execute:

$ git clone https://gerrit.fd.io/r/vpp
$ cd vpp/extras/libmemif
$ mkdir build
$ cd build
$ cmake ..
$ sudo make install

Build

Package libmemif is not part of the GoVPP core and as such it is not included in the make build target. Instead, it has its own target in the top-level Makefile used to build the attached examples with the adapter:

$ make extras

APIs

All Go-libmemif public APIs can be found in adapter.go. Please see the comments for a more detailed description. Additionally, a list of all errors thrown by libmemif can be found in error.go.

Usage

libmemif needs to be first initialized with Init(appName). This has to be done only once in the context of the entire process. Make sure to call Cleanup() to release all the resources allocated by libmemif before exiting your application. Consider calling Init() followed by Cleanup() scheduled with defer in the main() function.

Log messages are by default printed to stdout. Use SetLogger() to use your own customized logger (can be changed before Init()).

Once libmemif is initialized, new memif interfaces can be created with CreateInterface(config, callbacks). See MemifConfig structure definition to learn about possible memif configuration options. If successful, CreateInterface() returns an instance of Memif structure representing the underlying memif interface.

Callbacks are optional and can be shared across multiple memif instances. Available callbacks are:

  1. OnConnect: called when the connection is established. By the time the callback is called, the Rx/Tx queues are initialized and ready for data transmission. Interrupt channels are also created and ready to be read from. The user is expected to start polling for input packets via repeated calls to Memif.RxBurst(queueID, count) or to initiate select on the interrupt channels obtained with Get*InterruptChan(), depending on the Rx mode. By default, all memif Rx queues are created in the interrupt mode, but this can be changed per-queue with Memif.SetRxMode(queueID, mode).
  2. OnDisconnect: called after the connection was closed. Immediately after the user callback returns, Rx/Tx queues and interrupt channels are also deallocated. The user defined callback should therefore ensure that all the Rx/Tx operations are stopped before it returns.

libmemif was designed for a maximum possible performance. Packets are sent and received in bulks, rather than one-by-one, using Memif.TxBurst(queueID, packets) and Memif.RxBurst(queueID, count), respectively. Memif connection can consist of multiple queues in both directions. A queue is one-directional wait-free ring buffer. It is the unit of parallelism for data transmission. The maximum possible lock-free granularity is therefore one go routine for one queue.

Interrupt channel for one specific Rx queue can be obtained with GetQueueInterruptChan(queueID) as opposed to GetInterruptChan() for all the Rx queues. There is only one interrupt signal sent for an entire burst of packets, therefore an interrupt handling routine should repeatedly call RxBurst() until an empty slice of packets is returned. This way it is ensured that there are no packets left on the queue unread when the interrupt signal is cleared. Study the ReadAndPrintPackets() function in raw-data example.

For libmemif the packet is just an array of bytes. It does not care what the actual content is. It is not required for a packet to follow any network protocol in order to get transported from one end to another. See the type declaration for RawPacketData and its use in Memif.TxBurst() and Memif.RxBurst().

In order to remove a memif interface, call Memif.Close(). If the memif is in the connected state, the connection is first properly closed. Do not touch memif after it was closed, let garbage collector to remove the Memif instance. In the end, Cleanup() will also ensure that all active memif interfaces are closed before the cleanup finalizes.

To use libmemif with google/gopacket, simply call Memif.NewPacketHandle() to create google/gopacket/PacketDataSource from memif queue. After this you can use gopacket API to read from MemifPacketHandle as normal. You can pass optional rxCount when creating the packet handle and then when reading data, handle will try to read more packets at once and cache them for next iteration. Handle also includes convenience method MemifPacketHandle.WritePacketData() that is simply calling 1 Memif.TxBurst() for provided data.

Examples

Go-libmemif ships with two simple examples demonstrating the usage of the package with a detailed commentary. The examples can be found in the subdirectory examples.

Raw data (libmemif <-> libmemif)

raw-data is a basic example showing how to create a memif interface, handle events through callbacks and perform Rx/Tx of raw data. Before handling an actual packet it is important to understand the skeleton of libmemif-based applications.

Since VPP expects proper packet data, it is not very useful to connect raw-data example with VPP, even though it will work, since all the received data will get dropped on the VPP side.

To create a connection of two raw-data instances, start two processes concurrently in an arbitrary order:

  • master memif:
    $ cd extras/libmemif/examples/raw-data
    $ ./raw-data
    
  • slave memif:
    $ cd extras/libmemif/examples/raw-data
    $ ./raw-data --slave
    

Every 3 seconds both sides send 3 raw-data packets to the opposite end through each of the 3 queues. The received packets are printed to stdout.

Stop an instance of raw-data with an interrupt signal (^C).

Jumbo Frames Raw data (libmemif <-> libmemif)

jumbo-frames is simple example how to send larger and larger jumbo packets with libmemif adapter. This is simple copy of raw-data but with sending larger packets, so for more information read its code and documentation.

ICMP Responder

icmp-responder is a simple example showing how to answer APR and ICMP echo requests through a memif interface. Package google/gopacket is used to decode and construct packets.

The appropriate VPP configuration for the opposite memif is:

vpp$ create memif socket id 1 filename /tmp/icmp-responder-example
vpp$ create interface memif id 1 socket-id 1 slave secret secret no-zero-copy
vpp$ set int state memif1/1 up
vpp$ set int ip address memif1/1 192.168.1.2/24

To start the example, simply type:

root$ ./icmp-responder

icmp-responder needs to be run as root so that it can access the socket created by VPP.

Normally, the memif interface is in the master mode. Pass CLI flag --slave to create memif in the slave mode:

root$ ./icmp-responder --slave

Don't forget to put the opposite memif into the master mode in that case.

To verify the connection, run:

vpp$ ping 192.168.1.1
64 bytes from 192.168.1.1: icmp_seq=2 ttl=255 time=.6974 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=255 time=.6310 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=255 time=1.0350 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=255 time=.5359 ms

Statistics: 5 sent, 4 received, 20% packet loss
vpp$ sh ip arp
    Time           IP4       Flags      Ethernet              Interface
    68.5648   192.168.1.1     D    aa:aa:aa:aa:aa:aa memif0/1

Note: it is expected that the first ping is shown as lost. It was actually converted to an ARP request. This is a VPP specific feature common to all interface types.

Stop the example with an interrupt signal (^C).

GoPacket ICMP Responder

gopacket is a simple example showing how to answer APR and ICMP echo requests through a memif interface. This example is mostly identical to icmp-responder example, but it is using MemifPacketHandle API to read and write packets using gopacket API.

The appropriate VPP configuration for the opposite memif is:

vpp$ create memif socket id 1 filename /tmp/gopacket-example
vpp$ create interface memif id 1 socket-id 1 slave secret secret no-zero-copy
vpp$ set int state memif1/1 up
vpp$ set int ip address memif1/1 192.168.1.2/24

To start the example, simply type:

root$ ./gopacket

gopacket needs to be run as root so that it can access the socket created by VPP.

Normally, the memif interface is in the master mode. Pass CLI flag "--slave" to create memif in the slave mode:

root$ ./gopacket --slave

Don't forget to put the opposite memif into the master mode in that case.

To verify the connection, run:

vpp$ ping 192.168.1.1
64 bytes from 192.168.1.1: icmp_seq=2 ttl=255 time=.6974 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=255 time=.6310 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=255 time=1.0350 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=255 time=.5359 ms

Statistics: 5 sent, 4 received, 20% packet loss
vpp$ sh ip arp
Time           IP4       Flags      Ethernet              Interface
68.5648   192.168.1.1     D    aa:aa:aa:aa:aa:aa memif0/1

Note: it is expected that the first ping is shown as lost. It was actually converted to an ARP request. This is a VPP specific feature common to all interface types.

Stop the example with an interrupt signal (^C).

Documentation

Overview

Package libmemif is a Golang adapter for the libmemif library (extras/libmemif in the VPP repository). To differentiate between the adapter and the underlying C-written library, labels "Go-libmemif" and "C-libmemif" are used in the documentation.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrSyscall       = newMemifError(1)
	ErrConnRefused   = newMemifError(2)
	ErrAccess        = newMemifError(3)
	ErrNoFile        = newMemifError(4)
	ErrFileLimit     = newMemifError(5)
	ErrProcFileLimit = newMemifError(6)
	ErrAlready       = newMemifError(7)
	ErrAgain         = newMemifError(8)
	ErrBadFd         = newMemifError(9)
	ErrNoMem         = newMemifError(10)
	ErrInvalArgs     = newMemifError(11)
	ErrNoConn        = newMemifError(12)
	ErrConn          = newMemifError(13)
	ErrClbFDUpdate   = newMemifError(14)
	ErrFileNotSock   = newMemifError(15)
	ErrNoShmFD       = newMemifError(16)
	ErrCookie        = newMemifError(17)

	// Not thrown, instead properly handled inside the golang adapter:
	ErrNoBufRing    = newMemifError(18)
	ErrNoBuf        = newMemifError(19)
	ErrNoBufDetails = newMemifError(20)

	ErrIntWrite     = newMemifError(21)
	ErrMalformedMsg = newMemifError(22)
	ErrQueueID      = newMemifError(23)
	ErrProto        = newMemifError(24)
	ErrIfID         = newMemifError(25)
	ErrAcceptSlave  = newMemifError(26)
	ErrAlreadyConn  = newMemifError(27)
	ErrMode         = newMemifError(28)
	ErrSecret       = newMemifError(29)
	ErrNoSecret     = newMemifError(30)
	ErrMaxRegion    = newMemifError(31)
	ErrMaxRing      = newMemifError(32)
	ErrNotIntFD     = newMemifError(33)
	ErrDisconnect   = newMemifError(34)
	ErrDisconnected = newMemifError(35)
	ErrUnknownMsg   = newMemifError(36)
	ErrPollCanceled = newMemifError(37)

	// Errors added by the adapter:
	ErrNotInit     = newMemifError(100, "libmemif is not initialized")
	ErrAlreadyInit = newMemifError(101, "libmemif is already initialized")
	ErrUnsupported = newMemifError(102, "the feature is not supported by C-libmemif")

	// Received unrecognized error code from C-libmemif.
	ErrUnknown = newMemifError(-1, "unknown error")
)

List of errors thrown by go-libmemif. Error handling code should compare returned error by value against these variables.

Functions

func Cleanup

func Cleanup() error

Cleanup cleans up all the resources allocated by libmemif.

func Init

func Init(appName string) error

Init initializes the libmemif library. Must by called exactly once and before any libmemif functions. Do not forget to call Cleanup() before exiting your application. <appName> should be a human-readable string identifying your application. For example, VPP returns the version information ("show version" from VPP CLI).

func SetLogger

func SetLogger(l *logger.Logger)

SetLogger changes the logger for Go-libmemif to the provided one. The logger is not used for logging of C-libmemif.

Types

type CPacketBuffers

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

CPacketBuffers stores an array of memif buffers for use with TxBurst or RxBurst.

type ConnUpdateCallback

type ConnUpdateCallback func(memif *Memif) (err error)

ConnUpdateCallback is a callback type declaration used with callbacks related to connection status changes.

type Context

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

Context is a global Go-libmemif runtime context.

type IfMode

type IfMode int

IfMode represents the mode (layer/behaviour) in which the interface operates.

const (
	// IfModeEthernet tells memif to operate on the L2 layer.
	IfModeEthernet IfMode = iota

	// IfModeIP tells memif to operate on the L3 layer.
	IfModeIP

	// IfModePuntInject tells memif to behave as Inject/Punt interface.
	IfModePuntInject
)

type Memif

type Memif struct {
	MemifMeta
	// contains filtered or unexported fields
}

Memif represents a single memif interface. It provides methods to send/receive packets in bursts in either the polling mode or in the interrupt mode with the help of golang channels.

func CreateInterface

func CreateInterface(config *MemifConfig, callbacks *MemifCallbacks) (memif *Memif, err error)

CreateInterface creates a new memif interface with the given configuration. The same callbacks can be used with multiple memifs. The first callback input argument (*Memif) can be used to tell which memif the callback was triggered for. The method is thread-safe.

func (*Memif) Close

func (memif *Memif) Close() error

Close removes the memif interface. If the memif is in the connected state, the connection is first properly closed. Do not access memif after it is closed, let garbage collector to remove it.

func (*Memif) GetDetails

func (memif *Memif) GetDetails() (details *MemifDetails, err error)

GetDetails returns a detailed runtime information about this memif. The method is thread-safe.

func (*Memif) GetInterruptChan

func (memif *Memif) GetInterruptChan() (ch <-chan uint8)

GetInterruptChan returns a channel which is continuously being filled with IDs of queues with data ready to be received. Since there is only one interrupt signal sent for an entire burst of packets, an interrupt handling routine should repeatedly call RxBurst() until the function returns an empty slice of packets. This way it is ensured that there are no packets left on the queue unread when the interrupt signal is cleared. The method is thread-safe.

func (*Memif) GetInterruptErrorChan

func (memif *Memif) GetInterruptErrorChan() (ch <-chan error)

GetInterruptErrorChan returns an Error channel which fires if there are errors occurred while read data.

func (*Memif) GetQueueInterruptChan

func (memif *Memif) GetQueueInterruptChan(queueID uint8) (ch <-chan struct{}, err error)

GetQueueInterruptChan returns an empty-data channel which fires every time there are data to read on a given queue. It is only valid to call this function if memif is in the connected state. Channel is automatically closed when the connection goes down (but after the user provided callback OnDisconnect has executed). Since there is only one interrupt signal sent for an entire burst of packets, an interrupt handling routine should repeatedly call RxBurst() until the function returns an empty slice of packets. This way it is ensured that there are no packets left on the queue unread when the interrupt signal is cleared. The method is thread-safe.

func (*Memif) NewPacketHandle

func (memif *Memif) NewPacketHandle(queueId uint8, rxCount uint16) *MemifPacketHandle

Create new GoPacket packet handle from libmemif queue. rxCount determines how many packets will be read at once, minimum value is 1

func (*Memif) RxBurst

func (memif *Memif) RxBurst(queueID uint8, count uint16) (packets []RawPacketData, err error)

RxBurst is used to receive multiple packets in one call from a selected queue. <count> is the number of packets to receive. The actual number of packets received may be smaller. <count> effectively limits the maximum number of packets to receive in one burst (for a flat, predictable memory usage). The method is non-blocking even if there are no packets to receive. It is only valid to call this function if memif is in the connected state. Multiple RxBurst-s can run concurrently provided that each targets a different Rx queue.

func (*Memif) SetRxMode

func (memif *Memif) SetRxMode(queueID uint8, rxMode RxMode) (err error)

SetRxMode allows to switch between the interrupt and the polling mode for Rx. The method is thread-safe.

func (*Memif) TxBurst

func (memif *Memif) TxBurst(queueID uint8, packets []RawPacketData) (count uint16, err error)

TxBurst is used to send multiple packets in one call into a selected queue. The actual number of packets sent may be smaller and is returned as <count>. The method is non-blocking even if the ring is full and no packet can be sent. It is only valid to call this function if memif is in the connected state. Multiple TxBurst-s can run concurrently provided that each targets a different TX queue.

type MemifCallbacks

type MemifCallbacks struct {
	// OnConnect is triggered when a connection for a given memif was established.
	OnConnect ConnUpdateCallback

	// OnDisconnect is triggered when a connection for a given memif was lost.
	OnDisconnect ConnUpdateCallback
}

MemifCallbacks is a container for all callbacks provided by memif. Any callback can be nil, in which case it will be simply skipped. Important: Do not call CreateInterface() or Memif.Close() from within a callback or a deadlock will occur. Instead send signal through a channel to another go routine which will be able to create/remove memif interface(s).

type MemifConfig

type MemifConfig struct {
	MemifMeta
	MemifShmSpecs
}

MemifConfig is the memif configuration. Used as the input argument to CreateInterface(). It is the slave's config that mostly decides the parameters of the connection, but master may limit some of the quantities if needed (based on the memif protocol or master's configuration)

type MemifConnDetails

type MemifConnDetails struct {
	// RemoteIfName is the name of the memif on the opposite side.
	RemoteIfName string
	// RemoteInstanceName is the name of the endpoint on the opposite side.
	RemoteInstanceName string
	// HasLink is true if the connection has link (= is established and functional).
	HasLink bool
	// RxQueues contains details for each Rx queue.
	RxQueues []MemifQueueDetails
	// TxQueues contains details for each Tx queue.
	TxQueues []MemifQueueDetails
}

MemifConnDetails provides a detailed runtime information about a memif connection.

type MemifDetails

type MemifDetails struct {
	MemifMeta
	MemifConnDetails
}

MemifDetails provides a detailed runtime information about a memif interface.

type MemifError

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

MemifError implements and extends the error interface with the method Code(), which returns the integer error code as returned by C-libmemif.

func (*MemifError) Code

func (e *MemifError) Code() int

Code returns the integer error code as returned by C-libmemif.

func (*MemifError) Error

func (e *MemifError) Error() string

Error prints error description.

type MemifMeta

type MemifMeta struct {
	// IfName is the interface name. Has to be unique across all created memifs.
	// Interface name is truncated if needed to have no more than 32 characters.
	IfName string

	// InstanceName identifies the endpoint. If omitted, the application
	// name passed to Init() will be used instead.
	// Instance name is truncated if needed to have no more than 32 characters.
	InstanceName string

	// ConnID is a connection ID used to match opposite sides of the memif
	// connection.
	ConnID uint32

	// SocketFilename is the filename of the AF_UNIX socket through which
	// the connection is established.
	// The string is truncated if neede to fit into sockaddr_un.sun_path
	// (108 characters on Linux).
	SocketFilename string

	// Secret must be the same on both sides for the authentication to succeed.
	// Empty string is allowed.
	// The secret is truncated if needed to have no more than 24 characters.
	Secret string

	// IsMaster is set to true if memif operates in the Master mode.
	IsMaster bool

	// Mode is the mode (layer/behaviour) in which the memif operates.
	Mode IfMode
}

MemifMeta is used to store a basic memif metadata needed for identification and connection establishment.

type MemifPacketHandle

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

func (*MemifPacketHandle) Close

func (handle *MemifPacketHandle) Close()

Waits for all read and write operations to finish and then prevents more from occurring. Handle can be closed only once and then can never be opened again.

func (*MemifPacketHandle) ReadPacketData

func (handle *MemifPacketHandle) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error)

Reads packet data from memif in bursts, based on previously configured rxCount parameter. Then caches the resulting packets and returns them 1 by 1 from this method until queue is empty then tries to call new rx burst to read more data. If no data is returned, io.EOF error is thrown and caller should stop reading.

func (*MemifPacketHandle) WritePacketData

func (handle *MemifPacketHandle) WritePacketData(data []byte) (err error)

Writes packet data to memif in burst of 1 packet. In case no packet is sent, this method throws io.EOF error and called should stop trying to write packets.

type MemifQueueDetails

type MemifQueueDetails struct {
	// QueueID is the ID of the queue.
	QueueID uint8
	// RingSize is the number of slots in the ring (not logarithmic).
	RingSize uint32
	// BufferSize is the size of each buffer pointed to from the ring slots.
	BufferSize uint16
}

MemifQueueDetails provides a detailed runtime information about a memif queue. Queue = Ring + the associated buffers (one directional).

type MemifShmSpecs

type MemifShmSpecs struct {
	// NumRxQueues is the number of Rx queues.
	// Default is 1 (used if the value is 0).
	NumRxQueues uint8

	// NumTxQueues is the number of Tx queues.
	// Default is 1 (used if the value is 0).
	NumTxQueues uint8

	// BufferSize is the size of the buffer to hold one packet, or a single
	// fragment of a jumbo frame. Default is 2048 (used if the value is 0).
	BufferSize uint16

	// Log2RingSize is the number of items in the ring represented through
	// the logarithm base 2.
	// Default is 10 (used if the value is 0).
	Log2RingSize uint8
}

MemifShmSpecs is used to store the specification of the shared memory segment used by memif to send/receive packets.

type RawPacketData

type RawPacketData []byte

RawPacketData represents raw packet data. libmemif doesn't care what the actual content is, it only manipulates with raw bytes.

type RxMode

type RxMode int

RxMode is used to switch between polling and interrupt for RX.

const (
	// RxModeInterrupt tells libmemif to send interrupt signal when data are available.
	RxModeInterrupt RxMode = iota

	// RxModePolling means that the user needs to explicitly poll for data on RX
	// queues.
	RxModePolling
)

Directories

Path Synopsis
examples
gopacket
gopacket is a simple example showing how to answer APR and ICMP echo requests through a memif interface.
gopacket is a simple example showing how to answer APR and ICMP echo requests through a memif interface.
icmp-responder
icmp-responder is a simple example showing how to answer APR and ICMP echo requests through a memif interface.
icmp-responder is a simple example showing how to answer APR and ICMP echo requests through a memif interface.
jumbo-frames
jumbo-frames is simple example how to send larger and larger jumbo packets with libmemif adapter.
jumbo-frames is simple example how to send larger and larger jumbo packets with libmemif adapter.
raw-data
raw-data is a basic example showing how to create a memif interface, handle events through callbacks and perform Rx/Tx of raw data.
raw-data is a basic example showing how to create a memif interface, handle events through callbacks and perform Rx/Tx of raw data.

Jump to

Keyboard shortcuts

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