void UDPNetworkSocket::addTarget(const IPv4Address & address) { if (address.isValid()) data->targets.insert(address); }
/** * main method of the application. Responsible for parsing user args, preparing worker thread configuration, creating the worker threads and activate them. * At program termination worker threads are stopped, statistics are collected from them and printed to console */ int main(int argc, char* argv[]) { std::vector<int> dpdkPortVec; bool writePacketsToDisk = false; string packetFilePath = ""; CoreMask coreMaskToUse = getCoreMaskForAllMachineCores(); int sendPacketsToPort = -1; int optionIndex = 0; char opt = 0; uint32_t mBufPoolSize = DEFAULT_MBUF_POOL_SIZE; IPv4Address srcIPToMatch = IPv4Address::Zero; IPv4Address dstIPToMatch = IPv4Address::Zero; uint16_t srcPortToMatch = 0; uint16_t dstPortToMatch = 0; ProtocolType protocolToMatch = Unknown; while((opt = getopt_long (argc, argv, "d:c:s:f:m:i:I:p:P:r:hl", FilterTrafficOptions, &optionIndex)) != -1) { switch (opt) { case 0: { break; } case 'd': { string portListAsString = string(optarg); stringstream stream(portListAsString); string portAsString; int port; // break comma-separated string into string list while(getline(stream, portAsString, ',')) { char c; std::stringstream stream2(portAsString); stream2 >> port; if (stream2.fail() || stream2.get(c)) { // not an integer EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is invalid"); } dpdkPortVec.push_back(port); } // verify list is not empty if (dpdkPortVec.empty()) { EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is empty"); } break; } case 's': { sendPacketsToPort = atoi(optarg); break; } case 'c': { coreMaskToUse = atoi(optarg); break; } case 'f': { packetFilePath = string(optarg); writePacketsToDisk = true; if (packetFilePath.empty()) { EXIT_WITH_ERROR_AND_PRINT_USAGE("Filename to write packets is empty"); } break; } case 'm': { mBufPoolSize = atoi(optarg); 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': { listDpdkPorts(); exit(0); } default: { printUsage(); exit(0); } } } // verify list is not empty if (dpdkPortVec.empty()) { EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is empty. Please use the -d switch"); } // initialize DPDK if (!DpdkDeviceList::initDpdk(coreMaskToUse, mBufPoolSize)) { EXIT_WITH_ERROR("couldn't initialize DPDK"); } // removing DPDK master core from core mask because DPDK worker threads cannot run on master core coreMaskToUse = coreMaskToUse & ~(DpdkDeviceList::getInstance().getDpdkMasterCore().Mask); // extract core vector from core mask vector<SystemCore> coresToUse; createCoreVectorFromCoreMask(coreMaskToUse, coresToUse); // collect the list of DPDK devices vector<DpdkDevice*> dpdkDevicesToUse; for (vector<int>::iterator iter = dpdkPortVec.begin(); iter != dpdkPortVec.end(); iter++) { DpdkDevice* dev = DpdkDeviceList::getInstance().getDeviceByPort(*iter); if (dev == NULL) { EXIT_WITH_ERROR("DPDK device for port %d doesn't exist", *iter); } dpdkDevicesToUse.push_back(dev); } // get DPDK device to send packets to (or NULL if doesn't exist) DpdkDevice* sendPacketsTo = DpdkDeviceList::getInstance().getDeviceByPort(sendPacketsToPort); if (sendPacketsTo != NULL && !sendPacketsTo->open()) { EXIT_WITH_ERROR("Could not open port#%d for sending matched packets", sendPacketsToPort); } // go over all devices and open them for (vector<DpdkDevice*>::iterator iter = dpdkDevicesToUse.begin(); iter != dpdkDevicesToUse.end(); iter++) { if (!(*iter)->openMultiQueues((*iter)->getTotalNumOfRxQueues(), (*iter)->getTotalNumOfTxQueues())) { EXIT_WITH_ERROR("Couldn't open DPDK device #%d, PMD '%s'", (*iter)->getDeviceId(), (*iter)->getPMDName().c_str()); } } // prepare configuration for every core AppWorkerConfig workerConfigArr[coresToUse.size()]; prepareCoreConfiguration(dpdkDevicesToUse, coresToUse, writePacketsToDisk, packetFilePath, sendPacketsTo, workerConfigArr, coresToUse.size()); PacketMatchingEngine matchingEngine(srcIPToMatch, dstIPToMatch, srcPortToMatch, dstPortToMatch, protocolToMatch); // create worker thread for every core vector<DpdkWorkerThread*> workerThreadVec; int i = 0; for (vector<SystemCore>::iterator iter = coresToUse.begin(); iter != coresToUse.end(); iter++) { AppWorkerThread* newWorker = new AppWorkerThread(workerConfigArr[i], matchingEngine); workerThreadVec.push_back(newWorker); i++; } // start all worker threads if (!DpdkDeviceList::getInstance().startDpdkWorkerThreads(coreMaskToUse, workerThreadVec)) { EXIT_WITH_ERROR("Couldn't start worker threads"); } // register the on app close event to print summary stats on app termination FiltetTrafficArgs args; args.workerThreadsVector = &workerThreadVec; ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &args); // infinite loop (until program is terminated) while (!args.shouldStop) { sleep(5); } }
IPv4Address NetworkUtils::getIPv4Address(std::string hostname, PcapLiveDevice* device, double& dnsResponseTimeMS, uint32_t& dnsTTL, int dnsTimeout, IPv4Address dnsServerIP, IPv4Address gatewayIP) { IPv4Address result = IPv4Address::Zero; // open the device if not already opened bool closeDeviceAtTheEnd = false; if (!device->isOpened()) { closeDeviceAtTheEnd = true; if (!device->open()) { LOG_ERROR("Cannot open device"); return result; } } // first - resolve gateway MAC address // if gateway IP wasn't provided - try to find the default gateway if (gatewayIP == IPv4Address::Zero) { gatewayIP = device->getDefaultGateway(); } if (!gatewayIP.isValid() || gatewayIP == IPv4Address::Zero) { LOG_ERROR("Gateway address isn't valid or couldn't find default gateway"); return result; } // send the ARP request to find gateway MAC address double arpResTime; MacAddress gatewayMacAddress = getMacAddress(gatewayIP, device, arpResTime); if (gatewayMacAddress == MacAddress::Zero) { LOG_ERROR("Coulnd't resolve gateway MAC address"); return result; } if (dnsTimeout <= 0) dnsTimeout = NetworkUtils::DefaultTimeout; // validate DNS server IP. If it wasn't provided - set the system-configured DNS server if (dnsServerIP == IPv4Address::Zero && device->getDnsServers().size() > 0) { dnsServerIP = device->getDnsServers().at(0); } if (!dnsServerIP.isValid()) { LOG_ERROR("DNS server IP isn't valid"); return result; } // create DNS request Packet dnsRequest(100); MacAddress sourceMac = device->getMacAddress(); EthLayer ethLayer(sourceMac, gatewayMacAddress, PCPP_ETHERTYPE_IP); IPv4Layer ipLayer(device->getIPv4Address(), dnsServerIP); ipLayer.getIPv4Header()->timeToLive = 128; // randomize source port to a number >= 10000 int srcPortLowest = 10000; int srcPortRange = (2^16) - srcPortLowest; uint16_t srcPort = (rand() % srcPortRange) + srcPortLowest; UdpLayer udpLayer(srcPort, DNS_PORT); // create the DNS request for the hostname DnsLayer dnsLayer; // randomize transaction ID uint16_t transactionID = rand() % (2^16); dnsLayer.getDnsHeader()->transactionID = htons(transactionID); dnsLayer.addQuery(hostname, DNS_TYPE_A, DNS_CLASS_IN); // add all layers to packet if (!dnsRequest.addLayer(ðLayer) || !dnsRequest.addLayer(&ipLayer) || !dnsRequest.addLayer(&udpLayer) || !dnsRequest.addLayer(&dnsLayer)) { LOG_ERROR("Couldn't construct DNS query"); return result; } dnsRequest.computeCalculateFields(); // set a DNS response filter on the device PortFilter dnsResponseFilter(53, SRC); if (!device->setFilter(dnsResponseFilter)) { LOG_ERROR("Couldn't set DNS respnse filter"); return result; } // since packet capture is done on another thread, I use a conditional mutex with timeout to synchronize between the capture // thread and the main thread. When the capture thread starts running the main thread is blocking on the conditional mutex. // When the DNS response are captured the capture thread signals the main thread and the main thread stops capturing and continues // to the next iteration. if a timeout passes and no DNS response is captured, the main thread stops capturing pthread_mutex_t mutex; pthread_cond_t cond; // init the conditonal mutex pthread_mutex_init(&mutex, 0); pthread_cond_init(&cond, 0); // this is the token that passes between the 2 threads DNSRecievedData data = { &mutex, &cond, hostname, transactionID, clock(), IPv4Address::Zero, 0, 0 }; struct timeval now; gettimeofday(&now,NULL); // create the timeout timespec timeout = { now.tv_sec + dnsTimeout, now.tv_usec }; // start capturing. The capture is done on another thread, hence "dnsResponseRecieved" is running on that thread device->startCapture(dnsResponseRecieved, &data); // send the DNS request device->sendPacket(&dnsRequest); pthread_mutex_lock(&mutex); // block on the conditional mutex until capture thread signals or until timeout expires int res = pthread_cond_timedwait(&cond, &mutex, &timeout); // stop the capturing thread device->stopCapture(); pthread_mutex_unlock(&mutex); // check if timeout expired if (res == ETIMEDOUT) { LOG_ERROR("DNS request time out"); return result; } pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); if (closeDeviceAtTheEnd) device->close(); else device->clearFilter(); result = data.result; dnsResponseTimeMS = data.dnsResponseTime; dnsTTL = data.ttl; return result; }
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(); }