packetloss.c 115 KB
Newer Older
1
2
3
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
4
#include <errno.h>
5
6
7
8
#include <signal.h>
#include <string.h>
#include <ncurses.h>
#include <rrd.h>
's avatar
committed
9
#include <netinet/in.h>
10
11
12
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
13
#include <netdb.h>
14
#include <fcntl.h>
's avatar
committed
15
#include <mapi/expiredflowshash.h>
's avatar
committed
16
#include <mapi.h>
's avatar
committed
17
#include "cgi_headers_packetloss.h"
18

19
#define TIMEOUT 500         // time to drop an unmatched flow
20
#define HASHTABLE_SIZE 65536
21
22
#define WITH_NCURSES 0      // set to 1 if you want ncurses, 0 otherwise
#define REFRESH_TIME 10     // 10 seconds base interval with which data will be fed into the RRD
23

24
25
#define SHM_FLOWS_DEVICE_MAX 49999 // (DIMAPI_DATA_SIZE - sizeof(flows_stat) - sizeof(unsigned int)) / sizeof(struct flow_data))
#define SHM_FLOWS_TOTAL_MAX 250000
's avatar
committed
26
#define TIMESTAMP_TOLERANCE 430000000 // NTP 0.1
27
#define MIN(x, y) ((x) < (y) ? (x) : (y)) 
's avatar
committed
28
#define DIFF(x, y) ((x) > (y) ? ((x) - (y)) : ((y) - (x)))
29

30
#define LOGFILE 0           //Log enabled
31
#define LOGFILENAME_FULL "packetloss.full.log" //Log filename
's avatar
committed
32
33
34
#define LOGFILENAME_LOST "packetloss.lost.log"
#define LOGFILENAME_UNMATCHED "packetloss.unmatched.log"

35
36
#define DAEMONIZE 0

37
38
39
struct tstatistics {        // need for network load
	unsigned long long bytes;
	unsigned long long ts;
's avatar
committed
40
};
41

's avatar
committed
42
struct hash_bucket {
43
	struct hash_bucket *next;
's avatar
committed
44
	struct eflow_data data;
45
46
47
	unsigned int hsvalue;
	unsigned int sensor;
	unsigned int sensor2;
's avatar
committed
48
49
	unsigned int d; // XXX
	struct hash_bucket *assigned;
50
51
};

's avatar
committed
52
#define PCT(proto) ((proto.lost) == 0 ? 0 : (float) ((proto.lost) * 100) / (float) (proto.pkts))
53
54
#define ROF(part, base) ((base) == 0 ? 0 : (float) (part) / (float) (base))
#define PCTOF(part, base) (ROF(part, base) * 100)
's avatar
committed
55

56
57
58
59
60
61
62
63
64
65
66
struct tlcstat {
	union {
		unsigned long long pkts;
		unsigned long long count;
	};
	union {
		unsigned long long loss;
		unsigned long long lost;
	};
};

's avatar
committed
67
68
69
70
71
72
73
74
struct tlcmstat {
	unsigned long long loss;
	union {
		unsigned long long count;
		unsigned long long matched;
	};
};

75
struct tcmutstat {
's avatar
committed
76
77
	unsigned int count;
	unsigned int matched;
's avatar
committed
78
	unsigned int unmatched;
's avatar
committed
79
80
81
	unsigned int timeouted;
};

's avatar
committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// Variables for known ports 
struct pstat {
	struct tlcstat mapi;
	struct tlcstat https;
	struct tlcstat sftp;
	struct tlcstat rtsp;
	struct tlcstat http;
	struct tlcstat dns;
	struct tlcstat smtp;
	struct tlcstat openvpn;
	struct tlcstat telnet;
	struct tlcstat ssh;
	struct tlcstat ftp;
};

's avatar
committed
97
struct tstats {
98
	struct tcmutstat totalflows;
's avatar
committed
99
	struct tlcmstat totalpk;
100
	struct tlcmstat totalbytes;
's avatar
committed
101
	struct pstat pstats;
102
103
104
};

struct tlcstat totalpk      = {{0}, {0}};
105
struct tlcstat old_totalpk  = {{0}, {0}};
106
struct tlcstat totalbytes   = {{0}, {0}};
's avatar
committed
107
struct pstat pstats = {{{0}, {0}}, {{0}, {0}}, {{0}, {0}}, {{0}, {0}}, {{0}, {0}}, {{0}, {0}}, {{0}, {0}}, {{0}, {0}}, {{0}, {0}}, {{0}, {0}}, {{0}, {0}}};
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

struct t360stat {
	unsigned long long loss[360];
	unsigned long long count[360];
	unsigned long long pkloss;
	unsigned long long pkcount;
	unsigned long long counter;
};

struct t180stat {
	unsigned long long loss[180];
	unsigned long long count[180];
	unsigned long long pkloss;
	unsigned long long pkcount;
	unsigned long long counter;
};

struct t288stat {
	unsigned long long loss[288];
	unsigned long long count[288];
	unsigned long long pkloss;
	unsigned long long pkcount;
	unsigned long long counter;
};

struct t336stat {
	unsigned long long loss[336];
	unsigned long long count[336];
	unsigned long long pkloss;
	unsigned long long pkcount;
	unsigned long long counter;
};

struct t360stat onehour   = { {}, {}, 0, 0, 0};
struct t360stat onemonth  = { {}, {}, 0, 0, 0};
struct t360stat oneyear   = { {}, {}, 0, 0, 0};
struct t180stat threehour = { {}, {}, 0, 0, 0};
struct t288stat oneday    = { {}, {}, 0, 0, 0};
struct t336stat oneweek   = { {}, {}, 0, 0, 0};

's avatar
committed
148
unsigned long long lastmonth = 400, lastweek = 400;
149

's avatar
committed
150
151
152
// Log files pointers
FILE *fp_log = NULL, *fp_log_lost = NULL, *fp_log_unmatched = NULL;

153
struct hash_bucket *hashtable[HASHTABLE_SIZE];
154

155
156
157
158
159
160
161
162
struct flows_stat_total {
        unsigned long long sent;
        unsigned long long received;
        unsigned long long expired;
        unsigned long long ignored;
        unsigned long long dropped;
};

163
164
165
166
167
168
169
170
struct thost {
	char *name;            // host name from argv
//	struct hostent *ent; // hostent from gethostbyname
//	ip_addr ip;          // ip from gethostbyname
	int devices;         // number of devices at host
	int fd;              // descriptor of a flow opened to this host
	int fid_loss;        // id of a function (EXPIREDR_FLOWS, BPF_FILTER)
	int fid_load;        // id of a function (BYTE_COUNTER)
171
172
173
174
175
176
177
178
179
180
181
	mapi_results_t *results_dev;           // results for devices
	mapi_results_t *results_netload_dev;   // results for devices (netload)
	struct mapi_stat mapi_stats;           // stats (host)
	struct mapi_stat *mapi_stats_dev;      // stats for devices (mapi, order not guaranteed, result of mapi_stat)
	struct mapi_stat **mapi_stats_devptr;  // stats for devices (pointers to mapi_stats_dev -- sorted)
	struct tstatistics *netload_stats_dev; // stats for devices (netload)
	struct tstats stats;            // stats (host)
	struct tstats *stats_dev;       // stats (devices)
	struct tstats *statsrelwith;    // stats (host, in relation  to other hosts)
	struct tstats *statsdirto;      // stats (host, in direction to other hosts)
	struct tstats *statsdirfrom;    // stats (host, in direction from other hosts)
's avatar
committed
182
183
184
	struct tstats *oldstatsrelwith; // old "
	struct tstats *oldstatsdirto;   // old "
	struct tstats *oldstatsdirfrom; // old "
185
186
187
188
	struct flows_stat flowsstats;            // stats (host,    EXPIRED_FLOWS internal)
	struct flows_stat *flowsstats_dev;       // stats (devices, EXPIRED_FLOWS internal)
	struct flows_stat_total flowsstats_total;      // stats (host,    EXPIRED_FLOWS internal)
	struct flows_stat_total *flowsstats_total_dev; // stats (devices, EXPIRED_FLOWS internal)
189
190
191
192
};

struct thost *host; // hosts
int hosts = 0;      // number of hosts (total)
193

194
struct tstats stats; // all hosts stats
's avatar
committed
195

196
197
unsigned int totalflows; // count total # of flows (records) received from hosts/devices

's avatar
committed
198
// Functions declaration
's avatar
committed
199
static void terminate();
's avatar
committed
200
void print_protocols(FILE * fp, char *protocol, unsigned long long total_pkts, unsigned long long total_lost);
's avatar
committed
201
struct hash_bucket *compare_bucket(struct hash_bucket *bucket, struct flow_data *data, unsigned int sensor, int rev, int assigned);
202
unsigned long long cmp_results(struct flow_data *data, unsigned int sensor, unsigned int d);
203
int compare_ip(ip_addr ip1, ip_addr ip2);
204
void drop_old_and_unassigned_flows(void);
's avatar
committed
205
int print_flow(FILE * fp, flow_data * data, unsigned long long pkloss);
206
207
208
209
210
void my_rrd_create_packetloss(char *filename);  // need for packetloss graph
int create_packetloss_cgi();
void my_rrd_update(char *filename, char *data); // need for both graphs
void my_rrd_create_packetcount(char *filename);
int create_packetcount_cgi();
211
void store_results(unsigned int sensor, unsigned int d, unsigned int count, struct flow_data *flowdata);
212

's avatar
committed
213
214
215
216
217
218
219
220
221
222
223
224
225
226
inline unsigned int hash_function(flow_data record) {

	ip_addr saddr = record.saddr, daddr = record.daddr;

	if( (record.ptcl == IPPROTO_TCP) || (record.ptcl == IPPROTO_UDP) ) {
		return(((unsigned int)((saddr.byte1 + saddr.byte2 + saddr.byte3 + saddr.byte4)*record.sport +
					       	(daddr.byte1 + daddr.byte2 + daddr.byte3 + daddr.byte4 + 1)*record.dport))/137);
	}
	else {
		return(((unsigned int)((saddr.byte1 + saddr.byte2 + saddr.byte3 + saddr.byte4) +
					       	(daddr.byte1 + daddr.byte2 + daddr.byte3 + daddr.byte4+1)))/137);
	}
}

227
228
229
230
231
232
int getDevByName(struct thost host, char *dev, int tip) {
	int d = 0;

	// mapi_stats returned devices in expected order?
	if(strcmp(host.mapi_stats_devptr[tip]->dev, dev) == 0)
		return tip;
's avatar
committed
233
	// if not, lookup
234
235
236
237
238
	for(d = 0; d < host.devices; d++)
		if(strcmp(host.mapi_stats_devptr[d]->dev, dev) == 0)
			return d;
	fprintf(stderr, "packetloss: ERROR getDevByName(%s:%s, %s, %d) FAILED\n", host.name, host.mapi_stats_devptr[tip]->dev, dev, tip);
	exit(EXIT_FAILURE);
239
240
}

241
242
243
int nodroppedpkts() {
	int h = 0;
	int d = 0;
244

245
246
247
	for(h = 0; h < hosts; h++) {
		for(d = 0; d < host[h].devices; d++) {
			if(host[h].mapi_stats_devptr[d]->ps_drop != 0 || host[h].mapi_stats_devptr[d]->ps_ifdrop != 0) return 0;
248
249
250
251
252
		}
	}
	return 1;
}

253
254
255
256
char* statstostr() {
	int h = 0;
	int d = 0;

257
258
259
260
	char *toret;
	struct mapi_stat stats;
	
	//XXX fix the size
261
	toret = (char *)malloc(sizeof(char)*2048);
262
263
	toret[0] = '\0';
	
's avatar
committed
264
	for(h = 0; h < hosts; h++) { 
265
266
267
268
269
270
		sprintf(toret, "<center> <br> Sensor %d interfaces stats:<br>", h + 1);
		for(d = 0; d < host[h].devices; d++) {
			stats = host[h].mapi_stats_dev[d];
			sprintf(toret+strlen(toret), "<b>Interface: %s</b><br>received: %u<br>dropped: %u <br>dropped by interface: %u<br>Hostname: %s<br>", stats.dev, stats.ps_recv, stats.ps_drop, stats.ps_ifdrop, host[h].name);
		}
		sprintf(toret+strlen(toret), "</center>");
271
272
273
274
	}
	return(toret);
}

275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
static int continue_as_daemon() {
	int nullfd;

	printf("Closing stdin, stdout, stderr and going into background.\n");

	switch(fork()) {
		case 0: 
			break;
		case -1:
			fprintf(stderr, "ERROR: fork() failed %d - %s", errno, strerror(errno));
			return EXIT_FAILURE;
			break;
		default:
			_exit(0);
			break;
	}
	if(setsid() == -1) {
		fprintf(stderr, "ERROR: setsid() failed %d - %s", errno, strerror(errno));
		return EXIT_FAILURE;
	}

	setpgrp();

	switch(fork()) {
		case 0: 
			break;
		case -1:
			fprintf(stderr, "ERROR: fork() failed %d - %s", errno, strerror(errno));
			return EXIT_FAILURE;
			break;
		default:
			_exit(0);
			break;
	}

	chdir("/");
	
	nullfd = open("/dev/null", O_RDONLY);
	dup2(nullfd, STDIN_FILENO);
	close(nullfd);
	nullfd = open("/dev/null", O_WRONLY);
	dup2(nullfd, STDOUT_FILENO);
	dup2(nullfd, STDERR_FILENO);
	close(nullfd);

	return EXIT_SUCCESS;
}

's avatar
committed
323
324
int main(int argc, char *argv[])
{
325
326
327
328
329
330
331
332
333
334
335
336
337
338
	int i = 0;  // universal counters
	int counter = 0;
	unsigned int count;

	int h = 0;  // host counters
	int h1 = 0;
	int h2 = 0;
	int d = 0;  // device counter

	FILE *fp = NULL;                    // log
	FILE *fp_packetloss = NULL;         // packetloss log
	FILE *fp_packetloss_packets = NULL; // packetcount log

	unsigned int *results;      // number of results/flowdata = (unsigned int *) ...
339
	struct flows_stat *flowsstats;
340
	struct flow_data *flowdata; // result/flowdata            = (struct flow_data *)((char *) ... + sizeof(flows_stat) + sizeof(unsigned int));
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

	unsigned long long pkloss = 0;

	char filter[1024]; // pcap filter

	char *p;
	char *p2;
	char *tmp1 = NULL;
	char *k = NULL;

	// OBSOLETE or TODO ???
	//char load[512];
	//char *nl;

	// GUI - CGI
	char top_page_buf[2048];
	char sample[2][512];       // 0 - packetcount, 1 - packetloss
	char *sensorstats;
	char sensorstatslink[256];

	// GUI - ncurses
	struct tstatistics netload_stats = {0, 0}; // current values of netload stats
	int row, col;
	int maxpad = 13;
's avatar
committed
365
	float speed = 0.0;
366

367
	int devices = 0;   // number of devices (total)
368
369
370
371
372
	//int flows = 0;     // number of flows (total)

	char error[512];
	int  err_no = 0;

's avatar
committed
373
	unsigned int starttime;
374
375
376
377
378
379
380
	unsigned int loop_starttime;
	unsigned int loop_currenttime;
	int loop_busytime = 0;
	int loop_idletime = REFRESH_TIME;
	unsigned int loop_lastidletime = REFRESH_TIME;
	unsigned int uptime = 0;
	bool overload = false;
's avatar
committed
381

382
	unsigned int shm_flows;
's avatar
committed
383
	const unsigned int expired_flows_list_size_max = 0; //1249;
384
385
	const unsigned int packets_count_min = 2;

386
387
388
	signal(SIGINT, terminate);
	signal(SIGQUIT, terminate);
	signal(SIGTERM, terminate);
's avatar
committed
389

's avatar
committed
390
391
	starttime = (unsigned int) time(NULL);

392
393
394
395
	// Check arguments {{{
	if (argc < 4) {
		fprintf(stderr, "\nUsage: %s \"host1:interface1, host1:interface2, ...\" \"hostB:interface1, ...\" ... \"pcap filter\"\n\n", argv[0]);
		exit(EXIT_FAILURE);
396
	}
397
398
399
400
401
402
403
404
405

	hosts = argc - 2;

	for(h = 0; h < hosts; h++) {
		tmp1 = strchr(argv[h + 1], ':');
		if (tmp1 == NULL) {
			fprintf(stderr, "Error!! You must provide an interface (e.g eth0) for each IP.\n");
			exit(EXIT_FAILURE);
		}
406
	}
407
408
409
	// }}}

	// Initialize host {{{
's avatar
committed
410
411
	if((host = (struct thost *) calloc(hosts, sizeof(struct thost))) == NULL) {
		fprintf(stderr, "calloc(): could not allocate memory");
412
		return -1;
413
	}
414
415
	// }}}

416
	// Get host names, ents/IPs, # of devices, shm_flows {{{
417
418
419
420
421
422
423
424
425
426
427
428
429
430
	for(h = 0; h < hosts; h++) {
		// get host name
		tmp1 = strchr(argv[h + 1], ':');
		*tmp1 = '\0';
		host[h].name = strdup(argv[h + 1]);
		*tmp1 = ':';

		// count devices per host + total
		host[h].devices = 0;
		k = strtok(strdup(argv[h + 1]), ", ");
		while (k != NULL) {
			host[h].devices++;
			k = strtok(NULL,", ");
		}
431
		devices += host[h].devices;
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449

		// get host IPs by names
//		if((host[h].ent = gethostbyname(host[h].name)) == NULL) {
//			fprintf(stderr, "Cannot gethostbyname(\"%s\")\n", host[h].name);
//			exit(EXIT_FAILURE);
//		}
//		if(host[h].ent->h_addr == NULL) {
//			fprintf(stderr, "Cannot gethostbyname(\"%s\") - h_addr_list empty\n", host[h].name);
//		}
//		host[h].ip.byte1 = host[h].ent->h_addr[0];
//		host[h].ip.byte2 = host[h].ent->h_addr[1];
//		host[h].ip.byte3 = host[h].ent->h_addr[2];
//		host[h].ip.byte4 = host[h].ent->h_addr[3];

		// count flows/links
		//for(i = h; i > 0; i--) {
		//	flows += 2;
		//}
450
	}
451
452
453
	
	shm_flows = MIN(SHM_FLOWS_TOTAL_MAX / devices, SHM_FLOWS_DEVICE_MAX);
		
454
	// }}}
's avatar
committed
455

456
457
	// Initialize host arrays {{{
	for(h = 0; h < hosts; h++) {
458
459
460
461
462
463
		if((host[h].stats_dev = (struct tstats *) calloc(host[h].devices, sizeof(struct tstats))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
			return -1;
		}
		
		if((host[h].statsrelwith = (struct tstats *) calloc(hosts, sizeof(struct tstats))) == NULL) {
's avatar
committed
464
			fprintf(stderr, "calloc(): could not allocate memory");
465
466
			return -1;
		}
's avatar
test    
committed
467

's avatar
committed
468
469
		if((host[h].statsdirto = (struct tstats *) calloc(hosts, sizeof(struct tstats))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
's avatar
committed
470
471
			return -1;
		}
472

's avatar
committed
473
474
		if((host[h].statsdirfrom = (struct tstats *) calloc(hosts, sizeof(struct tstats))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
475
476
			return -1;
		}
477

's avatar
committed
478
479
		if((host[h].oldstatsrelwith = (struct tstats *) calloc(hosts, sizeof(struct tstats))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
's avatar
committed
480
481
482
			return -1;
		}

's avatar
committed
483
484
		if((host[h].oldstatsdirto = (struct tstats *) calloc(hosts, sizeof(struct tstats))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
's avatar
committed
485
486
487
			return -1;
		}

's avatar
committed
488
489
		if((host[h].oldstatsdirfrom = (struct tstats *) calloc(hosts, sizeof(struct tstats))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
's avatar
committed
490
491
			return -1;
		}
492

's avatar
committed
493
494
		if((host[h].mapi_stats_dev = (struct mapi_stat *) calloc(host[h].devices, sizeof(struct mapi_stat))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
495
496
497
			return -1;
		}
		
's avatar
committed
498
499
		if((host[h].mapi_stats_devptr = (struct mapi_stat **) calloc(host[h].devices, sizeof(struct mapi_stat *))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
500
501
502
503
			return -1;
		}
		for(d = 0; d < host[h].devices; d++) host[h].mapi_stats_devptr[d] = &host[h].mapi_stats_dev[d];

's avatar
committed
504
505
		if((host[h].netload_stats_dev = (struct tstatistics *) calloc(host[h].devices, sizeof(struct tstatistics))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
506
507
508
			return -1;
		}

509
510
511
512
513
514
		if((host[h].flowsstats_dev = (struct flows_stat *) calloc(host[h].devices, sizeof(struct flows_stat))) == NULL) {
			fprintf(stderr, "calloc(): could not allocate memory");
			return -1;
		}
		
		if((host[h].flowsstats_total_dev = (struct flows_stat_total *) calloc(host[h].devices, sizeof(struct flows_stat_total))) == NULL) {
's avatar
committed
515
			fprintf(stderr, "calloc(): could not allocate memory");
516
517
518
			return -1;
		}
		
519
	}
520
	// }}}
's avatar
committed
521

522
523
524
	// Create flows at hosts, build and apply filters, connect {{{
	for(h = 0; h < hosts; h++) {

's avatar
committed
525
526
		fprintf(stdout, "packetloss: mapi_create_flow(%s)\n", argv[h + 1]); fflush(stdout);

527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
		if ((host[h].fd = mapi_create_flow(argv[h + 1])) < 0) {
			fprintf(stderr, "Could not create flow using '%s'\n", argv[h + 1]);
			mapi_read_error(&err_no, error);
			fprintf(stderr, "Errorcode: %d description: %s\n", err_no, error);
			exit(EXIT_FAILURE);
		}

		// build pcap filter
//		sprintf(filter, "host %s and (host ", host[h].name);
//		p = strchr(filter, '\0');
//		h1 = 0;
//		for(h2 = 0; h2 < hosts; h2++) {
//			if(h2 != h) {
//				if(h1 < hosts - 2) sprintf(p, "%s or host ", host[h2].name);
//				else sprintf(p, "%s)", host[h2].name);
//				p = strchr(p, '\0');
//				h1++;
//			}
//		}
		sprintf(filter, argv[argc - 1]);

's avatar
committed
548
		fprintf(stdout, "packetloss: apply BPF_FILTER: \"%s\" at %s\n", filter, host[h].name); fflush(stdout);
549
550
551
552
553
554
555
556

		if ((host[h].fid_loss = mapi_apply_function(host[h].fd, "BPF_FILTER", filter)) < 0) {
				fprintf(stderr, "Count not apply function BPF_FILTER to flow %d\n", host[h].fd);
				mapi_read_error(&err_no, error);
				fprintf(stderr, "Errorcode: %d description: %s\n", err_no, error);
				exit(EXIT_FAILURE);
		}

's avatar
committed
557
558
		fprintf(stdout, "packetloss: apply BYTE_COUNTER at %s\n", host[h].name); fflush(stdout);

559
560
561
562
563
564
565
		if ((host[h].fid_load = mapi_apply_function(host[h].fd, "BYTE_COUNTER")) < 0) {	// need for netload
			fprintf(stderr, "Count not apply function BYTE_COUNTER to flow %d\n", host[h].fd);
			mapi_read_error(&err_no, error);
			fprintf(stderr, "Errorcode :%d description: %s\n", err_no, error);
			exit(EXIT_FAILURE);
		}

's avatar
committed
566
567
		fprintf(stdout, "packetloss: apply EXPIRED_FLOWS at %s\n", host[h].name); fflush(stdout);

568
		if ((host[h].fid_loss = mapi_apply_function(host[h].fd, "EXPIRED_FLOWS", shm_flows, expired_flows_list_size_max, packets_count_min)) < 0) {
569
570
571
572
573
			fprintf(stderr, "Count not apply function EXPIRED_FLOWS to flow %d\n", host[h].fd);
			mapi_read_error(&err_no, error);
			fprintf(stderr, "Errorcode: %d description: %s\n", err_no, error);
			exit(EXIT_FAILURE);
		}
's avatar
committed
574
575
	}

576
577
578
579
580
581
582
	for(h = 0; h < hosts; h++) {
		if (mapi_connect(host[h].fd) < 0) {
			fprintf(stderr, "Could not connect to flow %d\n", host[h].fd);
			mapi_read_error(&err_no, error);
			fprintf(stderr, "Errorcode: %d description: %s\n", err_no, error);
			exit(EXIT_FAILURE);
		}
583
	}
584
	// }}}
's avatar
test    
committed
585

586
	// RRD Files descriptors - init {{{
's avatar
committed
587
	if ((fp_packetloss = fopen(RRD_FILENAME, "r")) == NULL)	// packetloss graph
588
		my_rrd_create_packetloss(RRD_FILENAME);
's avatar
committed
589
	else {
590
		fclose(fp_packetloss);
's avatar
committed
591
	}
592
	if ((fp_packetloss_packets = fopen(RRD_FILENAME_PACKETS, "r")) == NULL)	// packetloss graph
593
		my_rrd_create_packetcount(RRD_FILENAME_PACKETS);
594
595
596
	else {
		fclose(fp_packetloss_packets);
	}
597
	// }}}
's avatar
committed
598

599
	// Log Files descriptors - init {{{
's avatar
committed
600
	if (LOGFILE) {
601
		if ((fp_log = fopen(LOGFILENAME_FULL, "a")) != NULL) {
's avatar
committed
602
			fprintf(stdout, "Writing log to %s...\n", LOGFILENAME_FULL);
's avatar
committed
603
			fprintf(fp_log, "\n\nPacketloss application started at %u\n\n", starttime); fflush(fp_log);
604
		}
's avatar
committed
605
606
607
608
		else {
			fprintf(stderr, "Log file %s couldn't be opened for writing!\nAborting!\n", LOGFILENAME_FULL);
			exit(EXIT_FAILURE);
		}
609
		if (((fp_log_lost = fopen(LOGFILENAME_LOST, "a")) != NULL))	{
's avatar
committed
610
			fprintf(stdout, "Writing log to %s...\n", LOGFILENAME_LOST);
's avatar
committed
611
			fprintf(fp_log_lost, "\n\nPacketloss application started at %u\n\n", starttime); fflush(fp_log);
612
		}
's avatar
committed
613
614
615
616
		else {
			fprintf(stderr, "Log file %s couldn't be opened for writing!\nAborting!\n", LOGFILENAME_LOST);
			exit(EXIT_FAILURE);
		}
617
		if ((fp_log_unmatched = fopen(LOGFILENAME_UNMATCHED, "a")) != NULL)	{
's avatar
committed
618
			fprintf(stdout, "Writing log to %s...\n", LOGFILENAME_UNMATCHED);
's avatar
committed
619
			fprintf(fp_log_unmatched, "\n\nPacketloss application started at %u\n\n", starttime); fflush(fp_log);
620
		}
's avatar
committed
621
622
623
624
		else {
			fprintf(stderr, "Log file %s couldn't be opened for writing!\nAborting!\n", LOGFILENAME_UNMATCHED);
			exit(EXIT_FAILURE);
		}
625
626
	}
	// }}}
's avatar
committed
627

628
629
630
631
	// GUI - ncurses - init {{{
	if (WITH_NCURSES) {
		initscr();				// start curses mode 
		getmaxyx(stdscr, row, col);	// places the current cursor position of the given window in the two integer variables row and col
's avatar
committed
632
	}
633
	// }}}
's avatar
committed
634

635
	// GUI - CGI - init {{{
's avatar
committed
636
	create_packetloss_cgi();  // packetloss graph
637
638
	create_packetcount_cgi(); // packetcount graph
	// }}}
's avatar
committed
639

640
	// Initialize counters {{{
641
	for(counter = 0; counter < 360; counter++) {
642
643
644
		onehour.loss[counter] = onehour.count[counter] = 0;
		onemonth.loss[counter] = onemonth.count[counter] = 0;
		oneyear.loss[counter] = oneyear.count[counter] = 0;
645
		if(counter < 180 )
646
			threehour.loss[counter] = threehour.count[counter] = 0;
647
		if(counter < 288)
648
			oneday.loss[counter] = oneday.count[counter] = 0;
649
		if(counter < 336) 
650
			oneweek.loss[counter] = oneweek.count[counter] = 0;
651
	}
652
	// }}}
653

654
655
	// Initialize netload (all devices) {{{
	// }}}
's avatar
committed
656

657
	// Main loop
's avatar
committed
658

659
660
	if(DAEMONIZE) continue_as_daemon();

661
	while (1) { // Infinite Loop. Forever, report the load and packet loss
's avatar
committed
662

663
664
665
		loop_starttime = (unsigned int) time(NULL);
		uptime = loop_starttime - starttime;

666
		// Read expired_flows results from hosts {{{
667
		for (h = 0; h < hosts; h++) {
's avatar
committed
668
			fprintf(stdout, "packetloss: reading results from %s...\n", host[h].name); fflush(stdout);
669
670
671
672
			host[h].results_dev = (mapi_results_t *) mapi_read_results(host[h].fd, host[h].fid_loss);
			host[h].results_netload_dev = (mapi_results_t *) mapi_read_results(host[h].fd, host[h].fid_load);
		}
		// }}}
673

674
		// Read mapi_stats from hosts {{{
675
		for(h = 0; h < hosts; h++) {
's avatar
committed
676
			fprintf(stdout, "packetloss: reading mapi_stats from %s...\n", host[h].name); fflush(stdout);
677
678
679
680
681
682
			if (mapi_stats(argv[h + 1], host[h].mapi_stats_dev) < 0) {
				fprintf(stderr, "Error in mapi_stats using '%s'\n", argv[h + 1]);
				mapi_read_error(&err_no, error);
				fprintf(stderr,"Errorcode :%d description: %s \n" ,err_no, error);
				return -1;
			}	
683
684
685
686

			host[h].mapi_stats.ps_recv   = 0;
			host[h].mapi_stats.ps_drop   = 0;
			host[h].mapi_stats.ps_ifdrop = 0;
687
688
689
			for(d = 0; d < host[h].devices; d++) {
				i = getDevByName(host[h], host[h].mapi_stats_devptr[d]->dev, d);
				host[h].mapi_stats_devptr[d] = &host[h].mapi_stats_dev[i];
690
691
692
				host[h].mapi_stats.ps_recv   += host[h].mapi_stats_dev[i].ps_recv;
				host[h].mapi_stats.ps_drop   += host[h].mapi_stats_dev[i].ps_drop;
				host[h].mapi_stats.ps_ifdrop += host[h].mapi_stats_dev[i].ps_ifdrop;
693
694
695
			}
		}
		// }}}
's avatar
committed
696

697
		// Store host results in the hashtable {{{
698
		totalflows = 0;
699
		for (h = 0; h < hosts; h++) {
700
701
702
703
704
705
			// initialize flowsstats
			host[h].flowsstats.received = 0;
			host[h].flowsstats.expired  = 0;
			host[h].flowsstats.sent     = 0;
			host[h].flowsstats.ignored  = 0;
			host[h].flowsstats.dropped  = 0;
706
			for (d = 0; d < host[h].devices; d++) {
707
708
709
710
				if(host[h].results_dev[d].res == NULL) {
					fprintf(stdout, "packetloss: ERROR, received NULL result\n"); fflush(stdout);
					exit(EXIT_FAILURE);
				}
711
				results = (unsigned int *) host[h].results_dev[d].res; // number of results/flows at host h, dev d
712
				flowsstats = (struct flows_stat *) host[h].results_dev[d].res;
713
				flowdata = (struct flow_data *)((char *) host[h].results_dev[d].res + sizeof(flows_stat) + sizeof(unsigned int)); // results/flows array
714
				fprintf(stdout, "packetloss: storing and matching results from %s:%d: %d\n", host[h].name, d, *results); fflush(stdout);
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730

				// store flowstats_dev
				memcpy(&host[h].flowsstats_dev[d], flowsstats, sizeof(flows_stat));
				// totalize flowstats_dev to flowsstats_total_dev
				host[h].flowsstats_total_dev[d].received += host[h].flowsstats_dev[d].received;
				host[h].flowsstats_total_dev[d].expired  += host[h].flowsstats_dev[d].expired;
				host[h].flowsstats_total_dev[d].sent     += host[h].flowsstats_dev[d].sent;
				host[h].flowsstats_total_dev[d].ignored  += host[h].flowsstats_dev[d].ignored;
				host[h].flowsstats_total_dev[d].dropped  += host[h].flowsstats_dev[d].dropped;
				// summarize flowsstats
				host[h].flowsstats.received += host[h].flowsstats_dev[d].received;
				host[h].flowsstats.expired  += host[h].flowsstats_dev[d].expired;
				host[h].flowsstats.sent     += host[h].flowsstats_dev[d].sent;
				host[h].flowsstats.ignored  += host[h].flowsstats_dev[d].ignored;
				host[h].flowsstats.dropped  += host[h].flowsstats_dev[d].dropped;

731
				if(*results) {
732
					totalflows += *results;
733
					store_results(h, d, *results, flowdata);
734
				}
's avatar
committed
735
			}
736
737
738
739
740
741
			// totalize flowstats to flowstats_total
			host[h].flowsstats_total.received += host[h].flowsstats.received;
			host[h].flowsstats_total.expired  += host[h].flowsstats.expired;
			host[h].flowsstats_total.sent     += host[h].flowsstats.sent;
			host[h].flowsstats_total.ignored  += host[h].flowsstats.ignored;
			host[h].flowsstats_total.dropped  += host[h].flowsstats.dropped;
742
743
		}
		// }}}
744

745
		// Process results {{{
746
		fprintf(stdout, "packetloss: processing results...\n"); fflush(stdout);
747
748
749
750
751
752
753
754
755
756
757
758
759
		i = 0;
		for (h = 0; h < hosts; h++) {
			for (d = 0; d < host[h].devices; d++, i++) {

				// GUI - ncurses - report network load {{{
				//XXX this is obselete, it should be updated!
				netload_stats.bytes = *((unsigned long long *) host[h].results_netload_dev[d].res);
				netload_stats.ts = host[h].results_netload_dev[d].ts;
				speed = (float) ((netload_stats.bytes - host[h].netload_stats_dev[d].bytes) * 8) / (float) (netload_stats.ts - host[h].netload_stats_dev[d].ts);

				if (WITH_NCURSES) {
					mvprintw(row / 2 + i + 2, 8, "%*s\t %6.2f Mbit/s  %8llu bytes in %1.2lf seconds\n", maxpad, argv[i + 1], speed, (netload_stats.bytes - host[h].netload_stats_dev[d].bytes), (double) (netload_stats.ts - host[h].netload_stats_dev[d].ts) / 1000000.0);
					refresh();		// refresh curses windows and lines
's avatar
committed
760
				}
761
762
763
764
765
				host[h].netload_stats_dev[d].bytes = netload_stats.bytes;
				host[h].netload_stats_dev[d].ts = netload_stats.ts;
				// }}}

				results = (unsigned int *) host[h].results_dev[d].res; // number of results/flows
766
				flowdata = (struct flow_data *) ((char *) host[h].results_dev[d].res + sizeof(flows_stat) + sizeof(unsigned int)); // results/flows array
's avatar
committed
767

768
				for (count = *results; count > 0; count--) {
's avatar
committed
769
					if (LOGFILE) {
's avatar
committed
770
						fprintf(fp_log, "h[%d]i[%d]\t%u\t", h, d, (unsigned int)time(0));
771
772
						print_flow(fp_log, flowdata, 0);
						fflush(fp_log);
's avatar
committed
773
774
					}

775
					if ((pkloss = cmp_results(flowdata, h, d)) != 0) {
776
777
778
779
780
781
782
						if (LOGFILE) {
							fprintf(fp_log_lost, "%u\t",(unsigned int) time(0));
							print_flow(fp_log_lost, flowdata, pkloss);
							fflush(fp_log_lost);
						}

					}
's avatar
committed
783

784
					flowdata++;
785
786

				}
787
788
			}
		}
789
		// }}}
's avatar
test    
committed
790

791
792
		// Network load graph {{{ // OBSOLETE or TODO ???
		/*
793
794
		nl = load;				// need for network load graph
		nl += snprintf(load, 24, "%u", (unsigned int) time(0));
's avatar
committed
795
		nl += snprintf(nl, 24, ":%f", speed);	// left frame
796
797
798
799
800
801
		*/
		// }}}

		// RRD - packetloss - update {{{
		p = sample[0];				// need for packetloss graph
		p += snprintf(sample[0], 24, "%u", (unsigned int) time(0));
802
		if ((totalpk.count - old_totalpk.count == 0) || (totalpk.loss - old_totalpk.loss == 0)) {	// left frame
803
804
805
			for(h = 0; h < hosts * (hosts - 1); h++) {
				p += snprintf(p, 24, ":%f", 0.0);
			}
's avatar
committed
806
		}
807
808
809
		else { // FIXME
			for(h1 = 0; h1 < hosts; h1++) {
				for(h2 = 0; h2 < hosts; h2++) {
810
					if(h2 != h1) p += snprintf(p, 24, ":%f", (float) (h2 > h1 ? -1 : 1) * ((host[h2].statsdirfrom[h1].totalpk.loss - host[h2].oldstatsdirfrom[h1].totalpk.loss) * 100) / (float) (host[h1].statsdirto[h2].totalpk.count - host[h1].oldstatsdirto[h2].totalpk.count));
811
812
				}
			}
813
		}
814
815
		my_rrd_update(RRD_FILENAME, sample[0]);	// update packetloss graph
		// }}}
816

817
818
819
820
821
822
823
824
825
826
827
		// RRD - packetcount - update {{{
		p2 = sample[1];				// need for packetcount graph
		p2 += snprintf(sample[1], 24, "%u", (unsigned int) time(0));
		if ((totalpk.count - old_totalpk.count == 0) ) {
			for(h = 0; h < hosts * (hosts - 1); h++) {
				p2 += snprintf(p2, 24, ":%f", 0.0);
			}
		}
		else { // FIXME
			for(h1 = 0; h1 < hosts; h1++) {
				for(h2 = 0; h2 < hosts; h2++) {
828
					if(h2 != h1) p2 += snprintf(p2, 24, ":%f", (float) (h2 > h1 ? -1 : 1) * (host[h2].statsdirfrom[h1].totalpk.loss - host[h2].oldstatsdirfrom[h1].totalpk.loss));
829
830
831
832
833
				}
			}
		}
		my_rrd_update(RRD_FILENAME_PACKETS, sample[1]);	// update packetloss graph
		// }}}
's avatar
test    
committed
834

835
		// Total packet loss and count - calculations {{{
836
		// One hour packet loss calculation
837
838
839
		onehour.pkloss -= onehour.loss[onehour.counter];                // - old 10s result
		onehour.loss[onehour.counter] = totalpk.loss - old_totalpk.loss; // save new 10s result
		onehour.pkloss += onehour.loss[onehour.counter];                // + new 10s result
840
		// One hour packet count calculation
841
842
843
		onehour.pkcount -= onehour.count[onehour.counter];
		onehour.count[onehour.counter] = totalpk.count - old_totalpk.count;
		onehour.pkcount += onehour.count[onehour.counter];
844

845
		onehour.counter = (onehour.counter+1)%360; // % (3600 / REFRESH_TIME)
846
847
848


		// Three hours
849
		if( onehour.counter % 6 == 0) {
850
			// Three hour packet loss and count calculation
851
852
853
854
			threehour.pkloss -= threehour.loss[threehour.counter];
			threehour.pkcount -= threehour.count[threehour.counter];
			threehour.loss[threehour.counter] = 0;
			threehour.count[threehour.counter] = 0;
855
			for(counter = 0; counter < 6; counter++) {
856
857
				threehour.loss[threehour.counter] += onehour.loss[((onehour.counter+354+counter)%360)];
				threehour.count[threehour.counter] += onehour.count[((onehour.counter+354+counter)%360)];
858
			}
859
860
			threehour.pkloss += threehour.loss[threehour.counter];
			threehour.pkcount += threehour.count[threehour.counter];
861

862
			threehour.counter = (threehour.counter+1)%180;
863
864
865
		}

		// One day
866
		if( onehour.counter % 30 == 0) { 
867
			// One day packet loss and count calculation
868
869
870
871
			oneday.pkloss -= oneday.loss[oneday.counter];
			oneday.pkcount -= oneday.count[oneday.counter];
			oneday.count[oneday.counter] = 0;
			oneday.loss[oneday.counter] = 0;
872
			for(counter = 0; counter < 30; counter++) {
873
874
				oneday.loss[oneday.counter] += onehour.loss[((onehour.counter+330+counter)%360)];
				oneday.count[oneday.counter] += onehour.count[((onehour.counter+330+counter)%360) ];
875
			}
876
877
			oneday.pkloss += oneday.loss[oneday.counter];
			oneday.pkcount += oneday.count[oneday.counter];
878

879
			oneday.counter = (oneday.counter+1)%288;
880
881
882
		}

		// One Week
883
		if( onehour.counter % 180 == 0) { 
884
			// One week packet loss and count calculation
885
886
887
888
			oneweek.pkloss -= oneweek.loss[oneweek.counter];
			oneweek.pkcount -= oneweek.count[oneweek.counter];
			oneweek.count[oneweek.counter] = 0;
			oneweek.loss[oneweek.counter] = 0;
889
			for(counter = 0; counter < 180; counter++) {
890
891
				oneweek.loss[oneweek.counter] += onehour.loss[((onehour.counter+180+counter)%360)];
				oneweek.count[oneweek.counter] += onehour.count[((onehour.counter+180+counter)%360)];
892
			}
893
894
			oneweek.pkloss += oneweek.loss[oneweek.counter];
			oneweek.pkcount += oneweek.count[oneweek.counter];
895

896
			oneweek.counter = (oneweek.counter+1)%336;
897
898
899
		}

		// One Month
900
901
		if( (oneweek.counter % 4 == 0) && (lastweek != oneweek.counter)) { 
			lastweek = oneweek.counter;
902
			// One month packet loss and count calculation
903
904
905
906
			onemonth.pkloss -= onemonth.loss[onemonth.counter];
			onemonth.pkcount -= onemonth.count[onemonth.counter];
			onemonth.count[onemonth.counter] = 0;
			onemonth.loss[onemonth.counter] = 0;
907
			for(counter = 0; counter < 4; counter++) {
908
909
				onemonth.loss[onemonth.counter] += oneweek.loss[((oneweek.counter+332+counter)%336)];
				onemonth.count[onemonth.counter] += oneweek.count[((oneweek.counter+332+counter)%336)];
910
			}
911
912
			onemonth.pkloss += onemonth.loss[onemonth.counter];
			onemonth.pkcount += onemonth.count[onemonth.counter];
's avatar
committed
913
		
914
			onemonth.counter = (onemonth.counter+1)%360;
915
916
917
		}

		// One Year
918
919
		if( (onemonth.counter % 12 == 0) && (lastmonth != onemonth.counter)) { 
			lastmonth = onemonth.counter;
920
			// One year packet loss and count calculation
921
922
923
924
			oneyear.pkloss -= oneyear.loss[oneyear.counter];
			oneyear.pkcount -= oneyear.count[oneyear.counter];
			oneyear.count[oneyear.counter] = 0;
			oneyear.loss[oneyear.counter] = 0;
925
			for(counter = 0; counter < 12; counter++) {
926
927
				oneyear.loss[oneyear.counter] += onemonth.loss[((onemonth.counter+348+counter)%360)];
				oneyear.count[oneyear.counter] += onemonth.count[((onemonth.counter+348+counter)%360)];
928
			}
929
930
			oneyear.pkloss += oneyear.loss[oneyear.counter];
			oneyear.pkcount += oneyear.count[oneyear.counter];
931

932
			oneyear.counter = (oneyear.counter+1)%360;
933
		}
934
		// }}}
935

936
937
		// Total packet loss and count - update old values {{{
		old_totalpk.count = totalpk.count;
938
		old_totalpk.loss = totalpk.loss;
939
940
		for(h1 = 0; h1 < hosts; h1++) {
			for(h2 = 0; h2 < hosts; h2++) {
's avatar
committed
941
				if(h2 != h1) {
942
					host[h1].oldstatsrelwith[h2].totalpk.loss = host[h1].statsrelwith[h2].totalpk.loss;
's avatar
committed
943
944
945
					host[h1].oldstatsdirto[h2].totalpk.loss = host[h1].statsdirto[h2].totalpk.loss;
					host[h1].oldstatsdirfrom[h2].totalpk.loss = host[h1].statsdirfrom[h2].totalpk.loss;
				}
946
947
948
			}
		}
		// }}}
's avatar
test    
committed
949

's avatar
committed
950
951
952
		// remove old unmatched flow from hashtable
		drop_old_and_unassigned_flows();

953
		// GUI - ncurses {{{
's avatar
committed
954
		if (WITH_NCURSES) {
955
			if (totalpk.count == 0) {
's avatar
committed
956
957
				mvprintw(row / 2 - 5, 8, "Total packet loss ratio : 0.00 %% (0 pkts)");
				mvprintw(row / 2 - 4, 8, "Total byte loss ratio : 0.00 %% (0 Kbytes)");
958
959
				mvprintw(row / 2 - 1, 8, "Packet lost from %s : 0 (0.00 %%)", host[0].name);
				mvprintw(row / 2, 8, "Packet lost from %s : 0 (0.00 %%)", host[1].name);
's avatar
committed
960
			} else {
961
962
				mvprintw(row / 2 - 5, 8, "Total packet loss ratio : %.2f %% (%llu pkts)", (float) (totalpk.loss * 100) / (float) totalpk.count, totalpk.loss);
				mvprintw(row / 2 - 4, 8, "Total byte loss ratio : %.2f %% (%.2f Kbytes)", (float) (totalbytes.lost * 100) / (float) totalbytes.count, (float) (totalbytes.lost / (float) 1024));
963
964
				mvprintw(row / 2 - 1, 8, "Total packets lost from %s : %llu (%.2f %%)", host[0].name, host[0].statsdirto[1].totalpk.loss, (float) (host[0].statsdirto[1].totalpk.loss * 100) / (float) totalpk.count);
				mvprintw(row / 2, 8, "Total packets lost from %s : %llu (%.2f %%)", host[1].name, host[1].statsdirto[0].totalpk.loss, (float) (host[1].statsdirto[0].totalpk.loss * 100) / (float) totalpk.count);
's avatar
committed
965
			}
's avatar
committed
966
967
			mvprintw(row / 2 - 3, 8, "Total packets captured at host %s : %llu", host[0].name, host[0].stats.totalpk.count);
			mvprintw(row / 2 - 2, 8, "Total packets captured at host %s : %llu", host[1].name, host[1].stats.totalpk.count);
's avatar
committed
968
			refresh();			// refresh curses windows and lines
's avatar
committed
969
		}
970
		// }}}
's avatar
committed
971

972
		// GUI - CSV - (new) {{{
973
		// Note: if you change this, change gui/lib/packetlossTop.php too
's avatar
committed
974

975
		if ((fp = fopen(TOP_FILENAME_CSV, "w")) != NULL) {
976
			fprintf(stdout, "packetloss: writing data to %s...\n", TOP_FILENAME_CSV);
977
978
979
980
981
		}
		else {
			fprintf(stderr, "CSV file %s couldn't be opened for writing!\nAborting!\n", TOP_FILENAME_CSV);
			exit(EXIT_FAILURE);
		}
982
		
's avatar
committed
983
		// misc basic and hidded {{{
984
985
986
987
988
		fprintf(fp, "*;basic;*;*;*;*;*;starttime;Measurement started at;%d;secs;\n", starttime);
		fprintf(fp, "*;basic;*;*;*;*;*;ts;Data valid to;%d;secs;\n", loop_starttime);
		fprintf(fp, "*;basic;*;*;*;*;*;uptime;Up time;%d;secs;\n", uptime);
		fprintf(fp, "*;basic;*;*;*;*;*;pcap_filter;The filter expression used (pcap filter);\"%s\";text;\n", filter);
		fprintf(fp, "*;basic;*;*;*;*;*;packets_count_min;Ignore flows with less packets than...;%u;text;\n", packets_count_min);
's avatar
committed
989
990
		fprintf(fp, "*;hidden;int_stats;*;*;*;*;shm_flows;Internal buffer size for expired flows at hosts/devices;%u;flows;\n", shm_flows);
		fprintf(fp, "*;hidden;int_stats;*;*;*;*;expired_flows_list_size_max;Internal buffer size for expired flows at hosts/devices (2);%u;flows;\n", expired_flows_list_size_max);