static void run(packet_function_t receive_packet, int socket, send_peer_t peer, time_t twin, time_t t_begin, int report_seq, int use_subdirs, char *time_extension, int compress, int do_xstat) { FlowSource_t *fs; struct sockaddr_storage sf_sender; socklen_t sf_sender_size = sizeof(sf_sender); time_t t_start, t_now; uint64_t export_packets; uint32_t blast_cnt, blast_failures, ignored_packets; ssize_t cnt; void *in_buff; int err; char *string; srecord_t *commbuff; Init_sflow(); in_buff = malloc(NETWORK_INPUT_BUFF_SIZE); if ( !in_buff ) { syslog(LOG_ERR, "malloc() buffer allocation error: %s", strerror(errno)); return; } // init vars commbuff = (srecord_t *)shmem; string = NULL; // Init each netflow source output data buffer fs = FlowSource; while ( fs ) { // prepare file fs->nffile = OpenNewFile(fs->current, NULL, compress, 0, NULL); if ( !fs->nffile ) { return; } if ( do_xstat ) { fs->xstat = InitXStat(fs->nffile); if ( !fs->xstat ) return; } // init stat vars fs->bad_packets = 0; fs->first_seen = (uint64_t)0xffffffffffffLL; fs->last_seen = 0; // next source fs = fs->next; } export_packets = blast_cnt = blast_failures = 0; t_start = t_begin; cnt = 0; ignored_packets = 0; // wake up at least at next time slot (twin) + some Overdue time alarm(t_start + twin + OVERDUE_TIME - time(NULL)); /* * Main processing loop: * this loop, continues until done = 1, set by the signal handler * The while loop will be breaked by the periodic file renaming code * for proper cleanup */ while ( 1 ) { struct timeval tv; /* read next bunch of data into beginn of input buffer */ if ( !done) { #ifdef PCAP // Debug code to read from pcap file // cnt = NextPacket(in_buff, NETWORK_INPUT_BUFF_SIZE); cnt = receive_packet (socket, in_buff, NETWORK_INPUT_BUFF_SIZE , 0, (struct sockaddr *)&sf_sender, &sf_sender_size); if ( cnt == -2 ) done = 1; #else cnt = recvfrom (socket, in_buff, NETWORK_INPUT_BUFF_SIZE , 0, (struct sockaddr *)&sf_sender, &sf_sender_size); #endif if ( cnt == -1 && errno != EINTR ) { syslog(LOG_ERR, "ERROR: recvfrom: %s", strerror(errno)); continue; } if ( peer.hostname ) { ssize_t len; len = sendto(peer.sockfd, in_buff, cnt, 0, (struct sockaddr *)&(peer.addr), peer.addrlen); if ( len < 0 ) { syslog(LOG_ERR, "ERROR: sendto(): %s", strerror(errno)); } } } /* Periodic file renaming, if time limit reached or if we are done. */ gettimeofday(&tv, NULL); t_now = tv.tv_sec; if ( ((t_now - t_start) >= twin) || done ) { char subfilename[64]; struct tm *now; char *subdir, fmt[64]; alarm(0); now = localtime(&t_start); strftime(fmt, sizeof fmt, time_extension, now); // prepare sub dir hierarchy if ( use_subdirs ) { subdir = GetSubDir(now); if ( !subdir ) { // failed to generate subdir path - put flows into base directory syslog(LOG_ERR, "Failed to create subdir path!"); // failed to generate subdir path - put flows into base directory subdir = NULL; snprintf(subfilename, 63, "nfcapd.%s", fmt); } else { snprintf(subfilename, 63, "%s/nfcapd.%s", subdir, fmt); } } else { subdir = NULL; snprintf(subfilename, 63, "nfcapd.%s", fmt); } subfilename[63] = '\0'; // for each flow source update the stats, close the file and re-initialize the new file fs = FlowSource; while ( fs ) { char nfcapd_filename[MAXPATHLEN]; char error[255]; nffile_t *nffile = fs->nffile; if ( verbose ) { // Dump to stdout format_file_block_header(nffile->block_header, &string, 0); printf("%s\n", string); } if ( nffile->block_header->NumRecords ) { // flush current buffer to disc if ( WriteBlock(nffile) <= 0 ) syslog(LOG_ERR, "Ident: %s, failed to write output buffer to disk: '%s'" , fs->Ident, strerror(errno)); } // else - no new records in current block // prepare filename snprintf(nfcapd_filename, MAXPATHLEN-1, "%s/%s", fs->datadir, subfilename); nfcapd_filename[MAXPATHLEN-1] = '\0'; // update stat record 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; if ( fs->xstat ) { if ( WriteExtraBlock(nffile, fs->xstat->block_header ) <= 0 ) syslog(LOG_ERR, "Ident: %s, failed to write xstat buffer to disk: '%s'" , fs->Ident, strerror(errno)); ResetPortHistogram(fs->xstat->port_histogram); ResetBppHistogram(fs->xstat->bpp_histogram); } // Flush Exporter Stat to file FlushExporterStats(fs); // Write Stat record and close file CloseUpdateFile(nffile, fs->Ident); 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. syslog(LOG_ERR, "Ident: %s, Failed to create sub hier directories: %s", fs->Ident, error ); } // 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 err = rename(fs->current, nfcapd_filename); if ( err ) { syslog(LOG_ERR, "Ident: %s, Can't rename dump file: %s", fs->Ident, strerror(errno)); syslog(LOG_ERR, "Ident: %s, Serious Problem! Fix manually", fs->Ident); 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; if ( launcher_pid ) commbuff->failed = 0; // Update books stat(nfcapd_filename, &fstat); UpdateBooks(fs->bookkeeper, t_start, 512*fstat.st_blocks); } // log stats syslog(LOG_INFO,"Ident: '%s' Flows: %llu, Packets: %llu, Bytes: %llu, Sequence Errors: %u, Bad Packets: %u", fs->Ident, (unsigned long long)nffile->stat_record->numflows, (unsigned long long)nffile->stat_record->numpackets, (unsigned long long)nffile->stat_record->numbytes, nffile->stat_record->sequence_failure, fs->bad_packets); // reset stat record fs->bad_packets = 0; fs->first_seen = 0xffffffffffffLL; fs->last_seen = 0; if ( !done ) { fs->nffile = OpenNewFile(fs->current, fs->nffile, compress, 0, NULL); if ( !fs->nffile ) { LogError("killed due to fatal error: ident: %s", fs->Ident); break; } /* XXX needs fixing */ if ( fs->xstat ) { // Add catalog entry } } // Dump all extension maps to the buffer FlushStdRecords(fs); // next flow source fs = fs->next; } // end of while (fs) // All flow sources updated - signal launcher if required if ( launcher_pid ) { // Signal launcher // prepare filename for %f expansion strncpy(commbuff->fname, subfilename, FNAME_SIZE-1); commbuff->fname[FNAME_SIZE-1] = 0; snprintf(commbuff->tstring, 16, "%i%02i%02i%02i%02i", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min); commbuff->tstring[15] = 0; commbuff->tstamp = t_start; if ( subdir ) strncpy(commbuff->subdir, subdir, FNAME_SIZE); else commbuff->subdir[0] = '\0'; if ( launcher_alive ) { syslog(LOG_DEBUG, "Signal launcher"); kill(launcher_pid, SIGHUP); } else syslog(LOG_ERR, "ERROR: Launcher died unexpectedly!"); } syslog(LOG_INFO, "Total ignored packets: %u", ignored_packets); ignored_packets = 0; if ( done ) break; // update alarm for next cycle t_start += twin; /* t_start = filename time stamp: begin of slot * + twin = end of next time interval * + OVERDUE_TIME = if no data is collected, this is at latest to act * - t_now = difference value to now */ alarm(t_start + twin + OVERDUE_TIME - t_now); } /* check for error condition or done . errno may only be EINTR */ if ( cnt < 0 ) { if ( periodic_trigger ) { // alarm triggered, no new flow data periodic_trigger = 0; continue; } if ( done ) // signaled to terminate - exit from loop break; else { /* this should never be executed as it should be caught in other places */ syslog(LOG_ERR, "error condition in '%s', line '%d', cnt: %i", __FILE__, __LINE__ ,(int)cnt); continue; } } /* enough data? */ if ( cnt == 0 ) continue; // get flow source record for current packet, identified by sender IP address fs = GetFlowSource(&sf_sender); if ( fs == NULL ) { syslog(LOG_WARNING, "Skip UDP packet. Ignored packets so far %u packets", ignored_packets); ignored_packets++; continue; } /* check for too little data - cnt must be > 0 at this point */ if ( cnt < sizeof(common_flow_header_t) ) { syslog(LOG_WARNING, "Ident: %s, Data length error: too little data for common netflow header. cnt: %i",fs->Ident, (int)cnt); fs->bad_packets++; continue; } fs->received = tv; /* Process data - have a look at the common header */ Process_sflow(in_buff, cnt, fs); // each Process_xx function has to process the entire input buffer, therefore it's empty now. export_packets++; // flush current buffer to disc if ( fs->nffile->block_header->size > BUFFSIZE ) { // fishy! - we already wrote into someone elses memory! - I'm sorry // reset output buffer - data may be lost, as we don not know, where it happen fs->nffile->block_header->size = 0; fs->nffile->block_header->NumRecords = 0; fs->nffile->buff_ptr = (void *)((pointer_addr_t)fs->nffile->block_header + sizeof(data_block_header_t) ); syslog(LOG_ERR, "### Software bug ### Ident: %s, output buffer overflow: expect memory inconsitency", fs->Ident); } } if ( verbose && blast_failures ) { fprintf(stderr, "Total missed packets: %u\n", blast_failures); } free(in_buff); fs = FlowSource; while ( fs ) { fs->nffile = DisposeFile(fs->nffile); fs = fs->next; } } /* End of run */
/** * \brief Converts packet from Netflow v5/v9 or sFlow format to IPFIX format * * Netflow v9 has almost the same format as ipfix but it has different Flowset IDs * and more informations in packet header. * Netflow v5 doesn't have (Option) Template Sets so they must be inserted into packet * with some other data that are missing (data set header etc.). Template is periodicaly * refreshed according to input_info. * sFlow format is very complicated - InMon Corp. source code is used (modified) * which converts it into Netflow v5 packet. * * \param[out] packet Flow information data in the form of IPFIX packet. * \param[in] len Length of packet * \param[in] input_info Information structure storing data needed for refreshing templates */ void convert_packet(char **packet, ssize_t *len, char *input_info) { struct ipfix_header *header = (struct ipfix_header *) *packet; uint16_t numOfFlowSamples = 0; info_list = (struct input_info_list *) input_info; switch (htons(header->version)) { /* Netflow v9 packet */ case NETFLOW_V9_VERSION: { uint64_t sysUp = ntohl(*((uint32_t *) (((uint8_t *)header) + 4))); uint64_t unSec = ntohl(*((uint32_t *) (((uint8_t *)header) + 8))); uint64_t time_header = (unSec * 1000) - sysUp; memmove(*packet + BYTES_4, *packet + BYTES_8, buff_len - BYTES_8); memset(*packet + buff_len - BYTES_8, 0, BYTES_4); *len -= BYTES_4; header->length = htons(IPFIX_HEADER_LENGTH); header->sequence_number = htonl(seqNo[NF9_SEQ_N]); uint8_t *p = (uint8_t *) (*packet + IPFIX_HEADER_LENGTH); struct ipfix_set_header *set_header; while (p < (uint8_t*) *packet + *len) { set_header = (struct ipfix_set_header*) p; switch (ntohs(set_header->flowset_id)) { case NETFLOW_V9_TEMPLATE_SET_ID: set_header->flowset_id = htons(IPFIX_TEMPLATE_FLOWSET_ID); if (ntohs(set_header->length) > 0) { if (insert_timestamp_template(set_header) != 0) { return; } /* Check for enterprise elements */ *len += unpack_enterprise_elements(set_header, *len - ntohs(header->length)); } break; case NETFLOW_V9_OPT_TEMPLATE_SET_ID: set_header->flowset_id = htons(IPFIX_OPTION_FLOWSET_ID); break; default: if (ntohs(set_header->length) > 0) { uint16_t shifted; shifted = insert_timestamp_data(set_header, time_header, *len - ntohs(header->length)); *len += (shifted * 8); } break; } header->length = htons(ntohs(header->length)+ntohs(set_header->length)); if (ntohs(header->length) > *len) { /* Real length of packet is smaller than it should be */ return; } if (ntohs(set_header->length) == 0) { break; } p += ntohs(set_header->length); } break; } /* Netflow v5 packet */ case NETFLOW_V5_VERSION: { uint64_t sysUp = ntohl(*((uint32_t *) (((uint8_t *)header) + 4))); uint64_t unSec = ntohl(*((uint32_t *) (((uint8_t *)header) + 8))); uint64_t unNsec = ntohl(*((uint32_t *) (((uint8_t *)header) + 12))); uint64_t time_header = (unSec * 1000) + unNsec/1000000; numOfFlowSamples = ntohs(header->length); /* Header modification */ header->export_time = header->sequence_number; memmove(*packet + BYTES_8, *packet + IPFIX_HEADER_LENGTH, buff_len - IPFIX_HEADER_LENGTH); memmove(*packet + BYTES_12, *packet + BYTES_12 + BYTES_1, BYTES_1); header->observation_domain_id = header->observation_domain_id&(0xF000); /* Update real packet length because of memmove() */ *len = *len - BYTES_8; /* We need to resize time element (first and last seen) from 32 bit to 64 bit */ int i; uint8_t *pkt; uint16_t shifted = 0; for (i = numOfFlowSamples - 1; i >= 0; i--) { /* Resize each timestamp in each data record to 64 bit */ pkt = (uint8_t *) (*packet + IPFIX_HEADER_LENGTH + (i * (NETFLOW_V5_DATA_SET_LEN - BYTES_4))); uint64_t first = ntohl(*((uint32_t *) (pkt + FIRST_OFFSET))); uint64_t last = ntohl(*((uint32_t *) (pkt + LAST_OFFSET))); memmove(pkt + LAST_OFFSET + BYTES_8, pkt + LAST_OFFSET, (shifted * (NETFLOW_V5_DATA_SET_LEN + BYTES_4)) + (NETFLOW_V5_DATA_SET_LEN - LAST_OFFSET)); /* Set time values */ *((uint64_t *)(pkt + FIRST_OFFSET)) = htobe64(time_header - (sysUp - first)); *((uint64_t *)(pkt + LAST_OFFSET + BYTES_4)) = htobe64(time_header - (sysUp - last)); shifted++; } /* Set right packet length according to memmoves */ *len += shifted * BYTES_8; /* Template Set insertion (if needed) and setting packet length */ header->length = insert_template_set(packet, numOfFlowSamples, len); header->sequence_number = htonl(seqNo[NF5_SEQ_N]); if (*len >= htons(header->length)) { seqNo[NF5_SEQ_N] += numOfFlowSamples; } break; } /* SFLOW packet (converted to Netflow v5 like packet */ default: /* Conversion from sflow to Netflow v5 like IPFIX packet */ numOfFlowSamples = Process_sflow(*packet, *len); /* Observation domain ID is unknown */ header->observation_domain_id = 0; // ?? header->export_time = htonl((uint32_t) time(NULL)); /* Template Set insertion (if needed) and setting total packet length */ header->length = insert_template_set(packet, numOfFlowSamples, len); header->sequence_number = htonl(seqNo[SF_SEQ_N]); if (*len >= htons(header->length)) { seqNo[SF_SEQ_N] += numOfFlowSamples; } break; } header->version = htons(IPFIX_VERSION); }