text: #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 Interace) 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 upto 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 handeled is defined by DPDK and so for
   * each type of request the application tht 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).
   *
   * Usefull 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 beeing
  	 *    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 hanled 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 happend 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 stoped 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 occuring (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] userCookie 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 */