Exemplo n.º 1
0
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 */
Exemplo n.º 2
0
/**
 * \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);
}