/* 1906, Sun 25 Nov 01 (PDT) NM_RC.C: A simple remote display console for NeTraMet Copyright (C) 1992-2002 by Nevil Brownlee, CAIDA | University of Auckland */ #define PERCENT_DEBUG 0 /* * $Log: nm_rc.c,v $ * Revision 1.1.1.2.2.9 2002/02/23 01:57:18 nevil * Moving srl examples to examples/ directory. Modified examples/Makefile.in * * Revision 1.1.1.2.2.5 2000/08/08 19:44:43 nevil * 44b8 release * * Revision 1.1.1.2.2.3 2000/06/06 03:38:09 nevil * Combine NEW_ATR with TCP_ATR, various bug fixes * * Revision 1.1.1.2 1999/10/03 21:06:15 nevil * *** empty log message *** * * Revision 1.1.1.1.2.6 1999/09/22 05:38:34 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.5 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.4 1999/02/15 21:24:06 nevil * Distribution file for 4.3b9 * * Revision 1.1.1.1.2.3 1999/01/27 04:26:13 nevil * Minor corrections to fix compiler warnings * * Revision 1.1.1.1.2.2 1999/01/20 04:01:33 nevil * Implementation of TCP attributes, part 4 * * Revision 1.1.1.1.2.1 1999/01/08 01:38:29 nevil * Distribution file for 4.3b7 * * 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:37 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:17:39 nevil * Display help message when run with no args (Nicolai) * * Revision 1.1.3.2.2.1 1998/10/23 03:57:10 nevil * Include set_mibfile() prototype from parse.h * * Revision 1.1.3.2 1998/10/18 23:44:06 nevil * Added Nicolai's patches, some 'tidying up' of the source * * Revision 1.1.3.1 1998/10/13 02:48:19 nevil * Import of Nicolai's 4.2.2 * * Revision 1.1.1.1 1998/08/24 12:09:28 nguba * NetraMet 4.2 Original Distribution * * Revision 1.5 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.4 1998/05/20 04:21:18 rtfm * Fix bugs in 4.2b2 */ #if HAVE_CONFIG_H #include #endif #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 #define EXTSNMP /* Declares au_snmp_port. Nevil, 29 Apr 97 */ #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" int request_stop = 0; void sigint_handler(int x) { request_stop = 1; } unsigned int max_flows; struct flow_data { /* Current values */ unsigned short ruleset; unsigned long starttime; counter64 upbci, /* To byte rate */ dnbci, /* From byte rate */ uppci, /* To packet rate */ dnpci; /* From packet rate */ #if NEW_ATR Bit32 n_subflows; #endif /* NEW_ATR */ }; struct flow_data *flow_table; struct flow_data32 { /* Sample-to-sample differences */ Bit32 upbci, /* To byte rate */ dnbci, /* From byte rate */ uppci, /* To packet rate */ dnpci; /* From packet rate */ #if NEW_ATR Bit32 n_subflow_i; #endif /* NEW_ATR */ }; struct flow_data32 curr_rate, total_rate; int nd_flows; /* Nbr of flows to display */ struct display_data { unsigned int traffic; struct flow_info *fi; }; struct flow_info *dflowi; #if NEW_ATR struct subflow_data *dsfdi; #endif /* NEW_ATR */ struct display_data *dflows; int criterion; /* How we'll choose flows to be displayed */ #define C_TOTBYTES 1 /* Max bytes up + down (default) */ #define C_STREAMS 2 /* Max TCP streams */ char show_names; char rfname[NAME_LN], meter[NAME_LN], owner[NAME_LN], community[NAME_LN]; unsigned int def_sync, def_sample_interval, def_lag, def_ci_Timeout, def_ci_MinPDUs, def_download, def_no_write_meter, def_GCIntervalReqd, def_InactivityTime, def_HighWaterMark, def_FloodMark, def_trace_interval; int no_user_format, plain_format; int sort_cmp(const void *a, const void *b) { if (((struct display_data *)a)->traffic < /* Reverse numeric order */ ((struct display_data *)b)->traffic) return 1; else if (((struct display_data *)a)->traffic == ((struct display_data *)b)->traffic) return 0; else return -1; } void no_write_warning(struct meter_status *ms) { if (ms->no_write_warned) return; fprintf(stdout,"Community %s doesn't have write access to meter %s!\n" " Collections won't trigger recovery of idle flows <<<\n", ms->community,ms->name); 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 */ } void ruleset_row(struct meter_status *ms, struct row_info *rip) { struct meter_rule_info mri; printf(">> ruleset %d: Status=%d, Owner=%s, Name=%s\n", rip->tr_Index, rip->tr_Status, rip->tr_Owner, rip->tr_Name); 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; } } void manager_row(struct meter_status *ms, struct row_info *rip) { printf(">> manager %d: Status=%d, Owner=%s\n", rip->tr_Index, rip->tr_Status, rip->tr_Owner); 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); } } void reader_row(struct meter_status *ms, struct row_info *rip) { printf(">> 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) { 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) { struct calendar_entry *entry; int a,b,n, syntax; if (ms->name[0] == '\0' || ms->community[0] == '\0') { fprintf(stdout,"Meter name or community not specified !!!\n"); return 0; } if (testing) printf("About to create_meter name=%s, community=%s\n", ms->name, ms->community); if (!start_snmp_session(ms)) return 0; ms->status = MT_MANAGE; if (meter_info(ms)) { ms->status |= (MT_UP | MT_INFO); ms->next_event = ms->next_sample = first_t; /* Get first sample immediately */ entry = (struct calendar_entry *)calloc( sizeof(struct calendar_entry), 1); add_event(entry,ms); if (testing) printf("t=%u, next_event=%u, next_keepalive=%u, next_sample=%u\n", first_t, ms->next_event, ms->next_keepalive, ms->next_sample); #ifndef _AIX if (!meter_is_current(ms)) { fprintf(stdout,"Warning: 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); #endif if (ms->MaxFlows > max_flows) max_flows = ms->MaxFlows; } else { fprintf(stdout,"Couldn't get meter info from %s!\n" " Does community %s have read or write access to the meter?\n", ms->name,ms->community); return 0; } 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 <<<"); return 0; } 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 !!!"); return 0;; } } set_meter_params(ms); if (ms->ci_Timeout == 0) /* Reader row timeout */ ms->ci_Timeout = 10*ms->sample_interval; search_table(ms, ST_READER, reader_row); search_table(ms, ST_MANAGER, manager_row); search_table(ms, ST_RULESET, ruleset_row); syntax = ms->download_level != 0; /* 0 => initial download of rules */ if (ms->c_ruleset.filename[0] == '\0') { /* No rule file specified */ fprintf(stdout,"No rule file specified !!!\n"); return 0; } else { /* Download the rules we want to use */ n = parse_rulefile(ms,listrules,syntax,0,0); /* Initial load */ if (!n || rferrors != 0) return 0; } /* search_table() finds NeTraMet's default ruleset */ reader_util(ms, CU_INIT, CU_CURRENT); reader_util(ms, CU_SET_MINPDUS, CU_CURRENT); /* Make sure we get the info we need to select 'top n' flows */ ms->required[FTFLOWINDEX] = ms->required[FTRULESET] = ms->required[FTFIRSTTIME] = ms->required[FTUPOCTETS] = ms->required[FTDOWNOCTETS] = ms->required[FTUPPDUS] = ms->required[FTDOWNPDUS] = ms->required[FTLOWPEERTYPE] = 1; #if NEW_ATR if (criterion == C_STREAMS) /* Max TCP streams */ ms->required[FTTCPDATA] = 1; #endif if (no_user_format = ms->format[0] == 0) { /* No format specified */ ms->format[0] = FTFIRSTTIME; ms->format[1] = FTUPPDUS; ms->format[2] = FTDOWNPDUS; ms->format[3] = FTUPOCTETS; ms->format[4] = FTDOWNOCTETS; ms->format[5] = FTLOWPEERTYPE; ms->format[6] = FTLOWPEERADDRESS; ms->required[FTLOWPEERADDRESS] = 1; ms->format[7] = FTHIPEERADDRESS; ms->required[FTHIPEERADDRESS] = 1; ms->format[8] = FTLOWTRANSTYPE; ms->required[FTLOWTRANSTYPE] = 1; ms->format[9] = FTLOWTRANSADDRESS; ms->required[FTLOWTRANSADDRESS] = 1; ms->format[10] = FTHITRANSADDRESS; ms->required[FTHITRANSADDRESS] = 1; #if NEW_ATR if (criterion == C_STREAMS) { /* Max TCP streams */ ms->format[11] = FTTCPDATA; ms->required[FTTCPDATA] = 1; } ms->format[12] = 0; #else ms->format[11] = 0; #endif for (a = 0; a != 14; ++a) ms->separator[a] = " "; } ++nmeters; if (!ms->write_OK) no_write_warning(ms); ms->OurLastCollectTime = 0; /* Was 1L */ fprintf(stdout, "#Format: "); if ((n = ms->format[a = 0]) != 0) for (;;) { for (b = 1; b <= SZ_ATTRIBS && attribs[b].index != n; ++b) ; if (b <= SZ_ATTRIBS) fprintf(stdout,attribs[b].name); else fprintf(stdout,"attr*%u", n); /* Unknown attribute */ if ((n = ms->format[a+1]) == 0) { if (strcmp(ms->separator[a]," ") != 0) fprintf(stdout, ms->separator[a]); break; } fprintf(stdout,ms->separator[a++]); } fprintf(stdout,"\n"); return 1; } int main(int argc, char *argv[]) { int syntax; 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 meter_rule_info mri; struct stat stat_buf; char have_config_file; FILE *cnf; char download_only; #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\n" "Remote Console 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" " -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" " -m PNO \t Specify UDP port to use for communication with meter\n" " -n NOF \t Number of flows to display after each collection\n" " -o FLM \t Set flook mark as percentage of flow space\n" " -p \t Plain output\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" " -u \t Samples should be unsyncronized, ie every SEC\n" " \t seconds from nm_rc startup\n" " -C \t Meter is reading a trace file, use its -T interval\n" " -E SEC \t Timeout in seconds for reader rows in meter\n" " -L NAM \t Name of log file to write\n" " -M MPK \t Only retrieve flows with MPK packets to or from\n" /* " -N \t Display domain names instead of IP addresses\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); fprintf(stdout,"nm_rc: Remote Console for NeTraMet: " ver_str "\n"); /* Check on meter version done in nmc_snmp.c */ incl_depth = syntax = verbose = testing = listrules = standard = 0; def_sync = 1; /* Samples synchronised with TOD clock */ def_sample_interval = 120; /* Default 2 minutes */ def_lag = 0; /* No time lag for collections */ rfname[0] = meter[0] = owner[0] = community[0] = '\0'; max_flows = 0; def_download = 0; /* Download rule sets on startup */ def_no_write_meter = 0; nd_flows = 10; /* Display top 10 flows by default */ def_HighWaterMark = 35; /* 35% for nm_rc and nifty */ def_ci_Timeout = 0; /* nm_rc collector rows never time out */ def_ci_MinPDUs = 0; /* Reader MinPDU filter value */ def_GCIntervalReqd = /* Use meter defaults for MIB variables */ def_InactivityTime = def_FloodMark = 0; def_trace_interval = 0; /* Live meter, not tracefile */ plain_format = 0; show_names = 0; criterion = C_TOTBYTES; /* How we select flows to display */ 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) download_only = 1; else def_sample_interval = j; break; case 'd': snmp_dump_packet++; break; case 'e': if (*ap == '\0') ap = argv[++a]; criterion = atoi(ap); 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 '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]; nd_flows = atoi(ap); /* Nbr of flows to display */ break; case 'o': if (*ap == '\0') ap = argv[++a]; def_FloodMark = atoi(ap); break; case 'p': plain_format++; break; case 'r': if (*ap == '\0') ap = argv[++a]; strcpy(rfname, ap); /* Rule file name */ 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 'E': if (*ap == '\0') ap = argv[++a]; def_ci_Timeout = atoi(ap); break; case 'L': if (*ap == '\0') ap = argv[++a]; strcpy(user_logfile, ap); /* Log file name */ break; case 'M': if (*ap == '\0') ap = argv[++a]; def_ci_MinPDUs = atoi(ap); break; case 'N': show_names = 1; break; case 'S': standard++; 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') strcpy(community,argv[a]); else if (owner[0] == '\0') { strncpy(owner,argv[a],NAMESZ); owner[NAMESZ] = '\0'; } } if (syntax) { /* Test a rule file */ 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,1,1,0,0); fprintf(stderr,"\n%d errors in rule file(s) %s\n\n", rferrors,rfname); exit(0); } if (nd_flows < 1) nd_flows = 1; logfile[0] = '\0'; if (user_logfile[0] != '\0') { /* nm_rc only write log messages if user explicitly asks for them */ strcpy(logfile,user_logfile); if ((logfl = fopen(logfile,"w")) == 0) { fprintf(stderr, "Failed to open %s as log file", logfile); exit(10); } } init_mib(); /* Initialise SNMP handler */ first_meter = (struct meter_status *)calloc (sizeof(struct meter_status), 1); strcpy(first_meter->c_ruleset.filename,rfname); strcpy(first_meter->name,meter); strcpy((char *)first_meter->owner_name, owner[0] != '\0' ? owner : "nm_rc"); strcpy((char *)first_meter->community, community[0] != '\0' ? community : "public"); first_meter->synchronised = def_sync; first_meter->lag_seconds = def_lag; first_meter->download_level = def_download; first_meter->no_write_meter = rfname[0] == '\0' ? 1 : def_no_write_meter; 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->lag_seconds = def_lag; first_meter->HighWaterMark = def_HighWaterMark; first_meter->FloodMark = def_FloodMark; if (first_meter->trace_interval = def_trace_interval) first_meter->sample_interval = 1; else first_meter->sample_interval = def_sample_interval; time(&t1); for (nmeters = 0, ms = first_meter; ms; ms = ms->next) create_meter(ms, t1, argv[0]); if (nmeters == 0) { fprintf(stderr,"No meters to monitor !!!\n"); exit(0); } /* Allocate two extra flows since they start from 2 not 0 */ if ((flow_table = (struct flow_data *)calloc( max_flows+2, sizeof(struct flow_data))) == NULL) { fprintf(stderr, "Failed to allocate memory for flow table!\n"); exit(11); } if ((dflowi = (struct flow_info *)calloc( nd_flows, sizeof(struct flow_info))) == NULL) { fprintf(stderr, "Failed to allocate memory for display info!\n"); exit(11); } #if NEW_ATR if ((dsfdi = (struct subflow_data *)calloc( nd_flows, sizeof(struct subflow_data))) == NULL) { fprintf(stderr, "Failed to allocate memory for tcp attrib display!\n"); exit(11); } #endif if ((dflows = (struct display_data *)calloc( nd_flows, sizeof(struct display_data))) == NULL) { fprintf(stderr, "Failed to allocate memory for display data!\n"); exit(11); } for (;;) { 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->status & MT_MANAGE) monitor(ms); 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 } } if (testing) fflush(stdout); time(&t1); if (!request_stop && calendar->next_event > t1) sleep(calendar->next_event-t1); if (request_stop) { /* Set by sigint_handler() */ reader_util(ms, CU_DESTROY, CU_CURRENT); /* reader_util(ms, CU_DESTROY, CU_DEFAULT); */ ms->mi_u_Index = ms->mi_Index; task_util(ms, TU_DESTROY); ruleset_util(ms, RU_DESTROY, &ms->c_ruleset); if (ms->trace_interval != 0) { /* Meter running tracefile */ cs = CT_SHUTDOWN; if (!TrState_util(ms, CSU_WRITE, &cs)) log_msg(LOG_INFO, FALSE, "Couldn't write CrlState!\n"); } exit(1); } } } int activeflows; /* Nbr of flows active this collection */ void process_row(struct flow_info *fp) { unsigned int fi, i,j,x; struct display_data *ddp, *mdp; unsigned long tb; int save_flow; fi = fp->FlowIndex; if (flow_table[fi].ruleset == 0 || flow_table[fi].ruleset != fp->FlowRuleSet || flow_table[fi].starttime != fp->FirstTime) { /* It's a new flow */ memset(&flow_table[fi], 0, sizeof(struct flow_data)); flow_table[fi].ruleset = fp->FlowRuleSet; flow_table[fi].starttime = fp->FirstTime; } memset(&curr_rate, 0, sizeof(curr_rate)); if (nz64(fp->FwdPackets)) { diff64(curr_rate.uppci, fp->FwdPackets,flow_table[fi].uppci); total_rate.uppci += curr_rate.uppci; assign64(flow_table[fi].uppci, fp->FwdPackets); } if (nz64(fp->BackPackets)) { diff64(curr_rate.dnpci, fp->BackPackets,flow_table[fi].dnpci); total_rate.dnpci += curr_rate.dnpci; assign64(flow_table[fi].dnpci, fp->BackPackets); } if (nz64(fp->FwdBytes)) { diff64(curr_rate.upbci, fp->FwdBytes,flow_table[fi].upbci); total_rate.upbci += curr_rate.upbci; assign64(flow_table[fi].upbci, fp->FwdBytes); } if (nz64(fp->BackBytes)) { diff64(curr_rate.dnbci, fp->BackBytes,flow_table[fi].dnbci); total_rate.dnbci += curr_rate.dnbci; assign64(flow_table[fi].dnbci, fp->BackBytes); } #if NEW_ATR if (fp->sfd != NULL) { curr_rate.n_subflow_i = fp->sfd->n_subflows - flow_table[fi].n_subflows; flow_table[fi].n_subflows = fp->sfd->n_subflows; } #endif #if PERCENT_DEBUG printf("F+B bytes = %lu, total = %lu\n", curr_rate.upbci+curr_rate.dnbci, total_rate.upbci+total_rate.dnbci); #endif mdp = &dflows[0]; x = mdp->traffic; j = 0; for (i = 1; i != nd_flows; ++i) { /* Find min display flow */ ddp = &dflows[i]; if (ddp->traffic < x) { mdp = ddp; x = ddp->traffic; j = i; } } switch(criterion) { case C_TOTBYTES: tb = curr_rate.upbci+curr_rate.dnbci; save_flow = tb > x; break; case C_STREAMS: #if NEW_ATR tb = curr_rate.n_subflow_i; save_flow = tb > x; #endif break; } if (save_flow) { mdp->traffic = tb; mdp->fi = &dflowi[j]; memcpy(mdp->fi, fp, sizeof(struct flow_info)); #if NEW_ATR if (fp->sfd != NULL) { memcpy(&dsfdi[j], fp->sfd, sizeof(struct subflow_data)); mdp->fi->sfd = &dsfdi[j]; } #endif #if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8 mdp->fi->FwdPackets = curr_rate.uppci; mdp->fi->BackPackets = curr_rate.dnpci; mdp->fi->FwdBytes = curr_rate.upbci; mdp->fi->BackBytes = curr_rate.dnbci; #else mdp->fi->FwdPackets.high = 0; mdp->fi->FwdPackets.low = curr_rate.uppci; mdp->fi->BackPackets.high = 0; mdp->fi->BackPackets.low = curr_rate.dnpci; mdp->fi->FwdBytes.high = 0; mdp->fi->FwdBytes.low = curr_rate.upbci; mdp->fi->BackBytes.high = 0; mdp->fi->BackBytes.low = curr_rate.dnbci; #endif } ++activeflows; } void monitor(struct meter_status *ms) /* Called every interval for each meter */ { time_t t, nst,si; char *ts; int a,col, reachable, keepalive, s_statsreqd, i,j; unsigned long last_uptime, tb, tp; double pc, tpc; char fdfbuf[500], *fbp; struct flow_info *fp; unsigned int fi, t_count,x; struct display_data *ddp, *mdp; char buf[50], *bp; time(&t); ts = fmt_time(&t); if (!ms->write_OK) no_write_warning(ms); last_uptime = ms->uptime; s_statsreqd = ms->statsreqd; /* Save stats request through keepalive */ reachable = 1; j = reader_util(ms, /* Tell meter we want a collection */ CU_SET_TIME, CU_CURRENT); if (j == 0) { /* Lost contact */ if (ms->status & MT_UP) { /* Was up */ fprintf(stdout,"%s -- %s: No response\n", ts,ms->name); } ms->status &= ~MT_UP; /* Mark as 'down' */ reachable = 0; } else if (!(ms->status & MT_UP)) { /* Have contact now, was down */ fprintf(stdout,"%s -- %s: Regained contact\n", ts,ms->name); ms->write_OK = 1; ms->no_write_warned = 0; } ms->status |= MT_UP; ms->statsreqd = s_statsreqd; /* Restore stats request from rule file */ /* Meter processing .. */ if (reachable) { meter_info(ms); /* ms->uptime = LastTime */ memset(&total_rate, 0, sizeof(total_rate)); memset(dflows, 0, nd_flows*sizeof(struct display_data)); activeflows = 0; if (ms->MIBver == 4) get_package_info(ms, ms->c_ruleset.ri_Index, ms->OurLastCollectTime, process_row); else get_colblob_data(ms, ms->c_ruleset.ri_Index, ms->OurLastCollectTime, process_row); if (verbose) printf( "%s %s: %d active flows from %lu to %lu\n", ts,ms->name, activeflows, ms->OurLastCollectTime,ms->uptime); qsort(dflows, nd_flows,sizeof(struct display_data), sort_cmp); tb = total_rate.upbci+total_rate.dnbci; tp = total_rate.uppci+total_rate.dnpci; fprintf(stdout,"#--- %s %s %u flows ", ms->name,ms->interface, activeflows); #if PERCENT_DEBUG printf("Total bytes = %lu\n", tb); #endif tpc = 0.0; bp = sdisplay_number(buf,tp/ms->sample_interval); bp = strmov(bp,"pps "); bp = sdisplay_number(bp,tb/ms->sample_interval); *bp = '\0'; fprintf(stdout,buf); fprintf(stdout,"Bps %s ---\n", ts); for (i = 0; i != nd_flows; ++i) { ddp = &dflows[i]; if (ddp->traffic == 0) break; fp = ddp->fi; fbp = fdfbuf; /* Build flow data file line */ if (!plain_format) { #if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8 pc = (fp->FwdBytes+fp->BackBytes)*100.0/tb; #else pc = (fp->FwdBytes.low+fp->BackBytes.low)*100.0/tb; #endif fprintf(stdout,"%2.0f%% ", pc); } for (col = ms->format[a = 0]; ; ) { if (col != '\0') { if (plain_format) fbp = sfmt_attrib(fbp, fp,col); else fbp = sdisplay_attrib(fbp, fp,col,ms); } if ((col = ms->format[a+1]) == '\0') { if (strcmp(ms->separator[a]," ") != 0) fbp = strmov(fbp,ms->separator[a]); break; } fbp = strmov(fbp,ms->separator[a++]); } *fbp = '\0'; fprintf(stdout,fdfbuf); fprintf(stdout,"\n"); #if PERCENT_DEBUG printf("Flow bytes: fwd=%lu, back=%lu, %% = %.3f\n", fp->FwdBytes.low,fp->BackBytes.low, pc); #endif tpc += pc; } fprintf(stdout,"%2d%% bytes in %d other flows\n", 100-(int)tpc,activeflows-i); fflush(stdout); } 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) printf("Sample: t=%u",ms->next_event); 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; if (testing) printf(", sync=%d, next_sample=%u, next_keepalive=%u\n", ms->synchronised, ms->next_sample, ms->next_keepalive); }