text: 
  #include <cstdio>
  #include <cstring>
  #include <cstdlib>
  #include <csignal>
  #include <ctime>
  #include <string>
  
  #include <unistd.h>
  #include <errno.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <netinet/ip.h>
  #include <arpa/inet.h>
  #include <poll.h>
  #include <getopt.h>
  
  #include <PcapPlusPlusVersion.h>
  #include <SystemUtils.h>
  
  #include <Packet.h>
  #include <EthLayer.h>
  #include <ArpLayer.h>
  #include <IPv4Layer.h>
  #include <UdpLayer.h>
  #include <PayloadLayer.h>
  
  #include <DpdkDeviceList.h>
  #include <KniDevice.h>
  #include <KniDeviceList.h>
  
  
  #define EXIT_WITH_ERROR(reason) do { \
  	std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \
  	exit(1); \
  	} while(0)
  
  
  #define IO_BUFF_SIZE (1 << 14)
  #define WANT_POLLIN (-2)
  #define WANT_POLLOUT (-3)
  #define DEFAULT_KNI_NAME "pcppkni0"
  #define DEFAULT_PORT 62604
  
  
  namespace
  {
  
  struct KniPongArgs
  {
  	std::string kniIp;
  	std::string outIp;
  	std::string kniName;
  	uint16_t kniPort;
  };
  
  
  typedef int linuxFd;
  
  
  struct LinuxSocket
  {
  	inline operator int() const { return m_Socket; }
  	linuxFd m_Socket;
  };
  
  
  struct PacketStats
  {
  	unsigned long totalPackets;
  	unsigned long udpPacketsIn;
  	unsigned long udpPacketsOutFail;
  	unsigned long arpPacketsIn;
  	unsigned long arpPacketsOutFail;
  };
  
  
  static bool doContinue = true;
  
  
  /**
   * Print application version
   */
  void printAppVersion()
  {
  	std::cout
  		<< pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl
  		<< "Built: " << pcpp::getBuildDateTime() << std::endl
  		<< "Built from: " << pcpp::getGitInfo() << std::endl;
  	exit(0);
  }
  
  
  /**
   * Print application usage
   */
  inline void printUsage()
  {
  	std::cout << std::endl
  		<< "Usage:" << std::endl
  		<< "------" << std::endl
  		<< pcpp::AppName::get() << " [-hv] [-n KNI_DEVICE_NAME] [-p PORT] -s SRC_IPV4 -d DST_IPV4" << std::endl
  		<< std::endl
  		<< "Options:" << std::endl
  		<< "    -s --src SRC_IPV4           : IPv4 address to assign to the created KNI device" << std::endl
  		<< "    -d --dst DST_IPV4           : Virtual IPv4 address to communicate with. Must be in /24 subnet with SRC_IPV4" << std::endl
  		<< "    -n --name KNI_DEVICE_NAME   : Name for KNI device. Default: \"" << DEFAULT_KNI_NAME << "\"" << std::endl
  		<< "    -p --port PORT              : Port for communication. Default: " << DEFAULT_PORT << std::endl
  		<< "    -v --version                : Displays the current version and exits" << std::endl
  		<< "    -h --help                   : Displays this help message and exits" << std::endl
  		<< std::endl;
  }
  
  
  inline void parseArgs(int argc, char* argv[], KniPongArgs& args)
  {
  	struct option KniPongOptions[] =
  	{
  		{"src", required_argument, NULL, 's'},
  		{"dst", required_argument, NULL, 'd'},
  		{"name", optional_argument, NULL, 'n'},
  		{"port", optional_argument, NULL, 'p'},
  		{"help", no_argument, NULL, 'h'},
  		{"version", no_argument, NULL, 'v'},
  		{NULL, 0, NULL, 0}
  	};
  	// Default port:
  	args.kniPort = DEFAULT_PORT;
  	int optionIndex = 0;
  	int opt = 0;
  	while ((opt = getopt_long(argc, argv, "s:d:n:p:hv", KniPongOptions, &optionIndex)) != -1)
  	{
  		switch (opt)
  		{
  			case 0:
  				break;
  			case 's':
  				args.kniIp = optarg;
  				break;
  			case 'd':
  				args.outIp = optarg;
  				break;
  			case 'n':
  				args.kniName = optarg;
  				break;
  			case 'p':
  				args.kniPort = std::strtoul(optarg, NULL, 10) & 0xFFFF;
  				break;
  			case 'v':
  				printAppVersion();
  				break;
  			case 'h':
  			{
  				printUsage();
  				std::exit(0);
  			} break;
  			default:
  			{
  				printUsage();
  				exit(1);
  			} break;
  		}
  	}
  	// Default name for KNI device:
  	if (args.kniName.empty())
  		args.kniName = DEFAULT_KNI_NAME;
  	if (args.kniIp.empty())
  	{
  		printUsage();
  		EXIT_WITH_ERROR("IP for KNI device not provided");
  	}
  	if (args.outIp.empty())
  	{
  		printUsage();
  		EXIT_WITH_ERROR("Virtual IP for communication not provided");
  	}
  	pcpp::IPv4Address kniIp = args.kniIp;
  	pcpp::IPv4Address outIp = args.outIp;
  	if (!(kniIp.isValid() && outIp.isValid()))
  	{
  		EXIT_WITH_ERROR("One of provided IPs is not valid");
  	}
  	if (!outIp.matchSubnet(kniIp, pcpp::IPv4Address("255.255.255.0")))
  	{
  		EXIT_WITH_ERROR(
  			"Provided Virtual IP '" << outIp << "' is not in same required subnet '255.255.255.0' as KNI IP '" << kniIp << "'"
  		);
  	}
  }
  
  
  /**
   * Simple dummy callbacks that always yields success for Linux Kernel
   */
  struct KniDummyCallbacks
  {
  	static int changeMtuNew(uint16_t, unsigned int) { return 0; }
  	static int changeMtuOld(uint8_t, unsigned int) { return 0; }
  	static int configNetworkIfNew(uint16_t, uint8_t) { return 0; }
  	static int configNetworkIfOld(uint8_t, uint8_t) { return 0; }
  	static int configMacAddress(uint16_t, uint8_t[]) { return 0; }
  	static int configPromiscusity(uint16_t, uint8_t) { return 0; }
  
  	static pcpp::KniDevice::KniIoctlCallbacks cbNew;
  	static pcpp::KniDevice::KniOldIoctlCallbacks cbOld;
  
  	static void setCallbacks()
  	{
  		cbNew.change_mtu = changeMtuNew;
  		cbNew.config_network_if = configNetworkIfNew;
  		cbNew.config_mac_address = configMacAddress;
  		cbNew.config_promiscusity = configPromiscusity;
  		cbOld.change_mtu = changeMtuOld;
  		cbOld.config_network_if = configNetworkIfOld;
  	}
  };
  pcpp::KniDevice::KniIoctlCallbacks KniDummyCallbacks::cbNew;
  pcpp::KniDevice::KniOldIoctlCallbacks KniDummyCallbacks::cbOld;
  
  
  /**
   * Setup IP of net device by calling the ip unix utility
   */
  inline bool setKniIp(const pcpp::IPv4Address& ip, const std::string& kniName)
  {
  	std::ostringstream command;
  	command << "ip a add " << ip << "/24 dev " << kniName;
  	pcpp::executeShellCommand(command.str());
  	command.str("");
  	command << "ip a | grep " << ip;
  	std::string result = pcpp::executeShellCommand(command.str());
  	return result != "" && result != "ERROR";
  }
  
  
  /**
   * KNI device setup routine
   */
  inline pcpp::KniDevice* setupKniDevice(const KniPongArgs& args)
  {
  	{
  		// Setup DPDK
  		pcpp::CoreMask cm = 0x3;
  		bool dpdkInitSuccess = pcpp::DpdkDeviceList::initDpdk(cm, 1023);
  		if (!dpdkInitSuccess)
  			EXIT_WITH_ERROR("Failed to init DPDK");
  	}
  	pcpp::IPv4Address kniIp = args.kniIp;
  	// Setup device config
  	pcpp::KniDevice* device = NULL;
  	pcpp::KniDevice::KniDeviceConfiguration devConfig;
  	devConfig.name = args.kniName;
  	KniDummyCallbacks::setCallbacks();
  	if (pcpp::KniDeviceList::callbackVersion() == pcpp::KniDeviceList::CALLBACKS_NEW)
  	{
  		devConfig.callbacks = &KniDummyCallbacks::cbNew;
  	}
  	else
  	{
  		devConfig.oldCallbacks = &KniDummyCallbacks::cbOld;
  	}
  	devConfig.bindKthread = false;
  	pcpp::KniDeviceList& kniDeviceList = pcpp::KniDeviceList::getInstance();
  	if (!kniDeviceList.isInitialized())
  		EXIT_WITH_ERROR("Can't initialize KNI device list");
  	device = kniDeviceList.createDevice(devConfig, 1024);
  	if (device == NULL)
  		EXIT_WITH_ERROR("Can't create KNI device");
  	// Check KNI device and start request thread
  	if (!device->isInitialized())
  		EXIT_WITH_ERROR("KNI device was not initialized correctly");
  	if (!device->open())
  		EXIT_WITH_ERROR("Could not open KNI device");
  	if (!device->startRequestHandlerThread(0, 500000000))
  		EXIT_WITH_ERROR("Could not start KNI device request handler thread");
  	// Assign IP
  	if (!setKniIp(kniIp, args.kniName))
  		EXIT_WITH_ERROR("Can't set KNI device IP");
  	// Turn device on for Linux Kernel
  	if (!device->setLinkState(pcpp::KniDevice::LINK_UP))
  		EXIT_WITH_ERROR("Can't set KNI device link state to UP");
  	return device;
  }
  
  
  /**
   * Open UDP socket for communication with KNI device
   */
  inline LinuxSocket setupLinuxSocket(const KniPongArgs& args)
  {	// Open socket
  	enum { INVALID_FD = -1 };
  	LinuxSocket sock;
  	sock.m_Socket = INVALID_FD;
  	if ((sock.m_Socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_FD)
  	{
  		int old_errno = errno;
  		EXIT_WITH_ERROR("Could not open socket" << std::endl << "Errno: " << std::strerror(old_errno));
  	}
  	// Bind socket to KNI device IP
  	struct sockaddr_in egress;
  	std::memset(&egress, 0, sizeof(egress));
  	egress.sin_family = AF_INET;
  	egress.sin_addr.s_addr = inet_addr(args.kniIp.c_str());
  	egress.sin_port = pcpp::hostToNet16(args.kniPort);
  	if (bind(sock, (struct sockaddr*)&egress, sizeof(egress)) == -1)
  	{
  		int old_errno = errno;
  		close(sock);
  		EXIT_WITH_ERROR("Could not bind socket" << std::endl << "Errno: " << std::strerror(old_errno));
  	}
  
  	return sock;
  }
  
  
  /**
   * Handle all ARP requests on KNI interface: map all IPs to same MAC
   */
  inline void processArp(pcpp::Packet& packet, pcpp::ArpLayer* arpLayer)
  {
  	pcpp::MacAddress rndMac("00:42:43:74:11:54");
  	pcpp::EthLayer* ethernetLayer = NULL;
  	pcpp::arphdr arpHdr;
  	pcpp::arphdr* origArpHdr = arpLayer->getArpHeader();
  	// Copy ARP request
  	std::memcpy(&arpHdr, origArpHdr, sizeof(arpHdr));
  	// Fill fields
  	arpHdr.hardwareType = pcpp::hostToNet16(0x0001); // ETHERNET
  	arpHdr.hardwareSize = sizeof(((pcpp::arphdr*)0)->senderMacAddr); // sizeof(MAC)
  	arpHdr.protocolSize = sizeof(((pcpp::arphdr*)0)->senderIpAddr);  // sizeof(IPv4)
  	arpHdr.opcode = pcpp::hostToNet16(pcpp::ARP_REPLY);
  	std::memcpy(arpHdr.targetMacAddr, origArpHdr->senderMacAddr, sizeof(((pcpp::arphdr*)0)->senderMacAddr));
  	std::memcpy(&arpHdr.targetIpAddr, &origArpHdr->senderIpAddr, sizeof(((pcpp::arphdr*)0)->senderIpAddr));
  	std::memcpy(&arpHdr.senderIpAddr, &origArpHdr->targetIpAddr, sizeof(((pcpp::arphdr*)0)->senderIpAddr));
  	// Set rnd MAC in response
  	rndMac.copyTo(arpHdr.senderMacAddr);
  	// Copy ready ARP response to packet
  	std::memcpy(origArpHdr, &arpHdr, sizeof(arpHdr));
  
  	// Setup Ethernet addresses in Ethernet layer
  	ethernetLayer = packet.getLayerOfType<pcpp::EthLayer>();
  	pcpp::ether_header ethHdr;
  	pcpp::ether_header* origEthHdr = ethernetLayer->getEthHeader();
  	std::memcpy(&ethHdr, origEthHdr, sizeof(ethHdr));
  	std::memcpy(ethHdr.dstMac, origEthHdr->srcMac, sizeof(ethHdr.dstMac));
  	rndMac.copyTo(ethHdr.srcMac);
  	// Copy ready Ethernet layer to packet
  	std::memcpy(origEthHdr, &ethHdr, sizeof(ethHdr));
  }
  
  
  /**
   * Handle all UDP packets as a packet carying a "ping" string to "pong" to with same string.
   * Handle only packets that are of type: Eth / Ip / Udp / Payload.
   */
  inline bool processUdp(pcpp::Packet& packet, pcpp::UdpLayer* udpLayer)
  {
  	pcpp::EthLayer* ethernetLayer = NULL;
  	pcpp::IPv4Layer* ipLayer = NULL;
  
  	ethernetLayer = packet.getLayerOfType<pcpp::EthLayer>();
  	pcpp::ether_header ethHdr;
  	pcpp::ether_header* origEthHdr = ethernetLayer->getEthHeader();
  	std::memcpy(&ethHdr, origEthHdr, sizeof(ethHdr));
  	// Swap MACs for Ethernet layer
  	std::memcpy(ethHdr.dstMac, origEthHdr->srcMac, sizeof(ethHdr.dstMac));
  	std::memcpy(ethHdr.srcMac, origEthHdr->dstMac, sizeof(ethHdr.srcMac));
  	std::memcpy(origEthHdr, &ethHdr, sizeof(ethHdr));
  
  	ipLayer = packet.getLayerOfType<pcpp::IPv4Layer>();
  	if (ipLayer == NULL) // Some invalid packet
  		return false;
  	pcpp::iphdr ipHdr;
  	pcpp::iphdr* origIpHdr = ipLayer->getIPv4Header();
  	std::memcpy(&ipHdr, origIpHdr, sizeof(ipHdr));
  	if (pcpp::netToHost16(ipHdr.fragmentOffset) & 0x1FFF) // Fragmanted packet
  		return false;
  	// Swap src and dst IPs
  	std::memcpy(&ipHdr.ipSrc, &origIpHdr->ipDst, sizeof(ipHdr.ipSrc));
  	std::memcpy(&ipHdr.ipDst, &origIpHdr->ipSrc, sizeof(ipHdr.ipDst));
  	// Randomize IP id
  	ipHdr.ipId = std::rand() & 0xFFFF;
  	// Set by RFC791
  	ipHdr.timeToLive = 64;
  	std::memcpy(origIpHdr, &ipHdr, sizeof(ipHdr));
  
  	pcpp::udphdr udpHdr;
  	pcpp::udphdr* origUdpHdr = udpLayer->getUdpHeader();
  	std::memcpy(&udpHdr, origUdpHdr, sizeof(udpHdr));
  	// Swap src and dst ports
  	std::memcpy(&udpHdr.portSrc, &origUdpHdr->portDst, sizeof(udpHdr.portSrc));
  	std::memcpy(&udpHdr.portDst, &origUdpHdr->portSrc, sizeof(udpHdr.portDst));
  	std::memcpy(origUdpHdr, &udpHdr, sizeof(udpHdr));
  
  	// Calculate checksums of IP and UDP layers
  	packet.computeCalculateFields();
  	// Packet is ready to be sent
  	return true;
  }
  
  
  /**
   * Process burst of packets
   */
  bool processBurst(pcpp::MBufRawPacket packets[], uint32_t numOfPackets, pcpp::KniDevice* kni, void* cookie)
  {
  	PacketStats* packetStats = (PacketStats*)cookie;
  	pcpp::Packet packet;
  	pcpp::ArpLayer* arpLayer = NULL;
  	pcpp::UdpLayer* udpLayer = NULL;
  
  	packetStats->totalPackets += numOfPackets;
  	for (uint32_t i = 0; i < numOfPackets; ++i)
  	{
  		packet.setRawPacket(packets + i, false);
  		if ((arpLayer = packet.getLayerOfType<pcpp::ArpLayer>()) != NULL)
  		{
  			++packetStats->arpPacketsIn;
  			processArp(packet, arpLayer);
  			// Packet is ready to be sent -> have no fields to recalculate
  			if (!kni->sendPacket(packet))
  				++packetStats->arpPacketsOutFail;
  			arpLayer = NULL;
  			continue;
  		}
  
  		if ((udpLayer = packet.getLayerOfType<pcpp::UdpLayer>()) != NULL)
  		{	
  			++packetStats->udpPacketsIn;
  			//! Warning (echo-Mike): DO NOT normalize next logic statement it relays on short circuiting
  			if (!processUdp(packet, udpLayer) || !kni->sendPacket(packet))
  				++packetStats->udpPacketsOutFail;
  			udpLayer = NULL;
  			continue;
  		}
  
  		// Other packets are just ignored
  	}
  
  	return true;
  }
  
  
  /**
   * Connect UDP socket to other IP:port pair derived from our args
   */
  void connectUDPSocket(const LinuxSocket& sock, const KniPongArgs& args)
  {
  	struct sockaddr_in ingress;
  	std::memset(&ingress, 0, sizeof(ingress));
  	ingress.sin_family = AF_INET;
  	ingress.sin_addr.s_addr = inet_addr(args.outIp.c_str());
  	ingress.sin_port = pcpp::hostToNet16(args.kniPort);
  	if (connect(sock, (struct sockaddr*)&ingress, sizeof(ingress)) == -1)
  	{
  		int old_errno = errno;
  		close(sock);
  		EXIT_WITH_ERROR("Could not connect socket" << std::endl << "Errno: " << std::strerror(old_errno));
  	}
  }
  
  
  /**
   * Reworked fillbuf from netcat. See description in pingPongProcess
   */
  ssize_t fillbuf(linuxFd fd, unsigned char buff[], size_t& buffPos)
  {
  	size_t num = IO_BUFF_SIZE - buffPos;
  	ssize_t n;
  
  	n = read(fd, buff + buffPos, num);
  	if (n == -1 && (errno == EAGAIN || errno == EINTR))
  		n = WANT_POLLIN;
  	if (n <= 0)
  		return n;
  	buffPos += n;
  	return n;
  }
  
  
  /**
   * Reworked drainbuf from netcat. See description in pingPongProcess
   */
  ssize_t drainbuf(linuxFd fd, unsigned char buff[], size_t& buffPos)
  {
  	ssize_t n;
  	ssize_t adjust;
  
  	n = write(fd, buff, buffPos);
  	if (n == -1 && (errno == EAGAIN || errno == EINTR))
  		n = WANT_POLLOUT;
  	if (n <= 0)
  		return n;
  	/* adjust buffer */
  	adjust = buffPos - n;
  	if (adjust > 0)
  		std::memmove(buff, buff + n, adjust);
  	buffPos -= n;
  	return n;
  }
  
  
  /**
   * Reworked readwrite from netcat.
   * 
   * Note (echo-Mike): This function and fillbuf/drainbuf
   * are analogous to code of NETCAT utility (OpenBSD version)
   * Authors of original codebase:
   *  - Eric Jackson <ericj@monkey.org>
   *  - Bob Beck
   *  - *Hobbit* <hobbit@avian.org>
   *  See: http://man7.org/linux/man-pages/man1/ncat.1.html
   */
  void pingPongProcess(const LinuxSocket& sock)
  {
  
  	struct pollfd pfd[4];
  	const int POLL_STDIN = 0, POLL_NETOUT = 1, POLL_NETIN = 2, POLL_STDOUT = 3;
  	const int DEFAULT_POLL_TIMEOUT = 3000;//milisec
  	unsigned char netbuff[IO_BUFF_SIZE];
  	size_t netbuffPos = 0;
  	unsigned char ttybuff[IO_BUFF_SIZE];
  	size_t ttybuffPos = 0;
  	int n, num_fds;
  	ssize_t ret;
  
  	/* stdin */
  	pfd[POLL_STDIN].fd = STDIN_FILENO;
  	pfd[POLL_STDIN].events = POLLIN;
  	/* network out */
  	pfd[POLL_NETOUT].fd = sock;
  	pfd[POLL_NETOUT].events = 0;
  	/* network in */
  	pfd[POLL_NETIN].fd = sock;
  	pfd[POLL_NETIN].events = POLLIN;
  	/* stdout */
  	pfd[POLL_STDOUT].fd = STDOUT_FILENO;
  	pfd[POLL_STDOUT].events = 0;
  
  	while (doContinue)
  	{
  		/* both inputs are gone, buffers are empty, we are done */
  		if (pfd[POLL_STDIN].fd == -1 &&
  			pfd[POLL_NETIN].fd == -1 &&
  			ttybuffPos == 0 &&
  			netbuffPos == 0
  		)
  		{
  			return;
  		}
  		/* both outputs are gone, we can't continue */
  		if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1)
  			return;
  
  		/* poll */
  		num_fds = poll(pfd, 4, DEFAULT_POLL_TIMEOUT);
  
  		/* treat poll errors */
  		if (num_fds == -1)
  		{
  			int old_errno = errno;
  			if (old_errno != EINTR)
  			{
  				close(sock);
  				EXIT_WITH_ERROR("poll returned an error" << std::endl << "Errno: " << std::strerror(old_errno));
  			}
  			continue;
  		}
  
  		if (num_fds == 0)
  		{
  			continue;
  		}
  
  		/* treat socket error conditions */
  		for (n = 0; n < 4; ++n)
  		{
  			if (pfd[n].revents & (POLLERR|POLLNVAL))
  			{
  				pfd[n].fd = -1;
  			}
  		}
  		/* reading is possible after HUP */
  		if (pfd[POLL_STDIN].events & POLLIN &&
  			pfd[POLL_STDIN].revents & POLLHUP &&
  			!(pfd[POLL_STDIN].revents & POLLIN)
  		)
  		{
  			pfd[POLL_STDIN].fd = -1;
  		}
  
  		if (pfd[POLL_NETIN].events & POLLIN &&
  		    pfd[POLL_NETIN].revents & POLLHUP &&
  		    !(pfd[POLL_NETIN].revents & POLLIN)
  		)
  		{
  			pfd[POLL_NETIN].fd = -1;
  		}
  
  		if (pfd[POLL_NETOUT].revents & POLLHUP)
  		{
  			pfd[POLL_NETOUT].fd = -1;
  		}
  		/* if HUP, stop watching stdout */
  		if (pfd[POLL_STDOUT].revents & POLLHUP)
  			pfd[POLL_STDOUT].fd = -1;
  		/* if no net out, stop watching stdin */
  		if (pfd[POLL_NETOUT].fd == -1)
  			pfd[POLL_STDIN].fd = -1;
  		/* if no stdout, stop watching net in */
  		if (pfd[POLL_STDOUT].fd == -1)
  		{
  			if (pfd[POLL_NETIN].fd != -1)
  				shutdown(pfd[POLL_NETIN].fd, SHUT_RD);
  			pfd[POLL_NETIN].fd = -1;
  		}
  
  		/* try to read from stdin */
  		if (pfd[POLL_STDIN].revents & POLLIN && ttybuffPos < IO_BUFF_SIZE)
  		{
  			ret = fillbuf(pfd[POLL_STDIN].fd, ttybuff, ttybuffPos);
  			if (ret == WANT_POLLIN)
  				pfd[POLL_STDIN].events = POLLIN;
  			else if (ret == 0 || ret == -1)
  				pfd[POLL_STDIN].fd = -1;
  			/* read something - poll net out */
  			if (ttybuffPos > 0)
  				pfd[POLL_NETOUT].events = POLLOUT;
  			/* filled buffer - remove self from polling */
  			if (ttybuffPos == IO_BUFF_SIZE)
  				pfd[POLL_STDIN].events = 0;
  		}
  		/* try to write to network */
  		if (pfd[POLL_NETOUT].revents & POLLOUT && ttybuffPos > 0)
  		{
  			ret = drainbuf(pfd[POLL_NETOUT].fd, ttybuff, ttybuffPos);
  			if (ret == WANT_POLLOUT)
  				pfd[POLL_NETOUT].events = POLLOUT;
  			else if (ret == -1)
  				pfd[POLL_NETOUT].fd = -1;
  			/* buffer empty - remove self from polling */
  			if (ttybuffPos == 0)
  				pfd[POLL_NETOUT].events = 0;
  			/* buffer no longer full - poll stdin again */
  			if (ttybuffPos < IO_BUFF_SIZE)
  				pfd[POLL_STDIN].events = POLLIN;
  		}
  		/* try to read from network */
  		if (pfd[POLL_NETIN].revents & POLLIN && netbuffPos < IO_BUFF_SIZE)
  		{
  			ret = fillbuf(pfd[POLL_NETIN].fd, netbuff, netbuffPos);
  			if (ret == WANT_POLLIN)
  				pfd[POLL_NETIN].events = POLLIN;
  			else if (ret == -1)
  				pfd[POLL_NETIN].fd = -1;
  			/* eof on net in - remove from pfd */
  			if (ret == 0)
  			{
  				shutdown(pfd[POLL_NETIN].fd, SHUT_RD);
  				pfd[POLL_NETIN].fd = -1;
  			}
  			/* read something - poll stdout */
  			if (netbuffPos > 0)
  				pfd[POLL_STDOUT].events = POLLOUT;
  			/* filled buffer - remove self from polling */
  			if (netbuffPos == IO_BUFF_SIZE)
  				pfd[POLL_NETIN].events = 0;
  		}
  		/* try to write to stdout */
  		if (pfd[POLL_STDOUT].revents & POLLOUT && netbuffPos > 0)
  		{
  			ret = drainbuf(pfd[POLL_STDOUT].fd, netbuff, netbuffPos);
  			if (ret == WANT_POLLOUT)
  				pfd[POLL_STDOUT].events = POLLOUT;
  			else if (ret == -1)
  				pfd[POLL_STDOUT].fd = -1;
  			/* buffer empty - remove self from polling */
  			if (netbuffPos == 0)
  				pfd[POLL_STDOUT].events = 0;
  			/* buffer no longer full - poll net in again */
  			if (netbuffPos < IO_BUFF_SIZE)
  				pfd[POLL_NETIN].events = POLLIN;
  		}
  
  		/* stdin gone and queue empty? */
  		if (pfd[POLL_STDIN].fd == -1 && ttybuffPos == 0)
  		{
  			pfd[POLL_NETOUT].fd = -1;
  		}
  		/* net in gone and queue empty? */
  		if (pfd[POLL_NETIN].fd == -1 && netbuffPos == 0)
  		{
  			pfd[POLL_STDOUT].fd = -1;
  		}
  	}
  }
  
  } // namespace
  
  
  extern "C" void signal_handler(int)
  {
  	doContinue = false;
  }
  
  
  /**
   * main method of the application
   */
  int main(int argc, char* argv[])
  {
  	PacketStats packetStats;
  	std::memset(&packetStats, 0, sizeof(packetStats));
  	KniPongArgs args;
  	std::srand(std::time(NULL));
  	pcpp::AppName::init(argc, argv);
  	parseArgs(argc, argv, args);
  	pcpp::KniDevice* device = setupKniDevice(args);
  	LinuxSocket sock = setupLinuxSocket(args);
  	if (!device->startCapture(processBurst, &packetStats))
  	{
  		close(sock);
  		EXIT_WITH_ERROR("Could not start capture thread on KNI device");
  	}
  	connectUDPSocket(sock, args);
  	std::signal(SIGINT, signal_handler);
  	std::cout << "Ready for input:" << std::endl;
  	pingPongProcess(sock);
  	//! Close socket before device
  	close(sock);
  	device->stopCapture();
  	device->close();
  	device->stopRequestHandlerThread();
  	std::cout << std::endl << std::endl
  		<< "Packet statistics from KNI thread:" << std::endl
  		<< "  Total packets met:         " << packetStats.totalPackets << std::endl
  		<< "  UDP packets met:           " << packetStats.udpPacketsIn << std::endl
  		<< "  Failed PONG packets:       " << packetStats.udpPacketsOutFail << std::endl
  		<< "  ARP packets met:           " << packetStats.arpPacketsIn << std::endl
  		<< "  Failed ARP replay packets: " << packetStats.arpPacketsOutFail << std::endl
  		<< std::endl;
  	return 0;
  }