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 ¶m_format[j];
816
817 if (longonly)
818 continue;
819
820 if (!strcmp(arg, param_format[j].sf))
821 return ¶m_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