snf

package
v0.2.6 Latest Latest
Warning

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

Go to latest
Published: Dec 15, 2022 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package snf is a wrapper for SNF library to support direct interaction with Myricom/CSPI boards.

The purpose of the package is to avoid using libpcap-wrapped SNF functionality in favor of more flexible and full-featured SNF C binding.

In order to be able to use google/gopacket (layers etc.) functionality, some interfaces in those packages are satisfied. Any feature requests regarding extension of such integration are welcomed.

Some examples are provided to show various use cases, features, limitations and so on.

Index

Examples

Constants

View Source
const (
	// Link is down.
	LinkDown int = C.SNF_LINK_DOWN
	// Link is up.
	LinkUp = C.SNF_LINK_UP
)

Underlying port's state (DOWN or UP)

View Source
const (
	// Local timesource (no external).
	// Returned if there is no available external
	// timesource or if its use was explicitly disabled.
	TimeSourceLocal int = C.SNF_TIMESOURCE_LOCAL

	// External Timesource: not synchronized (yet)
	TimeSourceExtUnsynced = C.SNF_TIMESOURCE_EXT_UNSYNCED

	// External Timesource: synchronized
	TimeSourceExtSynced = C.SNF_TIMESOURCE_EXT_SYNCED

	// External Timesource: NIC failure to connect to source
	TimeSourceExtFailed = C.SNF_TIMESOURCE_EXT_FAILED

	// Arista switch is sending ptp timestamps
	TimeSourceAristaActive = C.SNF_TIMESOURCE_ARISTA_ACTIVE

	// PPS is being used for time
	TimeSourcePPS = C.SNF_TIMESOURCE_PPS
)

Timesource state (for SYNC NICs)

View Source
const (
	// Include IP (v4 or v6) SRC/DST addr in hash
	RssIP int = C.SNF_RSS_IP
	// Include TCP/UDP/SCTP SRC port in hash
	RssSrcPort = C.SNF_RSS_SRC_PORT
	// Include TCP/UDP/SCTP DST port in hash
	RssDstPort = C.SNF_RSS_DST_PORT
	// Include GTP TEID in hash
	RssGtp = C.SNF_RSS_GTP
	// Include GRE contents in hash
	RssGre = C.SNF_RSS_GRE
)

RSS parameters for SNF_RSS_FLAGS, flags that can be specified to let the implementation know which fields are significant when generating the hash. By default, RSS is computed on IPv4/IPv6 addresses and source/destination ports when the protocol is TCP or UDP or SCTP, for example, "RssIP | RssSrcPort | RssDstPort" means IP addresses and TCP/UDP/SCTP ports will be applied in the hash.

View Source
const (
	// PShared flag states that device can be process-sharable. This
	// allows multiple independent processes to share rings on the
	// capturing device. This option can be used to design a custom
	// capture solution but is also used in libpcap when multiple
	// rings are requested. In this scenario, each libpcap device sees
	// a fraction of the traffic if multiple rings are used unless the
	// RxDuplicate option is used, in which case each libpcap device
	// sees the same incoming packets.
	PShared = C.SNF_F_PSHARED
	// AggregatePortMask shows that device can be opened for port
	// aggregation (or merging). When this flag is passed, the portnum
	// parameter in OpenHandleWithOpts() is interpreted as a bitmask
	// where each set bit position represents a port number. The
	// Sniffer library will then attempt to open every portnum with
	// its bit set in order to merge the incoming data to the user
	// from multiple ports. Subsequent calls to OpenRing() return a
	// ring handle that internally opens a ring on all underlying
	// ports.
	AggregatePortMask = C.SNF_F_AGGREGATE_PORTMASK
	// RxDuplicate shows that device can duplicate packets to multiple
	// rings as opposed to applying RSS in order to split incoming
	// packets across rings. Users should be aware that with N rings
	// opened, N times the link bandwidth is necessary to process
	// incoming packets without drops. The duplication happens in the
	// host rather than the NIC, so while only up to 10Gbits of
	// traffic crosses the PCIe, N times that bandwidth is necessary
	// on the host.
	//
	// When duplication is enabled, RSS options are ignored since
	// every packet is delivered to every ring.
	RxDuplicate = C.SNF_F_RX_DUPLICATE
)

Open flags for process-sharing, port aggregation and packet duplication. Used when opening a Handle with HandlerOptFlags option.

View Source
const (
	Version uint16 = C.SNF_VERSION_API
)

SNF API version number (16 bits).

Least significant byte increases for minor backwards compatible changes in the API. Most significant byte increases for incompatible changes in the API.

Variables

This section is empty.

Functions

func Init

func Init() error

Init initializes the sniffer library.

func PortMask

func PortMask() (linkup, valid uint32, err error)

PortMask returns a mask of all Sniffer-capable ports that have their link state set to UP and a mask of all Sniffer-capable ports. The least significant bit represents port 0.

ENODEV is returned in case of an error obtaining port information.

func SetAppID

func SetAppID(id int32) error

SetAppID sets the application ID.

The user may set the application ID after the call to Init(), but before opening handle. When the application ID is set, Sniffer duplicates receive packets to multiple applications. Each application must have a unique ID. Then, each application may utilize a different number of rings. The application can be a process with multiple rings and threads. In this case all rings have the same ID. Or, multiple processes may share the same application ID.

The user may store the application ID in the environment variable SNF_APP_ID, instead of calling this function. Both actions have the same effect. SNF_APP_ID overrides the ID set via SetAppID().

The user may not run a mix of processes with valid application IDs (not -1) and processes with no IDs (-1). Either all processes have valid IDs or none of them do.

id is a 32-bit signed integer representing the application ID. A valid ID is any value except -1 which is reserved and represents "no ID".

EINVAL is returned if Init() has not been called or id is -1.

Types

type CHashFunc added in v0.2.0

type CHashFunc C.rss_hash_fn

CHashFunc is the C implementation of hash function.

type ErrSignal added in v0.2.0

type ErrSignal struct{ os.Signal }

ErrSignal wraps os.Signal as an error.

func (*ErrSignal) Error added in v0.2.0

func (e *ErrSignal) Error() string

Error implements error interface.

type Handle

type Handle C.struct_snf_handle

Handle encapsulates a device handle.

func OpenHandle

func OpenHandle(portnum uint32, options ...HandlerOption) (*Handle, error)

OpenHandle opens a port for sniffing and allocates a device handle with specified options. See documentation for various options to see their applicability. None of the options is mandatory; if omitted default settings will be applied.

portnum can be interpreted as an integer for a specific port number or as a mask when AggregatePortMask is specified in flags option. Port information can be obtained through GetIfAddrs() and active/valid masks are available with PortMask() method. As a special case, if portnum -1 is passed, the library will internally open a portmask as in valid mask returned in PortMask().

The function returns a Handle object and a possible error. If error is nil the port is opened and a device handle is allocated (see remarks). In this case, the NIC switches from Ethernet mode to Capture mode and the Ethernet driver stops receiving packets. If successful, a call to Start() is required to the Sniffer-mode NIC to deliver packets to the host, and this call must occur after at least one ring is opened (OpenRing() method).

Possible errors include:

EBUSY: Device is already opened.

EINVAL: Invalid argument passed, most probably num_rings (if not, check syslog).

E2BIG: Driver could not allocate requested dataring_sz (check syslog).

ENOMEM: Either library or driver did not have enough memory to allocate handle descriptors (but not data ring).

ENODEV: Device portnum can't be opened.

Example (One)
package main

import (
	"github.com/yerden/go-snf/snf"
)

func main() {
	// This shows how to initialize a handle

	// First, initialize SNF library.
	if err := snf.Init(); err != nil {
		return
	}

	flags := snf.RssIP | snf.RssSrcPort | snf.RssDstPort
	// Initialize handle for port 0
	h, err := snf.OpenHandle(
		0,                             // number of port
		snf.HandlerOptNumRings(3),     // number of rings
		snf.HandlerOptRssFlags(flags), // rss flags
		snf.HandlerOptFlags(snf.PShared),
		snf.HandlerOptFlags(snf.RxDuplicate), // flags
		snf.HandlerOptDataRingSize(256),      // Megabytes for dataring size
	)

	if err != nil {
		return
	}

	defer h.Close()

	// initialize handle for port 1,
	// with default arguments which mostly imply
	// that we use environment variables to
	// alter the handle behaviour
	h, err = snf.OpenHandle(1)
	if err != nil {
		return
	}
	defer h.Close()
}
Output:

Example (Two)
package main

import (
	"sync"

	"github.com/yerden/go-snf/snf"
)

func main() {
	if err := snf.Init(); err != nil {
		return
	}

	// sample default handler
	h, err := snf.OpenHandle(0)
	if err != nil {
		return
	}
	defer h.Close()

	// start capturing traffic
	if err := h.Start(); err != nil {
		return
	}
	defer h.Stop()

	var wg sync.WaitGroup
	// wait for goroutines to exit
	defer wg.Wait()

	// open 2 rings and work on them; you should ensure there's at
	// least 2 rings through environment variables
	for i := 0; i < 2; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			r, err := h.OpenRing()
			if err != nil {
				return
			}
			defer r.Close()
			// handle this ring and read packets
			// ...
		}()
	}
}
Output:

func (*Handle) Close

func (h *Handle) Close() (err error)

Close port.

This function can be closed once all opened rings (if any) are closed through ring's Close() method. Once a port is determined to be closable, it is implicitly called as if a call had been previously made to Stop() method.

EBUSY is returned if some rings are still opened and the port cannot be closed (yet).

If successful, all resources allocated at open time are unallocated and the device switches from Sniffer mode to Ethernet mode such that the Ethernet driver resumes receiving packets.

func (*Handle) LinkSpeed

func (h *Handle) LinkSpeed() (uint64, error)

LinkSpeed gets link speed on opened handle.

Returns speed in bits-per-second for the link.

The cost of retrieving the link state requires a function call that reads state kept in kernel host memory (i.e. no PCI bus reads).

func (*Handle) LinkState

func (h *Handle) LinkState() (int, error)

LinkState gets link status on opened handle.

Returns one of LinkDown or LinkUp.

The cost of retrieving the link state requires a function call that reads state kept in kernel host memory (i.e. no PCI bus reads).

func (*Handle) OpenRing

func (h *Handle) OpenRing() (ring *Ring, err error)

OpenRing opens the next available ring.

Ring handle allocated if the call is successful.

EBUSY is returned if too many rings already opened.

This function will consider the value of the SNF_RING_ID environment variable. For more control over ring allocation, consider using OpenRingID() method instead.

Please be sure that you close the ring once you've done working on it. Leaking rings may lead to packet drops in neighbour applications working on the same NIC.

If successful, a call to Start() method is required to the Sniffer-mode NIC to deliver packets to the host.

func (*Handle) OpenRingID

func (h *Handle) OpenRingID(id int) (ring *Ring, err error)

OpenRingID opens a ring from an opened port.

ring_id Ring number to open, from 0 to num_rings - 1. If the value is -1, this function behaves as if OpenRing() was called.

Ring handle allocated if the call is successful.

EBUSY is returned if id == -1, Too many rings already opened. if id >= 0, that ring is already opened.

Unlike OpenRing(), this function ignores the environment variable SNF_RING_ID since the expectation is that users want to directly control ring allocation (unlike through libpcap).

Please be sure that you close the ring once you've done working on it. Leaking rings may lead to packet drops in neighbour applications working on the same NIC.

If successful, a call to Handle's Start() is required to the Sniffer-mode NIC to deliver packets to the host.

func (*Handle) ReflectEnable added in v0.0.9

func (h *Handle) ReflectEnable() (*ReflectHandle, error)

ReflectEnable enables a network device for packet reflection and returns ReflectHandle.

As stated in SNF documentation, this call is always a success.

func (*Handle) Start

func (h *Handle) Start() error

Start packet capture on a port. Packet capture is only started if it is currently stopped or has not yet started for the first time.

It is safe to restart packet capture via Start() and Stop() methods. This call must be called before any packet can be received.

func (*Handle) Stop

func (h *Handle) Stop() error

Stop packet capture on a port. This function should be used carefully in multi-process mode as a single stop command stops packet capture on all rings. It is usually best to simply Close() a ring to stop capture on a ring.

Stop instructs the NIC to drop all packets until the next Start() or until the port is closed. The NIC only resumes delivering packets when the port is closed, not when traffic is stopped.

func (*Handle) TimeSourceState

func (h *Handle) TimeSourceState() (int, error)

TimeSourceState returns timesource information from opened handle

Returns one of Timesource state constants.

The cost of retrieving the timesource state requires a function call that reads state kept in kernel host memory (i.e. no PCI bus reads).

type HandlerOption added in v0.0.10

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

HandlerOption specifies an option for opening a Handle.

func HandlerOptDataRingSize added in v0.0.10

func HandlerOptDataRingSize(n int64) HandlerOption

HandlerOptDataRingSize specifies the total amount of memory to be used to store incoming packet data for *all* rings to be opened. If the value is set to 0 or less than 0, the library tries to choose a sensible default unless SNF_DATARING_SIZE is set in the environment. The value can be specified in megabytes (if it is less than 1048576) or is otherwise considered to be in bytes. In either case, the library may slightly adjust the user's request to satisfy alignment requirements (typically 2MB boundaries).

func HandlerOptFlags added in v0.0.10

func HandlerOptFlags(flags int) HandlerOption

HandlerOptFlags specifies a mask of flags documented in SNF API Reference. You may specify a number of flags. They will be OR'ed before applying to the Handle.

func HandlerOptNumRings added in v0.0.10

func HandlerOptNumRings(n int) HandlerOption

HandlerOptNumRings specifies number of rings to allocate for receive-side scaling feature, which determines how many different threads can open their own ring via OpenRing(). If not specified or set to 0 or less than zero, default value is used unless SNF_NUM_RINGS is set in the environment.

func HandlerOptRssFlags added in v0.0.10

func HandlerOptRssFlags(flags int) HandlerOption

HandlerOptRssFlags specifies RSS flags to use by RSS mechanism. By default, the implementation will select its own mechanism to divide incoming packets across rings. This parameter is only meaningful if there are more than 1 rings to be opened.

Note that this option unsets HandlerOptRssFunc option.

func HandlerOptRssFunc added in v0.0.10

func HandlerOptRssFunc(fn *CHashFunc, ctx unsafe.Pointer) HandlerOption

HandlerOptRssFunc specifies custom hash function to use by RSS mechanism. By default, the implementation will select its own mechanism to divide incoming packets across rings. This parameter is only meaningful if there are more than 1 rings to be opened.

fn should comply with the following C function prototype:

int (*rss_hash_fn)(struct snf_recv_req *r, void *context, uint32_t *hashval);

ctx is an opaque context.

fn is a hash function provided by user as a pointer to C function. The callback is provided with a valid snf_recv_req structure which contains a packet as received by Sniffer. It is up to the user to inspect and parse the packet to produce a unique 32-bit hash. The implementation will map the 32-bit into one of the rings allocated in snf_open. The function must return one of three values:

0: The packet is queued in the ring based on the 32-bit hash value that is provided, which is hashval%num_rings.

<0: The packet is dropped and accounted as a drop in the ring corresponding to the 32-bit hash value provided by the user. fn is the pointer to Cgo function and ctx is the pointer to that function context.

Please be aware that applying custom hash function may impose some overhead on the hot path.

Note that this option unsets HandlerOptRssFlags option.

type IfAddrs

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

IfAddrs is a structure to map Interfaces to Sniffer port numbers. It can be copied by value.

func GetIfAddrByHW

func GetIfAddrByHW(addr net.HardwareAddr) (*IfAddrs, error)

GetIfAddrByHW gets a Sniffer-capable ethernet devices with matching MAC address.

Found IfAddr struct is returned. If not found, (nil, ENODEV) will be returned. If unable to retrieve interfaces from SNF, (nil, err) where err is correspoding error will be returned.

func GetIfAddrByName

func GetIfAddrByName(name string) (*IfAddrs, error)

GetIfAddrByName returns a Sniffer-capable ethernet devices with matching name.

Found IfAddr struct is returned. If not found, (nil, ENODEV) will be returned. If unable to retrieve interfaces from SNF, (nil, err) where err is correspoding error will be returned.

func GetIfAddrs

func GetIfAddrs() ([]IfAddrs, error)

GetIfAddrs gets a list of Sniffer-capable ethernet devices.

func (*IfAddrs) LinkSpeed

func (p *IfAddrs) LinkSpeed() uint64

LinkSpeed returns Link Speed in bps.

func (*IfAddrs) LinkState

func (p *IfAddrs) LinkState() int

LinkState returns underlying port's state (DOWN or UP).

func (*IfAddrs) MACAddr

func (p *IfAddrs) MACAddr() []byte

MACAddr returns MAC address of the port.

func (*IfAddrs) MaxInject

func (p *IfAddrs) MaxInject() int

MaxInject returns maximum TX injection handles supported by the port.

func (*IfAddrs) MaxRings

func (p *IfAddrs) MaxRings() int

MaxRings returns maximum RX rings supported by the port.

func (*IfAddrs) Name

func (p *IfAddrs) Name() string

Name returns interface name, as in ifconfig.

func (*IfAddrs) PortNum

func (p *IfAddrs) PortNum() uint32

PortNum returns port's index in SNF library.

func (*IfAddrs) String added in v0.2.0

func (p *IfAddrs) String() string

String implements fmt.Stringer interface.

type InjectHandle added in v0.0.9

type InjectHandle C.struct_snf_inject_handle

InjectHandle is an opaque injection handle, allocated by OpenInjectHandle. There are only a limited amount of injection handles per NIC/port.

func OpenInjectHandle added in v0.0.9

func OpenInjectHandle(portnum int, flags ...int) (h *InjectHandle, err error)

OpenInjectHandle opens a port for injection and allocate an injection handle.

portnum is an index of port from 0 to N-1 where ’N’ is the number of Myricom ports available on the system. GetIfAddrs() may be a useful utility to retrieve the port number by interface name or mac address if there are multiple.

flags are the flags for injection handle. None are currently defined.

An injection handle is opened and allocated if the error is nil.

EBUSY error means no more injection handles for this port are available. ENOMEM error means we ran out of memory to allocate new injection handle.

func (*InjectHandle) Close added in v0.0.9

func (h *InjectHandle) Close() error

Close closes injection handle and ensures that all pending sends are sent by the NIC.

Once closed, the injection handle will have ensured that any pending sends have been sent out on the wire. The handle is then made available again for the underlying port’s limited amount of handles.

func (*InjectHandle) GetSpeed added in v0.0.9

func (h *InjectHandle) GetSpeed() (speed uint64, err error)

GetSpeed retrieves link speed on opened injection handle.

The cost of retrieving the link speed requires a function call that reads information kept in kernel host memory (i.e. no PCI bus reads).

func (*InjectHandle) GetStats added in v0.0.9

func (h *InjectHandle) GetStats() (*InjectStats, error)

GetStats gets statistics from an injection handle.

This call is provided as a convenience and should not be relied on for time-critical applications or for high levels of accuracy. Statistics are only updated by the NIC periodically.

type InjectStats

type InjectStats C.struct_snf_inject_stats

InjectStats is a sructure to return statistics from an injection handle. The hardware-specific counters (nic_) apply to all injection handles.

func (*InjectStats) InjPktSend

func (s *InjectStats) InjPktSend() uint64

InjPktSend returns number of packets sent by this injection endpoint.

func (*InjectStats) NicBytesSend

func (s *InjectStats) NicBytesSend() uint64

NicBytesSend Number of raw bytes sent by Hardware Interface (see nic_bytes_recv).

func (*InjectStats) NicPktSend

func (s *InjectStats) NicPktSend() uint64

NicPktSend returns number of total packets sent by Hardware Interface.

type RecvReq

type RecvReq C.struct_snf_recv_req

RecvReq is a descriptor of a packet received on a data ring.

func (*RecvReq) CaptureInfo

func (req *RecvReq) CaptureInfo() (ci gopacket.CaptureInfo)

CaptureInfo returns gopacket.CaptureInfo metadata for retrieved packet.

func (*RecvReq) Data added in v0.1.0

func (req *RecvReq) Data() (data []byte)

Data returns data payload of the packet as a pointer directly in the given data ring.

User may not retain the slice returned by Data since the underlying memory chunk may be reused.

func (*RecvReq) HwHash added in v0.1.0

func (req *RecvReq) HwHash() uint32

HwHash returns hash calculated by the NIC.

func (*RecvReq) PortNum

func (req *RecvReq) PortNum() int

PortNum returns packet's origin port number.

func (*RecvReq) Timestamp

func (req *RecvReq) Timestamp() int64

Timestamp returns 64-bit timestamp in nanoseconds.

type ReflectHandle added in v0.0.9

type ReflectHandle C.char

ReflectHandle wraps SNF reflect capability.

Network packets acquired through Sniffer can be reflected back into the kernel path as if the device had initially sent then through to the regular network stack. While Sniffer users are typically expected to process a significant portion of their packets with less overhead in userspace, this feature is provided as a convenience to allow some packets to be processed back in the kernel. The implementation makes no explicit step to make the kernel-based processing any faster than it is when Sniffer is not being used (in fact, it is probably much slower).

func (*ReflectHandle) Reflect added in v0.0.9

func (ref *ReflectHandle) Reflect(pkt []byte) error

Reflect a packet to the network device.

pkt should hold the packet to be reflected to the network device. It should contain a complete Ethernet frame (without the trailing CRC) and start with a valid Ethernet header.

As stated in SNF documentation, this call is always a success. This package's Reflect will return io.EOF error in case the underlying Handle is about to close due to signal or user Close call.

type Ring

type Ring C.struct_snf_ring

Ring encapsulates a device's ring handle.

func (*Ring) Close

func (r *Ring) Close() error

Close a ring

This function is used to inform the underlying device that no further calls to Recv() will be made. If the device is not subsequently closed (Handle's Close()), all packets that would have been delivered to this ring are dropped. Also, by calling this function, users confirm that all packet processing for packets obtained on this ring via ring's Recv() is complete.

Returns 0 (successful).

The user has processed the last packet obtained with Recv() and such and the device can safely be closed via Handle's Close() if all other rings are also closed. All packet data memory returned by Ring or RingReceiver is reclaimed by SNF API and cannot be dereferenced.

func (*Ring) PortInfo

func (r *Ring) PortInfo() ([]RingPortInfo, error)

PortInfo returns information for the ring. For aggregated rings, returns information for each of the physical rings.

func (*Ring) Recv

func (r *Ring) Recv(timeout time.Duration, req *RecvReq) error

Recv receives next packet from a receive ring.

This function is used to return the next available packet in a receive ring. The function can block indefinitely, for a specific timeout or be used as a non-blocking call with a timeout of 0.

timeout is a receive timeout to control how the function blocks for the next packet. If the value is less than 0, the function can block indefinitely. If the value is 0, the function is guaranteed to never enter a blocking state and returns EAGAIN unless there is a packet waiting. If the value is greater than 0, the caller indicates a desired wait time in milliseconds. With a non-zero wait time, the function only blocks if there are no outstanding packets. If the timeout expires before a packet can be received, the function returns EAGAIN (and not ETIMEDOUT). In all cases, users should expect that the function may return EINTR as the result of signal delivery.

req is a Receive Packet structure, only updated when the function returns 0 for a successful packet receive (RecvReq).

Return values: nil is a successful packet delivery, req is updated with packet information. EINTR means the call was interrupted by a signal handler. EAGAIN means that no packets are available (only when timeout is >= 0).

The returned packet always points directly into the receive ring where the NIC has DMAed the packet (there are no copies). As such, the user obtains a pointer to library/driver allocated memory. Users can modify the contents of the packets but should remain within the slice boundaries.

Upon calling the function, the library assumes that the user is done processing the previous packet. The same assumption is made when the ring is closed (ring's Close() method).

func (*Ring) RecvMany added in v0.1.0

func (r *Ring) RecvMany(timeout time.Duration, reqs []RecvReq, qinfo *RingQInfo) (int, error)

RecvMany receives new packets from the ring following borrow-many-return-many receive model.

timeout semantics is as in Recv() method.

reqs is an array of user-allocated RecvReq structs which will be filled with received packets descriptors.

If qinfo is not nil, the struct will be filled with queue consumption information.

The output of this function is the actual number of descriptors filled in reqs and an error if any.

func (*Ring) ReturnMany added in v0.1.0

func (r *Ring) ReturnMany(reqs []RecvReq, qinfo *RingQInfo) error

ReturnMany returns memory of given packets back to the data ring. Please be aware SNF API returns queued data with no regard to supplied packets, i.e. in FIFO way.

Error is returned in case snf_ring_return_many() was unsuccessful.

func (*Ring) Stats

func (r *Ring) Stats() (*RingStats, error)

Stats returns statistics from a receive ring.

This call is provided as a convenience and should not be relied on for time-critical applications or for high levels of accuracy. Statistics are only updated by the NIC periodically.

Administrative clearing of NIC counters while a Sniffer-based application is running may cause some of the counters to be incorrect.

type RingPortInfo

type RingPortInfo C.struct_snf_ring_portinfo

RingPortInfo is a receive ring information.

func (*RingPortInfo) Data

func (pi *RingPortInfo) Data() []byte

Data returns underlying array of data for receive ring.

func (*RingPortInfo) PortCnt

func (pi *RingPortInfo) PortCnt() uint32

PortCnt returns how many physical ports deliver to this receive ring.

func (*RingPortInfo) PortMask added in v0.2.0

func (pi *RingPortInfo) PortMask() uint32

PortMask returns which ports deliver to this receive ring.

func (*RingPortInfo) QueueSize added in v0.2.0

func (pi *RingPortInfo) QueueSize() uintptr

QueueSize returns size of the data queue.

func (*RingPortInfo) Ring

func (pi *RingPortInfo) Ring() *Ring

Ring returns a physical ring which may be a part of aggregated ring.

type RingQInfo

type RingQInfo C.struct_snf_ring_qinfo

RingQInfo is a queue consumption information.

func (*RingQInfo) Avail

func (qinfo *RingQInfo) Avail() uintptr

Avail returns amount of data available not yet received (approximate).

func (*RingQInfo) Borrowed

func (qinfo *RingQInfo) Borrowed() uintptr

Borrowed returns amount of data currently borrowed (exact).

func (*RingQInfo) Free

func (qinfo *RingQInfo) Free() uintptr

Free returns amount of free space still available (approximate).

type RingReader added in v0.2.0

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

RingReader wraps SNF's borrow-many-return-many model of packets retrieval, along with google's gopacket interface. This allows us to access low-level SNF API but maintain compatibility with gopacket's layers decoding abilities.

Example
package main

import (
	"io"
	"time"

	"github.com/google/gopacket"
	"github.com/yerden/go-snf/snf"
)

// mock handler
func handleReq(*snf.RecvReq) {
}

func handlePacket(ci gopacket.CaptureInfo, data []byte) {
}

func main() {
	var h *snf.Handle
	h, err := snf.OpenHandle(0)
	if err != nil {
		return
	}
	defer h.Close()

	// open ring
	r, err := h.OpenRing()
	if err != nil {
		return
	}
	defer r.Close()

	// abstract ring operations in a RingReader object
	recv := snf.NewReader(
		r,           // Underlying ring
		time.Second, // timeout for receiving new packet
		256,         // how many packets to receive in one call
	)
	// if we exit, return all unclaimed packets to ring.
	defer recv.Free()

	// start capturing traffic
	if err := h.Start(); err != nil {
		return
	}

	// process traffic in bufio.Scanner-like way
	for recv.LoopNext() {
		handleReq(recv.RecvReq())
	}

	// Alternatively, you may utilize gopacket.ZeroCopyPacketDataSource
	// or gopacket.PacketDataSource.
	//
	// if err is io.EOF that means the ring was closed and receiving
	// operations should halt.
	for {
		data, ci, err := recv.ZeroCopyReadPacketData()
		if err == io.EOF {
			return
		} else if err != nil {
			panic(err.Error())
		}
		handlePacket(ci, data)
	}
}
Output:

func NewReader added in v0.2.0

func NewReader(r *Ring, timeout time.Duration, burst int) *RingReader

NewReader creates new RingReader. timeout semantics is the same as addressed in Recv() method. burst is the amount of packets received by underlying SNF's snf_ring_recv_many() function.

Warning: please be aware that snf_ring_recv_many() doesn't work with aggregated rings (flag AggregatePortMask must be off). If you want to use AggregatePortMask feature, please use burst==1. In that case, RingReader will utilize snf_ring_recv() which works in both cases.

func (*RingReader) Data added in v0.2.0

func (rr *RingReader) Data() []byte

Data gets retrieved packet's data. Please note that the underlying array of returned slice is owned by SNF API. Please make a copy if you want to retain it. The consecutive Next() call may erase this slice without prior notice.

func (*RingReader) Err added in v0.2.0

func (rr *RingReader) Err() error

Err returns error which was encountered during the last RingReader operation on a ring. If Next() method returned false, the error may be revised here.

func (*RingReader) Free added in v0.2.0

func (rr *RingReader) Free() error

Free returns all packets that were retrieved but not exposed to the user. Usually you should do this upon and only upon finishing working on the receiver.

Note that now, running this function is redundant if you don't intend to use underlying ring further until it Close()-s. Nevertheless, the use of this function is encouraged anyway as a matter of good code style.

func (*RingReader) LoopNext added in v0.2.0

func (rr *RingReader) LoopNext() bool

LoopNext is similar to Next() method but this one loops if EAGAIN is encountered. It means that timeout hit and the port should be polled again.

func (*RingReader) Next added in v0.2.0

func (rr *RingReader) Next() bool

Next gets next packet out of ring. If true, the operation is a success, otherwise you should halt all actions on the receiver until Err() error is examined and needed actions are performed.

func (*RingReader) NotifyWith added in v0.2.0

func (rr *RingReader) NotifyWith(ch <-chan os.Signal)

NotifyWith installs signal notification channel which is presumably registered via signal.Notify.

Please note that this function expects that specified channel is closed at some point to release acquired resources.

func (*RingReader) ReadPacketData added in v0.2.0

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

ReadPacketData implements gopacket.PacketDataSource.

func (*RingReader) RecvReq added in v0.2.0

func (rr *RingReader) RecvReq() *RecvReq

RecvReq returns current packet descriptor. This descriptor points to privately held instance of RecvReq so make a copy if you want to retain it.

func (*RingReader) Ring added in v0.2.2

func (rr *RingReader) Ring() *Ring

Ring returns underlying receive ring.

func (*RingReader) Stats added in v0.2.2

func (rr *RingReader) Stats() (*RingStats, error)

Stats returns statistics from a receive ring.

func (*RingReader) ZeroCopyReadPacketData added in v0.2.0

func (rr *RingReader) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error)

ZeroCopyReadPacketData implements gopacket.ZeroCopyPacketDataSource.

type RingStats

type RingStats struct {
	// Number of packets received by Hardware Interface
	NicPktRecv uint64
	// Number of packets dropped by Hardware Interface
	NicPktOverflow uint64
	// Number of Bad CRC/PHY packets seen by Hardware Interface
	NicPktBad uint64
	// Number of packets received into the receive ring
	RingPktRecv uint64
	// Number of packets dropped because of insufficient space in receive ring
	RingPktOverflow uint64
	// Number of raw bytes received by the Hardware Interface on
	// all rings. Each Ethernet data packet includes 8 bytes of HW
	// header, 4 bytes of CRC and the result is aligned to 16 bytes
	// such that a minimum size 60 byte packet counts for 80 bytes.
	NicBytesRecv uint64
	// Number of packets dropped because of insufficient space in
	// shared SNF buffering.
	SnfPktOverflow uint64
	// Number of packets droped, reflected in Packets Drop Filter
	//in Counters.
	NicPktDropped uint64
}

RingStats is a structure to return statistics from a ring. The Hardware-specific counters apply to all rings as they are counted before any demultiplexing to a ring is applied.

type Sender added in v0.0.9

type Sender struct {
	*InjectHandle
	// contains filtered or unexported fields
}

Sender object wraps SNF injection API and provides packet sending capabilities with some safeguarding.

func NewSender added in v0.2.0

func NewSender(h *InjectHandle, timeout time.Duration, flags int) *Sender

NewSender returns new Sender object with given timeout and flags for SNF injection.

Timeout in milliseconds to wait if insufficient send resources are available to inject a new packet. Insufficient resources can be a lack of send descriptors or a full send queue ring. If timeout is 0, the function won’t block for send resources and returns EAGAIN error.

Flags are currently not supported and should be set to 0.

func (*Sender) NotifyWith added in v0.2.0

func (s *Sender) NotifyWith(ch <-chan os.Signal)

NotifyWith installs signal notification channel which is presumably registered via signal.Notify.

func (*Sender) Sched added in v0.0.9

func (s *Sender) Sched(delayNs int64, pkt []byte) error

Sched sends a packet with hardware delay and optionally blocks until send resources are available. This send function is used for paced packet injection. This function can be used as part of a packet replay program. When the function returns successfully, the packet is guaranteed to be completely buffered by SNF: no references are kept to the input data and the caller is free to safely modify its contents. The SNF implementation delays transmitting the packet according to the delayNs parameter, relative to the start of the prior packet.

Packet must hold a complete Ethernet frame (without the trailing CRC) and start with a valid Ethernet header. The hardware will append 4-CRC bytes at the end of the packet. The maximum valid packet size is 9000 bytes and is enforced by the library. The minimum valid packet size is 60 bytes, although any packet smaller than 60 bytes will be accepted by the library and padded by the hardware.

delayNs is the minimum delay between the start of the prior packet and the start of this packet. Packets with a delay less than the time to send the prior packet are send immediately. It is recommended to use 0 as the delta on the first packet sent.

EAGAIN error will be returned in case there are insufficient resources to send packet. If timeout is non-zero, the caller will have blocked at least that many milliseconds before resources could become available.

EINVAL error will be returned in case packet length is larger than 9000 bytes.

ENOTSUP error will be returned in case hardware doesnt support injection pacing.

If successful, the packet is completely buffered for sending by SNF. The implementation guarantees that it will eventually send the packet out, as scheduled, without requiring further calls into SNF.

func (*Sender) SchedVec added in v0.0.9

func (s *Sender) SchedVec(delayNs int64, pkt ...[]byte) error

SchedVec sends a packet assembled from a vector of fragments at a scheduled point relative to the start of the prior packet and optionally block until send resources are available. This send function follows the same semantics as Sched except that the packet to be injected can be assembled from multiple fragments (or buffers).

Packet should hold 1 or more buffers/fragments that can be used to compose a complete Ethernet frame (not including the trailing CRC header). The first fragment must point to a valid Ethernet header and the hardware will append its own (valid 4-byte CRC) at the end of the last buffer/fragment passed in pkt. When all the fragments are added up, the maximum valid packet size is 9000 bytes and is enforced by the library. The minimum valid packet size is 60 bytes, although any packet smaller than 60 bytes will be accepted by the library and padded by the hardware.

delayNs is the minimum delay between the start of the prior packet and the start of this packet. Packets with a delay less than the time to send the prior packet are send immediately. It is recommended to use 0 as the delta on the first packet sent.

EAGAIN error will be returned in case there are insufficient resources to send packet. If timeout is non-zero, the caller will have blocked at least that many milliseconds before resources could become available.

EINVAL error will be returned in case packet length is larger than 9000 bytes.

ENOTSUP error will be returned in case hardware doesnt support injection pacing.

If successful, the packet is completely buffered for sending by SNF. The implementation guarantees that it will eventually send the packet out, as scheduled, without requiring further calls into SNF.

func (*Sender) Send added in v0.0.9

func (s *Sender) Send(pkt []byte) error

Send sends a packet and optionally block until send resources are available. This send function is optimized for high packet rate injection. While it can be coupled with a receive ring to reinject a packet, it is not strictly necessary. This function can be used as part of a packet generator. When the function returns successfully, the packet is guaranteed to be completely buffered by SNF: no references are kept to the input data and the caller is free to safely modify its contents. A successful return does not, however, guarantee that the packet has been injected into the network. The SNF implementation may choose to hold on to the packet for coalescing in order to improve packet throughput.

Packet must hold a complete Ethernet frame (without the trailing CRC) and start with a valid Ethernet header. The hardware will append 4-CRC bytes at the end of the packet. The maximum valid packet size is 9000 bytes and is enforced by the library. The minimum valid packet size is 60 bytes, although any packet smaller than 60 bytes will be accepted by the library and padded by the hardware.

EAGAIN error will be returned in case there are insufficient resources to send packet. If timeout is non-zero, the caller will have blocked at least that many milliseconds before resources could become available.

EINVAL error will be returned in case packet length is larger than 9000 bytes.

If successful, the packet is completely buffered for sending by SNF. The implementation guarantees that it will eventually send the packet out in a timely fashion without requiring further calls into SNF.

func (*Sender) SendBulk added in v0.2.5

func (s *Sender) SendBulk(pkts [][]byte) (int, error)

SendBulk sends packets in bulk using snf_inject_send. It returns number of packets successfully sent, and if there are errors, it returns the first error found, or nil.

func (*Sender) SendVec added in v0.0.9

func (s *Sender) SendVec(pkt ...[]byte) error

SendVec sends a packet assembled from a vector of fragments and optionally block until send resources are available. This send function follows the same semantics as Send except that the packet to be injected can be assembled from multiple fragments (or buffers).

Packet should hold 1 or more buffers/fragments that can be used to compose a complete Ethernet frame (not including the trailing CRC header). The first fragment must point to a valid Ethernet header and the hardware will append its own (valid 4-byte CRC) at the end of the last buffer/fragment passed in pkt. When all the fragments are added up, the maximum valid packet size is 9000 bytes and is enforced by the library. The minimum valid packet size is 60 bytes, although any packet smaller than 60 bytes will be accepted by the library and padded by the hardware.

EAGAIN error will be returned in case there are insufficient resources to send packet. If timeout is non-zero, the caller will have blocked at least that many milliseconds before resources could become available.

EINVAL error will be returned in case overall fragments length is larger than 9000 bytes.

If successful, the packet is completely buffered for sending by SNF. The implementation guarantees that it will eventually send the packet out in a timely fashion without requiring further calls into SNF.

Jump to

Keyboard shortcuts

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