static inline void ProcessTCPFlow(FlowSource_t *fs, struct FlowNode *NewNode ) { struct FlowNode *Node; assert(NewNode->memflag == NODE_IN_USE); Node = Insert_Node(NewNode); // Return existing Node if flow exists already, otherwise insert es new if ( Node == NULL ) { // Insert as new dbg_printf("New TCP flow: Packets: %u, Bytes: %u\n", NewNode->packets, NewNode->bytes); // in case it's a FIN/RST only packet - immediately flush it if ( NewNode->fin == FIN_NODE ) { // flush node if ( StorePcapFlow(fs, NewNode) ) { Remove_Node(NewNode); } } if ( !CacheCheck() ) { uint32_t NumFlows; LogError("Node cache exhausted! - Immediate flush - increase flow cache!!"); NumFlows = Flush_FlowTree(fs); LogError("Flushed flows: %u", NumFlows); } if ( Link_RevNode(NewNode)) { // if we could link this new node, it is the server answer // -> calculate server latency SetServer_latency(NewNode); } return; } assert(Node->memflag == NODE_IN_USE); // check for first client ACK for client latency if ( Node->latency.flag == 1 ) { SetClient_latency(Node, &(NewNode->t_first)); } else if ( Node->latency.flag == 2 ) { SetApplication_latency(Node, &(NewNode->t_first)); } // update existing flow Node->flags |= NewNode->flags; Node->packets++; Node->bytes += NewNode->bytes; Node->t_last = NewNode->t_last; dbg_printf("Existing TCP flow: Packets: %u, Bytes: %u\n", Node->packets, Node->bytes); if ( NewNode->fin == FIN_NODE) { // flush node Node->fin = FIN_NODE; if ( StorePcapFlow(fs, Node) ) { Remove_Node(Node); } } else { Free_Node(NewNode); } } // End of ProcessTCPFlow
static inline void ProcessTCPFlow (FlowSource_t *fs, struct FlowNode *NewNode) { struct FlowNode *Node; assert (NewNode->memflag == NODE_IN_USE); Node = Insert_Node (NewNode); // if insert fails, the existing node is returned -> flow exists already if (Node == NULL) { dbg_printf ("New TCP flow: Packets: %u, Bytes: %u\n", NewNode->packets, NewNode->bytes); // in case it's a FIN/RST only packet - immediately flush it if (NewNode->fin == FIN_NODE) { // flush node if (StorePcapFlow (fs, NewNode)) { Remove_Node (NewNode); } } if (!CacheCheck()) { uint32_t NumFlows; LogError ("Node cache exhausted! - Immediate flush - increase flow cache!!"); NumFlows = Flush_FlowTree (fs); LogError ("Flushed flows: %u", NumFlows); } return; } assert (Node->memflag == NODE_IN_USE); // update existing flow Node->flags |= NewNode->flags; Node->packets++; Node->bytes += NewNode->bytes; Node->t_last = NewNode->t_last; dbg_printf ("Existing TCP flow: Packets: %u, Bytes: %u\n", Node->packets, Node->bytes); if (NewNode->fin == FIN_NODE) { // flush node Node->fin = FIN_NODE; if (StorePcapFlow (fs, Node)) { Remove_Node (Node); } } else { Free_Node (NewNode); } } // End of ProcessTCPFlow
__attribute__((noreturn)) static void *p_flow_thread(void *thread_data) { // argument dispatching p_flow_thread_args_t *args = (p_flow_thread_args_t *)thread_data; time_t t_win = args->t_win; int subdir_index = args->subdir_index; int compress = args->compress; FlowSource_t *fs = args->fs; // locals time_t t_start, t_clock, t_udp_flush; int err, done; done = 0; args->done = 0; args->exit = 0; err = pthread_setspecific( buffer_key, (void *)args ); if ( err ) { LogError("[%lu] pthread_setspecific() error in %s line %d: %s\n", (long unsigned)args->tid, __FILE__, __LINE__, strerror(errno) ); args->done = 1; args->exit = 255; pthread_kill(args->parent, SIGUSR1); pthread_exit((void *)args); } if ( !Init_pcap2nf() ) { args->done = 1; args->exit = 255; pthread_kill(args->parent, SIGUSR1); pthread_exit((void *)args); } // prepare file fs->nffile = OpenNewFile(fs->current, NULL, compress, 0, NULL); if ( !fs->nffile ) { args->done = 1; args->exit = 255; pthread_kill(args->parent, SIGUSR1); pthread_exit((void *)args); } fs->xstat = NULL; // init vars fs->bad_packets = 0; fs->first_seen = 0xffffffffffffLL; fs->last_seen = 0; t_start = 0; t_clock = 0; t_udp_flush = 0; while ( 1 ) { struct FlowNode *Node; Node = Pop_Node(args->NodeList, &args->done); if ( Node ) { t_clock = Node->t_last.tv_sec; dbg_printf("p_flow_thread() Next Node\n"); } else { done = args->done; dbg_printf("p_flow_thread() NULL Node\n"); } if ( t_start == 0 ) { t_udp_flush = t_start = t_clock - (t_clock % t_win); } if (((t_clock - t_start) >= t_win) || done) { /* rotate file */ struct tm *when; nffile_t *nffile; char FullName[MAXPATHLEN]; char netflowFname[128]; char error[256]; char *subdir; // flush all flows to disk DumpNodeStat(); uint32_t NumFlows = Flush_FlowTree(fs); when = localtime(&t_start); nffile = fs->nffile; // prepare sub dir hierarchy if ( subdir_index ) { subdir = GetSubDir(when); if ( !subdir ) { // failed to generate subdir path - put flows into base directory LogError("Failed to create subdir path!"); // failed to generate subdir path - put flows into base directory subdir = NULL; snprintf(netflowFname, 127, "nfcapd.%i%02i%02i%02i%02i", when->tm_year + 1900, when->tm_mon + 1, when->tm_mday, when->tm_hour, when->tm_min); } else { snprintf(netflowFname, 127, "%s/nfcapd.%i%02i%02i%02i%02i", subdir, when->tm_year + 1900, when->tm_mon + 1, when->tm_mday, when->tm_hour, when->tm_min); } } else { subdir = NULL; snprintf(netflowFname, 127, "nfcapd.%i%02i%02i%02i%02i", when->tm_year + 1900, when->tm_mon + 1, when->tm_mday, when->tm_hour, when->tm_min); } netflowFname[127] = '\0'; if ( subdir && !SetupSubDir(fs->datadir, subdir, error, 255) ) { // in this case the flows get lost! - the rename will fail // but this should not happen anyway, unless i/o problems, inode problems etc. LogError("Ident: %s, Failed to create sub hier directories: %s", fs->Ident, error ); } if ( nffile->block_header->NumRecords ) { // flush current buffer to disc if ( WriteBlock(nffile) <= 0 ) LogError("Ident: %s, failed to write output buffer to disk: '%s'" , fs->Ident, strerror(errno)); } // else - no new records in current block // prepare full filename snprintf(FullName, MAXPATHLEN-1, "%s/%s", fs->datadir, netflowFname); FullName[MAXPATHLEN-1] = '\0'; // update stat record // if no flows were collected, fs->last_seen is still 0 // set first_seen to start of this time slot, with twin window size. if ( fs->last_seen == 0 ) { fs->first_seen = (uint64_t)1000 * (uint64_t)t_start; fs->last_seen = (uint64_t)1000 * (uint64_t)(t_start + t_win); } nffile->stat_record->first_seen = fs->first_seen/1000; nffile->stat_record->msec_first = fs->first_seen - nffile->stat_record->first_seen*1000; nffile->stat_record->last_seen = fs->last_seen/1000; nffile->stat_record->msec_last = fs->last_seen - nffile->stat_record->last_seen*1000; // Flush Exporter Stat to file FlushExporterStats(fs); // Close file CloseUpdateFile(nffile, fs->Ident); // if rename fails, we are in big trouble, as we need to get rid of the old .current file // otherwise, we will loose flows and can not continue collecting new flows if ( !RenameAppend(fs->current, FullName) ) { LogError("Ident: %s, Can't rename dump file: %s", fs->Ident, strerror(errno)); LogError("Ident: %s, Serious Problem! Fix manually", fs->Ident); /* XXX if ( launcher_pid ) commbuff->failed = 1; */ // we do not update the books here, as the file failed to rename properly // otherwise the books may be wrong } else { struct stat fstat; /* XXX if ( launcher_pid ) commbuff->failed = 0; */ // Update books stat(FullName, &fstat); UpdateBooks(fs->bookkeeper, t_start, 512*fstat.st_blocks); } LogInfo("Ident: '%s' Flows: %llu, Packets: %llu, Bytes: %llu, Max Flows: %u", fs->Ident, (unsigned long long)nffile->stat_record->numflows, (unsigned long long)nffile->stat_record->numpackets, (unsigned long long)nffile->stat_record->numbytes, NumFlows); // reset stats fs->bad_packets = 0; fs->first_seen = 0xffffffffffffLL; fs->last_seen = 0; // Dump all extension maps and exporters to the buffer FlushStdRecords(fs); if ( done ) break; t_start = t_clock - (t_clock % t_win); nffile = OpenNewFile(fs->current, nffile, compress, 0, NULL); if ( !nffile ) { LogError("Fatal: OpenNewFile() failed for ident: %s", fs->Ident); args->done = 1; args->exit = 255; pthread_kill(args->parent, SIGUSR1); break; } } if (((t_clock - t_udp_flush) >= 10) || !done) { /* flush inactive UDP list */ UDPexpire(fs, t_clock - 10 ); t_udp_flush = t_clock; } if ( Node->fin != SIGNAL_NODE ) // Process the Node ProcessFlowNode(fs, Node); } while ( fs ) { DisposeFile(fs->nffile); fs = fs->next; } LogInfo("Terminating flow processng: exit: %i", args->exit); dbg_printf("End flow thread[%lu]\n", (long unsigned)args->tid); pthread_exit((void *)args); /* NOTREACHED */ } // End of p_flow_thread