/* 1320, Sat 24 Feb 01 (PST) NMC.C: First attempt at a manager for the AU Accounting Meter Copyright (C) 1992-2002 by Nevil Brownlee, CAIDA | University of Auckland */ /* * $Log: nmc.c,v $ * Revision 1.1.1.2.2.11 2002/02/23 01:57:19 nevil * Moving srl examples to examples/ directory. Modified examples/Makefile.in * * Revision 1.1.1.2.2.6 2000/08/08 19:44:44 nevil * 44b8 release * * Revision 1.1.1.2.2.4 2000/06/06 03:38:11 nevil * Combine NEW_ATR with TCP_ATR, various bug fixes * * Revision 1.1.1.2.2.1 2000/01/12 02:57:03 nevil * Implement 'packet pair matched' turnaroundtime distribution attributes. * Fix ASN-related bugs in NeTraMet, distribution-related bugs in fd_filter. * * Revision 1.1.1.2 1999/10/03 21:06:16 nevil * *** empty log message *** * * Revision 1.1.1.1.2.5 1999/09/22 05:38:35 nevil * Improve code to work properly on 64-bit machines * - Add OS=ALPHA handling to configure.in * - Clean up the Alpha compiler warnings * - Change all the snmp-related code to use Bit32 instead of unsigned long * * Revision 1.1.1.1.2.4 1999/09/14 00:46:46 nevil * 4.3 Release .. * - Implement -D option to run NeMaC as a daemon * - Tidy up the on-line help displays * - Make IPv4 the default PeerType for sfmt_attribute() * * Revision 1.1.1.1.2.3 1999/05/25 22:30:49 nevil * Make sure IPv6 managers interwork properly with IPv4 meters * - Determine meter's RULE_ADDR_SIZE by reading mask from rule 1 * of default ruleset. * - Print warning if meter rulesize < manager rulesize. * - Use smaller of these when downloading rules. * * Revision 1.1.1.1.2.2 1999/01/08 01:38:30 nevil * Distribution file for 4.3b7 * * Revision 1.1.1.1.2.1 1998/12/23 01:14:10 nevil * Distribution file for 4.3b6 * * Revision 1.1.1.1 1998/11/16 03:57:27 nevil * Import of NeTraMet 4.3b3 * * Revision 1.1.1.1 1998/11/16 03:22:00 nevil * Import of release 4.3b3 * * Revision 1.1.1.1.2.1 1998/11/11 23:14:38 nevil * Only include malloc.h if we HAVE_MALLOC_H * * Revision 1.1.1.1 1998/10/28 20:31:24 nevil * Import of NeTraMet 4.3b1 * * Revision 1.1.3.2.2.2 1998/10/26 22:18:22 nevil * Tidy up help message a little * * Revision 1.1.3.2.2.1 1998/10/23 03:57:01 nevil * Include set_mibfile() prototype from parse.h * * Revision 1.1.3.2 1998/10/18 23:44:08 nevil * Added Nicolai's patches, some 'tidying up' of the source * * Revision 1.1.3.1 1998/10/13 02:48:21 nevil * Import of Nicolai's 4.2.2 * * Revision 1.15 1998/10/08 09:22:30 nguba * -Added -b switch to NeMaC so that MIBs can be loaded from the command * line instead of relying on the current working directory or an * environment variable. I have a mib in /etc/mib.txt but it gives me * errors. Being able to give a MIB via command line helps. * -I also had to increase the size of fn[64] to 256, which is --I * belive-- the maximum a Unix command line can take. I wonder whether * Unix systems define this one somewhere... Anyway, this variable is * #define'd via MAXLINE. * -Use the function set_mibfile(char*) to set the mibfile via * command-line. This function is defined in parse.h, implemented in * mib.c and is available via libsnmp.a * * Revision 1.14 1998/10/02 14:59:12 nguba * Added command line options/help screen for NeMaC * * Revision 1.12 1998/06/03 04:52:11 rtfm * Don't set snmp_delay (only done in nmc_snmp.c) * Use LastTime instead of sysUptime * * Revision 1.8 1998/04/23 00:48:20 rtfm * Fix bad call to printfd. Nevil * * Revision 1.6 1998/04/22 22:36:14 kawai * -Restructured flow data output. All writes to flowdata file now * go through printfd new routine to flush flow files flush_flows. * -Putting '+' on end of flowdata file name causes output to be * piped to stdout too. * -Deleted the 'restart' logic from monitor -- it never worked. * * Revision 1.5 1998/03/17 21:16:18 kawai * added signal handler for USR2 to turn testing on and off * * Revision 1.4 1998/03/17 03:32:03 kawai * -Signal USR1 now causes NeMaC to roll over the flow files * (same as flag file). * -Modified signal handling to raise SIGALRM so that sleep exits * and requested action is performed immediately * -Added code to boundcheck all strings passed from the command line or * config file. Over-long strings are simply truncated (no errors issued) * * Revision 1.3 1998/03/13 03:07:29 kawai * Moved initial open of log file to main line. * If append_log then don't create new logfile. * * Revision 1.2 1998/03/11 02:44:33 kawai * Moved the event scheduling code out of create_meter into mainline. * Changed var syslog to syslog_name and made it global. * * Revision 1.1 1998/03/09 23:05:58 kawai * -Changed all printf to fprintf(stderr, ... * -Changed all * check_log(1) * fprintf(logfl,... * check_log(0); * to: log_msg( LOG_INFO, FALSE, ..... * -Changed create_meter to increment nmeters even if meter does * not respond. changed main loop to try and create_meters if * status != MT_MANAGE */ #if HAVE_CONFIG_H #include #endif #define TEST_MIR 0 static char rcsid[] = "$Id: nmc.c,v 1.1.1.2.2.11 2002/02/23 01:57:19 nevil Exp $"; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_SYS_SELECT_H # include #endif #if HAVE_MALLOC_H # include #endif #include #define EXTSNMP #include "ausnmp.h" #include "asn1.h" #include "snmp.h" #include "snmpimpl.h" #include "snmpapi.h" #include "snmpclnt.h" #include "mib.h" #include "parse.h" #define EXTERN #include "nmc.h" #include "nmc_c64.h" char *ver = "NeMaC: NeTraMet Manager & Controller " ver_str "\n"; int request_stop = 0; int request_rollover = 0; void sigint_handler(int x) /* INT (keyboard) and TERM (kill) */ { /* To make ^c send INT, use stty intr '^c' */ request_stop = 1; } void sigusr1_handler(int x) /* kill -USR1 */ { request_rollover = 1; } void sigusr2_handler(int x) /* kill -USR2 */ { testing = !testing; } void no_write_warning(struct meter_status *ms) { if (ms->no_write_warned) return; fprintf(stderr, "Community %s doesn't have write access to meter %s!\n" " Collections won't trigger recovery of idle flows <<<\n", ms->community,ms->name); log_msg(LOG_INFO, FALSE, "Community %s doesn't have write access to meter!\n" " Collections won't trigger recovery of idle flows <<<\n", ms->community,ms->name); if (ms->statsreqd) { fprintf(stderr, " Meter statistics won't be zeroed after collections <<<\n"); log_msg(LOG_INFO, FALSE, " Meter statistics won't be zeroed after collections <<<\n"); } ms->no_write_warned = 1; } void add_event(struct calendar_entry *entry, struct meter_status *ms) { struct calendar_entry *cp, *lcp; entry->next_event = ms->next_event; entry->ms = ms; entry->next = NULL; if (calendar == NULL) { /* First entry in queue */ calendar = entry; return; } lcp = NULL; cp = calendar; do { if (ms->next_event < cp->next_event) { entry->next = cp; if (lcp == NULL) calendar = entry; /* Add to head */ else lcp->next = entry; /* Link into queue */ return; } lcp = cp; cp = cp->next; } while (cp != NULL); lcp->next = entry; /* Add to tail */ } /* functions to deal with flow data files */ void flush_flows (struct meter_status *ms) { if (ms->append_flowdata) { fclose(ms->flows); ms->flows = NULL; } else fflush(ms->flows); } void printfd(struct meter_status *ms, char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(ms->flows, fmt, ap); } int open_datafile(struct meter_status *ms) { char filename[NAME_LN*2], *cp; unsigned char a,b, n; time_t t; char *ts; if (!(ms->status & MT_UP)) /* Meter not reachable! */ return 0; /* We've already displayed a message */ if (ms->user_flowdatafile) /* User specified data file name */ ms->flows = fopen(ms->flowdatafile,"w"); /* Note: Don't open "a" here. Doing so produces a file with a ## record in the middle, and possibly with a group of flow records truncated at a block boundary as well. It's better to rename such a 'crashed' file in a startup script. */ else { sprintf(filename, "%s.flows", ms->name); /* Open flows file */ ms->flows = wfopen(ms->flowdatafile,filename); } if (ms->flows == NULL) { fprintf(stderr, "Failed to open flow data file for meter %s!\n", ms->name); log_msg(LOG_INFO, FALSE, "Failed to open flow data file for meter %s!\n", ms->name); return 0; } time(&t); ts = fmt_time(&t); printfd(ms, "##NeTraMet v%s: -", ms->version); if (ms->trace_interval == 0) printfd(ms, "c%d", ms->sample_interval); else { if (ms->crl_info != NULL) ms->trace_interval = atoi(ms->crl_info); printfd(ms, "T%d", ms->trace_interval); } printfd(ms, " -r %s ", ms->c_ruleset.filename); if (ms->s_ruleset.filename[0] != 0) printfd(ms, "-e %s ",ms->s_ruleset.filename); printfd(ms, " %s %s %u flows starting at %s%s\n", ms->name, ms->interface, ms->MaxFlows, ts, utc_time ? " (UTC)" : ""); if (ms->crl_info != NULL) { for (cp = ms->crl_info; *cp != ' '; ++cp); printfd(ms, "#CoralReef %s\n", cp+1); } printfd(ms, "#Format: "); if ((n = ms->format[a = 0]) != 0) for (;;) { for (b = 1; b <= SZ_ATTRIBS && attribs[b].index != n; ++b) ; if (b <= SZ_ATTRIBS) printfd(ms, attribs[b].name); else printfd(ms,"attr*%u", n); /* Unknown attribute */ if ((n = ms->format[a+1]) == 0) { if (strcmp(ms->separator[a]," ") != 0) printfd(ms, ms->separator[a]); break; } printfd(ms, ms->separator[a++]); } printfd(ms, "\n"); flush_flows(ms); ms->restarting = 1; /* Write #Ruleset records */ return 1; } void ruleset_row(struct meter_status *ms, struct row_info *rip) { struct meter_rule_info mri; if (testing) fprintf(stderr, ">> ruleset %d: Status=%d, Owner=%s, Name=%s\n", rip->tr_Index, rip->tr_Status, rip->tr_Owner, rip->tr_Name); if (!ms->use_meter_rows) { /* Clear meter rows */ if (strcmp(rip->tr_Owner, ms->owner_name) == 0) { mri.ri_Index = rip->tr_Index; ruleset_util(ms, RU_DESTROY, &mri); } else if (strcmp(rip->tr_Owner, "NeTraMet") == 0) { /* We've found NeTraMet meter's default ruleset */ ms->d_ruleset.ri_Index = rip->tr_Index; } } else { /* Use rows already in meter */ if (ms->download_level != 2) { /* We are the 'master' manager */ if (strcmp(rip->tr_Owner, ms->owner_name) == 0) { if (rip->tr_Index == ms->c_ruleset.ri_Index) { ms->c_ruleset.ri_RulesReady = rip->tr_Status == RS_ACTIVE; if (strcmp(rip->tr_Name, ms->c_ruleset.ri_Name) != 0) fprintf(stderr, "Old 'current' ruleset name != SET name <<<\n"); } if (rip->tr_Index == ms->s_ruleset.ri_Index) { ms->s_ruleset.ri_RulesReady = rip->tr_Status == RS_ACTIVE; if (strcmp(rip->tr_Name, ms->s_ruleset.ri_Name) != 0) fprintf(stderr, "Old 'standby' ruleset name != SET name <<<\n"); } } } else { /* We are a 'secondary' manager */ if(strcmp(rip->tr_Name, ms->c_ruleset.ri_Name) == 0) { ms->c_ruleset.ri_Index = rip->tr_Index; ms->c_ruleset.ri_RulesReady = rip->tr_Status == RS_ACTIVE; fprintf(stderr, " Current rule set is row %d\n", ms->c_ruleset.ri_Index); } if(strcmp(rip->tr_Name, ms->s_ruleset.ri_Name) == 0) { ms->s_ruleset.ri_Index = rip->tr_Index; ms->s_ruleset.ri_RulesReady = rip->tr_Status == RS_ACTIVE; fprintf(stderr, " Standby rule set is row %d\n", ms->s_ruleset.ri_Index); } } } } void manager_row(struct meter_status *ms, struct row_info *rip) { if (testing) fprintf(stderr, ">> manager %d: Status=%d, Owner=%s, Current=%d, Standby=%d\n", rip->tr_Index, rip->tr_Status, rip->tr_Owner, rip->tr_Current, rip->tr_Standby); if (!ms->use_meter_rows) { /* Clear meter rows */ if (strcmp(rip->tr_Owner, ms->owner_name) == 0 || strcmp(rip->tr_Owner, "NeTraMet") == 0) { ms->mi_u_Index = rip->tr_Index; task_util(ms, TU_DESTROY); } } else { /* Use rows already in meter */ if (ms->download_level != 2) { /* We are the 'master' manager */ if (strcmp(rip->tr_Owner, ms->owner_name) == 0) { ms->mi_Index = rip->tr_Index; ms->c_ruleset.ri_Index = rip->tr_Current; ms->s_ruleset.ri_Index = rip->tr_Standby; } } else { /* We are a 'secondary' manager */ /* Have to discover the ruleset rows, then make a new task */ } } } void reader_row(struct meter_status *ms, struct row_info *rip) { if (testing) fprintf(stderr, ">> reader %d: Status=%d, Owner=%s\n", rip->tr_Index, rip->tr_Status, rip->tr_Owner); if (strcmp(rip->tr_Owner, ms->owner_name) == 0) { if (ms->download_level == 2) fprintf(stderr, "Download level 2: Another manager running for this Owner <<<\n"); else { ms->ci_u_Index = rip->tr_Index; reader_util(ms, CU_DESTROY, CU_UTIL); } } } int create_meter(struct meter_status *ms, time_t first_t, char *pn) { int n, syntax, reachable; if (testing) fprintf(stderr, "About to create_meter name=%s, community=%s, owner=%s\n", ms->name, ms->community, ms->owner_name); if (ms->name[0] == '\0' || ms->community[0] == '\0') { fprintf(stderr, "Meter name or community not specified !!!\n"); return 0; } ++nmeters; if (!start_snmp_session(ms)) { log_msg(LOG_INFO, FALSE, "Meter %s failed to respond: it may be dead, unreachable or " "the community string may be wrong\n", ms->name); return 0; } ms->OurLastCollectTime = 0; /* Used to be 1L */ ms->status = MT_MANAGE; if (reachable = meter_info(ms)) { ms->status |= (MT_UP | MT_INFO); meter_print(stdout,ms); if (testing) { meter_print(NULL,ms); /* Print to the log file */ } #ifndef _AIX if (!meter_is_current(ms)) { fprintf(stderr, "Warning: meter %s (version %s) not same as %s!\n", ms->name,ms->version, pn); log_msg(LOG_INFO, FALSE, "Meter %s (version %s) not same as %s!\n", ms->name,ms->version, pn); } #endif #if V6 if (ms->meter_ra_len < RULE_ADDR_LEN) { fprintf(stdout, "Meter %s is not IPv6 capable!\n", ms->name); log_msg(LOG_WARNING, FALSE, "Meter %s is not IPv6 capable!\n", ms->name); } #endif } else { ms->status &= ~MT_UP; /* Mark as 'down' */ fprintf(stderr, "Couldn't get meter info from %s!\n", ms->name); fprintf(stderr, " Does community %s have read or write access to the meter?\n", ms->community); log_msg(LOG_INFO, FALSE, "Couldn't get meter info from %s!\n", ms->name); log_msg(LOG_INFO, FALSE, " Does community %s have read or write access to the meter?\n", ms->community); } if (testing) fprintf(stderr, "t=%u, next_event=%u, next_keepalive=%u, next_sample=%u\n", first_t, ms->next_event, ms->next_keepalive, ms->next_sample); if (reachable) { if (ms->trace_interval != 0) { /* User expceted a Trace File */ if (meter_maj(ms) < 4 || meter_min(ms) < 4) { fprintf(stderr,"Meter doesn't support trace files <<<\n"); log_msg(LOG_INFO, FALSE, "Meter doesn't support trace files <<<"); goto abort; } else if (ms->crl_info == NULL) { fprintf(stderr,"Meter isn't reading a trace file !!!\n"); log_msg(LOG_INFO, FALSE, "Meter isn't reading a trace file !!!"); goto abort; } } if (ms->download_level == 0) set_meter_params(ms); search_table(ms, ST_READER, reader_row); if (ms->download_level == 0) { search_table(ms, ST_MANAGER, manager_row); search_table(ms, ST_RULESET, ruleset_row); } else ms->use_meter_rows = 1; syntax = ms->download_level != 0; /* 0 => initial download of rules */ if (testing) fprintf(stderr, "standby rf %s, current rf %s\n", ms->s_ruleset.filename, ms->c_ruleset.filename); if (ms->s_ruleset.filename[0] != '\0') { n = parse_rulefile(ms,listrules,syntax,1,0); /* 0 => initial load */ if (!n || rferrors != 0) goto abort; } if (ms->c_ruleset.filename[0] == '\0') /* No rule file specified */ ms->ruleset = ms->CurrentRuleSet; else { /* Download the rules we want to use */ n = parse_rulefile(ms,listrules,syntax,0,0); /* Initial load */ if (!n || rferrors != 0) goto abort; } if (ms->use_meter_rows && ms->MIBver == 4) { /* Use running rule sets */ if (ms->download_level == 2 ) { search_table(ms, ST_RULESET, ruleset_row); /* Find rulesets */ if (ms->c_ruleset.ri_Index == 0) { fprintf(stderr, "Couldn't find ruleset %s on meter %s\n", ms->c_ruleset.ri_Name, ms->name); log_msg(LOG_INFO, FALSE, "Couldn't find ruleset %s on meter %s\n", ms->c_ruleset.ri_Name, ms->name); goto abort; } task_util(ms, TU_INIT); /* Create new manager task to use them */ if (ms->s_ruleset.filename[0] != '\0') task_util(ms, TU_STANDBY); task_util(ms, TU_CURRENT); } else { search_table(ms, ST_MANAGER, manager_row); search_table(ms, ST_RULESET, ruleset_row); } } reader_util(ms, CU_INIT, CU_CURRENT); reader_util(ms, CU_SET_MINPDUS, CU_CURRENT); if (ms->s_ruleset.filename[0] != '\0') reader_util(ms, CU_INIT, CU_STANDBY); if (!ms->write_OK) no_write_warning(ms); if (!open_datafile(ms)) goto abort; } if (testing) fprintf(stderr, "create_meter(%s): UpTime=%lu\n", ms->name, ms->uptime); return 1; abort: --nmeters; return 0; } void make_new_data_files(void) { struct meter_status *ms; if (testing) fprintf(stderr, "Making new data files\n"); for (ms = first_meter; ms; ms = ms->next) { if(ms->flows) fclose(ms->flows); /* Close old flow data file */ ms->flows = NULL; open_datafile(ms); /* Open new one */ } unlink(FLAGFILE); } char cfname[NAME_LN], fdfname[NAME_LN], rfname[NAME_LN], srfname[NAME_LN], meter[NAME_LN], owner[NAME_LN], community[NAME_LN]; int def_sync, def_sample_interval, def_keepalive_interval, def_ci_Timeout, def_ci_MinPDUs, def_GCIntervalReqd, def_InactivityTime, def_HighWaterMark, def_FloodMark, def_SamplingRate, def_lag, def_download, def_no_write_meter, def_append_flowdata, def_trace_interval, def_download_only; int main(int argc, char *argv[]) { int syntax, daemon; char *ap, arg[NAME_LN]; int a, j, cs; time_t t1,t2; int busy_seconds; struct meter_status *ms, *nms; struct calendar_entry *cp; struct stat stat_buf; char have_config_file; FILE *cnf; #define DEBUG_TRACE_STATE 0 #if DEBUG_TRACE_STATE int last_cs = -1; #endif if (argc < 2) { /* Help Screen when called with no args */ fprintf(stderr, "\nUsage: %s [OPTION]... meter-hostname community owner-name\n" "Combined manager and collector for the NeTraMet meter:\n\n" " -a SEC \t Seconds collections are to lag after their\n" " \t synchronised time\n" " -b MBF \t Read in alternate mib file, MBF\n" " -c SEC \t Required collection interval (seconds)\n" " \t If SEC is zero program will download rule files\n" " \t to the meter, then exit without collecting any\n" " \t flow data\n" " -e RLE \t Read in rules from file RLE\n" " -f CFG \t Read in configuration from file CFG\n" " -g GCI \t Specify garbage collection interval (seconds)\n" " -h HWM \t Set high water mark as percentage of flow space\n" " -i ITO \t Inactivity timeout interval (seconds)\n" " -k KII \t Keepalive interval (seconds)\n" " -l \t List rules as they are processed\n" " -m PNO \t Specify UDP port to use for communication with meter\n" " -o FLM \t Set flood mark as percentage of flow space\n" " -p \t Open-append-close flow data and log files\n" " \t Start new data file if old one is moved or renamed\n" " -r RLE \t Give name of rule file RLE, to be read and\n" " \t downloaded to one or more meters\n" " -s \t Check syntax of rule file but don't download to meter\n" " -t \t Provide extra diagnostic output for testing\n" " -u \t Samples should be unsyncronized, i.e. every SEC\n" " \t seconds from NeMaC startup\n" " -v \t Verbose mode\n" " -w DLL \t Download level\n" " \t DLL=0, download after startup and meter restart\n" " \t DLL=1, download only after meter restart\n" " \t DLL=2, never download\n" " -x \t Don't write anything to the meter\n" " -D \t Run NeMaC as a daemon\n" " -E SEC \t Timeout in seconds for reader rows in meter\n" " -F FDF \t Name of flow data file to write\n" " -L NAM \t Name of log file to write\n" " -M MPK \t Only retrieve flows with MPK packets to or from\n" " -P \t Open-append-close log file (subset of -p)\n" "\n" "For more information see the NeTraMet Reference Manual and User Guide.\n" "\n" "Report bugs to netramet@auckland.ac.nz\n", argv[0]); exit(0); } signal(SIGINT, sigint_handler); signal(SIGTERM, sigint_handler); signal(SIGUSR1, sigusr1_handler); signal(SIGUSR2, sigusr2_handler); syslog_name[0] = '\0'; incl_depth = syntax = daemon = verbose = testing = listrules = standard = utc_time = 0; def_sync = 1; /* Samples synchronised with TOD clock */ def_sample_interval = 120; /* Default 2 minutes */ def_lag = 0; /* No time lag for collections */ def_download = 0; /* Download rule sets on startup */ def_keepalive_interval = 0; /* Default no keepalives */ def_ci_Timeout = 0; /* NeMaC collector rows never time out */ def_ci_MinPDUs = 0; /* Reader MinPDU filter value */ def_HighWaterMark = 65; /* NeMaC uses old (v3) default */ def_GCIntervalReqd = /* Use meter defaults for MIB variables */ def_InactivityTime = def_SamplingRate = def_FloodMark = 0; def_append_flowdata = def_no_write_meter = 0; def_trace_interval = def_download_only = 0; strcpy(cfname, CFGFILE); srfname[0] = rfname[0] = fdfname[0] = meter[0] = community[0] = owner[0] = '\0'; user_logfile[0] = '\0'; append_log = 0; for (a = 1; a < argc; ++a) { if (argv[a][0] == '-') { ap = argv[a]+2; switch (argv[a][1]) { case 'a': if (*ap == '\0') ap = argv[++a]; def_lag = atoi(ap); break; case 'b': if (*ap == '\0') ap = argv[++a]; set_mibfile(ap); break; case 'c': if (*ap == '\0') ap = argv[++a]; j = atoi(ap); if (j == 0) def_download_only = 1; else def_sample_interval = j; break; case 'd': snmp_dump_packet++; break; case 'e': if (*ap == '\0') ap = argv[++a]; strcpy(srfname, ap); /* Standby rule file name */ break; case 'f': if (*ap == '\0') ap = argv[++a]; strncpy(cfname, ap, NAME_LN); /* Config file name */ cfname[NAME_LN-1] = '\0'; break; case 'g': if (*ap == '\0') ap = argv[++a]; def_GCIntervalReqd = atoi(ap); break; case 'h': if (*ap == '\0') ap = argv[++a]; def_HighWaterMark = atoi(ap); break; case 'i': if (*ap == '\0') ap = argv[++a]; def_InactivityTime = atoi(ap); break; case 'k': if (*ap == '\0') ap = argv[++a]; def_keepalive_interval = atoi(ap); break; case 'l': listrules++; break; case 'm': if (*ap == '\0') ap = argv[++a]; au_snmp_port = atoi(ap); break; case 'n': if (*ap == '\0') ap = argv[++a]; def_SamplingRate = atoi(ap); break; case 'o': if (*ap == '\0') ap = argv[++a]; def_FloodMark = atoi(ap); break; case 'p': def_append_flowdata++; append_log++; break; case 'r': if (*ap == '\0') ap = argv[++a]; strncpy(rfname, ap, NAME_LN); /* Rule file name */ rfname[NAME_LN-1] = '\0'; break; case 's': syntax++; break; case 't': testing++; break; case 'u': def_sync = 0; /* Unsynchronised */ break; case 'v': verbose++; break; case 'w': if (*ap == '\0') ap = argv[++a]; def_download = atoi(ap); break; case 'x': def_no_write_meter++; break; case 'C': /* It's a CoralReef tracefile meter */ def_trace_interval = 1; break; case 'D': daemon++; break; case 'E': if (*ap == '\0') ap = argv[++a]; def_ci_Timeout = atoi(ap); break; case 'F': if (*ap == '\0') ap = argv[++a]; strncpy(fdfname, ap, NAME_LN); /* Name of flow data file */ fdfname[NAME_LN-1] = '\0'; break; case 'L': if (*ap == '\0') ap = argv[++a]; strncpy(user_logfile, ap, NAME_LN ); /* Name of log file */ user_logfile[NAME_LN-1] = '\0'; break; case 'M': if (*ap == '\0') ap = argv[++a]; def_ci_MinPDUs = atoi(ap); break; case 'P': append_log++; /* Only append to log file, not data file(s) */ break; case 'S': standard++; break; case 'U': utc_time++; break; case 'Y': if (*ap == '\0') ap = argv[++a]; strncpy(syslog_name, ap, NAME_LN); syslog_name[NAME_LN-1] = '\0'; break; default: fprintf(stderr, "Invalid option: -%c\n", argv[a][1]); break; } continue; } if (meter[0] == '\0') strcpy(meter,argv[a]); else if (community[0] == '\0') { strncpy(community,argv[a], NAME_LN); community[NAME_LN-1] = '\0'; } else if (owner[0] == '\0') { strncpy(owner,argv[a],NAMESZ); owner[NAMESZ-1] = '\0'; } else { fprintf(stderr, "Identifier '%s' follows owner-name\n", argv[a]); exit(0); } } if (owner[0] == '\0') /* No owner-name specified */ strcpy(owner, "NeMaC"); if (syntax) { /* Test a rule file */ fprintf(stderr, ver); ms = (struct meter_status *)calloc(sizeof(struct meter_status), 1); strcpy(ms->c_ruleset.filename,rfname); if (ms->s_ruleset.filename[0] != '\0') parse_rulefile(ms,listrules,1,1,0); parse_rulefile(ms,listrules,1,0,0); fprintf(stderr, "\n%d errors in rule file(s) %s\n\n", rferrors,rfname); exit(0); } #ifdef LOG_LOCAL if (*syslog_name) openlog(syslog_name, LOG_PID, LOG_LOCAL); #endif init_mib(); /* Initialise SNMP handler */ time(&t1); have_config_file = 0; if (!parse_open(cfname)) { /* No config file */ first_meter = (struct meter_status *)calloc (sizeof(struct meter_status), 1); strcpy(first_meter->c_ruleset.filename,rfname); strcpy(first_meter->s_ruleset.filename,srfname); strcpy(first_meter->name,meter); strcpy((char *)first_meter->community,community); strcpy((char *)first_meter->owner_name,owner); if (fdfname[0] != '\0') { strcpy(first_meter->flowdatafile,fdfname); first_meter->user_flowdatafile = 1; } first_meter->append_flowdata = def_append_flowdata; first_meter->synchronised = def_sync; first_meter->keepalive_interval = def_keepalive_interval; first_meter->write_OK = 1; first_meter->ci_Timeout = def_ci_Timeout; first_meter->ci_MinPDUs = def_ci_MinPDUs; first_meter->GCIntervalReqd = def_GCIntervalReqd; first_meter->InactivityTime = def_InactivityTime; first_meter->SamplingRate = def_SamplingRate; first_meter->lag_seconds = def_lag; first_meter->HighWaterMark = def_HighWaterMark; first_meter->FloodMark = def_FloodMark; first_meter->download_level = def_download; first_meter->download_only = def_download_only; first_meter->no_write_meter = def_no_write_meter; if (first_meter->trace_interval = def_trace_interval) first_meter->sample_interval = 1; else first_meter->sample_interval = def_sample_interval; } else for (first_meter = NULL; ; ) { /* Parse config file */ have_config_file = 1; ms = (struct meter_status *)calloc(sizeof(struct meter_status), 1); strcpy(ms->c_ruleset.filename,rfname); strcpy(ms->s_ruleset.filename,srfname); if (fdfname[0] != '\0') { strcpy(ms->flowdatafile,fdfname); ms->user_flowdatafile = 1; } ms->synchronised = def_sync; ms->keepalive_interval = def_keepalive_interval; ms->write_OK = 1; ms->GCIntervalReqd = def_GCIntervalReqd; ms->InactivityTime = def_InactivityTime; ms->lag_seconds = def_lag; ms->SamplingRate = def_SamplingRate; ms->HighWaterMark = def_HighWaterMark; ms->FloodMark = def_FloodMark; ms->download_level = def_download; ms->download_only = def_download_only; ms->append_flowdata = def_append_flowdata; ms->no_write_meter = def_no_write_meter; if (ms->trace_interval = def_trace_interval) ms->sample_interval = 1; else ms->sample_interval = def_sample_interval; while (lic != '\n') { /* Start with first char of a line */ nextchar(); if (end_of_file()) break; } if (end_of_file()) break; getarg(arg); if (end_of_file()) break; for ( ; arg[0] == '-'; ) { int l; ap = &arg[2]; switch (arg[1]) { case 'a': if (*ap == '\0') getarg(ap = arg); ms->lag_seconds = atoi(ap); break; case 'c': if (*ap == '\0') getarg(ap = arg); j = atoi(ap); if (j == 0) ms->download_only = 1; else ms->sample_interval = j; break; case 'e': if (*ap == '\0') getarg(ap = arg); strcpy(ms->s_ruleset.filename, ap); /* Standby rule file name */ break; case 'g': if (*ap == '\0') getarg(ap = arg); ms->GCIntervalReqd = atoi(ap); break; case 'h': if (*ap == '\0') getarg(ap = arg); ms->HighWaterMark = atoi(ap); break; case 'i': if (*ap == '\0') getarg(ap = arg); ms->InactivityTime = atoi(ap); break; case 'k': if (*ap == '\0') getarg(ap = arg); ms->keepalive_interval = atoi(ap); break; case 'n': if (*ap == '\0') getarg(ap = arg); ms->SamplingRate = atoi(ap); break; case 'o': if (*ap == '\0') getarg(ap = arg); ms->FloodMark = atoi(ap); break; case 'p': ms->append_flowdata++; break; case 'r': if (*ap == '\0') getarg(ap = arg); strcpy(ms->c_ruleset.filename, ap); /* Rule file name */ break; case 'u': ms->synchronised = 0; /* Unsynchronised */ break; case 'w': if (*ap == '\0') getarg(ap = arg); ms->download_level = atoi(ap); break; case 'x': ms->no_write_meter++; break; case 'C': /* It's a CoralReef tracefile meter */ ms->trace_interval = 1; /* This is a 'CoralReef file' meter */ ms->sample_interval = 1; /* Check ms state every 1s */ break; case 'E': if (*ap == '\0') ap = argv[++a]; ms->ci_Timeout = atoi(ap); break; case 'F': if (*ap == '\0') getarg(ap = arg); strcpy(ms->flowdatafile, ap); /* Flow data file name */ l = strlen(ms->flowdatafile); ms->user_flowdatafile = 1; break; case 'L': if (*ap == '\0') getarg(ap = arg); strcpy(user_logfile, ap); /* Log file name */ break; case 'M': if (*ap == '\0') ap = argv[++a]; ms->ci_MinPDUs = atoi(ap); break; case 'P': append_log++; break; default: fprintf(stderr, "Invalid option in config file: %s\n", arg); break; } getarg(arg); } if (strcmp(arg,"default") == 0) { /* Set defaults from config record */ strcpy(rfname,ms->c_ruleset.filename); strcpy(srfname,ms->s_ruleset.filename); if (ms->flowdatafile[0] != '\0') strcpy(fdfname,ms->flowdatafile); def_sync = ms->synchronised; def_sample_interval = ms->sample_interval; def_keepalive_interval = ms->keepalive_interval; def_ci_Timeout = ms->ci_Timeout; def_ci_MinPDUs = ms->ci_MinPDUs; def_GCIntervalReqd = ms->GCIntervalReqd; def_InactivityTime = ms->InactivityTime; def_lag = ms->lag_seconds; def_SamplingRate = ms->SamplingRate; def_HighWaterMark = ms->HighWaterMark; def_FloodMark = ms->FloodMark; def_download = ms->download_level; def_append_flowdata = ms->append_flowdata; def_no_write_meter = ms->no_write_meter; def_download_only = ms->download_only; def_trace_interval = ms->trace_interval; free(ms); if (trailing_arg()) { getarg(community); if (trailing_arg()) { getarg(arg); strncpy(owner, arg, NAMESZ ); owner[NAMESZ] = '\0'; } } } else { /* Config record specified a meter */ strcpy(ms->name, arg); if (trailing_arg()) { getarg((char *)ms->community); if (trailing_arg()) { getarg(arg); strncpy(ms->owner_name, arg, NAMESZ); ms->owner_name[NAMESZ] = '\0'; } else strcpy((char *)ms->owner_name, owner); } else { strcpy((char *)ms->community, community); strcpy((char *)ms->owner_name, owner); } if (first_meter == NULL) first_meter = ms; else for (nms = first_meter; ; nms = nms->next) { if (nms->next == NULL) { nms->next = ms; break; } } } } if (daemon) { /* User asked to run NeMaC as daemon (suggested by Glen Eustace, 15 Jul 99) */ switch (fork()) { case 0: /* Child (left running on it's own) */ break; case -1: fprintf(stderr, "Fork failed!!\n"); exit(1); default: /* Parent */ exit(0); } setsid(); /* Move into a new session */ } /* Open log file */ if (user_logfile[0] != '\0') { strcpy(logfile,user_logfile); if (!append_log) { if ((logfl = fopen(logfile,"w")) == 0) { if (*syslog_name) syslog(LOG_ERR, "Failed to open logfile '%s':%s\n", logfile, strerror(errno)); else fprintf(stderr,"Failed to open logfile '%s':%s\n", logfile, strerror(errno)); } } } else if (!*syslog_name) logfl = wfopen(logfile, LOGFILE); log_msg(LOG_INFO, FALSE, "Starting %s", ver); /* Check on meter version done in nmc_snmp.c */ /* Create meter processes */ time(&t1); for (nmeters = 0, ms = first_meter; ms; ms = ms->next) { struct calendar_entry *entry; ms->status = 0; if (create_meter(ms, t1, argv[0]) == 0) { fprintf(stderr, "Warning!! Failed to start meter %s; check log for details\n", ms->name); } entry = (struct calendar_entry *)calloc(sizeof(struct calendar_entry), 1); if (ms->status & MT_MANAGE) { ms->next_event = ms->next_sample = t1; /* Get first sample now */ } else { ms->next_event = ms->next_keepalive = ms->keepalive_interval; } add_event(entry,ms); } if (nmeters == 0) { fprintf(stderr, "No meters to monitor !!!\n"); exit(0); } unlink(FLAGFILE); /* Make sure there isn't an old one lying about */ if (have_config_file) { /* Write 'pid' file */ sprintf(arg,"%s.pid",cfname); cnf = fopen(arg,"w"); a = getpid(); fprintf(cnf,"%d # %s started %s, config file %s", a, argv[0], fmt_time(&t1),cfname); fclose(cnf); } for (;;) { /* Main event loop */ while (calendar != NULL && calendar->next_event <= time(&t1)) { /* Things to do */ cp = calendar; calendar = cp->next; /* Take entry from queue */ ms = cp->ms; if (ms->trace_interval == 0) { /* Normal 'live' meter */ if (ms->download_only) { /* Shut down tAsk and rEader */ if (ms->mi_Index != 0) { reader_util(ms, CU_DESTROY, CU_CURRENT); if (ms->s_ruleset.filename[0] != '\0') reader_util(ms, CU_DESTROY, CU_STANDBY); ms->mi_u_Index = ms->mi_Index; task_util(ms, TU_DESTROY); } if (--nmeters == 0) exit(0); } else { if (ms->status & MT_MANAGE) monitor(ms); else { create_meter(ms, t1, argv[0]); if (ms->status & MT_MANAGE) { /* Get first sample now */ ms->next_event = ms->next_sample = t1; } else { ms->next_event = ms->next_keepalive = t1 + (ms->keepalive_interval ? ms->keepalive_interval : 120); } } add_event(cp,ms); } } else { /* Meter reading tracefile (Dag or CoralReef) */ if (!TrState_util(ms, CSU_READ, &cs)) { printf(">>> Couldn't read meter CrlState <<<\n"); log_msg(LOG_INFO, FALSE, "Couldn't read CrlState!\n"); } else { #if DEBUG_TRACE_STATE if (cs != last_cs) { printf(">>> trace: state=%d, ", cs); last_cs = cs; } #endif if (cs == CT_WAIT_FLOWS_READ) { if (ms->status & MT_MANAGE) monitor(ms); /* Read flow data from meter */ cs = CT_PROCESS_FLOWS; if (!TrState_util(ms, CSU_WRITE, &cs)) log_msg(LOG_INFO, FALSE, "Couldn't write CrlState!\n"); } else if (cs == CT_TRACE_EOF) { request_stop = 1; /* Will read last set of flows */ } } if (!request_stop) { ms->next_event = ms->next_keepalive = t1 + 2; add_event(cp,ms); } #if DEBUG_TRACE_STATE printf("new_cs=%d, req_stop=%d, next_event=%d\n", cs,request_stop,ms->next_event); #endif } } /* Finished processing scheduled events */ time(&t1); if (!request_stop && !request_rollover && stat(FLAGFILE,&stat_buf) != 0 && calendar->next_event > t1) sleep(calendar->next_event-t1); if (request_rollover || stat(FLAGFILE,&stat_buf) == 0) { log_msg(LOG_INFO, FALSE, "Rolling over flow files\n"); make_new_data_files(); if (request_rollover) { request_rollover = 0; } } if (request_stop) { /* Set by sigint_handler() */ for (ms = first_meter; ms; ms = ms->next) { if (ms->status & MT_MANAGE) monitor(ms); /* Don't loose flow data gathered since last collection! */ if (ms->mi_Index != 0) { /* All NeMaC shutdowns destroy their tasks and readers */ reader_util(ms, CU_DESTROY, CU_CURRENT); if (ms->s_ruleset.filename[0] != '\0') reader_util(ms, CU_DESTROY, CU_STANDBY); ms->mi_u_Index = ms->mi_Index; task_util(ms, TU_DESTROY); } if (ms->download_level != 2) { /* Only 'master' mgrs destroy rulesets */ ruleset_util(ms, RU_DESTROY, &ms->c_ruleset); if (ms->s_ruleset.filename[0] != '\0') ruleset_util(ms, RU_DESTROY, &ms->s_ruleset); } if (ms->trace_interval != 0) { /* Meter running CoralReef tracefile */ cs = CT_SHUTDOWN; if (!TrState_util(ms, CSU_WRITE, &cs)) log_msg(LOG_INFO, FALSE, "Couldn't write CrlState!\n"); } } log_msg( LOG_INFO, FALSE, "NeMaC Shutting down\n"); exit(1); } } } struct meter_status *cms; /* Current meter info */ int activeflows; /* Nbr of flows active this collection */ void process_row(struct flow_info *fp) { char fdfbuf[12000], *fbp; int a, col; fbp = fdfbuf; /* Build flow data file line */ for (col = cms->format[a = 0]; ; ) { if (col != '\0') fbp = sfmt_attrib(fbp, fp,col); if ((col = cms->format[a+1]) == '\0') { if (strcmp(cms->separator[a]," ") != 0) fbp = strmov(fbp,cms->separator[a]); break; } fbp = strmov(fbp,cms->separator[a++]); } *fbp = '\0'; printfd(cms, fdfbuf); printfd(cms, "\n"); ++activeflows; } void monitor(struct meter_status *ms) /* Called every interval for each meter */ { time_t t, nst,si; char *ts; int mir, reachable, keepalive, s_statsreqd; unsigned long aps,apb, last_uptime; unsigned int n, i,j; struct stat statbuf; int crl_h,crl_m,crl_s; time(&t); ts = fmt_time(&t); if (!ms->write_OK) no_write_warning(ms); last_uptime = ms->uptime; keepalive = ms->keepalive_interval != 0 && ms->next_event < ms->next_sample; s_statsreqd = ms->statsreqd; /* Save stats request through keepalive */ reachable = 1; if (keepalive) { ms->statsreqd = 0; /* Don't get stats for a keepalive */ mir = meter_info(ms); #if TEST_MIR fprintf(stderr, " @@ monitor(1): keepalive, mir=%d\n", mir); #endif } else { if (ms->MIBver == 4) { /* Tell meter we want to start a collection */ if (ms->c_ruleset.ri_Index != 0) { mir = reader_util(ms, CU_SET_TIME, CU_CURRENT); #if TEST_MIR fprintf(stderr, " @@ monitor(2): current, mir=%d\n", mir); #endif if (mir && ms->s_ruleset.ri_Index != 0) { mir = reader_util(ms, CU_SET_TIME, CU_STANDBY); #if TEST_MIR fprintf(stderr, " @@ monitor(3): standby, mir=%d\n", mir); #endif } } else mir = 1; /* No current ruleset (yet) */ } else { /* V3 MIB */ mir = reader_util(ms, CU_SET_TIME, CU_CURRENT); #if TEST_MIR fprintf(stderr, " @@ monitor(4): V3, mir=%d\n", mir); #endif } if (mir) { /* LastTime was set OK */ mir = meter_info(ms); /* ms->uptime = LastTime */ #if TEST_MIR fprintf(stderr, " @@ monitor(5): info, mir=%d\n", mir); #endif } } if (mir == 0) { /* Lost contact */ if (restart_snmp_session(ms)) { ms->uptime = 0; mir = meter_info(ms); } #if TEST_MIR fprintf(stderr, " @@ monitor(6): restart, MIBver=%d, uptime=%lu, mir=%d\n", ms->MIBver, ms->uptime, mir); #endif } if (mir == 0) { /* Couldn't re-establish contact */ log_msg(LOG_INFO, FALSE,"%s: No response\n", ms->name); reachable = 0; } ms->statsreqd = s_statsreqd; /* Restore stats request from rule file */ if (testing) fprintf(stderr, "reachable=%d, ms->uptime=%lu, last_uptime=%lu\n", reachable, ms->uptime, last_uptime); /* Meter processing .. */ if (reachable) { if (ms->uptime < last_uptime ) { /* Meter has restarted */ log_msg(LOG_INFO, FALSE, "%s: Meter has restarted\n", ms->name); ms->write_OK = 1; ms->no_write_warned = 0; ms->OurLastCollectTime = 0; /* Was 1L */ if (ms->download_level != 2) set_meter_params(ms); /* Clear old (e.g. NeTraMet) tasks from meter */ search_table(ms, ST_READER, reader_row); if (ms->download_level != 2) { ms->use_meter_rows = 0; search_table(ms, ST_MANAGER, manager_row); search_table(ms, ST_RULESET, ruleset_row); } else ms->use_meter_rows = 1; /* Forget our control rows - mustn't confuse the download */ ms->ci_c_Index = ms->ci_s_Index = 0; ms->mi_Index = 0; ms->c_ruleset.ri_Index = ms->s_ruleset.ri_Index = 0; if (ms->s_ruleset.filename[0] != '\0') { n = parse_rulefile(ms,listrules,0,1,1); /* Reload */ if (!n || rferrors != 0) return; } if (ms->c_ruleset.filename[0] != '\0') { /* Rule file was specified */ n = parse_rulefile(ms,listrules,0,0,1); /* Reload */ if (!n || rferrors != 0) return; } if (ms->download_level == 2 ) { search_table(ms, ST_RULESET, ruleset_row); /* Find rulesets */ if (ms->c_ruleset.ri_Index == 0) { /* Rulestes not there yet */ ms->mi_Index = 0; /* Try again */ } else { if (ms->c_ruleset.ri_Status == RS_ACTIVE) { task_util(ms, TU_INIT); /* Create 'non-master' task */ task_util(ms, TU_STANDBY); task_util(ms, TU_CURRENT); } else { /* Try again */ ms->mi_Index = 0; } } } if (ms->mi_Index != 0) { reader_util(ms, CU_INIT, CU_CURRENT); if (ms->s_ruleset.filename[0] != '\0') reader_util(ms, CU_INIT, CU_STANDBY); if (stat(ms->flowdatafile,&statbuf) != 0) /* File doesn't exist */ open_datafile(ms); if (!ms->write_OK) no_write_warning(ms); } ms->restarting = 1; /* Write #Ruleset records */ if (!keepalive) { /* Tell meter we want to start a collection */ if (ms->MIBver == 4) { if (ms->c_ruleset.ri_Index != 0) reader_util(ms, CU_SET_TIME, CU_CURRENT); if (ms->s_ruleset.ri_Index != 0) reader_util(ms, CU_SET_TIME, CU_STANDBY); } else reader_util(ms, CU_SET_TIME, CU_CURRENT); /* V3 MIB */ } meter_info(ms); } if (ms->mi_Index == 0 && ms->MIBver == 4) ms->uptime = last_uptime; /* Haven't got our task running yet */ } if (keepalive) { ms->next_keepalive += ms->keepalive_interval; if (testing) fprintf(stderr, "Keepalive: t=%u, next_keepalive=%u\n", ms->next_event, ms->next_keepalive); ms->next_event = ms->next_keepalive < ms->next_sample ? ms->next_keepalive : ms->next_sample; return; } if (reachable && (ms->mi_Index != 0 /* We have a task running */ || ms->MIBver == 3)) { /* v3 doesn't know about tasks */ if (ms->append_flowdata) { if (stat(ms->flowdatafile,&statbuf) != 0) /* File doesn't exist */ open_datafile(ms); if ((ms->flows = fopen(ms->flowdatafile,"a")) == 0) { fprintf(stderr, "failed to open '%s' for appending:%s\n", ms->flowdatafile, strerror(errno)); log_msg( LOG_ERR, TRUE, "failed to open '%s' for appending:%s\n", ms->flowdatafile, strerror(errno)); } } if (ms->trace_interval == 0) { /* Normal 'live' meter */ printfd(ms, "#Time: %s %s Flows from %lu to %lu\n", ts,ms->name, ms->OurLastCollectTime,ms->uptime); } else { /* Meter reading tracefile */ if (ms->trt == 0) { /* Can't get packet timestamps */ crl_h = (ms->uptime+50)/100; /* Round to seconds */ crl_s = crl_h%60; crl_h /= 60; crl_m = crl_h%60; crl_h /= 60; printfd(ms, "#Time: %02d:%02d:%02d", crl_h,crl_m,crl_s); } else { ts = fmt_time(&ms->trt); printfd(ms, "#Time: %s", ts); } printfd(ms, " in tracefile Flows from %lu to %lu\n", ms->OurLastCollectTime,ms->uptime); } if (ms->restarting) { printfd(ms, "#Ruleset: %d %s %s %s\n", ms->c_ruleset.ri_Index, ms->c_ruleset.ri_Name, ms->c_ruleset.filename, ms->owner_name); if (ms->s_ruleset.ri_Index != 0) printfd(ms, "#Ruleset: %d %s %s %s\n", ms->s_ruleset.ri_Index, ms->s_ruleset.ri_Name, ms->s_ruleset.filename, ms->owner_name); ms->restarting = 0; } if (ms->statsreqd) { if (ms->StatsTime != 0) { aps = (ms->NbrPackets*10+5L)/(ms->StatsTime*10L); apb = (ms->TotPktBacklog*10+5L)/(ms->StatsTime*10L); } else { aps = apb = 0; } printfd(ms,"#Stats: aps=%lu apb=%lu mps=%lu mpb=%lu lsp=%lu", aps,apb, ms->MaxPktRate,ms->MaxPktBacklog, ms->LostPackets); printfd(ms," avi=%u.%u mni=%u.%u", ms->AvIdle1000/10,ms->AvIdle1000%10, ms->MinIdle1000/10,ms->MinIdle1000%10); if (ms->NbrPackets != 0) i = (ms->RuleMatches*100L+5L)/(ms->NbrPackets*10L); else i = 0; printfd(ms," fiu=%u frc=%lu gci=%u rpp=%u.%u", ms->NbrFlows,ms->FlowsRecovered,ms->GCInterval, i/10,i%10); if (ms->NbrPackets != 0) i = (ms->HashSearches*100L+5L)/(ms->NbrPackets*10L); else i = 0; if (ms->HashSearches != 0) { j = (ms->HashCompares*100L+5L)/(ms->HashSearches*10L); } else j = 0; printfd(ms," tpp=%u.%u cpt=%u.%u tts=%u tsu=%u\n", i/10,i%10, j/10,j%10, ms->TotalHashSize,ms->NbrHashEntries); } if (ms->format[0] != 0) { /* Collect flow data */ activeflows = 0; cms = ms; /* Tell process_row about this meter */ if (ms->MIBver == 4) { if (ms->c_ruleset.ri_FlowRecords) { get_package_info(ms, ms->c_ruleset.ri_Index, ms->OurLastCollectTime, process_row); } if (ms->s_ruleset.ri_Index != 0) { if (ms->s_ruleset.ri_FlowRecords) { get_package_info(ms, ms->s_ruleset.ri_Index, ms->OurLastCollectTime, process_row); } } if (ms->mi_RunningStandby && ms->download_level != 2) { j = ms->HighWaterMark - 5; if (j < 0) j = 0; if (ms->ActivePercent <= j) /* About 5% below HighWaterMark */ task_util(ms, TU_NOSTAND); /* Switch back to 'current' */ } } else { /* Version 3: can't tell whether running standby */ get_colblob_data(ms, /* Collects data for ALL rulesets */ ms->c_ruleset.ri_Index, ms->OurLastCollectTime, process_row); } } printfd(ms, "#EndData: %s\n", ms->name); flush_flows(ms); if (verbose) fprintf(stderr, "%s %s: %d active flows from %lu to %lu\n", ts,ms->name, activeflows, ms->OurLastCollectTime,ms->uptime); ms->OurLastCollectTime = ms->uptime - 1L; /* -1 to make sure we don't miss any flows. We may collect some of them twice, but we don't mind that */ } if (testing) fprintf(stderr, "Sample: t=%u",ms->next_event); do { if (ms->keepalive_interval == 0) { /* No keepalives */ if (ms->synchronised) { nst = ms->next_sample + (si = ms->sample_interval); ms->next_sample = (nst / si) * si + ms->lag_seconds; } else ms->next_sample += ms->sample_interval; ms->next_event = ms->next_sample; } else { ms->next_keepalive = ms->next_sample + ms->keepalive_interval; if (ms->synchronised) { nst = ms->next_sample + (si = ms->sample_interval); ms->next_sample = (nst / si) * si + ms->lag_seconds; } else ms->next_sample += ms->sample_interval; ms->next_event = ms->next_keepalive < ms->next_sample ? ms->next_keepalive : ms->next_sample; } time(&t); /* Wall-clock time now */ } while (ms->next_event < t); /* Ignore missed collections */ if (testing) fprintf(stderr, ", sync=%d, next_sample=%u, next_keepalive=%u\n", ms->synchronised, ms->next_sample, ms->next_keepalive); } void meter_print(FILE *f,struct meter_status *ms) { time_t t; char tsbuf[32]; struct timeval tv; char buf[32]; if (!verbose) return; time(&t); strcpy(tsbuf,fmt_time(&t)); gettimeofday(&tv, (struct timezone *)0); tv.tv_sec -= ms->uptime / 100; if ((ms->uptime % 100)*10000 > tv.tv_usec) { tv.tv_sec--; tv.tv_usec += 1000000; } tv.tv_usec -= (ms->uptime % 100)*10000; if (f) { fprintf(f,"%s -- %s: %s\n\tUp %s (since %s)\n", tsbuf,ms->name, ms->descr, uptime_string(ms->uptime, buf), fmt_time((time_t *)&tv.tv_sec)); } else { log_msg(LOG_INFO, FALSE, "%s -- %s: %s\n\tUp %s (since %s)\n", tsbuf,ms->name, ms->descr, uptime_string(ms->uptime, buf), fmt_time((time_t *)&tv.tv_sec)); } }