1 /*
2  * Copyright (C) 2005-7, Hugo Santos <hugo@fivebits.net>
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * $Id: dbeacon.cpp 409 2007-07-13 13:52:14Z hugo $
14  */
15 
16 #include "dbeacon.h"
17 #include "address.h"
18 #include "msocket.h"
19 #include "protocol.h"
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdarg.h>
25 #include <sys/time.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/uio.h>
31 #include <sys/signal.h>
32 #include <sys/wait.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <net/if.h>
36 #include <math.h>
37 #include <errno.h>
38 #include <signal.h>
39 #include <libgen.h>
40 #include <ctype.h>
41 #include <syslog.h>
42 
43 #include <assert.h>
44 
45 #include <map>
46 #include <string>
47 #include <iostream>
48 #include <list>
49 #include <vector>
50 
51 using namespace std;
52 
53 const char * const versionInfo = "0.3.9.1 ($Rev: 409 $)";
54 
55 const char * const defaultIPv6SSMChannel = "ff3e::beac";
56 const char * const defaultIPv4SSMChannel = "232.2.3.2";
57 const char * const defaultPort = "10000";
58 #ifndef SOLARIS
59 const int defaultTTL = 127;
60 #else
61 const int defaultTTL = 64;
62 #endif
63 const char * const defaultDumpFile = "dump.xml";
64 
65 /* time related constants */
66 static const int timeOutI = 6;
67 static const int reportI = 2;
68 static const int ssmReportI = 4;
69 static const int mapReportI = 6;
70 static const int websiteReportI = 24;
71 /* other constants */
72 static const int probeBurstLength = 10;
73 
74 // Timer Events
75 enum {
76 	GARBAGE_COLLECT_EVENT,
77 	DUMP_EVENT,
78 	DUMP_BW_EVENT,
79 	DUMP_BIG_BW_EVENT,
80 
81 	SENDING_EVENT,
82 	WILLSEND_EVENT,
83 
84 	SSM_SENDING_EVENT,
85 	WILLSEND_SSM_EVENT,
86 
87 	// Report types
88 	REPORT_EVENT = 'R',
89 	SSM_REPORT_EVENT,
90 	MAP_REPORT_EVENT,
91 	WEBSITE_REPORT_EVENT
92 };
93 
94 // Timer event names
95 const char *TimerEventName[] = {
96 	"Garbage Collect",
97 	"Dump Stats",
98 	"Bandwidth stats",
99 	"Bandwidth stats [2]",
100 	"Send Probe",
101 	"New send probe process",
102 	"SSM Send Probe",
103 	"New SSM send probe process",
104 
105 	"Send Report",
106 	"Send SSM Report",
107 	"Send Map-Report",
108 	"Send Website-Report"
109 };
110 
EventName(int type)111 const char *EventName(int type) {
112 	if (type < REPORT_EVENT)
113 		return TimerEventName[type];
114 	return TimerEventName[type - REPORT_EVENT + 8];
115 }
116 
117 static const char *Flags[] = {
118 	"SSM",
119 	"SSMPing"
120 };
121 
122 static const uint32_t KnownFlags = 2;
123 
124 static uint32_t timeFact(int val, bool random = false);
125 
126 string beaconName, adminContact, twoLetterCC;
127 Sources sources;
128 WebSites webSites;
129 address beaconUnicastAddr;
130 int verbose = 0;
131 uint32_t flags = 0;
132 
133 int mcastInterface = 0;
134 
135 static char sessionName[256];
136 static address probeAddr;
137 static string probeAddrLiteral;
138 static string probeSSMAddrLiteral;
139 static bool useSSM = false;
140 static bool listenForSSM = false;
141 static bool useSSMPing = false;
142 static address ssmProbeAddr;
143 static int mcastSock, ssmMcastSock = 0;
144 static int largestSock = 0;
145 static fd_set readSet;
146 static bool dumpBwReport = false;
147 static string launchSomething;
148 
149 static double beacInt = 5.;
150 
151 static uint64_t startTime = 0;
152 
153 static string dumpFile;
154 
155 static vector<pair<address, content_type> > mcastListen;
156 static vector<pair<int, content_type> > mcastSocks;
157 
158 static vector<address> redist;
159 
160 static vector<address> ssmBootstrap;
161 
162 static uint32_t bytesReceived = 0;
163 static uint32_t bytesSent = 0;
164 
165 static uint64_t bigBytesReceived = 0;
166 static uint64_t bigBytesSent = 0;
167 static uint64_t lastDumpBwTS = 0;
168 
169 static uint64_t dumpBytesReceived = 0;
170 static uint64_t dumpBytesSent = 0;
171 static uint64_t lastDumpDumpBwTS = 0;
172 
173 static int dumpInterval = 5;
174 int forceFamily = AF_UNSPEC;
175 bool daemonize = false;
176 bool use_syslog = false;
177 bool past_init = false;
178 
179 const char *pidfile = NULL;
180 
181 static void next_event(timeval *);
182 static void insert_event(uint32_t, uint32_t);
183 static void handle_mcast(int, content_type);
184 static void handle_event();
185 static void handle_gc();
186 static int send_probe();
187 static int send_ssm_probe();
188 static int send_report(int);
189 
190 static void do_dump();
191 static void do_bw_dump(bool);
192 extern "C" void dumpBigBwStats(int);
193 extern "C" void sendLeaveReport(int);
194 
195 static int SetupSocketAndFDSet(const address &, bool, bool);
196 
Rand()197 static inline double Rand() {
198 	double f = rand();
199 
200 	/* Prevent 0.0 and 1.0, thanks to Alexander Gall */
201 	if (f == 0)
202 		f = 1;
203 	else if (f == RAND_MAX)
204 		f = RAND_MAX-1;
205 
206 	return f / (double)RAND_MAX;
207 }
208 
Exprnd(double mean)209 static inline double Exprnd(double mean) {
210 	return -mean * log(1 - Rand());
211 }
212 
213 static const int bufferLen = 8192;
214 static uint8_t buffer[bufferLen];
215 
usage()216 void usage() {
217 	fprintf(stdout, "Usage: dbeacon [OPTIONS...]\n\n");
218 	fprintf(stdout, "  -n NAME, -name NAME    Specifies the beacon name\n");
219 	fprintf(stdout, "  -a MAIL                Supply administration contact\n");
220 	fprintf(stdout, "  -i IN, -interface IN   Use IN instead of the default interface for multicast\n");
221 	fprintf(stdout, "  -b BEACON_ADDR[/PORT]  Multicast group address to send probes to\n");
222 	fprintf(stdout, "  -S [GROUP_ADDR[/PORT]] Enables SSM reception/sending on optional GROUP_ADDR/PORT\n");
223 	fprintf(stdout, "  -O                     Disables the joining of SSM groups but still sends via SSM.\n");
224 	fprintf(stdout, "                         Use this option if your operating system has problems with SSM\n");
225 	fprintf(stdout, "  -B ADDR                Bootstraps by joining the specified address\n");
226 	fprintf(stdout, "  -P, -ssmping           Enable the SSMPing server capability\n");
227 	fprintf(stdout, "  -s ADDR                Bind to local address\n");
228 	fprintf(stdout, "  -d [FILE]              Dump periodic reports to dump.xml or specified file\n");
229 	fprintf(stdout, "  -I N, -interval N      Interval between dumps. Defaults to 5 secs\n");
230 	fprintf(stdout, "  -W URL, -website URL   Specify a website to announce.\n");
231 	fprintf(stdout, "  -Wm URL, -matrix URL   Specify your matrix URL\n");
232 	fprintf(stdout, "  -Wl URL, -lg URL       Specify your LG URL\n");
233 	fprintf(stdout, "                         will announce an URL for that type instead\n");
234 	fprintf(stdout, "  -C CC                  Specify your two letter Country Code\n");
235 	fprintf(stdout, "  -L program             Launch program after each dump.\n");
236 	fprintf(stdout, "                         The first argument will be the dump filename\n");
237 	fprintf(stdout, "  -F flag                Set a dbeacon flag to be announced.\n");
238 	fprintf(stdout, "                         Available flags are: ssmping\n");
239 	fprintf(stdout, "  -4, -ipv4              Force IPv4 usage\n");
240 	fprintf(stdout, "  -6, -ipv6              Force IPv6 usage\n");
241 	fprintf(stdout, "  -v                     be verbose (use several for more verbosity)\n");
242 	fprintf(stdout, "  -U                     Dump periodic bandwidth usage reports to stdout\n");
243 	fprintf(stdout, "  -D, -daemon            fork to the background (daemonize)\n");
244 	fprintf(stdout, "  -pidfile FILE          Specifies the PID filename to use\n");
245 	fprintf(stdout, "  -syslog                Outputs using syslog facility.\n");
246 	fprintf(stdout, "  -c FILE                Specifies the configuration file\n");
247 	fprintf(stdout, "  -V, -version           Outputs version information and leaves\n");
248 	fprintf(stdout, "\n");
249 
250 	exit(1);
251 }
252 
logv(int level,const char * format,va_list vl)253 static void logv(int level, const char *format, va_list vl)
254 {
255 	char buffer[256];
256 	vsnprintf(buffer, sizeof(buffer), format, vl);
257 
258 	if (use_syslog && past_init) {
259 		syslog(level, buffer);
260 	} else {
261 		char tbuf[64];
262 		timeval tv;
263 		gettimeofday(&tv, 0);
264 
265 		/* Some FreeBSDs' tv.tv_sec isn't time_t */
266 		time_t tv_sec = tv.tv_sec;
267 		strftime(tbuf, sizeof(tbuf), "%b %d %H:%M:%S", localtime(&tv_sec));
268 
269 		fprintf(stderr, "%s.%06u %s\n", tbuf, (uint32_t)tv.tv_usec, buffer);
270 	}
271 }
272 
log(int level,const char * format,...)273 static void log(int level, const char *format, ...)
274 {
275 	va_list vl;
276 	va_start(vl, format);
277 	logv(level, format, vl);
278 	va_end(vl);
279 }
280 
info(const char * format,...)281 void info(const char *format, ...)
282 {
283 	va_list vl;
284 	va_start(vl, format);
285 	logv(LOG_INFO, format, vl);
286 	va_end(vl);
287 }
288 
fatal(const char * format,...)289 void fatal(const char *format, ...)
290 {
291 	va_list vl;
292 	va_start(vl, format);
293 	logv(LOG_CRIT, format, vl);
294 	va_end(vl);
295 	exit(-1);
296 }
297 
waitForMe(int)298 extern "C" void waitForMe(int) {
299 	int whocares;
300 	wait(&whocares);
301 }
302 
303 static void parse_arguments(int, char **);
304 
main(int argc,char ** argv)305 int main(int argc, char **argv) {
306 	int res;
307 
308 	srand(time(NULL));
309 
310 	char tmp[256];
311 	if (gethostname(tmp, sizeof(tmp)) != 0) {
312 		perror("Failed to get hostname");
313 		return -1;
314 	}
315 
316 	beaconName = tmp;
317 
318 	parse_arguments(argc, argv);
319 
320 	MulticastStartup();
321 
322 	if (beaconName.empty())
323 		fatal("No name supplied.");
324 
325 	if (!probeAddrLiteral.empty()) {
326 		if (!probeAddr.parse(probeAddrLiteral.c_str(), true))
327 			return -1;
328 
329 		probeAddr.print(sessionName, sizeof(sessionName));
330 
331 		if (!probeAddr.is_multicast())
332 			fatal("Specified probe addr (%s) is not of a multicast group.",
333 					sessionName);
334 
335 		if (adminContact.empty())
336 			fatal("No administration contact supplied.");
337 
338 		mcastListen.push_back(make_pair(probeAddr, NPROBE));
339 
340 		insert_event(SENDING_EVENT, 100);
341 		insert_event(REPORT_EVENT, 10000);
342 		insert_event(MAP_REPORT_EVENT, 30000);
343 		insert_event(WEBSITE_REPORT_EVENT, 120000);
344 
345 		redist.push_back(probeAddr);
346 
347 		if (useSSM) {
348 			if (probeSSMAddrLiteral.empty()) {
349 				int family = forceFamily;
350 
351 				if (family == AF_UNSPEC) {
352 					family = probeAddr.family();
353 				}
354 				if (family == AF_INET) {
355 					probeSSMAddrLiteral = defaultIPv4SSMChannel;
356 				} else {
357 					probeSSMAddrLiteral = defaultIPv6SSMChannel;
358 				}
359 			}
360 
361 			if (!ssmProbeAddr.parse(probeSSMAddrLiteral.c_str(), true)) {
362 				fatal("Bad address format for SSM channel.");
363 			} else if (!ssmProbeAddr.is_unspecified()) {
364 				insert_event(SSM_SENDING_EVENT, 100);
365 				insert_event(SSM_REPORT_EVENT, 15000);
366 
367 				if (listenForSSM) {
368 					mcastListen.push_back(make_pair(ssmProbeAddr, NSSMPROBE));
369 				}
370 			}
371 		}
372 	} else {
373 		if (mcastListen.empty())
374 			fatal("Nothing to do, check `dbeacon -h`.");
375 		else
376 			strcpy(sessionName, beaconName.c_str());
377 	}
378 
379 	FD_ZERO(&readSet);
380 
381 	address local;
382 	local.set_family(probeAddr.family());
383 
384 	mcastSock = SetupSocketAndFDSet(local, false, false);
385 	if (mcastSock < 0)
386 		return -1;
387 
388 	// connect the socket to probeAddr, so the source address can be determined
389 
390 	socklen_t addrlen = probeAddr.addrlen();
391 
392 	if (beaconUnicastAddr.is_unspecified()) {
393 		int tmpSock = socket(probeAddr.family(), SOCK_DGRAM, 0);
394 		if (tmpSock < 0) {
395 			perror("Failed to create socket to discover local addr");
396 			return -1;
397 		}
398 
399 		if (connect(tmpSock, probeAddr.saddr(), addrlen) != 0) {
400 			perror("Failed to connect multicast socket");
401 			return -1;
402 		}
403 
404 		beaconUnicastAddr.set_family(probeAddr.family());
405 		addrlen = beaconUnicastAddr.addrlen();
406 
407 		if (getsockname(tmpSock, beaconUnicastAddr.saddr(), &addrlen) != 0) {
408 			perror("getsockname");
409 			return -1;
410 		}
411 
412 		close(tmpSock);
413 	}
414 
415 	if (bind(mcastSock, beaconUnicastAddr.saddr(), beaconUnicastAddr.addrlen()) != 0) {
416 		perror("Failed to bind local socket");
417 		return -1;
418 	}
419 
420 	addrlen = beaconUnicastAddr.addrlen();
421 
422 	// Retrieve the used port
423 	if (getsockname(mcastSock, beaconUnicastAddr.saddr(), &addrlen) != 0) {
424 		perror("getsockname");
425 		return -1;
426 	}
427 
428 	for (vector<pair<address, content_type> >::iterator i = mcastListen.begin(); i != mcastListen.end(); i++) {
429 		int sock = SetupSocket(i->first, true, i->second == NSSMPROBE);
430 		if (sock < 0)
431 			return -1;
432 		ListenTo(i->second, sock);
433 		if (i->second == NSSMPROBE) {
434 			ssmMcastSock = sock;
435 		}
436 	}
437 
438 	if (useSSMPing) {
439 		if (SetupSSMPing() < 0)
440 			log(LOG_ERR, "Failed to setup SSM Ping.");
441 		else
442 			flags |= SSMPING_CAPABLE;
443 	}
444 
445 	if (ssmMcastSock) {
446 		flags |= SSM_CAPABLE;
447 
448 		uint64_t now = get_timestamp();
449 		for (vector<address>::const_iterator i = ssmBootstrap.begin(); i != ssmBootstrap.end(); i++) {
450 			getSource(*i, 0, now, 0, false);
451 		}
452 	} else if (!ssmBootstrap.empty())
453 		log(LOG_WARNING, "Tried to bootstrap using SSM when SSM is not enabled.");
454 
455 	if (daemonize || use_syslog) {
456 		use_syslog = true;
457 		openlog("dbeacon", LOG_NDELAY | LOG_PID, LOG_DAEMON);
458 	}
459 
460 	past_init = true;
461 
462 	if (daemonize) {
463 		if (daemon(0, 0)) {
464 			perror("Failed to daemon()ize.");
465 			return -1;
466 		}
467 		if (pidfile) {
468 			FILE *f = fopen(pidfile, "w");
469 			if (f) {
470 				fprintf(f, "%u\n", getpid());
471 				fclose(f);
472 			} else {
473 				log(LOG_ERR, "Failed to open PID file to write.");
474 			}
475 		}
476 	}
477 
478 	// Init timer events
479 	insert_event(GARBAGE_COLLECT_EVENT, 30000);
480 
481 	if (!dumpFile.empty())
482 		insert_event(DUMP_EVENT, dumpInterval * 1000);
483 
484 	insert_event(DUMP_BW_EVENT, 10000);
485 
486 	if (dumpBwReport)
487 		insert_event(DUMP_BIG_BW_EVENT, 600000);
488 
489 	send_report(WEBSITE_REPORT_EVENT);
490 
491 	beaconUnicastAddr.print(tmp, sizeof(tmp), false);
492 
493 	info("Local name is `%s` [Beacon group: %s, Local address: %s]",
494 			beaconName.c_str(), sessionName, tmp);
495 
496 	signal(SIGUSR1, dumpBigBwStats);
497 	signal(SIGINT, sendLeaveReport);
498 	signal(SIGTERM, sendLeaveReport);
499 
500 	signal(SIGCHLD, waitForMe); // bloody fork, we dont want to wait for thee
501 
502 	startTime = lastDumpBwTS = lastDumpDumpBwTS = get_timestamp();
503 
504 	while (1) {
505 		fd_set readset;
506 		timeval eventm;
507 
508 		memcpy(&readset, &readSet, sizeof(fd_set));
509 
510 		next_event(&eventm);
511 
512 		res = select(largestSock + 1, &readset, 0, 0, &eventm);
513 
514 		if (res < 0) {
515 			if (errno == EINTR)
516 				continue;
517 			fatal("Select failed: %s", strerror(errno));
518 		} else {
519 			for (vector<pair<int, content_type> >::const_iterator
520 					i = mcastSocks.begin(); i != mcastSocks.end(); ++i)
521 				if (FD_ISSET(i->first, &readset))
522 					handle_mcast(i->first, i->second);
523 
524 			handle_event();
525 		}
526 	}
527 
528 	return 0;
529 }
530 
ListenTo(content_type content,int sock)531 void ListenTo(content_type content, int sock) {
532 	SetupFDSet(sock);
533 
534 	mcastSocks.push_back(make_pair(sock, content));
535 }
536 
show_version()537 void show_version() {
538 	fprintf(stderr, "\n");
539 	fprintf(stderr, "dbeacon - a Multicast Beacon %s\n", versionInfo);
540 	fprintf(stderr, "\n");
541 	fprintf(stderr, "  Copyright (c) 2005-7, Hugo Santos <hugo@fivebits.net>\n");
542 	fprintf(stderr, "\n");
543 	fprintf(stderr, "  http://fivebits.net/proj/dbeacon\n");
544 	fprintf(stderr, "\n");
545 	fprintf(stderr, "  o Ideas, IPv4 port, SSM pushing by Hoerdt Mickael;\n");
546 	fprintf(stderr, "  o Ideas and testing by Sebastien Chaumontet;\n");
547 	fprintf(stderr, "  o SSM Ping originaly by Stig Venaas\n");
548 	fprintf(stderr, "    - first proposed by Pavan Namburi, Kamil Sarac and Kevin C. Almeroth;\n");
549 	fprintf(stderr, "  o Bernhard Schmidt provided valuable resources and helped during testing.\n");
550 	fprintf(stderr, "\n");
551 
552 	exit(1);
553 }
554 
555 enum {
556 	NAME = 1,
557 	CONTACT,
558 	INTERFACE,
559 	BEACONADDR,
560 	SSMADDR,
561 	SSMSENDONLY,
562 	BOOTSTRAP,
563 	ENABLESSMPING,
564 	SOURCEADDR,
565 	DUMP,
566 	DUMPINTERVAL,
567 	DUMPEXEC,
568 	SPECWEBSITE,
569 	SPECMATRIX,
570 	SPECLG,
571 	COUNTRY,
572 	SPECFLAG,
573 	VERBOSE,
574 	DUMPBW,
575 	HELP,
576 	FORCEv4,
577 	FORCEv6,
578 	SHOWVERSION,
579 	DAEMON,
580 	PIDFILE,
581 	USE_SYSLOG,
582 	CONFFILE
583 };
584 
585 enum {
586 	NO_ARG = 0,
587 	REQ_ARG,
588 	OPT_ARG
589 };
590 
591 static const struct param_tok {
592 	int name;
593 	const char *sf, *lf;
594 	int param;
595 } param_format[] = {
596 	{ NAME,		"n", "name", REQ_ARG },
597 	{ CONTACT,	"a", "contact", REQ_ARG },
598 	{ INTERFACE,	"i", "interface", REQ_ARG },
599 	{ BEACONADDR,	"b", "addr", REQ_ARG },
600 	{ SSMADDR,	"S", "ssm_addr", OPT_ARG },
601 	{ SSMSENDONLY,	"O", "ssm_send_only", OPT_ARG },
602 	{ BOOTSTRAP,	"B", "bootstrap", REQ_ARG },
603 	{ ENABLESSMPING,"P", "ssmping", OPT_ARG },
604 	{ SOURCEADDR,	"s", "source", REQ_ARG },
605 	{ DUMP,		"d", "dump", OPT_ARG },
606 	{ DUMPINTERVAL,	"I", "interval", REQ_ARG },
607 	{ DUMPEXEC,	"L", "exec", REQ_ARG },
608 	{ SPECWEBSITE,	"W", "website", REQ_ARG },
609 	{ SPECMATRIX,	"Wm", "matrix", REQ_ARG },
610 	{ SPECLG,	"Wl", "lg", REQ_ARG },
611 	{ COUNTRY,	"C", "CC", REQ_ARG },
612 	{ SPECFLAG,	"F", "flag", REQ_ARG },
613 	{ VERBOSE,	"v", "verbose", OPT_ARG },
614 	{ DUMPBW,	"U", "dump-bw", OPT_ARG },
615 	{ HELP,		"h", "help", NO_ARG },
616 	{ FORCEv4,	"4", "ipv4", NO_ARG },
617 	{ FORCEv6,	"6", "ipv6", NO_ARG },
618 	{ DAEMON,	"D", "daemon", NO_ARG },
619 	{ PIDFILE,	"p", "pidfile", REQ_ARG },
620 	{ USE_SYSLOG,	"Y", "syslog", NO_ARG },
621 	{ CONFFILE,	"c", NULL, REQ_ARG },
622 	{ SHOWVERSION,	"V", "version", NO_ARG },
623 	{ 0, NULL, NULL, 0 }
624 };
625 
check_good_string(const char * what,const char * value)626 static const char *check_good_string(const char *what, const char *value) {
627 	int l = strlen(value);
628 
629 	for (int i = 0; i < l; i++) {
630 		if (!isprint(value[i])) {
631 			fprintf(stderr, "Invalid `%s` string.\n", what);
632 			exit(1);
633 		}
634 	}
635 
636 	return value;
637 }
638 
parse_or_fail(address * addr,const char * arg,bool mc,bool addport)639 static void parse_or_fail(address *addr, const char *arg, bool mc, bool addport) {
640 	if (!addr->parse(arg, mc, addport))
641 		fatal("Bad address format.");
642 }
643 
add_bootstrap_address(const char * arg)644 static void add_bootstrap_address(const char *arg) {
645 	address addr;
646 	parse_or_fail(&addr, arg, false, true);
647 	ssmBootstrap.push_back(addr);
648 }
649 
parse_u32(const char * name,const char * arg)650 static uint32_t parse_u32(const char *name, const char *arg) {
651 	uint32_t result;
652 	char *end;
653 
654 	result = strtoul(arg, &end, 10);
655 	if (end[0] != 0)
656 		fatal("%s: Expected unsigned integer.", name);
657 
658 	return result;
659 }
660 
parse_bool(const char * name,const char * arg,bool def)661 static bool parse_bool(const char *name, const char *arg, bool def) {
662 	if (arg == NULL)
663 		return def;
664 
665 	if (!strcasecmp(arg, "yes"))
666 		return true;
667 	else if (!strcasecmp(arg, "true"))
668 		return true;
669 	else if (!strcasecmp(arg, "1"))
670 		return true;
671 	else if (!strcasecmp(arg, "no"))
672 		return false;
673 	else if (!strcasecmp(arg, "false"))
674 		return false;
675 	else if (!strcasecmp(arg, "0"))
676 		return false;
677 
678 	fatal("%s: Expected one of \'yes\', \'true\', \'no\' or \'false\'.");
679 	return false;
680 }
681 
682 static void parse_config_file(const char *);
683 
process_param(const param_tok * tok,const char * arg)684 static void process_param(const param_tok *tok, const char *arg) {
685 	switch (tok->name) {
686 	case NAME:
687 		beaconName = check_good_string("name", arg);
688 		break;
689 	case CONTACT:
690 		if (!strchr(arg, '@'))
691 			fatal("Not a valid email address.");
692 
693 		adminContact = check_good_string("admin contact", arg);
694 		break;
695 	case INTERFACE:
696 		mcastInterface = if_nametoindex(arg);
697 		if (mcastInterface <= 0)
698 			fatal("Invalid interface name.");
699 		break;
700 	case BEACONADDR:
701 		probeAddrLiteral = arg;
702 		break;
703 	case SSMADDR:
704 		probeSSMAddrLiteral = arg;
705 		useSSM = true;
706 		listenForSSM = true;
707 		break;
708 	case SSMSENDONLY:
709 		useSSM = true;
710 		listenForSSM = parse_bool("SSMSendOnly", arg, false);
711 		break;
712 	case BOOTSTRAP:
713 		add_bootstrap_address(arg);
714 		break;
715 	case ENABLESSMPING:
716 		useSSMPing = parse_bool("SSMPing", arg, true);
717 		break;
718 	case SOURCEADDR:
719 		parse_or_fail(&beaconUnicastAddr, arg, false, false);
720 		break;
721 	case DUMP:
722 		dumpFile = arg ? arg : defaultDumpFile;
723 		break;
724 	case DUMPINTERVAL:
725 		dumpInterval = parse_u32("Dump interval", arg);
726 		if (dumpInterval < 5)
727 			dumpInterval = 5;
728 		break;
729 	case DUMPEXEC:
730 		launchSomething = arg;
731 		break;
732 	case SPECWEBSITE:
733 		if (strncmp(arg, "lg$", 3) == 0) {
734 			webSites[T_WEBSITE_LG] =
735 				check_good_string("LG website", arg + 3);
736 		} else if (strncmp(arg, "matrix$", 7) == 0) {
737 			webSites[T_WEBSITE_MATRIX] =
738 				check_good_string("matrix url", arg + 7);
739 		} else {
740 			webSites[T_WEBSITE_GENERIC] =
741 				check_good_string("website", arg);
742 		}
743 		break;
744 	case SPECMATRIX:
745 		webSites[T_WEBSITE_MATRIX] =
746 			check_good_string("matrix url", arg);
747 		break;
748 	case SPECLG:
749 		webSites[T_WEBSITE_LG] =
750 			check_good_string("lg url", arg);
751 		break;
752 	case COUNTRY:
753 		if (strlen(arg) != 2)
754 			fatal("Bad country code.");
755 		twoLetterCC = check_good_string("country", arg);
756 		break;
757 	case SPECFLAG:
758 		if (!strcmp(arg, "ssmping")) {
759 			flags |= SSMPING_CAPABLE;
760 		} else {
761 			fprintf(stderr, "Unknown flag \"%s\"\n", arg);
762 		}
763 		break;
764 	case VERBOSE:
765 		if (arg)
766 			verbose = parse_u32("Verbose", arg);
767 		else
768 			verbose ++;
769 		break;
770 	case DUMPBW:
771 		dumpBwReport = parse_bool("DumpBandwidth", arg, true);
772 		break;
773 	case HELP:
774 		usage();
775 		break;
776 	case FORCEv4:
777 		forceFamily = AF_INET;
778 		break;
779 	case FORCEv6:
780 		forceFamily = AF_INET6;
781 		break;
782 	case SHOWVERSION:
783 		show_version();
784 		break;
785 	case DAEMON:
786 		daemonize = true;
787 		break;
788 	case PIDFILE:
789 		pidfile = check_good_string("pidfile", arg);
790 		break;
791 	case USE_SYSLOG:
792 		use_syslog = true;
793 		break;
794 	case CONFFILE:
795 		parse_config_file(arg);
796 		break;
797 	}
798 }
799 
skip_spaces(char * in)800 static char *skip_spaces(char *in) {
801 	while (isspace(in[0]))
802 		in++;
803 	return in;
804 }
805 
terminate_str(char * left,char * right)806 static char *terminate_str(char *left, char *right) {
807 	for (; left < right && isspace(*right); right--);
808 	right[1] = 0;
809 	return left;
810 }
811 
resolve_tok(const char * arg,bool longonly)812 static const param_tok *resolve_tok(const char *arg, bool longonly) {
813 	for (int j = 0; param_format[j].sf != NULL; j++) {
814 		if (param_format[j].lf && !strcmp(arg, param_format[j].lf))
815 			return &param_format[j];
816 
817 		if (longonly)
818 			continue;
819 
820 		if (!strcmp(arg, param_format[j].sf))
821 			return &param_format[j];
822 	}
823 
824 	return NULL;
825 }
826 
resolve_string(const char * name,char ** ptr)827 static void resolve_string(const char *name, char **ptr) {
828 	char *p, *str = (*ptr);
829 
830 	if (str[0] != '\"')
831 		return;
832 
833 	for (p = str + 1; (*p) != '\"'; p++);
834 
835 	if (p[0] == 0 || p[1] != 0)
836 		fatal("%s: Bad string format.", name);
837 
838 	p[0] = 0;
839 
840 	(*ptr) = str + 1;
841 }
842 
check_option_value(const param_tok * tok,const char * lp,const char * value)843 static void check_option_value(const param_tok *tok, const char *lp,
844 	const char *value)
845 {
846 	if (tok == NULL)
847 		fatal("Unknown option `%s`", lp);
848 	else if (tok->param == REQ_ARG && value == NULL)
849 		fatal("Parameter `%s` requires an argument.", lp);
850 	else if (tok->param == NO_ARG && value != NULL)
851 		fatal("Parameter `%s` doesn't accept an argument.", lp);
852 }
853 
parse_config_file(const char * filename)854 static void parse_config_file(const char *filename) {
855 	FILE *f = fopen(filename, "r");
856 
857 	if (f == NULL)
858 		fatal("Failed to open configuration file.");
859 
860 	char linebuf[256];
861 	int lc = 0;
862 
863 	while (fgets(linebuf, sizeof(linebuf), f)) {
864 		char *lp = skip_spaces(linebuf);
865 		char *val, *end = lp + strlen(lp);
866 
867 		lc++;
868 
869 		if (lp[0] == 0 || lp[0] == '#' || strncmp(lp, "//", 2) == 0)
870 			continue;
871 
872 		val = strchr(lp, ':');
873 		if (val) {
874 			terminate_str(lp, val - 1);
875 			val = terminate_str(skip_spaces(val + 1), end - 1);
876 		} else {
877 			terminate_str(lp, lp + strlen(lp) - 1);
878 		}
879 
880 		const param_tok *tok = resolve_tok(lp, true);
881 
882 		check_option_value(tok, lp, val);
883 
884 		if (val)
885 			resolve_string(lp, &val);
886 		process_param(tok, val);
887 	}
888 
889 	fclose(f);
890 }
891 
892 typedef pair<const char *, const char *> string_pair;
893 
parse_arguments(int argc,char ** argv)894 void parse_arguments(int argc, char **argv) {
895 	vector<string_pair> args;
896 	vector<const char *> stray;
897 
898 	for (int i = 1; i < argc; i++) {
899 		if (argv[i][0] == '-') {
900 			const char *mast = argv[i];
901 			const char *arg = 0;
902 			if ((i + 1) < argc && argv[i+1][0] != '-') {
903 				arg = argv[i+1];
904 				i++;
905 			}
906 			args.push_back(make_pair(mast + 1, arg));
907 		} else {
908 			stray.push_back(argv[i]);
909 		}
910 	}
911 
912 	for (vector<string_pair>::const_iterator i = args.begin();
913 						 i != args.end(); ++i) {
914 		const param_tok *tok = resolve_tok(i->first, false);
915 		check_option_value(tok, i->first, i->second);
916 		process_param(tok, i->second);
917 	}
918 }
919 
920 struct timer {
921 	uint32_t type, interval;
922 	uint32_t target;
923 };
924 
925 typedef std::list<timer> tq_def;
926 static tq_def timers;
927 
928 /* accumulated time waiting to be spent by events */
929 static uint32_t taccum = 0;
930 static uint64_t lastclk = 0;
931 
update_taccum()932 static void update_taccum() {
933 	uint64_t now = get_timestamp();
934 	int32_t diff = now - (int64_t)lastclk;
935 
936 	assert(now >= lastclk);
937 
938 	lastclk = now;
939 	taccum += diff;
940 }
941 
next_event(timeval * eventm)942 void next_event(timeval *eventm) {
943 	update_taccum();
944 
945 	timer &h = *timers.begin();
946 
947 	/* we assume we always have a timer in the list */
948 	if (taccum > h.target) {
949 		taccum -= h.target;
950 		h.target = 0;
951 	} else {
952 		h.target -= taccum;
953 		taccum = 0;
954 	}
955 
956 	eventm->tv_sec = h.target / 1000;
957 	eventm->tv_usec = (h.target % 1000) * 1000;
958 }
959 
insert_sorted_event(timer & t)960 void insert_sorted_event(timer &t) {
961 	uint32_t accum = 0;
962 
963 	tq_def::iterator i = timers.begin();
964 
965 	while (1) {
966 		if (i == timers.end() || (accum + i->target) >= t.interval)
967 			break;
968 		accum += i->target;
969 		++i;
970 	}
971 
972 	t.target = t.interval - accum;
973 
974 	if (i != timers.end())
975 		i->target -= t.target;
976 
977 	if (timers.empty()) {
978 		lastclk = get_timestamp();
979 		taccum = 0;
980 	}
981 
982 	timers.insert(i, t);
983 }
984 
insert_event(uint32_t type,uint32_t interval)985 void insert_event(uint32_t type, uint32_t interval) {
986 	timer t;
987 	t.type = type;
988 	t.interval = interval;
989 
990 	insert_sorted_event(t);
991 }
992 
993 static int send_count = 0;
994 static int send_ssm_count = 0;
995 
timeFact(int val,bool random)996 uint32_t timeFact(int val, bool random) {
997 	return (uint32_t) ((random ? ceil(Exprnd(beacInt * val)) : (beacInt * val)) * 1000);
998 }
999 
handle_single_event()1000 static void handle_single_event() {
1001 	timer t = *timers.begin();
1002 	timers.erase(timers.begin());
1003 
1004 	switch (t.type) {
1005 	case SENDING_EVENT:
1006 		send_probe();
1007 		send_count++;
1008 		break;
1009 	case SSM_SENDING_EVENT:
1010 		send_ssm_probe();
1011 		send_ssm_count++;
1012 		break;
1013 	case REPORT_EVENT:
1014 	case SSM_REPORT_EVENT:
1015 	case MAP_REPORT_EVENT:
1016 	case WEBSITE_REPORT_EVENT:
1017 		send_report(t.type);
1018 		break;
1019 	case GARBAGE_COLLECT_EVENT:
1020 		handle_gc();
1021 		break;
1022 	case DUMP_EVENT:
1023 		do_dump();
1024 		break;
1025 	case DUMP_BW_EVENT:
1026 	case DUMP_BIG_BW_EVENT:
1027 		do_bw_dump(t.type == DUMP_BIG_BW_EVENT);
1028 		break;
1029 	}
1030 
1031 	if (t.type == WILLSEND_EVENT) {
1032 		insert_event(SENDING_EVENT, 100);
1033 		send_count = 0;
1034 	} else if (t.type == WILLSEND_SSM_EVENT) {
1035 		insert_event(SSM_SENDING_EVENT, 100);
1036 		send_ssm_count = 0;
1037 	} else if (t.type == SENDING_EVENT && send_count == probeBurstLength) {
1038 		insert_event(WILLSEND_EVENT, timeFact(1, true));
1039 	} else if (t.type == SSM_SENDING_EVENT && send_ssm_count == probeBurstLength) {
1040 		insert_event(WILLSEND_SSM_EVENT, timeFact(1, true));
1041 	} else if (t.type == REPORT_EVENT) {
1042 		insert_event(REPORT_EVENT, timeFact(reportI));
1043 	} else if (t.type == SSM_REPORT_EVENT) {
1044 		insert_event(SSM_REPORT_EVENT, timeFact(ssmReportI));
1045 	} else if (t.type == MAP_REPORT_EVENT) {
1046 		insert_event(MAP_REPORT_EVENT, timeFact(mapReportI));
1047 	} else if (t.type == WEBSITE_REPORT_EVENT) {
1048 		insert_event(WEBSITE_REPORT_EVENT, timeFact(websiteReportI));
1049 	} else {
1050 		insert_sorted_event(t);
1051 	}
1052 }
1053 
handle_event()1054 void handle_event() {
1055 	update_taccum();
1056 
1057 	while (!timers.empty()) {
1058 		if (timers.begin()->target > taccum) {
1059 			return;
1060 		}
1061 
1062 		taccum -= timers.begin()->target;
1063 
1064 		handle_single_event();
1065 	}
1066 }
1067 
handle_gc()1068 void handle_gc() {
1069 	Sources::iterator i = sources.begin();
1070 
1071 	uint64_t now = get_timestamp();
1072 
1073 	while (i != sources.end()) {
1074 		bool remove = false;
1075 		if ((now - i->second.lastevent) > timeFact(timeOutI)) {
1076 			remove = true;
1077 		}
1078 		if (!remove) {
1079 			i->second.ASM.s.check_validity(now);
1080 			i->second.SSM.s.check_validity(now);
1081 
1082 			beaconSource::ExternalSources::iterator j = i->second.externalSources.begin();
1083 			while (j != i->second.externalSources.end()) {
1084 				if ((now - j->second.lastupdate) > timeFact(timeOutI)) {
1085 					beaconSource::ExternalSources::iterator k = j;
1086 					j++;
1087 					i->second.externalSources.erase(k);
1088 				} else {
1089 					j->second.ASM.check_validity(now);
1090 					j->second.SSM.check_validity(now);
1091 
1092 					j++;
1093 				}
1094 			}
1095 
1096 			i++;
1097 		} else {
1098 			Sources::iterator j = i;
1099 			i++;
1100 
1101 			removeSource(j->first, true);
1102 		}
1103 	}
1104 }
1105 
handle_mcast(int sock,content_type type)1106 void handle_mcast(int sock, content_type type) {
1107 	address from, to;
1108 
1109 	uint64_t recvdts;
1110 	int ttl;
1111 
1112 	int len = RecvMsg(sock, from, to, buffer, bufferLen, ttl, recvdts);
1113 	if (len < 0)
1114 		return;
1115 
1116 	if (from.is_equal(beaconUnicastAddr))
1117 		return;
1118 
1119 	if (verbose > 3) {
1120 		char tmp[64];
1121 		from.print(tmp, sizeof(tmp));
1122 		info("RecvMsg(%s): len = %u", tmp, len);
1123 	}
1124 
1125 	if (type == SSMPING) {
1126 		handle_ssmping(sock, from, to, buffer, len, recvdts);
1127 	} else if (type == NPROBE || type == NSSMPROBE) {
1128 		bytesReceived += len;
1129 
1130 		handle_nmsg(from, recvdts, ttl, buffer, len, type == NSSMPROBE);
1131 	}
1132 }
1133 
Stats()1134 Stats::Stats() {
1135 	valid = false;
1136 	timestamp = lastupdate = 0;
1137 	avgdelay = avgjitter = avgloss = avgdup = avgooo = 0;
1138 	rttl = 0;
1139 }
1140 
check_validity(uint64_t now)1141 void Stats::check_validity(uint64_t now) {
1142 	if ((now - lastupdate) > timeFact(timeOutI))
1143 		valid = false;
1144 }
1145 
beaconExternalStats()1146 beaconExternalStats::beaconExternalStats() : identified(false) {}
1147 
getSource(const address & baddr,const char * name,uint64_t now,uint64_t recvdts,bool rx_local)1148 beaconSource &getSource(const address &baddr, const char *name, uint64_t now, uint64_t recvdts, bool rx_local) {
1149 	Sources::iterator i = sources.find(baddr);
1150 	if (i != sources.end()) {
1151 		i->second.lastevent = now;
1152 		if (rx_local)
1153 			i->second.lastlocalevent = now;
1154 		return i->second;
1155 	}
1156 
1157 	beaconSource &src = sources[baddr];
1158 
1159 	if (verbose) {
1160 		char tmp[64];
1161 
1162 		baddr.print(tmp, sizeof(tmp));
1163 
1164 		if (name) {
1165 			info("Adding source %s [%s]", tmp, name);
1166 		} else {
1167 			info("Adding source %s", tmp);
1168 		}
1169 	}
1170 
1171 	if (name)
1172 		src.setName(name);
1173 
1174 	src.creation = now;
1175 	src.lastevent = now;
1176 	if (rx_local)
1177 		src.lastlocalevent = now;
1178 
1179 	if (ssmMcastSock) {
1180 		if (SSMJoin(ssmMcastSock, ssmProbeAddr, baddr) != 0) {
1181 			if (verbose) {
1182 				char tmp[64];
1183 				baddr.print(tmp, sizeof(tmp));
1184 				info("Failed to join SSM (S,G) where S = %s, reason: %s",
1185 					tmp, strerror(errno));
1186 			}
1187 		} else {
1188 			if (verbose > 1) {
1189 				char tmp[64];
1190 				baddr.print(tmp, sizeof(tmp));
1191 				info("Joined SSM (S, G) where S = %s", tmp);
1192 			}
1193 		}
1194 	}
1195 
1196 	return src;
1197 }
1198 
removeSource(const address & baddr,bool timeout)1199 void removeSource(const address &baddr, bool timeout) {
1200 	Sources::iterator i = sources.find(baddr);
1201 	if (i != sources.end()) {
1202 		if (verbose) {
1203 			char tmp[64];
1204 
1205 			baddr.print(tmp, sizeof(tmp));
1206 
1207 			if (i->second.identified) {
1208 				info("Removing source %s [%s]%s",
1209 					tmp, i->second.name.c_str(), (timeout ? " by Timeout" : ""));
1210 			} else {
1211 				info("Removing source %s%s",
1212 					tmp, (timeout ? " by Timeout" : ""));
1213 			}
1214 		}
1215 
1216 		if (ssmMcastSock) {
1217 			SSMLeave(ssmMcastSock, ssmProbeAddr, baddr);
1218 		}
1219 
1220 		sources.erase(i);
1221 	}
1222 }
1223 
beaconSource()1224 beaconSource::beaconSource()
1225 	: identified(false) {
1226 	sttl = 0;
1227 	lastlocalevent = 0;
1228 	Flags = 0;
1229 }
1230 
setName(const string & n)1231 void beaconSource::setName(const string &n) {
1232 	name = n;
1233 	identified = true;
1234 }
1235 
getExternal(const address & baddr,uint64_t now,uint64_t ts)1236 beaconExternalStats &beaconSource::getExternal(const address &baddr, uint64_t now, uint64_t ts) {
1237 	ExternalSources::iterator k = externalSources.find(baddr);
1238 	if (k == externalSources.end()) {
1239 		externalSources.insert(make_pair(baddr, beaconExternalStats()));
1240 		k = externalSources.find(baddr);
1241 
1242 		k->second.age = 0;
1243 
1244 		char tmp[64];
1245 		baddr.print(tmp, sizeof(tmp));
1246 
1247 		if (verbose)
1248 			info("Adding external source (%s) %s", name.c_str(), tmp);
1249 	}
1250 
1251 	beaconExternalStats &stats = k->second;
1252 
1253 	stats.lastupdate = now;
1254 
1255 	return stats;
1256 }
1257 
udiff(T a,T b)1258 template<typename T> T udiff(T a, T b) { if (a > b) return a - b; return b - a; }
1259 
update(uint8_t ttl,uint32_t seqnum,uint64_t timestamp,uint64_t now,uint64_t recvts,bool ssm)1260 void beaconSource::update(uint8_t ttl, uint32_t seqnum, uint64_t timestamp, uint64_t now, uint64_t recvts, bool ssm) {
1261 	if (verbose > 2)
1262 		info("beacon(%s%s) update %u, %llu, %llu",
1263 			name.c_str(), (ssm ? "/SSM" : ""), seqnum, timestamp, now);
1264 
1265 	beaconMcastState *st = ssm ? &SSM : &ASM;
1266 
1267 	st->update(ttl, seqnum, timestamp, now, recvts);
1268 }
1269 
rxlocal(uint64_t now) const1270 bool beaconSource::rxlocal(uint64_t now) const {
1271 	return (now - lastlocalevent) < timeFact(timeOutI);
1272 }
1273 
beaconMcastState()1274 beaconMcastState::beaconMcastState() {
1275 	refresh(0, 0);
1276 }
1277 
refresh(uint32_t seq,uint64_t now)1278 void beaconMcastState::refresh(uint32_t seq, uint64_t now) {
1279 	lastseq = seq;
1280 	s.timestamp = 0;
1281 	s.lastupdate = now;
1282 
1283 	packetcount = packetcountreal = 0;
1284 	pointer = 0;
1285 
1286 	lastdelay = lastjitter = lastloss = lastdup = lastooo = 0;
1287 	s.avgdelay = s.avgjitter = s.avgloss = s.avgdup = s.avgooo = 0;
1288 	s.valid = false;
1289 }
1290 
abs64(int64_t foo)1291 int64_t abs64(int64_t foo) { return foo < 0 ? -foo : foo; }
1292 
1293 // logic adapted from java beacon
1294 
update(uint8_t ttl,uint32_t seqnum,uint64_t timestamp,uint64_t tsnow,uint64_t _now)1295 void beaconMcastState::update(uint8_t ttl, uint32_t seqnum, uint64_t timestamp, uint64_t tsnow, uint64_t _now) {
1296 	/*
1297 	 * ttl - received TTL
1298 	 * seqnum - received seqnum in probe
1299 	 * timestamp - received timestamp in probe (timeofday in sender)
1300 	 * _now - when this packet was received locally (timeofday of host)
1301 	 */
1302 
1303 	int64_t now = (uint32_t)_now;
1304 
1305 	int64_t diff = now - timestamp;
1306 	int64_t absdiff = abs64(diff);
1307 
1308 	if (udiff(seqnum, lastseq) > PACKETS_VERY_OLD) {
1309 		refresh(seqnum - 1, tsnow);
1310 	}
1311 
1312 	if (seqnum < lastseq && (lastseq - seqnum) >= packetcount)
1313 		return;
1314 
1315 	s.timestamp = timestamp;
1316 	s.lastupdate = tsnow;
1317 
1318 	bool dup = false;
1319 
1320 	uint32_t expectseq = lastseq + 1;
1321 
1322 	if (seqnum < expectseq) {
1323 		for (uint32_t i = 0; i < pointer; i++) {
1324 			if (cacheseqnum[i] == seqnum) {
1325 				dup = true;
1326 				break;
1327 			}
1328 		}
1329 	}
1330 
1331 	s.rttl = ttl;
1332 
1333 	if (dup) {
1334 		lastdup ++;
1335 	} else {
1336 		packetcountreal++;
1337 
1338 		cacheseqnum[pointer++] = seqnum;
1339 
1340 		lastdelay += diff;
1341 
1342 		int newjitter = absdiff - lastjitter;
1343 		lastjitter = absdiff;
1344 		if (newjitter < 0)
1345 			newjitter = -newjitter;
1346 		s.avgjitter = 15/16. * s.avgjitter + 1/16. * newjitter;
1347 
1348 		if (expectseq == seqnum) {
1349 			packetcount ++;
1350 		} else if (seqnum > expectseq) {
1351 			packetcount += seqnum - lastseq;
1352 
1353 			lastloss += seqnum - lastseq - 1;
1354 		} else {
1355 			lastloss --;
1356 			lastooo ++;
1357 		}
1358 
1359 		if (expectseq <= seqnum) {
1360 			lastseq = seqnum;
1361 		}
1362 	}
1363 
1364 	if (packetcount >= PACKETS_PERIOD) {
1365 		s.avgdelay = lastdelay / (float)packetcountreal;
1366 		s.avgloss = lastloss / (float)packetcount;
1367 		s.avgooo = lastooo / (float)packetcount;
1368 		s.avgdup = lastdup / (float)packetcount;
1369 
1370 		s.valid = true;
1371 
1372 		lastdelay = 0;
1373 		lastloss = 0;
1374 		lastooo = 0;
1375 		lastdup = 0;
1376 		packetcount = 0;
1377 		packetcountreal = 0;
1378 		pointer = 0;
1379 	}
1380 }
1381 
send_nprobe(const address & addr,uint32_t & seq)1382 static int send_nprobe(const address &addr, uint32_t &seq) {
1383 	int len;
1384 
1385 	len = build_probe(buffer, bufferLen, seq, get_time_of_day());
1386 	seq++;
1387 
1388 	len = sendto(mcastSock, buffer, len, 0, addr.saddr(), addr.addrlen());
1389 	if (len > 0)
1390 		bytesSent += len;
1391 	return len;
1392 }
1393 
send_probe()1394 int send_probe() {
1395 	static uint32_t seq = rand();
1396 
1397 	return send_nprobe(probeAddr, seq);
1398 }
1399 
send_ssm_probe()1400 int send_ssm_probe() {
1401 	static uint32_t seq = rand();
1402 
1403 	return send_nprobe(ssmProbeAddr, seq);
1404 }
1405 
send_report(int type)1406 int send_report(int type) {
1407 	int len;
1408 
1409 	len = build_report(buffer, bufferLen, type == SSM_REPORT ? STATS_REPORT : type, true);
1410 	if (len < 0)
1411 		return len;
1412 
1413 	int res;
1414 
1415 	if (type == SSM_REPORT) {
1416 		if ((res = sendto(mcastSock, buffer, len, 0, ssmProbeAddr.saddr(), ssmProbeAddr.addrlen())) < 0) {
1417 			cerr << "Failed to send SSM report: " << strerror(errno) << endl;
1418 		} else {
1419 			bytesSent += res;
1420 		}
1421 	} else {
1422 		for (vector<address>::const_iterator i = redist.begin(); i != redist.end(); i++) {
1423 			const address *to = &(*i);
1424 
1425 			char tmp[64];
1426 			to->print(tmp, sizeof(tmp));
1427 
1428 			if (verbose) {
1429 				cerr << "Sending Report to " << tmp << endl;
1430 			}
1431 
1432 			if ((res = sendto(mcastSock, buffer, len, 0, to->saddr(), to->addrlen())) < 0) {
1433 				cerr << "Failed to send report to " << tmp << ": " << strerror(errno) << endl;
1434 			} else {
1435 				bytesSent += res;
1436 			}
1437 		}
1438 	}
1439 
1440 	return 0;
1441 }
1442 
dumpStats(FILE * fp,const char * tag,const Stats & s,uint64_t now,int sttl,bool diff)1443 void dumpStats(FILE *fp, const char *tag, const Stats &s, uint64_t now, int sttl, bool diff) {
1444 	fprintf(fp, "\t\t\t\t<%s", tag);
1445 	if (!diff)
1446 		fprintf(fp, " ttl=\"%i\"", s.rttl);
1447 	else if (sttl)
1448 		fprintf(fp, " ttl=\"%i\"", sttl - s.rttl);
1449 	fprintf(fp, " rptage=\"%u\"", (uint32_t)((now - s.lastupdate) / 1000));
1450 	fprintf(fp, " loss=\"%.1f\"", s.avgloss * 100);
1451 	fprintf(fp, " delay=\"%.3f\"", fabs(s.avgdelay));
1452 	if (s.avgdelay < 0) {
1453 		fprintf(fp, " future=\"true\"");
1454 	}
1455 	fprintf(fp, " jitter=\"%.3f\"", s.avgjitter);
1456 	fprintf(fp, " ooo=\"%.3f\"", s.avgooo * 100);
1457 	fprintf(fp, " dup=\"%.3f\"", s.avgdup * 100);
1458 	fprintf(fp, " />\n");
1459 }
1460 
1461 static void doLaunchSomething();
1462 
do_dump()1463 void do_dump() {
1464 	string tmpf = dumpFile;
1465 	tmpf += ".working";
1466 
1467 	FILE *fp = fopen(tmpf.c_str(), "w");
1468 	if (!fp)
1469 		return;
1470 
1471 	char tmp[64];
1472 
1473 	uint64_t now = get_timestamp();
1474 	uint64_t diff = now - lastDumpDumpBwTS;
1475 	lastDumpDumpBwTS = now;
1476 
1477 	double rxRate = dumpBytesReceived * 8 / ((double)diff);
1478 	double txRate = dumpBytesSent * 8 / ((double)diff);
1479 	dumpBytesReceived = 0;
1480 	dumpBytesSent = 0;
1481 
1482 	fprintf(fp, "<beacons rxrate=\"%.2f\" txrate=\"%.2f\" versioninfo=\"%s\">\n", rxRate, txRate, versionInfo);
1483 
1484 	fprintf(fp, "<group addr=\"%s\"", sessionName);
1485 
1486 	if (ssmMcastSock) {
1487 		ssmProbeAddr.print(tmp, sizeof(tmp));
1488 		fprintf(fp, " ssmgroup=\"%s\"", tmp);
1489 	}
1490 
1491 	fprintf(fp, " int=\"%.2f\">\n", beacInt);
1492 
1493 	if (!probeAddr.is_unspecified()) {
1494 		beaconUnicastAddr.print(tmp, sizeof(tmp));
1495 
1496 		fprintf(fp, "\t<beacon name=\"%s\" addr=\"%s\"", beaconName.c_str(), tmp);
1497 		if (!adminContact.empty())
1498 			fprintf(fp, " contact=\"%s\"", adminContact.c_str());
1499 		if (!twoLetterCC.empty())
1500 			fprintf(fp, " country=\"%s\"", twoLetterCC.c_str());
1501 		fprintf(fp, " age=\"%lu\" lastupdate=\"0\" rxlocal=\"true\">\n", (now - startTime) / 1000);
1502 
1503 		for (uint32_t k = 0; k < KnownFlags; k++) {
1504 			if (flags & (1 << k)) {
1505 				fprintf(fp, "\t\t<flag name=\"%s\" value=\"true\" />\n", Flags[k]);
1506 			}
1507 		}
1508 
1509 		for (WebSites::const_iterator j = webSites.begin(); j != webSites.end(); j++) {
1510 			const char *typnam = j->first == T_WEBSITE_GENERIC ?
1511 				"generic" : (j->first == T_WEBSITE_LG ? "lg" : "matrix");
1512 			fprintf(fp, "\t\t<website type=\"%s\" url=\"%s\" />\n", typnam, j->second.c_str());
1513 		}
1514 
1515 		fprintf(fp, "\t\t<sources>\n");
1516 
1517 		for (Sources::const_iterator i = sources.begin(); i != sources.end(); i++) {
1518 			i->first.print(tmp, sizeof(tmp));
1519 			fprintf(fp, "\t\t\t<source addr=\"%s\"", tmp);
1520 			if (i->second.identified) {
1521 				fprintf(fp, " name=\"%s\"", i->second.name.c_str());
1522 				if (!i->second.adminContact.empty())
1523 					fprintf(fp, " contact=\"%s\"", i->second.adminContact.c_str());
1524 			}
1525 
1526 			if (!i->second.CC.empty())
1527 				fprintf(fp, " country=\"%s\"", i->second.CC.c_str());
1528 
1529 			fprintf(fp, " age=\"%lu\"", (now - i->second.creation) / 1000);
1530 			fprintf(fp, " lastupdate=\"%lu\">\n", (now - i->second.lastevent) / 1000);
1531 
1532 			if (i->second.ASM.s.valid)
1533 				dumpStats(fp, "asm", i->second.ASM.s, now, i->second.sttl, true);
1534 
1535 			if (i->second.SSM.s.valid)
1536 				dumpStats(fp, "ssm", i->second.SSM.s, now, i->second.sttl, true);
1537 
1538 			fprintf(fp, "\t\t\t</source>\n");
1539 		}
1540 
1541 		fprintf(fp, "\t\t</sources>\n");
1542 		fprintf(fp, "\t</beacon>\n");
1543 		fprintf(fp, "\n");
1544 	}
1545 
1546 	for (Sources::const_iterator i = sources.begin(); i != sources.end(); i++) {
1547 		fprintf(fp, "\t<beacon");
1548 		if (i->second.identified) {
1549 			fprintf(fp, " name=\"%s\"", i->second.name.c_str());
1550 			if (!i->second.adminContact.empty())
1551 				fprintf(fp, " contact=\"%s\"", i->second.adminContact.c_str());
1552 		}
1553 		i->first.print(tmp, sizeof(tmp));
1554 		fprintf(fp, " addr=\"%s\"", tmp);
1555 		fprintf(fp, " age=\"%lu\"", (now - i->second.creation) / 1000);
1556 		fprintf(fp, " rxlocal=\"%s\"", i->second.rxlocal(now) ? "true" : "false");
1557 		fprintf(fp, " lastupdate=\"%lu\">\n", (now - i->second.lastevent) / 1000);
1558 
1559 		for (uint32_t k = 0; k < KnownFlags; k++) {
1560 			if (i->second.Flags & (1 << k)) {
1561 				fprintf(fp, "\t\t<flag name=\"%s\" value=\"true\" />\n", Flags[k]);
1562 			}
1563 		}
1564 
1565 		for (WebSites::const_iterator j = i->second.webSites.begin();
1566 						j != i->second.webSites.end(); j++) {
1567 			const char *typnam = j->first == T_WEBSITE_GENERIC ?
1568 				"generic" : (j->first == T_WEBSITE_LG ? "lg" : "matrix");
1569 			fprintf(fp, "\t\t<website type=\"%s\" url=\"%s\" />\n",
1570 						typnam, j->second.c_str());
1571 		}
1572 
1573 		fprintf(fp, "\t\t<sources>\n");
1574 
1575 		for (beaconSource::ExternalSources::const_iterator j = i->second.externalSources.begin();
1576 				j != i->second.externalSources.end(); j++) {
1577 			fprintf(fp, "\t\t\t<source");
1578 			if (j->second.identified) {
1579 				fprintf(fp, " name=\"%s\"", j->second.name.c_str());
1580 				fprintf(fp, " contact=\"%s\"", j->second.contact.c_str());
1581 			}
1582 			j->first.print(tmp, sizeof(tmp));
1583 			fprintf(fp, " addr=\"%s\"", tmp);
1584 			fprintf(fp, " age=\"%u\">\n", j->second.age);
1585 			if (j->second.ASM.valid)
1586 				dumpStats(fp, "asm", j->second.ASM, now, i->second.sttl, false);
1587 			if (j->second.SSM.valid)
1588 				dumpStats(fp, "ssm", j->second.SSM, now, i->second.sttl, false);
1589 			fprintf(fp, "\t\t\t</source>\n");
1590 		}
1591 
1592 		fprintf(fp, "\t\t</sources>\n");
1593 		fprintf(fp, "\t</beacon>\n");
1594 	}
1595 
1596 	fprintf(fp, "</group>\n</beacons>\n");
1597 
1598 	fclose(fp);
1599 
1600 	rename(tmpf.c_str(), dumpFile.c_str());
1601 
1602 	if (!launchSomething.empty())
1603 		doLaunchSomething();
1604 }
1605 
doLaunchSomething()1606 void doLaunchSomething() {
1607 	pid_t p = fork();
1608 	if (p == 0) {
1609 		execlp(launchSomething.c_str(), launchSomething.c_str(),
1610 		       dumpFile.c_str(), NULL);
1611 	}
1612 }
1613 
1614 static void
outputBwStats(uint32_t diff,uint64_t txbytes,double txrate,uint64_t rxbytes,double rxrate)1615 outputBwStats(uint32_t diff, uint64_t txbytes, double txrate, uint64_t rxbytes,
1616 				double rxrate) {
1617 	info("BW Usage for %u secs: RX %llu bytes (%.2f Kb/s) TX %llu "
1618 			"bytes (%.2f Kb/s)", diff, txbytes, txrate, rxbytes, rxrate);
1619 }
1620 
do_bw_dump(bool big)1621 void do_bw_dump(bool big) {
1622 	if (big) {
1623 		outputBwStats(600, bigBytesReceived, bigBytesReceived * 8 / (1000. * 600),
1624 					bigBytesSent, bigBytesSent * 8 / (1000. * 600));
1625 		bigBytesReceived = 0;
1626 		bigBytesSent = 0;
1627 		lastDumpBwTS = get_timestamp();
1628 	} else {
1629 		double incomingRate = bytesReceived * 8 / 10000.;
1630 
1631 		if (dumpBwReport) {
1632 			log(LOG_DEBUG, "BW: Received %u bytes (%.2f Kb/s) Sent %u bytes (%.2f Kb/s)",
1633 					bytesReceived, incomingRate, bytesSent, bytesSent * 8 / 10000.);
1634 		}
1635 
1636 		bigBytesReceived += bytesReceived;
1637 		bigBytesSent += bytesSent;
1638 		dumpBytesReceived += bytesReceived;
1639 		dumpBytesSent += bytesSent;
1640 		bytesReceived = 0;
1641 		bytesSent = 0;
1642 
1643 		// adjust beacInt
1644 		if (incomingRate < 4.)
1645 			incomingRate = 4.;
1646 
1647 		// Increase traffic will result in a larger interval between probe sending events
1648 		beacInt = 4 * (log(incomingRate) / 1.38);
1649 	}
1650 }
1651 
dumpBigBwStats(int)1652 void dumpBigBwStats(int) {
1653 	uint64_t diff = (get_timestamp() - lastDumpBwTS) / 1000;
1654 	outputBwStats((uint32_t)diff, bigBytesReceived, bigBytesReceived * 8 / (1000. * diff),
1655 					bigBytesSent, bigBytesSent * 8 / (1000. * diff));
1656 }
1657 
sendLeaveReport(int)1658 void sendLeaveReport(int) {
1659 	send_report(LEAVE_REPORT);
1660 	if (daemonize && pidfile)
1661 		unlink(pidfile);
1662 	exit(0);
1663 }
1664 
SetupFDSet(int sock)1665 void SetupFDSet(int sock) {
1666 	if (sock > largestSock)
1667 		largestSock = sock;
1668 
1669 	FD_SET(sock, &readSet);
1670 }
1671 
SetupSocketAndFDSet(const address & addr,bool shouldbind,bool ssm)1672 int SetupSocketAndFDSet(const address &addr, bool shouldbind, bool ssm) {
1673 	int sock = SetupSocket(addr, shouldbind, ssm);
1674 
1675 	if (sock > 0) {
1676 		SetupFDSet(sock);
1677 	}
1678 
1679 	return sock;
1680 }
1681 
1682