libraries/pcappp/include/pcapplusplus/KniDevice.h

652 lines
32 KiB
C++

#ifndef PCAPPP_KNI_DEVICE
#define PCAPPP_KNI_DEVICE
#include <string>
#include "Device.h"
#include "MacAddress.h"
#include "MBufRawPacket.h"
#include "LinuxNicInformationSocket.h"
/**
* @file
* @brief This file and KniDeviceList.h provide PcapPlusPlus C++ wrapper
* for DPDK KNI (Kernel Network Interface) library (librte_kni).
* The main propose of the rte_kni library is to provide a way to forward
* packets received by DPDK application to Linux kernel (a.e. to processes that
* have opened some kind of a net socket) for further processing and to obtain
* packets from Linux kernel. The KNI device is the bridge that accomplish the
* translation between DPDK packets (mbuf) and Linux kernel/socket packets
* (skbuf). Currently KNI devices report speeds up to 10 GBit/s.<BR>
* KNI device is a virtual network interface so it can be created and destroyed
* programmatically at will. As a real network interface KNI deivice must
* be managed appropriately like other interfaces. To start operate it MUST be
* given an IP address for example by the means of <B>ip a</B> command and
* MUST be put up for example by <B>ip l set [interface] up</B>.<BR>
* Additionally KNI interfaces support 4 other settings (depends on DPDK
* version used):
* - change link state: <B>ip l set [interface] up/down</B>
* - change MTU: <B>ip l set dev [interface] mtu [mtu_count]</B>
* - change MAC address: <B>ip l set [interface] address [new_mac]</B>
* - change promiscuous mode: <B>ip l set [interface] promisc on/off</B>
*
* Changes of each of this settings generates an event/request that must be
* handled by an application that have created the KNI device in 3 second
* period or it will be rejected and Linux kernel will not apply the change.
* The way that this requests MUST be handled is defined by DPDK and so for
* each type of request the application that creates the KNI device must provide
* the callback function to call. This callbacks are set in time of KNI device
* creation via KniIoctlCallbacks or KniOldIoctlCallbacks structures (the
* structure used is dependent on DPDK version).<BR>
* There is a way to enable <B>ethtool</B> on KNI devices that include
* recompilation of DPDK and strict correspondence between KNI port id and DPDK
* port id, but it is not currently supported by PcapPlusPlus.<BR>
*
* Known issues:
* - KNI device may not be able to set/update it's link status up
* (LINK_ERROR returned):
* The problem is laying in DPDK in rte_kni_update_link function
* (it is DPDK BUG if rte_kni_update_link is __rte_experimental).
* It is recommended to load rte_kni.ko module with "carrier=on" DPDK
* default is "carrier=off", provided setup-dpdk.sh by default loads with
* "carrier=on" if Your DPDK version supports it. The good indication of
* this issue are "DPDK KNI Failed to update links state for device"
* messages when Pcap++Test application is being run.
* - Packets may not be seen by applications that have open sockets on KNI
* device:
* Check your <B>iptables</B> settings and other packet filters - KNI device
* is traditional network device so all caveats apply;
* - I see a lot of unknown traffic - what is it?
* This must be Linux kernel trying to communicate with other devices from
* KNI device and it will do so as long as You give the KNI device any IP
* address via ip command or by record in /etc/networking/interfaces;
* - On old versions of DPDK only 4 KNI devices may be created:
* Yep this is a limitation. You can change MAX_KNI_DEVICES constant in
* KniDeviceList.cpp to unlock this limit;
* - Any set* method never succeeds:
* You may forgot that they generate KNI requests that Your application MUST
* handle. Just use KniDevice#startRequestHandlerThread to handle all
* requests automatically. Or user running the application don't have
* suitable access rights (must have CAP_NET_ADMIN).
*
* Useful links:
* - <a href="https://doc.dpdk.org/guides/prog_guide/kernel_nic_interface.html">KNI interface concept DPDK documentation</a>
* - <a href="https://doc.dpdk.org/guides/nics/kni.html">KNI PMD</a>
* - <a href="https://doc.dpdk.org/guides/sample_app_ug/kernel_nic_interface.html">KNI DPDK sample application</a>
* - <a href="https://doc.dpdk.org/dts/test_plans/kni_test_plan.html">KNI DPDK test plan</a>
*/
struct rte_kni;
/**
* \namespace pcpp
* \brief The main namespace for the PcapPlusPlus lib
*/
namespace pcpp
{
class KniDevice;
class KniDeviceList;
/**
* Defines the signature callback used by capturing API on KNI device
*/
typedef bool (*OnKniPacketArriveCallback)(MBufRawPacket* packets, uint32_t numOfPackets, KniDevice* device, void* userCookie);
/**
* @class KniDevice
* This class represents special kind of DPDK devices called KNI - Kernel Network Interface
* that are used to exchange DPDK mbuf packets with Linux kernel network stack.
* This devices have only one RX and one TX queue so MT receiving or MT sending is not
* safe but simultaneous receiving and sending packets is MT safe.
* The RX queue of KNI device is pointed from kernel to application and TX queue is
* pointed in opposite direction - from application to kernel. So receive* methods will
* obtain packets from kernel and send* methods will send them to kernel.<BR>
* The lifecycle of the KNI device is as follows:
* - KniDeviceConfiguration structure is created by user and filled with device settings;
* - The KniDeviceList#getInstance method is called to initialize the KNI module and
* obtain the KniDeviceList singleton;
* - KniDeviceList#createDevice method of the singleton is called to allocate new KNI device;
* - Device then must be opened by calling KniDevice#open;
* - During lifetime the application must handle the requests from kernel either by calling
* KniDevice#handleRequests to handle them synchronously or by starting separate thread
* to do this by calling KniDevice#startRequestHandlerThread;
* - During lifetime the packets may be send to/received from KNI device via
* calls to synchronous API (send/receive methods) or asynchronously by
* running capturing thread using KniDevice#startCapture;
* - KNI device will be destroyed or implicitly on application exit. User must assure
* that NO OTHER linux application is using KNI device when and after it is being
* destroyed otherwise Linux kernel may crush dramatically.
*/
class KniDevice : public IDevice
{
friend class KniDeviceList;
friend class MBufRawPacket;
public:
/**
* Various link related constants for KNI device
*/
enum KniLinkState
{
/** Returned by KNI functions if DPDK version used don't support link setup capability */
LINK_NOT_SUPPORTED = -2,
/** Returned by KNI functions if link changing function meets an error */
LINK_ERROR = -1,
/** Used to put link status on KNI device DOWN */
LINK_DOWN = 0,
/** Used to put link status on KNI device UP */
LINK_UP = 1
};
/**
* Various information related constants for KNI device
*/
enum KniInfoState
{
/** Used to identify intent to obtain cached version of KNI device information */
INFO_CACHED = 0,
/** Used to identify intent to renew/update KNI device information */
INFO_RENEW = 1
};
/**
* Promiscuous mode related constants for KNI device
*/
enum KniPromiscuousMode
{
/** Used to DISABLE promiscuous mode on KNI device */
PROMISC_DISABLE = 0,
/** Used to ENABLE promiscuous mode on KNI device */
PROMISC_ENABLE = 1
};
/**
* @brief New callbacks for KNI device events.
* This structure MUST be used ONLY when KniDeviceList#callbackVersion returns
* KniDeviceList#KniCallbackVersion#CALLBACKS_NEW.
* Or if You are sure that DPDK version used is 17.11 or higher.
* If some callback is not provided (NULL) the request will always succeeds
* if other is not specified in callback description.
* @note This callbacks are direct copy of one defined in rte_kni_ops. Future
* maintainers of KNI device feature MUST refer to rte_kni_ops structure in
* rte_kni.h header file of DPDK to track the difference in signatures
*/
struct KniIoctlCallbacks
{
/**
* Pointer to function of changing MTU.
* Must return 0 in case of success or negative error code
*/
int (*change_mtu)(uint16_t port_id, unsigned int new_mtu);
/**
* Pointer to function of configuring network interface.
* Must return 0 in case of success or negative error code
*/
int (*config_network_if)(uint16_t port_id, uint8_t if_up);
/**
* Pointer to function of configuring mac address.
* If callback is not provided and port_id of KNI device is not UINT16_MAX
* then DPDK will use predefined callback witch sets MAC address of
* DPDK port port_id via rte_eth_dev_default_mac_addr_set.
* Must return 0 in case of success or negative error code
*/
int (*config_mac_address)(uint16_t port_id, uint8_t mac_addr[]);
/**
* Pointer to function of configuring promiscuous mode.
* If callback is not provided and port_id of KNI device is not UINT16_MAX
* then DPDK will use predefined callback witch sets promiscuous mode of
* DPDK port port_id via rte_eth_promiscuous_enable/rte_eth_promiscuous_disable.
* Must return 0 in case of success or negative error code
*/
int (*config_promiscusity)(uint16_t port_id, uint8_t to_on);
};
/**
* @brief Old callbacks for KNI device events.
* This structure MUST be used ONLY when KniDeviceList#callbackVersion returns
* KniDeviceList#KniCallbackVersion#CALLBACKS_OLD.
* Or if You are sure that DPDK version used is lower than 17.11.
* If some callback is not provided (NULL) the request will always succeeds.
*/
struct KniOldIoctlCallbacks
{
/**
* Pointer to function of changing MTU.
* Must return 0 in case of success or negative error code
*/
int (*change_mtu)(uint8_t port_id, unsigned int new_mtu);
/**
* Pointer to function of configuring network interface.
* Must return 0 in case of success or negative error code
*/
int (*config_network_if)(uint8_t port_id, uint8_t if_up);
};
/**
* @brief KNI device initialization data.
* Used to create new KNI device.
* Usage of callbacks member or oldCallbacks member is defined by
* result of KniDeviceList#callbackVersion
*/
struct KniDeviceConfiguration
{
/**
* Name used to display device in system.
* Must not interfere with already existing network interfaces.
* Must be less than or equal to IFNAMSIZ (16 chars including \0 on most systems)
*/
std::string name;
union
{
KniIoctlCallbacks* callbacks;
KniOldIoctlCallbacks* oldCallbacks;
};
/**
* MAC (ETHERNET) address of new KNI device.
* If MacAddress::Zero is provided then
* some valid address will be automatically generated.
* If provided (not MacAddress::Zero) will be cached by new KNI device info structure.
*/
MacAddress mac;
/**
* Used in same way as DPDK port id.
* If some new callbacks are omitted then have separate meaning
* (see KniIoctlCallbacks#config_mac_address and KniIoctlCallbacks#config_promiscusity).
* Can be set to UINT16_MAX.
*/
uint16_t portId;
/** MTU of new KNI device. Will be cached by new KNI device info structure */
uint16_t mtu;
/**
* If set forces to bind KNI Linux kernel thread (NOT userspace thread) to specific core.
* If rte_kni kernel module is loaded with "kthread_mode=single" then -
* rebinds kernel thread used for all KNI devices to specified core.
* If rte_kni kernel module is loaded with "kthread_mode=multiple" then -
* binds new kernel thread for this device to specified core.
*/
bool bindKthread;
/** ID of core to bind Linux kernel thread to (same as DPDK cores IDs) */
uint32_t kthreadCoreId;
};
private:
/** All instances of this class MUST be produced by KniDeviceList class */
KniDevice(const KniDeviceConfiguration& conf, size_t mempoolSize, int unique);
/** This class is not copyable */
KniDevice(const KniDevice&);
/** This class is not copyable */
KniDevice& operator=(const KniDevice&);
/** All instances of this class MUST be destroyed by KniDeviceList class */
~KniDevice();
public:
/* Information getters */
/**
* Indicates whether the KNI device was initialized successfully
*/
inline bool isInitialized() const { return !(m_Device == NULL || m_MBufMempool == NULL); }
/**
* Obtains name of KNI device in form of C++ string
*/
inline std::string getName() const { return std::string(m_DeviceInfo.name); }
/**
* Obtains port ID of KNI device
*/
inline uint16_t getPort() const { return m_DeviceInfo.portId; }
/**
* @brief Obtains link status of KNI device.
* If called with INFO_CACHED - returns cached data about link state (SUPER FAST may be INACCURATE).
* If called with INFO_RENEW - makes system call to Linux to obtain
* link information and caches it (VERY SLOW but ACCURATE).
* @param[in] state Defines information relevance level
* @return LINK_UP, LINK_DOWN, LINK_NOT_SUPPORTED if device is not initialized, some times LINK_ERROR
*/
KniLinkState getLinkState(KniInfoState state = INFO_CACHED);
/**
* @brief Obtains MAC address of KNI device.
* If called with INFO_CACHED - returns cached data about MAC address (SUPER FAST may be INACCURATE).
* If called with INFO_RENEW - makes system call to Linux to obtain
* MAC address and caches it (VERY SLOW but ACCURATE).
* @param[in] state Defines information relevance level
* @return Known MAC address of KNI interface
*/
MacAddress getMacAddress(KniInfoState state = INFO_CACHED);
/**
* @brief Obtains MTU of KNI device.
* If called with INFO_CACHED - returns cached data about MTU (SUPER FAST may be INACCURATE).
* If called with INFO_RENEW - makes system call to Linux to obtain
* MTU and caches it (VERY SLOW but ACCURATE).
* @param[in] state Defines information relevance level
* @return Known MTU of KNI interface
*/
uint16_t getMtu(KniInfoState state = INFO_CACHED);
/**
* @brief Obtains information about promiscuous mode of KNI device.
* If called with INFO_CACHED - returns cached data about promiscuous mode (SUPER FAST may be INACCURATE).
* If called with INFO_RENEW - makes system call to Linux to obtain
* promiscuous mode and caches it (VERY SLOW but ACCURATE).
* @param[in] state Defines information relevance level
* @return Known promiscuous mode of KNI interface
*/
KniPromiscuousMode getPromiscuous(KniInfoState state = INFO_CACHED);
/* Information setters */
/**
* @brief Sets link state of KNI device.
* Firstly the link information is updated as by call to getLinkState(INFO_RENEW).
* Then link state is set only if obtained state differs from provided.
* New link state of KNI device is cached.
* @note You must be using sudo or be root or have CAP_NET_ADMIN capability to use this function
* @note Generates change link state request
* @param[in] state Must be LINK_UP or LINK_DOWN
* @return true if desired link state of KNI device is set (was as provided or set successfully),
* false if some error occurred (debug info is printed)
*/
bool setLinkState(KniLinkState state);
/**
* @brief Sets MAC address of KNI device.
* Unconditionally changes MAC of KNI device.
* If MAC is updated successfully then it is cached.
* @note You must be using sudo or be root or have CAP_NET_ADMIN capability to use this function
* @note Generates change mac request
* @param[in] mac New MAC address of KNI device
* @return true if desired MAC address is set, false if not and some error occurred (debug info is printed)
*/
bool setMacAddress(MacAddress mac);
/**
* @brief Sets MTU of KNI device.
* Unconditionally changes MTU of KNI device.
* If MTU is updated successfully then it is cached.
* @note You must be using sudo or be root or have CAP_NET_ADMIN capability to use this function
* @note Generates change mtu request
* @warning Low MTU values may crush Linux kernel. Follow Your kernel version documentation for details
* @param[in] mtu New MTU address of KNI device
* @return true if desired MTU is set, false if not and some error occurred (debug info is printed)
*/
bool setMtu(uint16_t mtu);
/**
* @brief Sets promiscuous mode of KNI device.
* Firstly the promiscuous mode information is updated as by call to getPromiscuous(INFO_RENEW).
* Then promiscuous mode is set only if obtained mode differs from provided.
* New promiscuous mode of KNI device is cached.
* @note You must be using sudo or be root or have CAP_NET_ADMIN capability to use this function
* @note Generates promiscuous mode request
* @param[in] mode Must be PROMISC_DISABLE or PROMISC_ENABLE
* @return true if desired promiscuous mode of KNI device is set (was as provided or set successfully),
* false if some error occurred (debug info is printed)
*/
bool setPromiscuous(KniPromiscuousMode mode);
/**
* @brief Updates link state of KNI device.
* Unconditionally updates link state of KNI device via call to DPDK librte_kni API.
* FASTER THAN setLinkState(state) but may not be supported or may fail.
* If link state is updated successfully then it is cached.
* @param[in] state New link state of KNI device
* @return LINK_NOT_SUPPORTED if this capability is not supported by DPDK version used (DPDK ver < 18.11),
* LINK_ERROR if attempt to set link state failed (may always fail on some systems see class description)
* LINK_DOWN - previous link state was DOWN, state is successfully updated to provided one
* LINK_UP - previous link state was UP, state is successfully updated to provided one
*/
KniLinkState updateLinkState(KniLinkState state);
/* Requests */
/**
* @brief Handle requests from Linux kernel synchronously in calling thread.
* When one of events which is needed application attention occurres it must be handled by calling this
* function (or by running RequestHandlerThread for this device).
* Until the request is handled the Linux kernel thread that manages this KNI is blocked.
* If it is not handled by application in 3 seconds the request is reported to kernel as failed one.
* Current known requests are:
* - change link state: ip l set [interface] up/down
* - change mtu: ip l set dev [interface] mtu [mtu_count]
* - change mac: ip l set [interface] address [new_mac]
* - change promiscuous mode: ip l set [interface] promisc on/off
* @warning Functions setLinkState, setMacAddress, setMtu and setPromiscuous will generate this requests.
* @note Callbacks provided for this KNI device will be called synchronously in calling thread during execution of this function
* @return true if no error happened during request handling false otherwise
*/
bool handleRequests();
/**
* @brief Starts new thread (using pthread) to asynchronously handle KNI device requests.
* See description of handleRequests() about requests.
* New thread is detached using pthread_detach.
* This thread can be stopped explicitly by calling stopRequestHandlerThread() or
* implicitly on KNI device destruction.
* Linux <a href="http://man7.org/linux/man-pages/man2/nanosleep.2.html">nanosleep()</a> function is used for sleeping.
* @note Callbacks provided for this KNI device will be called asynchronously in new thread
* @param[in] sleepSeconds Sleeping time in seconds
* @param[in] sleepNanoSeconds Sleeping time in nanoseconds
* @return true if new thread is started successfully false otherwise
*/
bool startRequestHandlerThread(long sleepSeconds, long sleepNanoSeconds = 0);
/**
* @brief Explicitly stops request thread for this device if it was running.
* See description of handleRequests() about requests.
* @warning There may be a rare error when request thread handles requests on already
* destroyed device. It occurres only because request thread is detached one but it is really really rare.
* In case of this error occurring (must be SIGSEGV) change type of created thread in startRequestHandlerThread
* function from DETACHED to JOINABLE.
*/
void stopRequestHandlerThread();
/* Packet receive */
/**
* @brief Receive raw packets from kernel.
* @param[out] rawPacketsArr A vector where all received packets will be written into
* @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log
*/
uint16_t receivePackets(MBufRawPacketVector& rawPacketsArr);
/**
* @brief Receive raw packets from kernel.
* Please notice that in terms of performance, this is the best method to use
* for receiving packets because out of all receivePackets overloads this method requires the least overhead and is
* almost as efficient as receiving packets directly through DPDK. So if performance is a critical factor in your
* application, please use this method
* @param[out] rawPacketsArr A pointer to an array of MBufRawPacket pointers where all received packets will be written into. The array is expected to
* be allocated by the user and its length should be provided in rawPacketArrLength. Number of packets received will be returned.
* Notice it's the user responsibility to free the array and its content when done using it
* @param[out] rawPacketArrLength The length of MBufRawPacket pointers array
* @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log
*/
uint16_t receivePackets(MBufRawPacket** rawPacketsArr, uint16_t rawPacketArrLength);
/**
* @brief Receive parsed packets from kernel.
* @param[out] packetsArr A pointer to an allocated array of Packet pointers where all received packets will be written into. The array is expected to
* be allocated by the user and its length should be provided in packetsArrLength. Number of packets received will be returned.
* Notice it's the user responsibility to free the array and its content when done using it
* @param[out] packetsArrLength The length of Packet pointers array
* @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log
*/
uint16_t receivePackets(Packet** packetsArr, uint16_t packetsArrLength);
/* Packet send */
/**
* @brief Send an array of MBufRawPacket to kernel.
* Please notice the following:<BR>
* - In terms of performance, this is the best method to use for sending packets because out of all sendPackets overloads
* this method requires the least overhead and is almost as efficient as sending the packets directly through DPDK. So if performance
* is a critical factor in your application, please use this method
* - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each
* iteration of 64 packets
* - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK
* <BR><BR>
* @param[in] rawPacketsArr A pointer to an array of MBufRawPacket
* @param[in] arrLength The length of the array
* @return The number of packets actually and successfully sent
*/
uint16_t sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength);
/**
* @brief Send an array of parsed packets to kernel.
* Please notice the following:<BR>
* - If some or all of the packets contain raw packets which aren't of type MBufRawPacket, a new temp MBufRawPacket instances
* will be created and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent.
* If performance is a critical factor please make sure you send parsed packets
* that contain only raw packets of type MBufRawPacket
* - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each
* iteration of 64 packets
* - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK
* <BR><BR>
* @param[in] packetsArr A pointer to an array of parsed packet pointers
* @param[in] arrLength The length of the array
* @return The number of packets actually and successfully sent
*/
uint16_t sendPackets(Packet** packetsArr, uint16_t arrLength);
/**
* @brief Send a vector of MBufRawPacket pointers to kernel.
* Please notice the following:<BR>
* - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each
* iteration of 64 packets
* - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK
* <BR><BR>
* @param[in] rawPacketsVec The vector of raw packet
* @return The number of packets actually and successfully sent
*/
uint16_t sendPackets(MBufRawPacketVector& rawPacketsVec);
/**
* @brief Send a vector of RawPacket pointers to kernel.
* Please notice the following:<BR>
* - If some or all of the raw packets aren't of type MBufRawPacket, a new temp MBufRawPacket instances will be created
* and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent. If
* performance is a critical factor please make sure you send only raw packets of type MBufRawPacket
* (or use the sendPackets overload that sends MBufRawPacketVector)
* - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each
* iteration of 64 packets
* - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK
* <BR><BR>
* @param[in] rawPacketsVec The vector of raw packet
* @return The number of packets actually and successfully sent
*/
uint16_t sendPackets(RawPacketVector& rawPacketsVec);
/**
* @brief Send a raw packet to kernel.
* Please notice that if the raw packet isn't of type MBufRawPacket, a new temp MBufRawPacket
* will be created and the data will be copied to it. This is necessary to allocate an mbuf which will store the data to be sent.
* If performance is a critical factor please make sure you send a raw packet of type MBufRawPacket.
* Please also notice that the mbuf used or allocated in this method isn't freed by this method, it will be transparently freed by DPDK
* @param[in] rawPacket The raw packet to send
* @return True if packet was sent successfully or false if the packet wasn't sent for any other reason
*/
bool sendPacket(RawPacket& rawPacket);
/**
* @brief Send a MBufRawPacket to kernel.
* Please notice that the mbuf used in this method isn't freed by this method, it will be transparently freed by DPDK
* @param[in] rawPacket The MBufRawPacket to send
* @return True if packet was sent successfully or false if device is not opened or if the packet wasn't sent for any other reason
*/
bool sendPacket(MBufRawPacket& rawPacket);
/**
* @brief Send a parsed packet to kernel.
* Please notice that the mbuf used or allocated in this method isn't freed by this method, it will be transparently freed by DPDK
* @param[in] packet The parsed packet to send. Please notice that if the packet contains a raw packet which isn't of type
* MBufRawPacket, a new temp MBufRawPacket will be created and the data will be copied to it. This is necessary to
* allocate an mbuf which will store the data to be sent. If performance is a critical factor please make sure you send a
* parsed packet that contains a raw packet of type MBufRawPacket
* @return True if packet was sent successfully or false if device is not opened or if the packet wasn't sent for any other reason
*/
bool sendPacket(Packet& packet);
/* Packet capture */
/**
* @brief Start capturing packets asynchronously on this KNI interface.
* Each time a burst of packets is captured the onPacketArrives callback is called.
* The capture is done on a new thread created by this method, meaning all callback
* calls are done in a thread other than the caller thread.
* Capture process will stop and this capture thread will be terminated when calling stopCapture().
* This method must be called after the device is opened (i.e the open() method was called), otherwise an error will be returned.
* Capturing thread will be terminated automatically on KNI device destruction or when close() is called.
* @param[in] onPacketArrives A callback that is called each time a burst of packets is captured
* @param[in] onPacketArrivesUserCookie A pointer to a user provided object. This object will be transferred to the onPacketArrives callback
* each time it is called. This cookie is very useful for transferring objects that give context to the capture callback, for example:
* objects that counts packets, manages flow state or manages the application state according to the packet that was captured
* @return True if capture started successfully, false if (relevant log error is printed in any case):
* - Capture is already running
* - Device is not opened
* - Capture thread could not be created
*/
bool startCapture(OnKniPacketArriveCallback onPacketArrives, void* onPacketArrivesUserCookie);
/**
* @brief Start capturing packets synchronously on this KNI interface in blocking mode.
* Blocking mode means that this method block and won't return until the user frees the blocking
* (via onPacketArrives callback) or until a user defined timeout expires.
* Whenever a burst of packets is captured the onPacketArrives callback is called and lets the user handle the packet.
* In each callback call the user should return true if he wants to release the block or false if it wants it to keep blocking.
* Regardless of this callback a timeout is defined when stop capturing.
* When this timeout expires the method will return.<BR>
* Please notice that stopCapture() isn't needed here because when the method returns (after timeout or per user decision) capturing
* on the device is stopped.
* @param[in] onPacketArrives A callback given by the user for handling incoming packets. After handling each burst of packets
* the user needs to return a boolean value. True value indicates stop capturing and stop blocking and
* false value indicates continue capturing and blocking
* @param[in] onPacketArrivesUserCookie A pointer to a user provided object. This object will be transferred to the onPacketArrives callback
* each time it is called. This cookie is very useful for transferring objects that give context to the capture callback, for example:
* objects that counts packets, manages flow state or manages the application state according to the packet that was captured
* @param[in] timeout A timeout in seconds for the blocking to stop even if the user didn't return "true" in the onPacketArrives callback
* If this timeout is set to 0 or less the timeout will be ignored, meaning the method will keep blocking until the user frees it via
* the onPacketArrives callback
* @return -1 if timeout expired, 1 if blocking was stopped via onPacketArrives callback or 0 if an error occurred (such as device
* not open etc.). When returning 0 an appropriate error message is printed to log
*/
int startCaptureBlockingMode(OnKniPacketArriveCallback onPacketArrives, void* onPacketArrivesUserCookie, int timeout);
/**
* Stop a currently running asynchronous packet capture.
*/
void stopCapture();
/* Device control */
/**
* Takes appropriate actions for opening KNI device.
* @return true if the device was opened successfully, false if device is already opened,
* or KNI device configuration and startup failed
*/
bool open();
/**
* @brief Close the KNI device.
* When device is closed it's not possible to work with it.
* Stops asynchronous packet capture if it is running.
*/
void close();
private:
struct rte_kni* m_Device;
struct rte_mempool* m_MBufMempool;
struct KniDeviceInfo
{
LinuxNicInformationSocket soc;
KniLinkState link;
KniPromiscuousMode promisc;
uint16_t portId;
uint16_t mtu;
MacAddress mac;
std::string name;
bool init(const KniDeviceConfiguration& conf);
} m_DeviceInfo;
struct KniThread;
struct KniCapturing
{
OnKniPacketArriveCallback callback;
void* userCookie;
KniThread* thread;
static void* runCapture(void* devicePointer);
inline bool isRunning() const { return thread != NULL; }
void cleanup();
} m_Capturing;
struct KniRequests
{
long sleepS;
long sleepNs;
KniThread* thread;
static void* runRequests(void* devicePointer);
void cleanup();
} m_Requests;
};
} // namespace pcpp
#endif /* PCAPPP_KNI_DEVICE */