652 lines
32 KiB
C++
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 */
|