packetloss.c 169 KB
Newer Older
1
#define _GNU_SOURCE // sched.h
2
3
4
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
5
#include <errno.h>
's avatar
committed
6
#include <stdarg.h>
7
#include <signal.h>
's avatar
committed
8
#include <getopt.h>
9
10
#include <string.h>
#include <rrd.h>
's avatar
committed
11
#include <netinet/in.h>
12
13
14
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
15
#include <netdb.h>
16
#include <sched.h>
17
#include <fcntl.h>
's avatar
committed
18
#include <mapi/expiredflowshash.h>
's avatar
committed
19
#include <mapi.h>
's avatar
committed
20
21
22
23
24

//#define NEWGUIONLY

#ifndef NEWGUIONLY
#include <ncurses.h>
's avatar
committed
25
#include "cgi_headers_packetloss.h"
's avatar
committed
26
27
28
29
#endif

#include "pl_misc.h"
#include "pl_conf.h"
30

31
#define TIMEOUT 500         // time to drop an unmatched flow
's avatar
committed
32
#define HASHTABLE_SIZE 131072
33
34
#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
35

's avatar
committed
36
#define SHM_FLOWS_DEVICE_MAX 49998 // (DIMAPI_DATA_SIZE - sizeof(flows_stat) - cca24) / sizeof(struct flow_data))
's avatar
committed
37
#ifndef NEWGUIONLY
38
#define SHM_FLOWS_TOTAL_MAX 250000
's avatar
committed
39
// #define TIMESTAMP_TOLERANCE_JOIN 11600000000 // NTP 2.7
's avatar
committed
40
#define TIMESTAMP_TOLERANCE 430000000 // NTP 0.1
's avatar
committed
41
#endif
42
43
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
's avatar
committed
44
#define DIFF(x, y) ((x) > (y) ? ((x) - (y)) : ((y) - (x)))
's avatar
committed
45
#define ALARM(low, middle, high) ((high) ? 3 : (middle) ? 2 : (low) ? 1 : 0)
46

47
#define LOGFILE 0           //Log enabled
48
#define LOGFILENAME_FULL "packetloss.full.log" //Log filename
's avatar
committed
49
50
51
#define LOGFILENAME_LOST "packetloss.lost.log"
#define LOGFILENAME_UNMATCHED "packetloss.unmatched.log"

52
53
54
55
56
57
58
struct pl_conf *pl_conf;

#ifdef NEWGUIONLY
struct pl_conf *category;
struct pl_conf *parameter;
#endif

's avatar
committed
59
60
61
62
63
struct configuration {
	char *pl_conf_filename;
	char *packetloss_rrd_filename;
	char *packetloss_packets_rrd_filename;
	char *packetloss_flows_rrd_filename;
64
	char *packetloss_matchedflows_rrd_filename;
's avatar
committed
65
66
67
68
69
70
71
72
	char *packetloss_status_filename;
	char *packetloss_starts_filename;
	char *packetloss_startlog_filename;
	int flows_max;
	unsigned long long timestamp_tolerance;
	char *bpf_filter; // bpf filter
	char **flow;
	int still_reconnect; // every loop try to connect?
73
74
75
#ifdef _GNU_SOURCE
	int cpu;
#endif
's avatar
committed
76
77
78
79
80
81
82
83
};

struct configuration *configuration;

unsigned int shm_flows;
const unsigned int expired_flows_list_size_max = 0; //1249;
const unsigned int packets_count_min = 2;

84
int hup = 0;
's avatar
committed
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
int reconnect = 0;
int resetstats = 0;

// packetloss states {{{
#define STATES 15

enum stateid {
	S_GETOPT,
	S_CONFIG,
	S_STARTLOG,
	S_CREATE_RRD,
	S_MAPI_CREATE_FLOW,
	S_MAPI_APPLY_FUNCTION,
	S_MAPI_CONNECT,
	S_STARTS,
	S_LOOP,
	S_MAPI_READ_RESULTS,
	S_STORE_RESULTS,
	S_PROCESS_RESULTS,
	S_STORE_RRD,
	S_STATUSFILE,
	S_IDLE
};
108

's avatar
committed
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
char *statestr[STATES] = {
	"01. Processing command line arguments...\n",
	"02. Processing configuration file: \"%s\" ...\n",
	"03. Writing to startlog file \"%s\" ...\n",
	"04. Creating/opening rrd file \"%s\" ...\n",
	"05. Creating mapi flow \"%s\" ...\n",
	"06. Applying function %s to mapi flow at %s ...\n",
	"07. Connecting to remote flow at %s ...\n",
	"08. Writing to starts file \"%s\" ...\n",
	"09. Entering measurement loop ...\n",
	"10. Retrieving data from remote host %s ...\n",
	"11. Storing data received from %s:%s ...\n",
	"12. Processing data received from %s:%s (%d records)...\n",
	"13. Storing data to rrd \"%s\" ...\n",
	"14. Writing to status file \"%s\" ...\n",
	"15. Idle for %u sec ...\n"
's avatar
committed
125
};
's avatar
committed
126
// }}}
127

's avatar
committed
128
struct hash_bucket {
129
	struct hash_bucket *next;
's avatar
committed
130
	struct eflow_data data;
131
132
133
	unsigned int hsvalue;
	unsigned int sensor;
	unsigned int sensor2;
's avatar
committed
134
135
	unsigned int d; // XXX
	struct hash_bucket *assigned;
136
137
};

's avatar
committed
138
#define PCT(proto) ((proto.lost) == 0 ? 0 : (float) ((proto.lost) * 100) / (float) (proto.pkts))
139
140
#define ROF(part, base) ((base) == 0 ? 0 : (float) (part) / (float) (base))
#define PCTOF(part, base) (ROF(part, base) * 100)
's avatar
committed
141

's avatar
committed
142
143
144
145
146
147
148
// statistics {{{

struct tstatistics {        // need for network load
	unsigned long long bytes;
	unsigned long long ts;
};

149
150
151
152
153
154
155
156
157
158
159
struct tlcstat {
	union {
		unsigned long long pkts;
		unsigned long long count;
	};
	union {
		unsigned long long loss;
		unsigned long long lost;
	};
};

's avatar
committed
160
161
162
163
164
165
166
167
struct tlcmstat {
	unsigned long long loss;
	union {
		unsigned long long count;
		unsigned long long matched;
	};
};

168
struct tcmutstat {
's avatar
committed
169
170
	unsigned int count;
	unsigned int matched;
's avatar
committed
171
	unsigned int unmatched;
's avatar
committed
172
173
174
	unsigned int timeouted;
};

175
// Variables for known ports
's avatar
committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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
190
struct tstats {
191
	struct tcmutstat totalflows;
's avatar
committed
192
	struct tlcmstat totalpk;
193
	struct tlcmstat totalbytes;
's avatar
committed
194
	struct pstat pstats;
195
196
197
};

struct tlcstat totalpk      = {{0}, {0}};
198
struct tlcstat old_totalpk  = {{0}, {0}};
199
struct tlcstat totalbytes   = {{0}, {0}};
's avatar
committed
200
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}}};
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

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
241
unsigned long long lastmonth = 400, lastweek = 400;
242

's avatar
committed
243
244
245
246
247
struct mapi_stat_ull {            // for summarizing mapi_stat (all devices at host)
	unsigned long long ps_recv;
	unsigned long long ps_drop;
	unsigned long long ps_ifdrop;
};
248

's avatar
committed
249
struct flows_stat_total {         // for total flows_stats
250
251
252
253
254
255
256
        unsigned long long sent;
        unsigned long long received;
        unsigned long long expired;
        unsigned long long ignored;
        unsigned long long dropped;
};

's avatar
committed
257
258
259
260
261
262
263
264
265
266
// }}}

// Log files pointers
FILE *fp_log = NULL;
FILE *fp_log_lost = NULL;
FILE *fp_log_unmatched = NULL;
FILE *fp_startlog = NULL;

struct hash_bucket *hashtable[HASHTABLE_SIZE];

267
268
269
270
271
272
273
struct tsubject {
	char *label;
	int host_id_in;
	int host_id_out;
	int enabled;
};

274
struct thost {
275
	int id;
's avatar
committed
276
	char *name;          // host name from argv
277
	int devices;         // number of devices at host
's avatar
committed
278
	char *label;         // label used instead of name for labeling
279
280
281
	char *description;   //
	char *direction;     //
	int enabled;         //
282
283
	int fd;              // descriptor of a flow opened to this host
	int fid_loss;        // id of a function (EXPIREDR_FLOWS, BPF_FILTER)
284
#ifndef NEWGUIONLY
285
	int fid_load;        // id of a function (BYTE_COUNTER)
286
#endif
's avatar
committed
287
	int connected;       // host connected
288
	mapi_results_t *results_dev;           // results for devices
289
#ifndef NEWGUIONLY
290
	mapi_results_t *results_netload_dev;   // results for devices (netload)
291
#endif
's avatar
committed
292
293
	struct mapi_stat_ull mapi_stats;       // mapi stats (host)
	struct mapi_stat *mapi_stats_dev;      // mapi stats for devices (order not guaranteed, result of mapi_stat)
294
	struct mapi_stat **mapi_stats_devptr;  // stats for devices (pointers to mapi_stats_dev -- sorted)
295
#ifndef NEWGUIONLY
296
	struct tstatistics *netload_stats_dev; // stats for devices (netload)
297
#endif
298
299
300
301
302
	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
303
304
305
	struct tstats *oldstatsrelwith; // old "
	struct tstats *oldstatsdirto;   // old "
	struct tstats *oldstatsdirfrom; // old "
306
307
308
309
	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)
310
311
};

312
struct tsubject *subject; // subjects
313
struct thost *host; // hosts
314
int subjects = 0;   // number of subjects (total)
315
int hosts = 0;      // number of hosts (total)
's avatar
committed
316
int devices = 0;    // number of devices (total)
317

318
struct tstats stats; // all hosts stats
's avatar
committed
319

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

's avatar
committed
322
// Functions declaration
's avatar
committed
323
static void terminate();
's avatar
committed
324
void print_protocols(FILE * fp, char *protocol, unsigned long long total_pkts, unsigned long long total_lost);
's avatar
committed
325
struct hash_bucket *compare_bucket(struct hash_bucket *bucket, struct flow_data *data, unsigned int sensor, int rev, int assigned);
326
unsigned long long cmp_results(struct flow_data *data, unsigned int sensor, unsigned int d);
327
int compare_ip(ip_addr ip1, ip_addr ip2);
328
void drop_old_and_unassigned_flows(void);
's avatar
committed
329
int print_flow(FILE * fp, flow_data * data, unsigned long long pkloss);
330
331
332
333
334
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();
's avatar
committed
335
void my_rrd_create_flows(char *filename);
336
void my_rrd_create_matchedflows(char *filename);
337
void store_results(unsigned int sensor, unsigned int d, unsigned int count, struct flow_data *flowdata);
338

's avatar
committed
339
340
341
342
343
344
345
346
347
348
349
350
351
352
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);
	}
}

353
354
355
356
357
358
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
359
	// if not, lookup
360
361
362
363
364
	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);
365
366
}

367
368
369
int nodroppedpkts() {
	int h = 0;
	int d = 0;
370

371
372
373
	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;
374
375
376
377
378
		}
	}
	return 1;
}

379
380
381
382
char* statstostr() {
	int h = 0;
	int d = 0;

383
384
	char *toret;
	struct mapi_stat stats;
385

386
	//XXX fix the size
's avatar
committed
387
	toret = (char *) getcmem(2048, sizeof(char));
388
	toret[0] = '\0';
389
390

	for(h = 0; h < hosts; h++) {
391
392
393
394
395
396
		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>");
397
398
399
400
	}
	return(toret);
}

401
402
403
404
405
406
static int continue_as_daemon() {
	int nullfd;

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

	switch(fork()) {
407
		case 0:
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
			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()) {
425
		case 0:
426
427
428
429
430
431
432
433
434
435
436
			break;
		case -1:
			fprintf(stderr, "ERROR: fork() failed %d - %s", errno, strerror(errno));
			return EXIT_FAILURE;
			break;
		default:
			_exit(0);
			break;
	}

	chdir("/");
437

438
439
440
441
442
443
444
445
446
447
448
	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;
}

449
450
451
452
453
454
455
456
457
458
459
#ifdef _GNU_SOURCE
void taskset_cpu(int cpu) {
	if(cpu >= 0) {
		cpu_set_t mask;
		CPU_ZERO(&mask);
		CPU_SET(cpu, &mask);
		sched_setaffinity(0, sizeof(cpu_set_t), &mask); // glibc 2.3.3 does not include parameter sizeof(...)
	}
}
#endif

's avatar
committed
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
void quiet() {
	int nullfd;
	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);
}

// getopt {{{
int c;

#define PACKETLOSS_OPTIONS 11

enum optionid {
	HELP = 1,   // 0 is reserved for options with flag set
	SHOWCONFIG,
	CHECKCONFIG,
	DAEMON,
	QUIET,
	SILENT,
	CONFIGFILE,
	BPF_FILTER,
	HOST,
	DEVICE
};

int intoptionarr[PACKETLOSS_OPTIONS] = {
			0,  // 0 is reserved for options with flag set
	    0,  // HELP
	    0,  // SHOWCONFIG
	    0,  // CHECKCONFIG
	    0,  // DAEMON
	    0,  // QUIET
	    0,  // SILENT
	    0,  // CONFIGFILE
	    0,  // BPF_FILTER
			0,
			0
};

unsigned int intoptionarrf[PACKETLOSS_OPTIONS] = {0,0,0,0,0};

int option_index = 0;

static struct option long_options[PACKETLOSS_OPTIONS + 2] = {
	{"help",         optional_argument, &intoptionarr[HELP], HELP},
	{"show-config",  optional_argument, &intoptionarr[SHOWCONFIG], SHOWCONFIG},
	{"show-conf",    optional_argument, &intoptionarr[SHOWCONFIG], SHOWCONFIG},
	{"check-config", optional_argument, &intoptionarr[CHECKCONFIG], CHECKCONFIG},
	{"check-conf",   optional_argument, &intoptionarr[CHECKCONFIG], CHECKCONFIG},
	{"daemon",       optional_argument, &intoptionarr[DAEMON], DAEMON},
	{"quiet",        optional_argument, &intoptionarr[QUIET], QUIET},
	{"silent",       optional_argument, &intoptionarr[SILENT], SILENT},
	{"file",         required_argument, NULL, CONFIGFILE},
	{"filter",       required_argument, NULL, BPF_FILTER},
	{"host",         required_argument, NULL, HOST},
	{"interface",    required_argument, NULL, DEVICE},
};
// }}}

// configuration {{{
void configuration_print() {
	int h;
	fprintf(stdout, "\n");
	fprintf(stdout, "Packetloss configuration: %s\n", configuration->pl_conf_filename);
	fprintf(stdout, "\n");
529
530
531
532
	fprintf(stdout, "Subjects: %d\n", subjects);
	for(h = 0; h < subjects; h++)
		fprintf(stdout, "  Subj #%d: (lab: %s, host_id_in: %d, host_id_out: %d, enabled: %d)\n", h, subject[h].label, subject[h].host_id_in, subject[h].host_id_out, subject[h].enabled);
	fprintf(stdout, "\n");
's avatar
committed
533
534
	fprintf(stdout, "Hosts: %d\n", hosts);
	for(h = 0; h < hosts; h++)
535
		fprintf(stdout, "  Host #%d: %s (lab: %s, desc: %s, dir: %s, enabled: %d, devices: %d)\n", h, host[h].name, host[h].label, host[h].description, host[h].direction, host[h].enabled, host[h].devices);
's avatar
committed
536
537
538
539
540
541
542
543
	fprintf(stdout, "\n");
	fprintf(stdout, "Flows: %d\n", hosts);
	for(h = 0; h < hosts; h++)
		fprintf(stdout, "  Flow #%d: %s\n", h, configuration->flow[h]);
	fprintf(stdout, "\n");
	fprintf(stdout, "packetloss_rrd_filename         = %s\n", configuration->packetloss_rrd_filename);
	fprintf(stdout, "packetloss_packets_rrd_filename = %s\n", configuration->packetloss_packets_rrd_filename);
	fprintf(stdout, "packetloss_flows_rrd_filename   = %s\n", configuration->packetloss_flows_rrd_filename);
544
	fprintf(stdout, "packetloss_matchedflows_rrd_filename   = %s\n", configuration->packetloss_matchedflows_rrd_filename);
's avatar
committed
545
546
547
548
549
550
551
552
	fprintf(stdout, "packetloss_status_filename      = %s\n", configuration->packetloss_status_filename);
	fprintf(stdout, "packetloss_starts_filename      = %s\n", configuration->packetloss_starts_filename);
	fprintf(stdout, "packetloss_startlog_filename    = %s\n", configuration->packetloss_startlog_filename);
	fprintf(stdout, "\n");
	fprintf(stdout, "BPF filter: %s\n", configuration->bpf_filter);
	fprintf(stdout, "flows_max           = %d\n", configuration->flows_max);
	fprintf(stdout, "timestamp_tolerance = %llu\n", configuration->timestamp_tolerance);
	fprintf(stdout, "still_reconnect = %d\n", configuration->still_reconnect);
553
554
555
#ifdef _GNU_SOURCE
	fprintf(stdout, "cpu = %d\n", configuration->cpu);
#endif
's avatar
committed
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
	fprintf(stdout, "\n");
	fprintf(stdout, "--daemon = %s\n", intoptionarr[DAEMON] ? "yes" : "no");
}

void configuration_addtoflow(int flow, const char *hostname, const char *device) {
	char *p;
	const char *h;
	const char *d;
	p = configuration->flow[flow];
	h = hostname;
	d = device;
	while(*p != '\0') p++;
	if(p != configuration->flow[flow]) { *p++ = ','; *p++ = ' '; }
	while(*h != '\0') *p++ = *h++;
	*p++ = ':';
	while(*d != '\0') *p++ = *d++;
572
	DEBUG(fprintf(stdout, "packetloss: DEBUG configuration->flow[%d] = %s\n", flow, configuration->flow[flow]); fflush(stdout);)
's avatar
committed
573
}
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624

void config_reload() {
	int h = 0;
	// Get from pl_conf {{{
	pl_conf = pl_conf_load(configuration->pl_conf_filename);
	if((category = pl_conf_get_category(pl_conf, "global")) != NULL) {
		if((parameter = pl_conf_get_parameter(category, "timestamp_tolerance")) != NULL)
			configuration->timestamp_tolerance = (unsigned long long) atoll(parameter->value); // XXX Hope ! NaN
		if((parameter = pl_conf_get_parameter(category, "still_reconnect")) != NULL)
			configuration->still_reconnect = atoi(parameter->value); // XXX Hope ! NaN
	}
	h = 0;
	// first subject
	if((category = pl_conf_get_category(pl_conf, "subject")) != NULL) {
		if((parameter = pl_conf_get_parameter(category, "label")) != NULL)
			{ free(subject[h].label); subject[h].label = strdup(parameter->value); }
		if((parameter = pl_conf_get_parameter(category, "enabled")) != NULL)
			subject[h].enabled = strncmp(parameter->value, "true", 4) == 0 || strncmp(parameter->value, "TRUE", 4) == 0 || strncmp(parameter->value, "True", 4) == 0;
		// other subjects
		while((category = pl_conf_get_next_category(category, "subject")) != NULL) {
			h++;
			if((parameter = pl_conf_get_parameter(category, "label")) != NULL)
				{ free(subject[h].label); subject[h].label = strdup(parameter->value); }
			if((parameter = pl_conf_get_parameter(category, "enabled")) != NULL)
				subject[h].enabled = strncmp(parameter->value, "true", 4) == 0 || strncmp(parameter->value, "TRUE", 4) == 0 || strncmp(parameter->value, "True", 4) == 0;
		}
	}
	h = 0;
	// first host
	if((category = pl_conf_get_category(pl_conf, "host")) != NULL) {
		if((parameter = pl_conf_get_parameter(category, "label")) != NULL)
			{ free(host[h].label); host[h].label = strdup(parameter->value); }
		if((parameter = pl_conf_get_parameter(category, "description")) != NULL)
			{ free(host[h].description); host[h].description = strdup(parameter->value); }
		if((parameter = pl_conf_get_parameter(category, "enabled")) != NULL)
			host[h].enabled = strncmp(parameter->value, "true", 4) == 0 || strncmp(parameter->value, "TRUE", 4) == 0 || strncmp(parameter->value, "True", 4) == 0;
		// other hosts
		while((category = pl_conf_get_next_category(category, "host")) != NULL) {
			h++;
			if((parameter = pl_conf_get_parameter(category, "label")) != NULL)
				{ free(host[h].label); host[h].label = strdup(parameter->value); }
			if((parameter = pl_conf_get_parameter(category, "description")) != NULL)
				{ free(host[h].description); host[h].description = strdup(parameter->value); }
			if((parameter = pl_conf_get_parameter(category, "enabled")) != NULL)
				host[h].enabled = strncmp(parameter->value, "true", 4) == 0 || strncmp(parameter->value, "TRUE", 4) == 0 || strncmp(parameter->value, "True", 4) == 0;
		}
	}
	pl_conf_unload(pl_conf);
	// }}}
}

's avatar
committed
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
// }}}

void state(enum stateid stateid, ...) {
	va_list args;

	va_start(args, stateid);
	vfprintf(stdout, statestr[stateid], args); fflush(stdout);
	va_end(args);

//	if(fp_startlog != NULL) {
//		va_start(args, stateid);
//		vfprintf(fp_startlog, statestr[stateid], args);
//		va_end(args);
//	}
};

void connect_hosts() { // {{{
	int err_no = 0;
	char error[512];
	int h;

	for(h = 0; h < hosts; h++) {
	if(host[h].enabled && !host[h].connected && !mapi_is_sensor_down(host[h].fd)) {

's avatar
committed
649
		if(host[h].fd != -1) { // probably forced reconnection, try to close first
's avatar
committed
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
			fprintf(stdout, "packetloss: close flow to %s\n", host[h].name); fflush(stdout);
			if (mapi_close_flow(host[h].fd) < 0) {
				fprintf(stderr, "packetloss: Close flow failed\n");
				mapi_read_error(&err_no, error);
				fprintf(stderr, "packetloss: Errorcode: %d description: %s\n", err_no, error);
				//exit(EXIT_FAILURE);
			}
			else host[h].fd = -1; // closed
		}

		state(S_MAPI_CREATE_FLOW, configuration->flow[h]);

		if ((host[h].fd = mapi_create_flow(configuration->flow[h])) < 0) {
			fprintf(stderr, "packetloss: Could not create flow using '%s'\n", configuration->flow[h]);
			mapi_read_error(&err_no, error);
			fprintf(stderr, "packetloss: Errorcode: %d description: %s\n", err_no, error);
			//exit(EXIT_FAILURE);
		}
	}
	}

	for(h = 0; h < hosts; h++) {
	if(host[h].enabled && !host[h].connected && host[h].fd >= 0) {

		state(S_MAPI_APPLY_FUNCTION, "BPF_FILTER", host[h].name);

		if ((host[h].fid_loss = mapi_apply_function(host[h].fd, "BPF_FILTER", configuration->bpf_filter)) < 0) {
677
678
679
680
			fprintf(stderr, "packetloss: Count not apply function BPF_FILTER to flow %d\n", host[h].fd);
			mapi_read_error(&err_no, error);
			fprintf(stderr, "packetloss: Errorcode: %d description: %s\n", err_no, error);
			exit(EXIT_FAILURE);
's avatar
committed
681
682
683
684
		}
	}
	}

685
#ifndef NEWGUIONLY
's avatar
committed
686
687
688
689
690
691
692
693
694
695
696
697
698
	for(h = 0; h < hosts; h++) {
	if(host[h].enabled && !host[h].connected && host[h].fd >= 0) {

		state(S_MAPI_APPLY_FUNCTION, "BYTE_COUNTER", host[h].name);

		if ((host[h].fid_load = mapi_apply_function(host[h].fd, "BYTE_COUNTER")) < 0) {	// need for netload
			fprintf(stderr, "packetloss: Count not apply function BYTE_COUNTER to flow %d\n", host[h].fd);
			mapi_read_error(&err_no, error);
			fprintf(stderr, "packetloss: Errorcode :%d description: %s\n", err_no, error);
			exit(EXIT_FAILURE);
		}
	}
	}
699
#endif
's avatar
committed
700

's avatar
committed
701
	for(h = 0; h < hosts; h++) {
's avatar
committed
702
703
704
705
706
707
708
709
710
	if(host[h].enabled && !host[h].connected && host[h].fd >= 0) {

		state(S_MAPI_APPLY_FUNCTION, "EXPIRED_FLOWS", host[h].name);

		if ((host[h].fid_loss = mapi_apply_function(host[h].fd, "EXPIRED_FLOWS", shm_flows, expired_flows_list_size_max, packets_count_min)) < 0) {
			fprintf(stderr, "packetloss: Count not apply function EXPIRED_FLOWS to flow %d\n", host[h].fd);
			mapi_read_error(&err_no, error);
			fprintf(stderr, "packetloss: Errorcode: %d description: %s\n", err_no, error);
			exit(EXIT_FAILURE);
's avatar
committed
711
712
		}
	}
's avatar
committed
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
	}

	for(h = 0; h < hosts; h++) {
	if(host[h].enabled && !host[h].connected && host[h].fd >= 0) {

		state(S_MAPI_CONNECT, host[h].name);

		if (mapi_connect(host[h].fd) < 0) {
			fprintf(stderr, "packetloss: Could not connect to flow %d\n", host[h].fd);
			mapi_read_error(&err_no, error);
			fprintf(stderr, "packetloss: Errorcode: %d description: %s\n", err_no, error);
			//exit(EXIT_FAILURE);
			if (mapi_close_flow(host[h].fd) < 0) {
				fprintf(stderr, "packetloss: Close flow failed\n");
				mapi_read_error(&err_no, error);
				fprintf(stderr, "packetloss: Errorcode: %d description: %s\n", err_no, error);
				exit(EXIT_FAILURE);
			}
			else host[h].fd = -1; // closed
		}
		else {
			host[h].connected = 1;
		}
	}
	}
738
739
740
741
742

	// if configuration changed (e.g. after reloading), set up connected state to safe one
	for(h = 0; h < hosts; h++) {
		if(!host[h].enabled) host[h].connected = 0;
	}
's avatar
committed
743
744
745
746
747
748
749
750
751
752
753
754
755
756
} // }}}

void reset_stats() { // {{{
	int h;
	for(h = 0; h < hosts; h++) {
		memset(&host[h].stats, 0, sizeof(struct tstats));
		memset(host[h].stats_dev, 0, host[h].devices * sizeof(struct tstats));
		memset(host[h].statsrelwith, 0, hosts * sizeof(struct tstats));
		memset(host[h].statsdirto, 0, hosts * sizeof(struct tstats));
		memset(host[h].statsdirfrom, 0, hosts * sizeof(struct tstats));
		memset(host[h].oldstatsrelwith, 0, hosts * sizeof(struct tstats));
		memset(host[h].oldstatsdirto, 0, hosts * sizeof(struct tstats));
		memset(host[h].oldstatsdirfrom, 0, hosts * sizeof(struct tstats));
		memset(host[h].mapi_stats_dev, 0, host[h].devices * sizeof(struct mapi_stat));
757
#ifndef NEWGUIONLY
's avatar
committed
758
		memset(host[h].netload_stats_dev, 0, host[h].devices * sizeof(struct tstatistics));
759
#endif
's avatar
committed
760
761
762
763
764
765
766
767
768
		memset(&host[h].flowsstats, 0, sizeof(struct flows_stat));
		memset(&host[h].flowsstats_total, 0, sizeof(struct flows_stat_total));
		memset(host[h].flowsstats_dev, 0, host[h].devices * sizeof(struct flows_stat));
		memset(host[h].flowsstats_total_dev, 0, host[h].devices * sizeof(struct flows_stat_total));
	}

} // }}}

int usage(int argc, char *argv[]) {
769
	if(argc) {};
's avatar
committed
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
	printf("\n");
	printf("Usage: %s --filter <bpf_filter> [OPTIONS]\n", argv[0]);
	printf("       %s --help\n", argv[0]);
	printf("\n");
	printf("OPTIONS:\n");
	printf("\t--help             show this help and exit.                \n");
	printf("\t--file <conf_file> use <conf_file> as configuration file   \n");
	printf("\t--show-config      show configuration and exit             \n");
	printf("\t--check-config     check configuration and exit            \n");
	printf("\t--filter <filter>  use pcap filter <filter>                \n");
	printf("\t--daemon           run as a daemon (go to background)      \n");
	printf("\t--quiet            no output                               \n");
	printf("\t--silent           silent run (no continuous output)       \n");
	printf("                                                             \n");
	printf("\n");
	printf("EXAMPLES:\n");
	printf("\n");
	printf("       %s --filter \"greater 0\"                                     \n", argv[0]);
	printf("\n");
	return 0;
's avatar
committed
790
791
}

792
793
794
795
796
void sighup() {
	hup = 1;
	fprintf(stdout, "packetloss: SIGHUP, reloading configuration...\n"); fflush(stdout);
}

's avatar
committed
797
798
799
800
801
802
803
804
805
806
807
void sigusr1() {
	reconnect = 1;
	fprintf(stdout, "packetloss: SIGUSR1, reconnect...\n"); fflush(stdout);
}

void sigusr2() {
	resetstats = 1;
	fprintf(stdout, "packetloss: SIGUSR2, reset stats...\n"); fflush(stdout);
}

int main(int argc, char *argv[]) {
808
809
810
811
	int i = 0;  // universal counters
	int counter = 0;
	unsigned int count;

812
813
814
	int s = 0;  // subject counters
	int s1 = 0;
	int s2 = 0;
815
	int h = 0;  // host counters
816
817
	int hi = 0;
	int ho = 0;
818
819
	int h1 = 0;
	int h2 = 0;
820
821
822
823
	int h1o = 0;
	int h1i = 0;
	int h2o = 0;
	int h2i = 0;
824
825
826
827
	int d = 0;  // device counter

	FILE *fp = NULL;                    // log
	FILE *fp_packetloss = NULL;         // packetloss log
's avatar
committed
828
829
830
	FILE *fp_packetloss_packets = NULL; // packetloss_packets log
#ifdef NEWGUIONLY
	FILE *fp_packetloss_flows = NULL; // packetloss_packets log
831
	FILE *fp_packetloss_matchedflows = NULL; // packetloss_packets log
's avatar
committed
832
#endif
833
	unsigned int *results;      // number of results/flowdata = (unsigned int *) ...
834
	struct flows_stat *flowsstats;
's avatar
committed
835
	struct flow_data *flowdata; // result/flowdata            = (struct flow_data *)((char *) ... + sizeof(flows_stat));
836
837
838
839
840

	unsigned long long pkloss = 0;

	char *p;
	char *p2;
's avatar
committed
841
#ifndef NEWGUIONLY
842
843
	char *tmp1 = NULL;
	char *k = NULL;
's avatar
committed
844
#endif
845
846
847
848
849

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

's avatar
committed
850
851
852
853
	// RRD
	char sample[2][512];       // 0 - packetloss_packets, 1 - packetloss

#ifndef NEWGUIONLY
854
855
856
857
	// GUI - CGI
	char top_page_buf[2048];
	char *sensorstats;
	char sensorstatslink[256];
's avatar
committed
858
#endif
859

's avatar
committed
860
#ifndef NEWGUIONLY
861
862
863
864
	// GUI - ncurses
	struct tstatistics netload_stats = {0, 0}; // current values of netload stats
	int row, col;
	int maxpad = 13;
's avatar
committed
865
	float speed = 0.0;
's avatar
committed
866
#endif
867
868
869
870
871
872

	//int flows = 0;     // number of flows (total)

	char error[512];
	int  err_no = 0;

's avatar
committed
873
	unsigned int starttime;
874
875
876
877
878
879
	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;
's avatar
committed
880
881
	int overload = 0;

882
883
884
	signal(SIGINT, terminate);
	signal(SIGQUIT, terminate);
	signal(SIGTERM, terminate);
885
	signal(SIGHUP, sighup);
's avatar
committed
886
887
	signal(SIGUSR1, sigusr1);
	signal(SIGUSR2, sigusr2);
's avatar
committed
888

's avatar
committed
889
890
	starttime = (unsigned int) time(NULL);

's avatar
committed
891
	configuration = getmem(sizeof(struct configuration));
892
893
894
#ifdef _GNU_SOURCE
	configuration->cpu = -1;
#endif
's avatar
committed
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982

	// Options - parse {{{
	state(S_GETOPT);
	c = getopt_long(argc, argv, "", long_options, &option_index);
	while (c != -1) {
		switch (c) {
			case 0: // option with flag set
				if (long_options[option_index].flag != NULL) break; // done by getopt
				printf("packetloss: WARNING: invalid option %s", long_options[option_index].name);
				if (optarg) printf (" with arg %s", optarg);
				printf("\n");
				break;
			case BPF_FILTER:
				if(optarg) {
					if(strlen(optarg) > MAPI_STR_LENGTH - 1) {
						fprintf(stderr, "packetloss: Error: invalid bpf filter (too long) \"%s\"\n", optarg);
						exit(EXIT_FAILURE);
					}
					if(configuration->bpf_filter != NULL) free(configuration->bpf_filter);
					configuration->bpf_filter = strdup(optarg);
					intoptionarrf[c] = 1;
				}
				else {
					fprintf(stderr, "packetloss: Error: invalid bpf filter (none?)\n");
					exit(EXIT_FAILURE);
				}
				break;
			case CONFIGFILE:
				if(optarg) {
					if(configuration->pl_conf_filename != NULL) free(configuration->pl_conf_filename);
					configuration->pl_conf_filename = strdup(optarg);
				}
				else {
					fprintf(stderr, "packetloss: Error: invalid config filename (none?)\n");
					exit(EXIT_FAILURE);
				}
				break;
			case HELP:   // all options where typeof(option) == int
			case SHOWCONFIG:
			case CHECKCONFIG:
			case DAEMON:
			case QUIET:
			case SILENT:
				if (optarg) {
						intoptionarr[c] = atoi(optarg); // store value, hope ! NaN
						intoptionarrf[c] = 1; // flag user changes
				}
				break;
			case '?':
				// done by getopt
				break;
			default:
				abort();
				break;
		}
		c = getopt_long (argc, argv, "", long_options, &option_index);
	}
	// }}}
	// Options - process {{{
	if(intoptionarr[HELP]) {
		usage(argc, argv);
		exit(EXIT_SUCCESS);
	}

	if (optind < argc - 1) {
		while (optind < argc - 1) printf ("Warning: Unrecognized argument: %s\n", argv[optind++]);
	}

	if(intoptionarr[QUIET]) quiet();

	if(configuration->pl_conf_filename == NULL) configuration->pl_conf_filename = strdup("etc/packetloss.conf");
	// }}}

	// Configuration file - parse {{{
	state(S_CONFIG, configuration->pl_conf_filename);
	pl_conf = pl_conf_load(configuration->pl_conf_filename);
	// }}}
	// Configuration file - store {{{
#ifdef NEWGUIONLY
	// Get from pl_conf {{{
	if(pl_conf) {
		if((category = pl_conf_get_category(pl_conf, "global")) != NULL) {
			if((parameter = pl_conf_get_parameter(category, "packetloss_rrd_filename")) != NULL)
				configuration->packetloss_rrd_filename = strdup(parameter->value);
			if((parameter = pl_conf_get_parameter(category, "packetloss_packets_rrd_filename")) != NULL)
				configuration->packetloss_packets_rrd_filename = strdup(parameter->value);
			if((parameter = pl_conf_get_parameter(category, "packetloss_flows_rrd_filename")) != NULL)
				configuration->packetloss_flows_rrd_filename = strdup(parameter->value);
983
984
			if((parameter = pl_conf_get_parameter(category, "packetloss_matchedflows_rrd_filename")) != NULL)
				configuration->packetloss_matchedflows_rrd_filename = strdup(parameter->value);
's avatar
committed
985
986
987
988
989
990
991
992
993
994
995
996
			if((parameter = pl_conf_get_parameter(category, "packetloss_status_filename")) != NULL)
				configuration->packetloss_status_filename = strdup(parameter->value);
			if((parameter = pl_conf_get_parameter(category, "packetloss_starts_filename")) != NULL)
				configuration->packetloss_starts_filename = strdup(parameter->value);
			if((parameter = pl_conf_get_parameter(category, "packetloss_startlog_filename")) != NULL)
				configuration->packetloss_startlog_filename = strdup(parameter->value);
			if((parameter = pl_conf_get_parameter(category, "flows_max")) != NULL)
				configuration->flows_max = atoi(parameter->value); // XXX Hope ! NaN
			if((parameter = pl_conf_get_parameter(category, "timestamp_tolerance")) != NULL)
				configuration->timestamp_tolerance = (unsigned long long) atoll(parameter->value); // XXX Hope ! NaN
			if((parameter = pl_conf_get_parameter(category, "still_reconnect")) != NULL)
				configuration->still_reconnect = atoi(parameter->value); // XXX Hope ! NaN
997
998
999
1000
#ifdef _GNU_SOURCE
			if((parameter = pl_conf_get_parameter(category, "cpu")) != NULL)
				configuration->cpu = atoi(parameter->value); // XXX Hope ! NaN
#endif