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 ¶
- Constants
- func Init() error
- func PortMask() (linkup, valid uint32, err error)
- func SetAppID(id int32) error
- type CHashFunc
- type ErrSignal
- type Handle
- func (h *Handle) Close() (err error)
- func (h *Handle) LinkSpeed() (uint64, error)
- func (h *Handle) LinkState() (int, error)
- func (h *Handle) OpenRing() (ring *Ring, err error)
- func (h *Handle) OpenRingID(id int) (ring *Ring, err error)
- func (h *Handle) ReflectEnable() (*ReflectHandle, error)
- func (h *Handle) Start() error
- func (h *Handle) Stop() error
- func (h *Handle) TimeSourceState() (int, error)
- type HandlerOption
- type IfAddrs
- type InjectHandle
- type InjectStats
- type RecvReq
- type ReflectHandle
- type Ring
- func (r *Ring) Close() error
- func (r *Ring) PortInfo() ([]RingPortInfo, error)
- func (r *Ring) Recv(timeout time.Duration, req *RecvReq) error
- func (r *Ring) RecvMany(timeout time.Duration, reqs []RecvReq, qinfo *RingQInfo) (int, error)
- func (r *Ring) ReturnMany(reqs []RecvReq, qinfo *RingQInfo) error
- func (r *Ring) Stats() (*RingStats, error)
- type RingPortInfo
- type RingQInfo
- type RingReader
- func (rr *RingReader) Data() []byte
- func (rr *RingReader) Err() error
- func (rr *RingReader) Free() error
- func (rr *RingReader) LoopNext() bool
- func (rr *RingReader) Next() bool
- func (rr *RingReader) NotifyWith(ch <-chan os.Signal)
- func (rr *RingReader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error)
- func (rr *RingReader) RecvReq() *RecvReq
- func (rr *RingReader) Ring() *Ring
- func (rr *RingReader) Stats() (*RingStats, error)
- func (rr *RingReader) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error)
- type RingStats
- type Sender
- func (s *Sender) NotifyWith(ch <-chan os.Signal)
- func (s *Sender) Sched(delayNs int64, pkt []byte) error
- func (s *Sender) SchedVec(delayNs int64, pkt ...[]byte) error
- func (s *Sender) Send(pkt []byte) error
- func (s *Sender) SendBulk(pkts [][]byte) (int, error)
- func (s *Sender) SendVec(pkt ...[]byte) error
Examples ¶
Constants ¶
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)
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)
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.
const ( // 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.
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 PortMask ¶
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 ¶
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 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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
GetIfAddrs gets a list of Sniffer-capable ethernet devices.
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
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.
type ReflectHandle ¶ added in v0.0.9
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 ¶
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 ¶
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
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
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 ¶
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.
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
NotifyWith installs signal notification channel which is presumably registered via signal.Notify.
func (*Sender) Sched ¶ added in v0.0.9
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
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
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
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
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.