void* PfRingDevice::captureThreadMain(void *ptr)
{
	PfRingDevice* device = (PfRingDevice*)ptr;
	int coreId = device->getCurrentCoreId().Id;
	pfring* ring = NULL;

	LOG_DEBUG("Starting capture thread %d", coreId);

	ring = device->m_CoreConfiguration[coreId].Channel;

	if (ring == NULL)
	{
		LOG_ERROR("Couldn't find ring for core %d. Exiting capture thread", coreId);
		return (void*)NULL;
	}

	while (!device->m_StopThread)
	{
		// if buffer is NULL PF_RING avoids copy of the data
		uint8_t* buffer = NULL;
		uint32_t bufferLen = 0;

		// in multi-threaded mode flag PF_RING_REENTRANT is set, and this flag doesn't work with zero copy
		// so I need to allocate a buffer and set buffer to point to it
		if (device->m_ReentrantMode)
		{
			uint8_t tempBuffer[MAX_PACKET_SIZE];
			buffer = tempBuffer;
			bufferLen = MAX_PACKET_SIZE;
		}

		struct pfring_pkthdr pktHdr;
		int recvRes = pfring_recv(ring, &buffer, bufferLen, &pktHdr, 0);
		if (recvRes > 0)
		{
			// if caplen < len it means we don't have the whole packet. Treat this case as packet drop
			// TODO: add this packet to dropped packet stats
//			if (pktHdr.caplen != pktHdr.len)
//			{
//				LOG_ERROR("Packet dropped due to len != caplen");
//				continue;
//			}

			RawPacket rawPacket(buffer, pktHdr.caplen, pktHdr.ts, false);
			device->m_OnPacketsArriveCallback(&rawPacket, 1, coreId, device, device->m_OnPacketsArriveUserCookie);
		}
		else if (recvRes < 0)
		{
			LOG_ERROR("pfring_recv returned an error: [Err=%d]", recvRes);
		}
	}

	LOG_DEBUG("Exiting capture thread %d", coreId);
	return (void*)NULL;
}
Exemple #2
0
int main(int argc, char* argv[])
{
	PfRingDevice* dev = NULL;

	int totalNumOfCores = getNumOfCores();
	int numOfCaptureThreads = totalNumOfCores-1;

	PfRingDevice* sendPacketsToIface = NULL;

	std::string packetFilePath = "";
	bool writePacketsToDisk = true;

	IPv4Address 	srcIPToMatch = IPv4Address::Zero;
	IPv4Address 	dstIPToMatch = IPv4Address::Zero;
	uint16_t 		srcPortToMatch = 0;
	uint16_t 		dstPortToMatch = 0;
	ProtocolType	protocolToMatch = Unknown;

	int optionIndex = 0;
	char opt = 0;

	while((opt = getopt_long (argc, argv, "n:s:t:f:i:I:p:P:r:hl", PfFilterTrafficOptions, &optionIndex)) != -1)
	{
		switch (opt)
		{
			case 0:
			{
				break;
			}
			case 'n':
			{
				std::string ifaceName = std::string(optarg);
				dev = PfRingDeviceList::getInstance().getPfRingDeviceByName(ifaceName);
				if (dev == NULL)
					EXIT_WITH_ERROR("Could not find PF_RING device '%s'", ifaceName.c_str());
				break;
			}
			case 's':
			{
				std::string sendPacketsToIfaceName = std::string(optarg);
				sendPacketsToIface = PfRingDeviceList::getInstance().getPfRingDeviceByName(sendPacketsToIfaceName);
				if (sendPacketsToIface == NULL)
					EXIT_WITH_ERROR("Could not find PF_RING device '%s'", sendPacketsToIfaceName.c_str());

				break;
			}
			case 't':
			{
				numOfCaptureThreads = atoi(optarg);
				if (numOfCaptureThreads < 1 || numOfCaptureThreads > totalNumOfCores-1)
					EXIT_WITH_ERROR("Number of capture threads must be in the range of 1 to %d", totalNumOfCores-1);
				break;
			}
			case 'f':
			{
				packetFilePath = string(optarg);
				// make sure the path ends with '/'
				if (packetFilePath.length() > 1 && (0 != packetFilePath.compare(packetFilePath.length()-1, 1, "/")))
					packetFilePath += "/";

				writePacketsToDisk = true;
				break;
			}
			case 'i':
			{
				srcIPToMatch = IPv4Address(optarg);
				if (!srcIPToMatch.isValid())
				{
					EXIT_WITH_ERROR_AND_PRINT_USAGE("Source IP to match isn't a valid IP address");
				}
				break;
			}
			case 'I':
			{
				dstIPToMatch = IPv4Address(optarg);
				if (!dstIPToMatch.isValid())
				{
					EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination IP to match isn't a valid IP address");
				}
				break;
			}
			case 'p':
			{
				srcPortToMatch = atoi(optarg);
				if (srcPortToMatch <= 0)
				{
					EXIT_WITH_ERROR_AND_PRINT_USAGE("Source port to match isn't a valid TCP/UDP port");
				}
				break;
			}
			case 'P':
			{
				dstPortToMatch = atoi(optarg);
				if (dstPortToMatch <= 0)
				{
					EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination port to match isn't a valid TCP/UDP port");
				}
				break;
			}
			case 'r':
			{
				string protocol = string(optarg);
				if (protocol == "TCP")
					protocolToMatch = TCP;
				else if (protocol == "UDP")
					protocolToMatch = UDP;
				else
				{
					EXIT_WITH_ERROR_AND_PRINT_USAGE("Protocol to match isn't TCP or UDP");
				}
				break;
			}
			case 'h':
			{
				printUsage();
				exit(0);
			}
			case 'l':
			{
				listPfRingDevices();
				exit(0);
			}
			default:
			{
				printUsage();
				exit(0);
			}
		}
	}

	if (dev == NULL)
		EXIT_WITH_ERROR_AND_PRINT_USAGE("Interface name was not provided");

	// open the PF_RING device in multi-thread mode. Distribution of packets between threads will be done per-flow (as opposed to
	// round-robin)
	if (!dev->openMultiRxChannels(numOfCaptureThreads, PfRingDevice::PerFlow))
		EXIT_WITH_ERROR("Couldn't open %d RX channels on interface '%s'", numOfCaptureThreads, dev->getDeviceName().c_str());

	if (sendPacketsToIface != NULL && !sendPacketsToIface->open())
		EXIT_WITH_ERROR("Couldn't open PF_RING device '%s' for sending matched packets", sendPacketsToIface->getDeviceName().c_str());

	CoreMask coreMask = 0;
	int threadId = 0;
	int threadCount = 0;

	// create an array of packet stats with the size of all machine cores
	PacketStats packetStatsArr[totalNumOfCores];

	// init each packet stats instance with an illegal core ID
	for (int coreId = 0; coreId < totalNumOfCores; coreId++)
		packetStatsArr[coreId].ThreadId = MAX_NUM_OF_CORES+1;

	// mark only relevant cores by adding them to core mask
	// mark only relevant packet stats instances by setting their core ID
	while (threadCount < numOfCaptureThreads)
	{
		if (SystemCores::IdToSystemCore[threadId].Id != dev->getCurrentCoreId().Id)
		{
			coreMask |= SystemCores::IdToSystemCore[threadId].Mask;
			packetStatsArr[threadId].ThreadId = SystemCores::IdToSystemCore[threadId].Id;
			threadCount++;
		}

		threadId++;
	}

	// create the matching engine instance
	PacketMatchingEngine matchingEngine(srcIPToMatch, dstIPToMatch, srcPortToMatch, dstPortToMatch, protocolToMatch);

	// create a flow table for each core
	map<uint32_t, bool> flowTables[totalNumOfCores];

	PcapFileWriterDevice** pcapWriters = NULL;

	// if needed, prepare pcap writers for all capturing threads
	if (writePacketsToDisk)
	{
		pcapWriters = new PcapFileWriterDevice*[totalNumOfCores];

		for (int coreId = 0; coreId < totalNumOfCores; coreId++)
		{
			// if core doesn't participate in capturing, skip it
			if ((coreMask & SystemCores::IdToSystemCore[coreId].Mask) == 0)
			{
				pcapWriters[coreId] = NULL;
				continue;
			}

			std::stringstream packetFileName;
			packetFileName << packetFilePath << "Thread" << coreId << ".pcap";
			pcapWriters[coreId] = new PcapFileWriterDevice(packetFileName.str().c_str());
			if (!pcapWriters[coreId]->open())
			{
				EXIT_WITH_ERROR("Couldn't open pcap writer device for core %d", coreId);
			}
		}
	}


	printf("Start capturing on %d threads core mask = 0x%X\n", numOfCaptureThreads, coreMask);

	// prepare packet capture configuration
	CaptureThreadArgs args;
	args.packetStatArr = packetStatsArr;
	args.matchingEngine = &matchingEngine;
	args.flowTables = flowTables;
	args.sendPacketsTo = sendPacketsToIface;
	args.pcapWriters = pcapWriters;

	// start capturing packets on all threads
	if (!dev->startCaptureMultiThread(packetArrived, &args, coreMask))
		EXIT_WITH_ERROR("Couldn't start capturing on core mask %X on interface '%s'", coreMask, dev->getDeviceName().c_str());

	bool shouldStop = false;

	// register the on app close event to print summary stats on app termination
	ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &shouldStop);

	// infinite loop (until program is terminated)
	while (!shouldStop)
	{
		sleep(5);
	}

	// stop capturing packets, close the device
	dev->stopCapture();
	dev->close();

	// close and delete pcap writers
	if (writePacketsToDisk)
	{
		for (int coreId = 0; coreId < totalNumOfCores; coreId++)
		{
			if ((coreMask & SystemCores::IdToSystemCore[coreId].Mask) == 0)
				continue;

			pcapWriters[coreId]->close();
			delete pcapWriters[coreId];
		}
	}

	printf("\n\nApplication stopped\n");

	// print final stats for every capture thread plus sum of all threads and free worker threads memory
	PacketStats aggregatedStats;
	bool printedStatsHeadline = false;
	for (int i = 0; i < totalNumOfCores; i++)
	{
		if (packetStatsArr[i].ThreadId == MAX_NUM_OF_CORES+1)
			continue;

		aggregatedStats.collectStats(packetStatsArr[i]);
		if (!printedStatsHeadline)
		{
			packetStatsArr[i].printStatsHeadline();
			printedStatsHeadline = true;
		}
		packetStatsArr[i].printStats();
	}
	aggregatedStats.printStats();
}