/* 1620, Sat 17 Nov 01 (PST) METER_UX.C: The AU Internet Accouting Meter mainline Copyright (C) 1992-2002 by Nevil Brownlee, CAIDA | University of Auckland */ /* * $Log: meter_ux.c,v $ * Revision 1.1.1.2.2.12 2002/02/23 01:57:33 nevil * Moving srl examples to examples/ directory. Modified examples/Makefile.in * * Revision 1.1.1.2.2.10 2001/06/07 05:33:41 nevil * Fork daemon before starting interface(s). * Linux 2.2.19 kernel reset promiscuous mode at the fork! * * Revision 1.1.1.2.2.9 2001/05/24 02:19:56 nevil * LfapMet implemented by Remco Poortinga. * MinPDUs implemented by Nevil. * * Revision 1.1.1.2.2.6 2000/08/08 19:44:53 nevil * 44b8 release * * Revision 1.1.1.2.2.4 2000/06/06 03:38:22 nevil * Combine NEW_ATR with TCP_ATR, various bug fixes * * Revision 1.1.1.2.2.1 2000/01/12 02:57:12 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:26 nevil * *** empty log message *** * * Revision 1.1.1.1.2.14 1999/09/29 22:29:15 nevil * Changes (mainly changing // to /* comments) for Irix * * Revision 1.1.1.1.2.13 1999/09/22 05:38:43 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.12 1999/09/14 00:45:29 nevil * 4.3 Release .. * - Implement -D option to run NeTraMet as a daemon * - Tidy up the on-line help displays * * Revision 1.1.1.1.2.11 1999/05/26 02:41:41 nevil * Integrate V6 and ASN code into PC versions of the meter. * This required a rework of the makefiles, using @cflags.opt files * to provide a much longer command line to the Borland C compiler. * * Revision 1.1.1.1.2.10 1999/05/17 00:11:58 nevil * Fixed 'reuse of ruleset row' problem: * When a ruleset row is destroyed, set_RowStatus calls free_rulespace() * and recover_flows(). free_rulespace() zeroed its varibales in the * ri[] row, but recover_flows() didn't (it should have zeroed * ri_flow_chain). This left ri_flow_chain pointing to a free flow, * which cause problems if a manager tried to reuse this row for a * new ruleset. Fixed by zeroing all of the ri[] row. (Also commented * out the zeroing code from free_rulesace). * * Revision 1.1.1.1.2.9 1999/04/20 04:45:59 nevil * Add some checking to handle_keyboard() so as to prevent it from * trying to process garbage from a failed fgets() call on stdin. * This was particularly nasty if stdin hadn't been opened (e.g. by * using the shell's "<&-" construct) - this resulted in a read from * fd0, which was being used for the SNMP socket! * (Patch supplied by Tony Stoneley) * * Revision 1.1.1.1.2.8 1999/04/13 05:13:35 nevil * Use log_msg for ALL error messages * * Revision 1.1.1.1.2.7 1999/02/16 03:59:11 nevil * *** empty log message *** * * Revision 1.1.1.1.2.6 1999/02/16 03:42:18 nevil * *** empty log message *** * * Revision 1.1.1.1.2.5 1999/02/15 21:24:09 nevil * Distribution file for 4.3b9 * * Revision 1.1.1.1.2.4 1999/02/03 21:56:20 nevil * Distribution file for 4.3b8 * * Revision 1.1.1.1.2.3 1999/01/20 03:55:52 nevil * Implementation of TCP attributes, part 4 * * Revision 1.1.1.1.2.1 1998/11/25 03:30:13 nevil * Fix endian problems in NetFlowMet * * Revision 1.1.1.1 1998/11/16 03:57:29 nevil * Import of NeTraMet 4.3b3 * * Revision 1.1.3.1.2.2 1998/10/19 22:32:49 nevil * Meter improvements, mostly arising from developments for the * OCxMON meter. These are documented in notes_oc.txt * * Revision 1.1.3.1.2.1 1998/10/18 20:51:27 nevil * Added Nicolai's patches, some 'tidying up' of the source * * Revision 1.1.3.1 1998/10/13 02:48:41 nevil * Import of Nicolai's 4.2.2 * * Revision 1.6 1998/10/02 15:00:30 nguba * Added print_help to display command-line options * * Revision 1.5 1998/06/10 21:12:03 rtfm * Implement popto/poptoact actions for SRL * * Revision 1.3 1998/05/22 03:57:35 rtfm * Implement better hashing algorithm for flow and rule tables */ #if HAVE_CONFIG_H #include #endif #define CORAL_TIME_OFFSET 0 #define CORAL_BUFFER_TRACE 1 #define LOG_BAD_TIMESTAMPS 1 /* If you set this, run meter inside gdb, and set breakpoint on quack */ #define noUX_TESTING #define noCOPY_STRUCTS /* True to copy fddi 'ethernet header' info. Solaris 2.4 doesn't need this */ #define CHECK_BIT_ALIGN 1 /* Check that structures have multiples of 4 bytes */ #include #include #include #if HAVE_SYS_SELECT_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SUNOS #include #define void #define const #endif #include "../bgp/types/types.h" /* Needed here for subnet.h */ #define EXTSNMP #include "ausnmp.h" #include "asn1.h" #include "snmp.h" #include "snmpimpl.h" #define MXINTERFACES 4 /* Max nbr of interfaces to meter */ #define PKTSNAP #include "pktsnap.h" #include "flowhash.h" #include "met_vars.h" #define APPLETALK extern char version_descr[]; /* In met_vars.c */ extern char *communities[]; /* In snmp/snmpagnt.c */ extern unsigned long pc_noflowpkts; /* In met_vars.c */ #if NF_CISCO_DATA # include "flowdata.h" #elif LFAP_METER # include "lfapmet.h" # include # include # include "snmpclnt.h" #else # include # include "pcap-int.h" # include "pcap.h" # include "llc.h" # if CR_DATA # include "libcoral.h" /* CoralReef needs libpcap */ # endif #endif #if NF_OCX_BGP #include "../bgp/integrat/readbgp.h" #include "../bgp/integrat/subnet.h" #endif struct ether_hdr { unsigned char dest[6]; /* MAC addresses */ unsigned char source[6]; Bit16 type; /* Length or protocol type */ }; #if DAG_DIRECT #include "dag.h" #include /* Dag trace file record layouts ... Note that they all use 64-byte fixed-length records */ struct snap_hdr { Bit16 lsap; /* AAAA */ Bit8 ctrl; /* 03 */ Bit8 oid1,oid2,oid3; /* 00 00 00 */ Bit16 etype; /* 0800 */ }; struct dag_atm { /* Dag record for ATM (first cell only, a la OC3MON) */ Bit32 crc; /* Always zero */ Bit32 atm_hdr; /* VPI:VCI, etc */ struct snap_hdr sh; /* LLC snap header (8 bytes) */ Bit8 pload[40]; /* First 40 bytes of IP packet */ }; struct dag_ether { /* Dag record for Ethernet */ Bit16 wlen; /* Wire length (bytes) */ struct ether_hdr eh; /* Ethernet MAC header */ Bit8 pload[40]; }; struct dag_pos { /* Dag record for PoS */ Bit32 slen; /* Snap length, must be 64 for now */ Bit32 wlen; /* Bytes on the wire, seems to include FCS */ Bit8 chdlc; /* Cisco HDLC header */ Bit8 unused; /* Padding */ Bit16 etype; /* EtherType */ Bit8 pload[44]; /* 4 more bytes than ATM and Ether */ }; #define TT_ATM 0 /* Dag trace file record types */ #define TT_ETHER 1 #define TT_POS 2 struct dag_rec { long long ts; /* Dag timestamp (64-bit) */ union { struct dag_atm atm; struct dag_ether eth; struct dag_pos pos; } d; }; #endif #define SZ_IFNAME 64 struct interface_info { u_char nbr; /* Interface number (for Surce/DestInterface attributes) */ u_char adjtype; /* Adjacent type for interface */ char name[SZ_IFNAME+1]; /* Interface name, e.g. nf0 */ u_int SampleRate, /* 1 = count every packet on this interface */ sample_count; int fd, last_errno; u_int ipackets; /* Total packets since last stats reset */ u_int idrops; /* Dropped packets since last stats reset */ u_int noflowpackets; #if NF_CISCO_DATA struct sockaddr_in from; char *nf_buf; #elif LFAP_METER FASInfo fasinfo; #elif DAG_DIRECT || CR_DATA # define MN_SELECT_US 5000 /* 5 ms */ # define MX_SELECT_US (8*MN_SELECT_US) # define TARGET_CPB 500 /* Try to vary select_us to keep cpb above this */ int tcells, icells,ncells, nblocks; u_int badtspkts; /* Packets with bad timestamps */ u_int no_data_warned; int last_s; /* State from last read */ # define S_NORMAL 0 # define S_EMPTY 1 # define S_FULL 2 u_int full,empty, mx_full,mx_empty, consec_badts; # if DAG_DIRECT struct dag_rec *d_buf; struct dag_rec *drp; long long card_ts; /* For next cell */ int dag_type; # else /* CR_DATA */ coral_source_t *crl_src; coral_iface_t *iface; struct timeval card_ts; /* For next cell */ coral_atm_cell_t *cellp; # if CORAL_TIME_OFFSET struct timeval ts_offset; /* Accumulated time error from card */ # endif # endif #else /* ADSL or libpcap */ # if !DAG_DATA /* libpcap */ pcap_t *pd; pcap_handler callback; # endif int if_delta_set; struct timeval if_time_delta; /* First packet time - meter_start_time */ #endif }; struct interface_info *pci[MXINTERFACES]; int n_interfaces; /* Nbr of active interfaces */ int live_source = 1; /* Set to zero to use trace files */ static int crl_n_max = 0; /* Set by -N option */ static int crl_n_count; /* Counting to crl_n_max */ static int trace_count; /* Counter for 1s intervals within trace_interval */ /* tv_sub(x,y): subtracts y from x; x,y are timevals */ #define tv_sub(x, y) { \ if ((x).tv_usec < (y).tv_usec) { \ --(x).tv_sec; \ (x).tv_usec += 1000000; \ } \ (x).tv_sec -= (y).tv_sec; \ (x).tv_usec -= (y).tv_usec; \ } /* tv_add(x,y): adds y to x; x,y are timevals */ #define tv_add(x, y) { \ (x).tv_usec += (y).tv_usec; \ if ((x).tv_usec > 1000000) { \ ++(x).tv_sec; \ (x).tv_usec -= 1000000; \ } \ (x).tv_sec += (y).tv_sec; \ } static unsigned long npackets_org = 0L, lostpackets_org = 0L, pkts = 0L, drops = 0L; #if DAG_DIRECT long long meter_start_time; /* Dag (64-bit) timestamp */ #else struct timeval meter_start_time; #endif char *strmov(char *d, char *s) { while (*s != '\0') *d++ = *s++; return d; } /* Make SIGINT or SIGTERM shut the meter down gracefully */ int request_stop = 0; void sigint_handler(int x) /* INT (keyboard) and TERM (kill) */ { /* To make ^c send INT, use stty intr '^c' */ request_stop = 1; } void stop_meter(int code) { display_msg(1,"Shutting down"); #if CR_DATA coral_stop_all(); /* Leave Dag cards in a graceful state */ coral_close_all(); #endif exit(code); } #if NF_CISCO_DATA void interface_read(struct interface_info *pi) { int flen, j; char msg[60]; int nf_version; IPStat1Msg *nf1; IPFlow1Stat *fr1; IPStat5Msg *nf5; IPFlow5Stat *fr5; struct pkt pp; Bit16 as; Bit32 uptime_delta; /* SysUptime (from packet header) is normally a few seconds behind the NetFlowMet meter, which means that flow FirstTime and LastTime are negative numbers. To get this to work properly, we force an integer divide by 10 when converting to centiseconds (below). InactivityTimeout is usually measured in minutes (default 600s), and garbage_collect() uses unsigned compares against LastTime, so NetFlowMet will time flows out properly. */ flen = sizeof(pi->from); if (recvfrom(pi->fd, pi->nf_buf, MAX_FLOW_PAK_SIZE, 0, (struct sockaddr *)&pi->from, &flen) < 0) { log_msg(LOG_ERR, 0, "nf_read(%s): recvfrom failed", pi->name); return; } nf_version = pi->nf_buf[0] << 8 | pi->nf_buf[1]; if (nf_version == FLOW_VERSION_1) { nf1 = (IPStat1Msg*)pi->nf_buf; uptime_delta = ntohl(nf1->header.SysUptime)/10 - uptime_cs(); for (fr1 = nf1->records, j = 0; j != ntohs(nf1->header.count); ++fr1, ++j) { pi->ipackets += ntohl(fr1->dPkts); pp.Low.Interface = ntohs(fr1->input); pp.High.Interface = ntohs(fr1->output); pp.PeerAddrType = AT_IP4; /* Dummy up an IP packet */ memcpy(&pp.Low.PeerAddress.byte,&fr1->srcaddr,IP4_ADDR_LEN); memcpy(&pp.High.PeerAddress.byte,&fr1->dstaddr,IP4_ADDR_LEN); pp.TransAddrType = fr1->prot; pp.Low.TransAddress = fr1->srcport; /* Network byte order */ pp.High.TransAddress = fr1->dstport; pp.DSCodePoint = fr1->tos >> 2; pp.dPkts = ntohl(fr1->dPkts); pp.dOctets = ntohl(fr1->dOctets); pp.dFirst = ntohl(fr1->First)/10 - uptime_delta; /* Centiseconds */ pp.dLast = ntohl(fr1->Last)/10 - uptime_delta; pp.Low.AdjType = 0; /* NetFlow attributes */ pp.Low.AdjAddr_ms4 = 0; pp.Low.AdjAddr_ls2 = 0; #if 0 /* RTFM meter can't match S->D and D->S without having both */ pp.High.AdjType = AT_IP4; /* addresses for EVERY packet */ pp.High.AdjAddr_ms4 = fr1->nexthop; pp.High.AdjAddr_ls2 = 0; #else pp.High.AdjType = 0; pp.High.AdjAddr_ms4 = 0; pp.High.AdjAddr_ls2 = 0; #endif pp.Low.nf_ASN = pp.High.nf_ASN = 0; pp.Low.nf_mask = pp.High.nf_mask = 0; pp.ntm_interface = pi->nbr; /* Remember interface from -i option */ pkt_monitor(&pp); } } else if (nf_version == FLOW_VERSION_5) { nf5 = (IPStat5Msg*)pi->nf_buf; uptime_delta = ntohl(nf5->header.SysUptime)/10 - uptime_cs(); for (fr5 = nf5->records, j = 0; j != ntohs(nf5->header.count); ++fr5, ++j) { pi->ipackets += ntohl(fr5->dPkts); pp.Low.Interface = ntohs(fr5->input); pp.High.Interface = ntohs(fr5->output); pp.PeerAddrType = AT_IP4; /* Dummy up an IP packet */ memcpy(&pp.Low.PeerAddress.byte,&fr5->srcaddr,IP4_ADDR_LEN); memcpy(&pp.High.PeerAddress.byte,&fr5->dstaddr,IP4_ADDR_LEN); pp.TransAddrType = fr5->prot; pp.Low.TransAddress = fr5->srcport; /* Network byte order */ pp.High.TransAddress = fr5->dstport; pp.DSCodePoint = fr5->tos >> 2; pp.dPkts = ntohl(fr5->dPkts); pp.dOctets = ntohl(fr5->dOctets); pp.dFirst = ntohl(fr5->First)/10 - uptime_delta; /* Centiseconds */ pp.dLast = ntohl(fr5->Last)/10 - uptime_delta; /* Was ntohl(fr5->First/10) !!! Bug reported by Oleg O. Orlov , 3 Oct 01 */ pp.Low.AdjType = 0; /* NetFlow attributes */ pp.Low.AdjAddr_ms4 = 0; pp.Low.AdjAddr_ls2 = 0; #if 0 /* RTFM meter can't match S->D and D->S without having both */ pp.High.AdjType = AT_IP4; /* addresses for EVERY packet */ pp.High.AdjAddr_ms4 = fr5->nexthop; pp.High.AdjAddr_ls2 = 0; #else pp.High.AdjType = 0; pp.High.AdjAddr_ms4 = 0; pp.High.AdjAddr_ls2 = 0; #endif #if 0 /* This works on PC, but crashes on SPARC - not short-aligned */ *(Bit16 *)&pp.Low.nf_ASN = ntohs(fr5->src_as); *(Bit16 *)&pp.High.nf_ASN = ntohs(fr5->dst_as); #endif pp.Low.nf_ASN = fr5->src_as; /* Network byte order */ pp.High.nf_ASN = fr5->dst_as; pp.Low.nf_mask = fr5->src_mask; pp.High.nf_mask = fr5->dst_mask; pp.ntm_interface = pi->nbr; /* Remember interface from -i option */ pkt_monitor(&pp); } } else { log_msg(LOG_ERR, 0, msg, "nf_read(%s): NF version %d ???", pi->name, nf_version); return; } } int init_interface(struct interface_info *pi) /* NetFlow */ { int soc; struct sockaddr_in socadr; struct in_addr netid; ushort port; if (pi->name[0] == '\0') strcpy(pi->name, "9996"); port = atoi(pi->name); /* Open a UDP port to the NetFlow router */ socadr.sin_family = AF_INET; socadr.sin_addr.s_addr = htonl(INADDR_ANY); socadr.sin_port = htons(port); netid.s_addr = socadr.sin_addr.s_addr; if ((soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { log_msg(LOG_ERR, 0, "interface %d: couldn't get socket, errno %d\n", pi->nbr, (errno)); return 0; /* Fail */ } /* Bind the socket */ if (bind(soc, (struct sockaddr *)&socadr, sizeof(socadr)) < 0) { log_msg(LOG_ERR, 0, "interface %d: couldn't bind socket, errno %d\n", pi->nbr, (errno)); return 0; /* Fail */ } sprintf(pi->name, "udp-%u", port); pi->fd = soc; pi->nf_buf = (char *)malloc(MAX_FLOW_PAK_SIZE); return 1; /* OK */ } unsigned int pkt_counts(int reset) { int j; struct interface_info *pi; if (reset) { npackets_org = pkts; lostpackets_org = drops; } pkts = drops = 0L; /* Get total packets seen and dropped */ for (j = 0; j != n_interfaces; ++j) { pi = pci[j]; pkts += pi->ipackets; drops += pi->idrops; } npackets = pkts - npackets_org; lostpackets = drops - lostpackets_org; return 1; } #elif CR_DATA /* !NF_CISCO_DATA */ /* ===== Interface code using CoralReef to get packet headers ===== */ #define MERGE_TEST 0 #define MX_TS_JUMP 10000000 /* in us, i.e. 10 seconds */ int tv_diff_us(struct timeval x, struct timeval y) { /* Returns x-y microseconds, **assuming y >= x** */ int s,u; s = x.tv_sec - y.tv_sec; if ((u = x.tv_usec - y.tv_usec) < 0) { --s; u += 1000000; } return s*1000000 + u; } int waiting_live_source = -1; static struct timeval last_ts; /* Used by uptime() to get CoralReef times */ int select_us = 4*MN_SELECT_US; struct timeval select_tv = {0,4*MN_SELECT_US}; #define MX_NODATA_READS 50 /* Don't wait too long! */ void handle_coral_packet(int src_nbr, coral_pkt_buffer_t *header, coral_pkt_buffer_t *packet) { union atm_hdr atm_h; coral_llcsnap_t *snap_h; coral_pkt_buffer_t payload; struct ether_hdr *ethp; struct llc *llcp; unsigned int ether_type, lsap, atype; Bit8 *p; struct ether_hdr ehdr; struct pkt pmp; switch (packet->protocol) { case CORAL_DLT_ATM_RFC1483: atm_h.ui = ntohl(*(Bit32 *)header->buf); snap_h = (coral_llcsnap_t *)packet->buf; if (coral_get_payload(packet, &payload) < 0) { log_msg(LOG_INFO, 0, "coral_get_payload failed"); return; } if (adj_reqd) { /* Don't do this unless we have to! */ /* Use UNI VPI and VCI as low 3 bytes of MAC address. Put it in both source and dest so that rulesets which test only one will work as expected */ *(Bit16 *)&ehdr.dest[0] = *(Bit16 *)&ehdr.source[0] = 0; *(Bit32 *)&ehdr.dest[2] = *(Bit32 *)&ehdr.source[2] = atm_h.h.vpvc; } ether_type = ntohs(snap_h->snap_type); lsap = 0xAAAA; ethp = &ehdr; p = (Bit8 *)payload.buf; atype = AT_AAL5; break; case CORAL_DLT_ATM_AAL5: log_msg(LOG_ERR, 0, "coral_packet(): ATM_AAL5 unimplemented"); return; case CORAL_DLT_LANE_IEEE8023: log_msg(LOG_ERR, 0, "coral_packet(): LANE_IEEE8023 unimplemented"); return; case CORAL_DLT_EN10MB: ethp = (struct ether_hdr *)packet->buf; ether_type = ntohs(ethp->type); if (coral_get_payload(packet, &payload) < 0) { log_msg(LOG_INFO, 0, "coral_get_payload failed"); return; } lsap = 0xAAAA; p = (Bit8 *)payload.buf; atype = AT_ETHERNET; break; case CORAL_DLT_IEEE802: ethp = (struct ether_hdr *)packet->buf; if (coral_get_payload(packet, &payload) < 0) { log_msg(LOG_INFO, 0, "coral_get_payload failed"); return; } p = (Bit8 *)payload.buf; llcp = (struct llc *)p; lsap = llcp->dsap << 8 | llcp->ssap; if (lsap == 0xAAAA) { /* SNAP packet */ ether_type = ntohs(*(Bit16 *)llcp->ctl.snap_ether.snap_ethertype); p += sizeof(struct llc); } else ether_type = 0; /* Other 802.2 packet */ atype = AT_ETHERNET; break; case CORAL_DLT_GIGX: log_msg(LOG_ERR, 0, "coral_packet(): GIGX unimplemented"); return; default: #if 0 { int j; Bit8 *p8 = (Bit8 *)packet->buf; for (j = 0; j != 40; ++j) printf(" %02x", p8[j]); printf("\n"); } log_msg(LOG_ERR, 0, "coral_packet(): unknown protocol %d", packet->protocol); #endif return; } pmp.p_len = 0; /* use_ip_length will get length from IP header */ if (handle_pkt(&pmp, ether_type,lsap, (unsigned char *)ethp, p, payload.caplen)) { pmp.Low.Interface = pmp.High.Interface = src_nbr+1; /* 1-org */ pmp.Low.AdjType = pmp.High.AdjType = atype; pmp.arrival_time = last_ts; /* struct timeval */ pkt_monitor(&pmp); } } int read_live_block(int); void fmt_ts(char *t_buf, struct timeval t) { tv_sub(t, meter_start_time); sprintf(t_buf, "%lu.%06lu", t.tv_sec, t.tv_usec); } void bad_ts_msg(char *msg, int s, struct interface_info *pi, struct timeval ts, struct timeval tb, char *comment) /* ts = last good timestamp, tb = timestamp from buffer. diff (as printed) = tb - ts */ { int d = tv_diff_us(tb,ts)/1000000; char b_ts[30], b_tb[30]; fmt_ts(b_ts, ts); fmt_ts(b_tb, tb); log_msg(LOG_ERR, 0, "%s i/f %d: cells=%u/%u, %s-%s, diff=%ds%s", msg, s+1, pi->ncells,pi->icells, b_ts,b_tb, d, comment); } int ts_seq_ok(struct timeval ts, struct timeval tb) { int td = tv_diff_us(tb,ts); if (td < 0) td = -td; return td <= MX_TS_JUMP; } coral_merge() { struct timeval ts, mints; int j, minj, tsec, n; struct interface_info *pi; coral_pkt_buffer_t outer, hdr; char nbt[60]; pi = pci[0]; fmt_ts(nbt, pi->card_ts); for (;;) { mints = pci[0]->card_ts; minj = 0; for (j = 1; j != n_interfaces; ++j) { /* Find min timestamp */ ts = pci[j]->card_ts; if ((ts.tv_sec == mints.tv_sec && ts.tv_usec <= mints.tv_usec) || ts.tv_sec < mints.tv_sec) { mints = ts; minj = j; } } pi = pci[minj]; last_ts = mints; /* Used by uptime() routines */ if (--pi->sample_count == 0) { /* Process only 1 pkt per SampleRate */ pi->sample_count = pi->SampleRate; if (coral_cell_to_pkt(pi->iface, pi->cellp, &hdr)) { /* Dummy up an RFC1483 packet */ outer.protocol = CORAL_DLT_ATM_RFC1483; outer.buf = (Bit8 *)coral_cell_header(pi->iface, pi->cellp); handle_coral_packet(pi->nbr, &outer, &hdr); } } if (last_ts.tv_sec > last_t_sec) { last_t_sec = last_ts.tv_sec; one_second_process(); /* NeTraMet housekeeping */ /* Note this happens at time seen by interface, could get several seconds of data in buffer */ } ++pi->ipackets; /* For global statistics */ if (--pi->ncells == 0) { waiting_live_source = minj; return; /* Keep outer block alive */ } else { pi->cellp += coral_cell_size(pi->iface); CORAL_TIMESTAMP_TO_TIMEVAL(pi->iface, coral_cell_time(pi->iface, pi->cellp), &ts); if (ts_seq_ok(mints, ts)) { pi->card_ts = ts; /* Timestamp looks OK */ } else { /* Bad timestamp, look through block for a good one */ for (n = pi->ncells; pi->ncells != 0; --pi->ncells, pi->cellp += coral_cell_size(pi->iface)) { CORAL_TIMESTAMP_TO_TIMEVAL(pi->iface, coral_cell_time(pi->iface, pi->cellp), &ts); if (ts_seq_ok(mints, ts)) break; /* Found a likely timestamp */ ++pi->badtspkts; } if (pi->ncells == 0) { /* Ignore bad pkts at end of buffer */ sprintf(nbt, ": %d pkts ignored (end of buffer)", n); bad_ts_msg("**T", minj, pi, mints, ts, nbt); pi->idrops += n; waiting_live_source = minj; return; /* Keep outer block alive */ } else { /* Ignore bad pkts within buffer */ sprintf(nbt, ": %d pkts ignored (within buffer)", n-pi->ncells); bad_ts_msg("*T*", minj, pi, mints, ts, nbt); pi->idrops += n-pi->ncells; pi->card_ts = ts; /* Change timestamp */ } } } } } int read_live_block(int i) { struct interface_info *pi = pci[i]; coral_iface_t *iface; coral_blk_info_t *binfop; coral_atm_cell_t *cellp; struct timeval ts; char g_ts[30], b_ts[30]; errno = 0; /* System call error variable */ if ((iface = coral_read_block(pi->crl_src, &binfop, &cellp, &select_tv)) == NULL) { if (errno != pi->last_errno) { if (errno != EAGAIN) log_msg(LOG_ERR, 0, "if_read(1): i/f %d, error %d (%s)", pi->nbr, errno, strerror(errno)); pi->last_errno = errno; } /* NOTE: The Coral Dag driver doesn't return partial blocks, we have to sit around waiting until we get 16384 cells on an interface, which can take several seconds on a link which doesn't have much traffic! */ if (++pi->empty > MX_NODATA_READS) { /* Guess lowest ts for next pkt */ tv_add(pi->card_ts,select_tv); pi->empty = 0; if (!pi->no_data_warned) { fmt_ts(g_ts, pi->card_ts); log_msg(LOG_WARNING, 0, "i/f %d, no data, ts set to %s", pi->nbr, g_ts); pi->no_data_warned = 1; } } return 0; /* Try again */ } else { /* Data bytes read */ pi->no_data_warned = pi->last_errno = 0; pi->iface = iface; pi->cellp = cellp; pi->icells = pi->ncells = ntohl(binfop->cell_count); pi->idrops += ntohl(binfop->cells_lost); #if MERGE_TEST == 2 printf("\nif_read(2): blocks=%d, icells=%d", pi->nblocks,pi->icells); #endif CORAL_TIMESTAMP_TO_TIMEVAL(pi->iface, coral_cell_time(pi->iface, pi->cellp), &ts); if (pi->nblocks == 0 || /* Just getting started */ pi->empty != 0 || /* Had some no_data reads */ ts_seq_ok(pi->card_ts, ts)) { pi->card_ts = ts; /* Timestamps OK between blocks */ } else { /* Bad timestamp at start of block */ fmt_ts(g_ts, pi->card_ts); fmt_ts(b_ts, ts); log_msg(LOG_ERR, 0, "nif_read(5): i/f %d, block %d, %d cells, %s -> %s, " "bad ts at start of block !!!!!", pi->nbr, pi->nblocks, pi->ncells, g_ts, b_ts); pi->idrops += pi->icells; ++pi->badtspkts; return 0; } ++pi->nblocks; pi->tcells += pi->ncells; #if MERGE_TEST >= 1 fmt_ts(b_ts, pi->card_ts); printf("\nif_read(7): i/f %d, ts=%s, %d ncells, %d nblocks\n", pi->nbr, b_ts, pi->ncells, pi->nblocks); #endif } return 1; } void init_live_sources(void) { #define STARTUP_TRIES 2000 /* There may not be very much traffic */ int j,n,b; struct timeval ts; sleep(10); /* Wait until we get a full buffer */ for (j = 0; j != n_interfaces; ++j) pci[j]->nblocks = 0; for (n = 0; n != STARTUP_TRIES; ++n) { for (j = 0; j != n_interfaces; ++j) { /* Get block for each source */ read_live_block(j); } for (b = j = 0; j != n_interfaces; ++j) if (pci[j]->nblocks != 0) ++b; if (b == n_interfaces) break; } if (n == STARTUP_TRIES) log_msg(LOG_ERR, 98, "init_live_sources(): couldn't get data from interface(s)"); meter_start_time = pci[0]->card_ts; /* Set to min Coral timestamp */ for (j = 1; j != n_interfaces; ++j) { ts = pci[j]->card_ts; if (ts.tv_sec == meter_start_time.tv_sec && ts.tv_usec <= meter_start_time.tv_usec) meter_start_time = ts; else if (ts.tv_sec < meter_start_time.tv_sec) meter_start_time = ts; } last_ts = meter_start_time; printf("init_live_sources(): first blocks read\n"); fflush(stdout); } int interface_read_trace(struct interface_info *pi) { coral_iface_t *iface; coral_pkt_result_t pkt_result; coral_interval_result_t interval_result; int src_nbr; if ((iface = coral_read_pkt(&pkt_result, &interval_result)) == NULL) { if (errno == 0) { /* End of trace file or stopped device */ show_memory(); show_stream_hash(); } else log_msg(LOG_ERR, 0, "coral_read_pkt() failed, %s", strerror(errno)); return 0; /* Signals EOF to non-live loop */ } else { src_nbr = coral_source_get_number(coral_interface_get_src(iface)); if (pkt_result.packet != NULL) { /* We have a packet */ CORAL_TIMESTAMP_TO_TIMEVAL(iface, pkt_result.timestamp, &last_ts); pi->sample_count = pi->SampleRate; handle_coral_packet(src_nbr, pkt_result.header, pkt_result.packet); if (last_ts.tv_sec != last_t_sec) { last_t_sec = last_ts.tv_sec; one_second_process(); /* NeTraMet housekeeping */ --trace_count; } } else if (interval_result.stats == NULL) { /* Coral interval has begun */ last_ts = interval_result.begin; } else { /* A Coral interval has ended **or** we've reached EOF !!! */ pi->ipackets += interval_result.stats->pkts_recv; pi->idrops += interval_result.stats->pkts_drop; printf("End(%d): ts=%d.%03d, active=%u, new=%d, recov=%u, " "(GC: %u s, %u flow), pkts=%d, streams=%d\n", trace_count, last_ts.tv_sec,last_ts.tv_usec/1000, active_flows(), FlowsCreated, FlowsRecovered, gc_interval, gc_f, mxpktdatas-n_free_pktdata, mxstreams-n_free_streams); zero_stats(0); last_ts = interval_result.end; if (last_ts.tv_sec != last_t_sec) --trace_count; /* Tell receive() loop we're at interval end */ } } return 1; } char *cs_type(int cst) { switch (cst) { case CORAL_TYPE_FILE: return "CoralReef file"; case CORAL_TYPE_PCAP: return "tcpdump file"; case CORAL_TYPE_DAGFILE: return "Dag file"; case CORAL_TYPE_TSH: return "tsh file"; case CORAL_TYPE_NONE: return "Invalid or Unknown"; case CORAL_TYPE_FATM: return "FORE card"; case CORAL_TYPE_POINT: return "POINT card"; case CORAL_TYPE_DAG: return "DAG card"; case CORAL_TYPE_PCAPLIVE: return "pcap interface"; default: return "Bad cs_type"; } } int init_interface(struct interface_info *pi) /* CoralReef */ { int src_nbr,src_type, if_nbr, info_len, len, ir; time_t ct, min_ct; char buf[150], *bp, *crl_info_string; coral_source_t *i_src; struct timeval c_interval; coral_iface_t *iface; int ifn, ift; time_t cap_time; if (pi->nbr == 1) { /* Handling first CoralReef source */ coral_set_options(0, CORAL_OPT_NORMALIZE_TIME); /* We want timestamps in Unix epoch across all sources */ } if (coral_open(i_src = pi->crl_src) < 0) { log_msg(LOG_ERR, 0, "coral_open(%s) failed", pi->name); return 0; /* Fail */ } src_nbr = coral_source_get_number(i_src); if (src_nbr != pi->nbr-1) log_msg(LOG_ERR, 0, "src_nbr(%d) != pi->nbr(%d)", src_nbr,pi->nbr-1); src_type = coral_source_get_type(i_src); switch (src_type) { case CORAL_TYPE_FILE: /* CoralReef trace file */ case CORAL_TYPE_PCAP: /* pcap (tcpdump) trace file */ case CORAL_TYPE_DAGFILE: case CORAL_TYPE_TSH: case CORAL_TYPE_NONE: /* Invalid or unknown */ default: sprintf(pi->name, "crl%d (%s)", src_nbr, cs_type(src_type)); pi->adjtype = AT_TRACE; /* Don't know original interface type */ live_source = 0; break; case CORAL_TYPE_FATM: /* FORE OC3 card */ case CORAL_TYPE_POINT: /* Apptel POINT card */ case CORAL_TYPE_DAG: /* DAG card */ case CORAL_TYPE_PCAPLIVE: sprintf(pi->name, "%s (%s)", coral_source_get_filename(i_src), cs_type(src_type)); pi->adjtype = AT_ATM; live_source = 1; break; } if (pi->nbr == n_interfaces) { /* Handling last CoralReef source */ log_msg(LOG_INFO, 0, "%d coral interfaces opened", n_interfaces); if (coral_start_all() < 0) { /* Also synchronises all sources */ log_msg(LOG_ERR, 0, "coral_start failed"); return 0; /* Fail */ } if (!live_source) { cap_time = 0; iface = NULL; do { iface = coral_next_interface(iface); if (iface != NULL) { if_nbr = coral_interface_get_number(iface); ct = coral_interface_get_capturetime(iface); if (if_nbr == 0) min_ct = ct; else if (ct < min_ct) min_ct = ct; } } while (iface != NULL); sprintf(buf, "%d source file %s cap_time %u ", trace_interval, coral_source_get_filename(i_src), min_ct); info_len = strlen(buf); bp = &buf[info_len]; cap_time = 0; iface = NULL; do { iface = coral_next_interface(iface); if (iface != NULL) { if_nbr = coral_interface_get_number(iface); src_type = coral_interface_get_type(iface); cap_time = coral_interface_get_capturetime(iface); sprintf(bp, " (i/f %d %s %d)", if_nbr, cs_type(src_type), cap_time); len = strlen(bp); bp += len; info_len += len; } } while (iface != NULL); if (info_len > sizeof(buf)) log_msg(LOG_ERR, 1, "NeTraMet Coral info string overflow !!!"); crl_info_string = (char *)malloc(info_len+1); strcpy(crl_info_string, buf); crl_info = crl_info_string; /* NeMaC writes this to a #CoralReef record */ } else { /* Live source */ cap_time = 0; iface = NULL; do { iface = coral_next_interface(iface); if (iface != NULL) { if_nbr = coral_interface_get_number(iface); ct = coral_interface_get_capturetime(iface); if (if_nbr == 0) min_ct = ct; else if (ct < min_ct) min_ct = ct; } } while (iface != NULL); last_t_sec = meter_start_time.tv_sec = min_ct; meter_start_time.tv_usec = 0; } if (!live_source) { if (trace_interval <= 0) { printf("No meter reading interval specified (-T option)\n"); log_msg(LOG_ERR, 0, "No meter reading interval specified (-T option)"); return 0; /* Fail */ } c_interval.tv_sec = trace_interval; c_interval.tv_usec = 0; if ((ir = coral_read_pkt_init(NULL, NULL, &c_interval)) != 0) { /* For one_second_process() */ log_msg(LOG_ERR, 0, "coral_read_pkt_init failed, result=%d", ir); return 0; /* Fail */ } /* We only want one interval per second, not one per source */ trace_count = trace_interval; crl_n_count = 0; } } return 1; /* OK */ } unsigned int pkt_counts(int reset) /* Get total packets seen and dropped */ { /* 'Source' counters updated at end of CoralReef intervals */ int j; struct interface_info *pi; if (reset) { npackets_org = pkts; lostpackets_org = drops; } pkts = drops = 0L; for (j = 0; j != n_interfaces; ++j) { /* One NetraMet interface per source */ pi = pci[j]; pkts += pi->ipackets; drops += pi->idrops; } npackets = pkts - npackets_org; lostpackets = drops - lostpackets_org; return 1; } #elif LFAP_METER /* !NF_CISCO_DATA && !CR_DATA */ int lfap_server_socket; int lfap_info_level = 0; int lfap_keepalive_interval = 30; /* in seconds */ char lfap_community[SZ_IFNAME]; struct sockaddr_in lfap_server_addr; val64 fasPrefix; int handleVR(struct interface_info *pi); int handleFAR(struct interface_info *pi); int handleFUN(struct interface_info *pi); int handleAR(struct interface_info *pi); int handleARA(struct interface_info *pi); void processMessage(struct interface_info *pi); void handleChangedFlows(struct interface_info *pi); void removeFlow(struct interface_info *pi, int flownr); void tick(struct interface_info *pi); int start_lfap(struct utsname *name); #include "lfapmet.c" /* Too much code to keep in this file! */ #elif DAG_DATA /* !NF_CISCO_DATA && !CR_DATA && !LFAP_METER */ /* ===== Dag ADSL (via input pipe) interface code ===== */ #include /* DAG_ADSL should select this format for the Dag data ... */ struct adsl_rec { long long ts; /* Dag timestamp (64-bit) */ Bit32 atm_hdr; /* First byte 0x01 or 00: packet direction */ Bit8 stuff[32]; /* PPPoE bridged over AAL5 stuff */ Bit8 ip_hdr[64]; /* First 64 bytes of IP packet */ }; #if 0 ts=0x3a94c97f2708f900 atm=0x00a00660 0xaaaa0300 0x80c20007 0x0000 0090 0x3935403f Concentrator MAC addr 0x00d0590d 0x325b 8864 0x1100 0066 0x05ae 0021 Host MAC addr PPPoE vt SessID len PPP protocol 0x450005ac 0x42964000 0x4006d73e 0x0a64034a PPP payload 0x0ac80302 0x08830014 0x0abf8a22 0x00545b09 0x50107edc 0xb8ce0000 0xecfe1d19 0xa5da1b3c pkt0x3c334000 0x0001121a 0x8be23cfa 0x3683523c #endif void handle_adsl_record(struct interface_info *pi, struct adsl_rec *a_buf) { struct timeval tv; struct pkt pp; struct ether_hdr ehdr; Bit32 ah; Bit8 dirn; /* printf("ts=%qx atm=%lx\n", a_buf->ts, a_buf->atm_hdr); */ tv.tv_sec = (long)(a_buf->ts >> 32); tv.tv_usec = ((a_buf->ts & 0xffffffffLL) * 1000000) >> 32; if (pi->if_delta_set == 0) { /* First timestamp for this interface */ pi->if_time_delta = tv; tv_sub(pi->if_time_delta, meter_start_time); pi->if_delta_set = 1; } if (--pi->sample_count == 0) { /* Process only 1 pkt per SampleRate */ pi->sample_count = pi->SampleRate; ah = ntohl(a_buf->atm_hdr); dirn = *(Bit8 *)&ah; /* 0 or 1, indicates dag0 or dag1 */ if (adj_reqd) { ehdr.source[0] = ehdr.source[1] = 0; memcpy(&ehdr.source[3], (Bit8 *)&ah + 1, 3); memcpy(&ehdr.dest, &ehdr.source, MAC_ADDR_LEN); if (dirn) { ehdr.source[2] = 2; ehdr.dest[2] = 1; } else { ehdr.source[2] = 1; ehdr.dest[2] = 2; } } if (handle_pkt(&pp, 0x0800, 0xAAAA, (Bit8 *)&ehdr, /* Will need to work out what eth_type/lsap should be */ a_buf->ip_hdr, 64)) { pp.Low.AdjType = pp.High.AdjType = AT_PPP; /* Generic PPP */ if (dirn) { pp.Low.Interface = 2; pp.High.Interface = 1; } else { pp.Low.Interface = 1; pp.High.Interface = 2; } pp.arrival_time = tv; pkt_monitor(&pp); if (pp.arrival_time.tv_sec != last_t_sec) { last_t_sec = pp.arrival_time.tv_sec; one_second_process(); } } } } Bit8 a_b[sizeof(struct adsl_rec)]; /* ADSL record buffer */ int n_b = 0; /* Bytes already in a_b */ void interface_read(struct interface_info *pi) { int n, r; for (n = 20; n != 0; --n) { if ((r = read(pi->fd, &a_b[n_b], sizeof(struct adsl_rec)-n_b)) <= 0) { if (errno == EAGAIN) { return; /* No more data in pipe */ } else { log_msg(LOG_ERR, 0, "adsl_read(): %s\n", strerror(errno)); return; } } else { n_b += r; if (n_b != sizeof(struct adsl_rec)) return; /* Wait for rest of record */ } handle_adsl_record(pi, (struct adsl_rec *)a_b); n_b = 0; } } int init_interface(struct interface_info *pi) /* Dag ADSL */ { int fd, flags; if (strcmp(pi->name,"stdin") == 0) { fd = dup(STDIN_FILENO); close(STDIN_FILENO); if (kb_enabled) { if (open("/dev/tty", O_RDONLY) != 0) { log_msg(LOG_ERR, 0, "adsl_open(%s): coudn't open /dev/tty, %s\n", pi->name, strerror(errno)); exit(1); } } } else if ((fd = open(pi->name, O_RDONLY)) < 0) { log_msg(LOG_ERR, 0, "adsl_open(%s): %s\n", pi->name, strerror(errno)); return 0; /* Fail */ } if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { log_msg(LOG_ERR, 0, "adsl_open(%s): fcntl get failed\n", pi->name); return 0; /* Fail */ } flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) { log_msg(LOG_ERR, 0, "adsl_open(%s): fcntl set failed\n", pi->name); return 0; /* Fail */ } pi->fd = fd; return 1; /* OK */ } unsigned int pkt_counts(int reset) { if (reset) { npackets_org = pkts; lostpackets_org = drops; } pkts = drops = 0L; /* Get total packets seen and dropped */ return 1; } /* ===== End of ADSL interface code ===== */ #elif DAG_DIRECT /* !NF_CISCO_DATA && !CR_DATA && !LFAP_METER && !ADSL_DATA */ /* ===== Dag direct interface code ===== */ #include #define MERGE_TEST 0 #define MAX_CONSEC_BADTS 25 /* Shut down meter after this many bad timestamps */ #define DAG_BUF_SZ 262144 /* Records */ #define MX_TS_JUMP 10 /* Seconds */ int waiting_live_source = -1; long long last_ts; int select_us = 4*MN_SELECT_US; long long select_time; #define MX_NODATA_READS 100 /* Conversion functions: s and us from Dag (64-bit) timestamp */ #define DAGT_S(dts) (long)((dts) >> 32) #define DAGT_US(dts) (long)((((dts) & 0xffffffffLL) * 1000000) >> 32) #define US_DAGT(us) (((long long)(us) << 32)/1000000LL) void handle_dag_record(struct interface_info *pi) { Bit32 ah; Bit32 vpvcmask = 0x0ffffff0; /* VPI 8 bits, VCI 16 bits */ struct snap_hdr *sh; struct ether_hdr ehdr, *eh; Bit8 *p; int plen; Bit16 lsap, ether_type; struct pkt pp; int r; if (pi->dag_type == TT_POS) { #if MERGE_TEST == 2 printf("%d", pi->nbr); /* */ #endif if (adj_reqd) { pp.Low.AdjType = pp.High.AdjType = AT_SONET; memset(&ehdr, 0, sizeof(struct ether_hdr)); } r = handle_pkt(&pp, ntohs(pi->drp->d.pos.etype), 0, (Bit8 *)&ehdr, pi->drp->d.pos.pload, sizeof(pi->drp->d.pos.pload)); } else if (pi->dag_type == TT_ATM) { ah = ntohl(pi->drp->d.atm.atm_hdr); ah &= vpvcmask; ah >>= 4; /* VPI:VCI */ /* printf("ts=%qx (%u.%06u), atm=%06x (%08x) ", pi->drp->d.atm.ts, DAGT_S(pi->drp->d.atm.ts),DAGT_US(pi->drp->d.atm.ts), ah, ntohl(pi->drp->d.atm.atm_hdr)); /* */ sh = &pi->drp->d.atm.sh; if ((lsap = ntohs(sh->lsap)) == 0xAAAA) { /* SNAP packet */ ether_type = ntohs(sh->etype); #if MERGE_TEST == 2 printf("%d", pi->nbr); /* */ #endif } else { ether_type = ntohs(sh->etype); /* printf("\natm=%06x: ", ah, lsap, ether_type); snp = (Bit8 *)&pi->drp->d.atm.sh; for (j = 0; j != 24; ++j) printf(" %02x", snp[j]); printf("\n"); */ /* Will need to work out what eth_type/lsap should really be! */ pp.p_len = 48; /* Guess it's one ATM cell (!) */ } if (adj_reqd) { pp.Low.AdjType = pp.High.AdjType = AT_AAL5; *(Bit16 *)&ehdr.dest[0] = *(Bit16 *)&ehdr.source[0] = 0; *(Bit32 *)&ehdr.dest[2] = *(Bit32 *)&ehdr.source[2] = ah; } r = handle_pkt(&pp, ether_type, lsap, (Bit8 *)&ehdr, pi->drp->d.atm.pload, sizeof(pi->drp->d.atm.pload)); } else if (pi->dag_type == TT_ETHER) { #if MERGE_TEST == 2 printf("%d", pi->nbr); /* */ #endif p = pi->drp->d.eth.pload; plen = sizeof(pi->drp->d.eth.pload); eh = &pi->drp->d.eth.eh; if ((ether_type = ntohs(eh->type)) <= 1500) { /* 802.3 packet */ sh = (struct snap_hdr *)p; if ((lsap = ntohs(sh->lsap)) == 0xAAAA) { /* SNAP packet */ ether_type = ntohs(sh->etype); p += sizeof(struct snap_hdr); plen -= sizeof(struct snap_hdr); } else ether_type = 0; /* Other 802.2 packet */ } else lsap = 0; /* Blue book packet */ pp.p_len = pi->drp->d.eth.wlen; /* Overwritten if use_ip_len true */ if (adj_reqd) pp.Low.AdjType = pp.High.AdjType = AT_ETHERNET; r = handle_pkt(&pp, ether_type, lsap, (Bit8 *)eh, p, plen); } /* for (j = 8; j != 24; ++j) printf(" %02x", pi->drp->pload[j]); printf(" "); /* */ if (r) { if (n_interfaces == 2) { /* Assume they're two directions */ if (pi->nbr == 2) { /* of a single link */ pp.Low.Interface = 2; pp.High.Interface = 1; } else { pp.Low.Interface = 1; pp.High.Interface = 2; } } else { /* Can only see one packet at a time */ if (live_source) { pp.Low.Interface = pp.High.Interface = pi->nbr; } else { /* Assume trace file is dagmerge of *two* Dag files */ if (pi->drp->ts & 0x1ULL) { /* LSB of timestamp */ pp.Low.Interface = 2; pp.High.Interface = 1; } else { pp.Low.Interface = 1; pp.High.Interface = 2; } } } pp.arrival_time = pi->drp->ts; pkt_monitor(&pp); } } int read_live_block(int); void fmt_ts(char *t_buf, long long t) { long long mt = t - meter_start_time; sprintf(t_buf, "%lu.%06lu", DAGT_S(mt),DAGT_US(mt)); } void bad_ts_msg(char *msg, int s, struct interface_info *pi, long long ts, long long tb, char *comment) /* ts = last good timestamp, tb = timestamp from buffer. diff (as printed) = tb - ts */ { int d = DAGT_S(tb - ts); char b_ts[30], b_tb[30]; fmt_ts(b_ts, ts); fmt_ts(b_tb, tb); log_msg(LOG_ERR, 0, "%s i/f %d: cells=%u/%u, %s-%s, diff=%ds%s", msg, s+1, pi->ncells,pi->icells, b_ts,b_tb, d, comment); } int ts_seq_ok(long long ts, long long tb) { long long td = tb-ts; if (td < 0) td = -td; return DAGT_S(td) <= MX_TS_JUMP; } dag_merge() { long long ts, mints; /* Dag timestamp (64-bit) */ int j, minj, tsec, n; struct interface_info *pi; char nbt[60]; for (;;) { mints = pci[0]->card_ts; minj = 0; for (j = 1; j != n_interfaces; ++j) { /* Find min timestamp */ ts = pci[j]->card_ts; if (ts <= mints) { mints = ts; minj = j; } } pi = pci[minj]; last_ts = mints; /* Used by uptime() routines */ if (--pi->sample_count == 0) { /* Process only 1 pkt per SampleRate */ pi->sample_count = pi->SampleRate; handle_dag_record(pi); tsec = DAGT_S(mints); if (tsec > last_t_sec) { last_t_sec = tsec; one_second_process(); /* NeTraMet housekeeping */ /* Note this happens at the time seen by the interface, we could get several seconds worth of data in a buffer */ if (!live_source && --trace_count == 0) { waiting_live_source = minj; return; /* Keep trace file loop alive */ } } } ++pi->ipackets; /* For global statistics */ if (--pi->ncells == 0) { waiting_live_source = minj; return; /* Keep outer block alive */ } else { ++pi->drp; ts = pi->drp->ts; if (ts_seq_ok(mints, ts)) { pi->card_ts = ts; /* Timestamp looks OK */ } else { /* Bad timestamp, look through buffer for a good one */ for (n = pi->ncells; pi->ncells != 0; --pi->ncells, ++pi->drp) { if (ts_seq_ok(mints, ts = pi->drp->ts)) break; /* Found a likely timestamp */ ++pi->badtspkts; } if (pi->ncells == 0) { /* Ignore bad pkts at end of buffer */ sprintf(nbt, ": %d pkts ignored (end of buffer)", n); bad_ts_msg("**T", minj, pi, mints, ts, nbt); pi->idrops += n; waiting_live_source = minj; return; /* Keep outer block alive */ } else { /* Ignore bad pkts within buffer */ sprintf(nbt, ": %d pkts ignored (within buffer)", n-pi->ncells); bad_ts_msg("*T*", minj, pi, mints, ts, nbt); pi->idrops += n-pi->ncells; pi->card_ts = ts; /* Change timestamp */ } } } } } FILE *dag_data_log = NULL; void log_dag_data(int cmd, int iface, int block, struct dag_rec *d_buf, int bytes) { Bit8 *bp; int recs, j, k; if (dag_data_log == NULL) dag_data_log = fopen("dag_data", "w"); switch (cmd) { case 1: /* Log last 2 records plus trailing bytes */ fprintf(dag_data_log, "i/f %d, block %d, last 2+ records:\n", iface, block); recs = (bytes + sizeof(struct dag_rec)-1)/sizeof(struct dag_rec); j = (recs-3)*sizeof(struct dag_rec); bp = (Bit8 *)d_buf + j; break; case 2: /* Log first 3 records */ fprintf(dag_data_log, "i/f %d, block %d, first 3 records:\n", iface, block); j = 0; bp = (Bit8 *)d_buf; break; } for (k = 0; k != 3; ++k) { fprintf(dag_data_log, " %02x", *bp++); for (++j; j < bytes; ) { fprintf(dag_data_log, " %02x", *bp++); if (++j % sizeof(struct dag_rec) == 0) break; if (j % 32 == 0) fprintf(dag_data_log, " "); } fprintf(dag_data_log, "\n"); } fprintf(dag_data_log, "\n"); } void dag_reset(int i) { /* Sometimes, after running happily for hours or days, a Dag card can just stop writing cells into memory. When this happens, DAG_DIRECT reads just keep returning 0 bytes + errno 11 (EAGAIN). A level 2 reset of the Dag card seems to get it working again! Nevil (with help from Stephen and David), Fri 11 Jan 02 */ dagioaddr dagioa; int r; /* The card has a reset register at 0x88. The relevant bitmap is: Bit 31: Trigger level 2 reset (auto clearing) Bit 30: Trigger level 1 reset (not auto clearing) */ dagioa.offset = 0x88; dagioa.val = 0x80000000; r = ioctl(pci[i]->fd, DAG_IOWRITE, &dagioa); log_msg(LOG_ERR, 0,"=== dag_reset(%d): ioctl returned %d", i,r); if (r != 0) log_msg(LOG_ERR, 0," error %d, %s", errno, strerror(errno)); } int read_live_block(int i) { struct interface_info *pi = pci[i]; int r, en, s, n, rr; long long prev_ts; char g_ts[30], b_ts[30]; Bit8 *p8; errno = 0; /* System call error variable */ if ((r = read(pi->fd, pi->d_buf, sizeof(struct dag_rec)*DAG_BUF_SZ)) <= 0) { if (!live_source) { if (r == 0) { /* EOF on trace file */ show_memory(); return 0; } } else if ((en = errno) != pi->last_errno) { log_msg(LOG_ERR, 0,"if_read(1): i/f %d, error %d (%s)", pi->nbr, en, strerror(en)); pi->last_errno = errno; } /* No data. This includes EAGAIN reads! */ if (pi->empty > MX_NODATA_READS) { /* Guess lowest ts for next pkt */ pi->card_ts = last_ts + select_time; if (!pi->no_data_warned) { fmt_ts(g_ts, pi->card_ts); log_msg(LOG_WARNING, 0, "i/f %d, no data, ts set to %s", pi->nbr, g_ts); pi->no_data_warned = 1; } dag_reset(i); /* Try to reset the Dag card */ } r = 0; s = S_EMPTY; /* No data in pipe */ } else { /* Data bytes read */ #if MERGE_TEST == 2 printf("\nif_read(2): i/f %d, %d records read ", pi->nbr, r/sizeof(struct dag_rec)); #endif pi->no_data_warned = pi->last_errno = 0; rr = r; n = r % sizeof(struct dag_rec); if (n != 0 && n != 32) { /* Dag4 cards have 'half record' bug */ fmt_ts(g_ts, pi->card_ts); fmt_ts(b_ts, pi->drp->ts); log_msg(LOG_ERR, 0, "if_read(3): i/f %d, block %d," " %d bytes, %d remain, %s -> %s <---", pi->nbr, pi->nblocks, r, n, g_ts, b_ts); log_dag_data(2, i, pi->nblocks, pi->d_buf, rr); /*+ Log first 3 records */ log_dag_data(1, i, pi->nblocks, pi->d_buf, rr); /*+ Log last 2 records plus trailing bytes */ } pi->drp = pi->d_buf; pi->icells = pi->ncells = r / sizeof(struct dag_rec); if (r != 0) { if (pi->nblocks == 0 || /* Just getting started */ pi->empty != 0 || /* Had some no_data reads */ ts_seq_ok(pi->card_ts, pi->drp->ts)) { pi->card_ts = pi->drp->ts; /* Timestamps OK between blocks */ } else { /* Bad timestamp at start of block */ if ((n == 0 || n == 32) && pi->dag_type == TT_POS) { /* Recover from 'half record' glitch */ p8 = (Bit8 *)pi->drp; p8 += 32; if (ntohl(((struct dag_rec *)p8)->d.pos.slen) == 64) { pi->drp = (struct dag_rec *)p8; r -= n; ++pi->idrops; } else { log_msg(LOG_ERR, 0, "if_read(4): i/f %d," " couldn't recover Dag4 'half record' !!!", pi->nbr); r = 0; } } else r = 0; if (r == 0 && ts_seq_ok(pi->drp->ts, pi->drp[1].ts)) { fmt_ts(g_ts, pi->card_ts); fmt_ts(b_ts, pi->drp->ts); log_msg(LOG_ERR, 0,"if_read(5): i/f %d, card_ts = %s, " "first two timestamps OK (%s). Resetting card_ts", pi->nbr, g_ts,b_ts); pi->card_ts = pi->drp->ts; r = rr; } if (r == 0) { /* Couldn't recover */ fmt_ts(g_ts, pi->card_ts); fmt_ts(b_ts, pi->drp->ts); log_msg(LOG_ERR, 0, "if_read(6): i/f %d, block %d, %d cells, %s -> %s, " "bad ts at start of block !!!!!", pi->nbr, pi->nblocks, pi->ncells, g_ts, b_ts); log_dag_data(2, i, pi->nblocks, pi->d_buf, rr); /*+ Log first 3 records */ if (++pi->consec_badts == MAX_CONSEC_BADTS) { log_msg(LOG_ERR, 1, "Too many bad timestamps, shutting down"); } pi->idrops += pi->icells; ++pi->badtspkts; } else pi->consec_badts = 0; } ++pi->nblocks; pi->tcells += pi->ncells; } s = pi->ncells == DAG_BUF_SZ ? S_FULL : S_NORMAL; #if MERGE_TEST >= 1 fmt_ts(b_ts, pi->card_ts); printf("\nif_read(7): i/f %d, ts=%s, %d ccells, %d nblocks\n", pi->nbr, b_ts, pi->ncells, pi->nblocks); #endif } switch (pi->last_s) { case S_NORMAL: switch (s) { case S_NORMAL: break; case S_FULL: pi->full = 1; break; case S_EMPTY: pi->empty = 1; break; } break; case S_FULL: switch (s) { case S_NORMAL: if (pi->full > pi->mx_full) pi->mx_full = pi->full; pi->full = 0; break; case S_EMPTY: if (pi->full > pi->mx_full) pi->mx_full = pi->full; pi->full = 0; pi->empty = 1; break; case S_FULL: ++pi->full; break; } break; case S_EMPTY: switch (s) { case S_NORMAL: if (pi->empty > pi->mx_empty) pi->mx_empty = pi->empty; pi->empty = 0; break; case S_FULL: if (pi->empty > pi->mx_empty) pi->mx_empty = pi->empty; pi->empty = 0; pi->full = 1; break; case S_EMPTY: ++pi->empty; break; } break; } pi->last_s = s; return r; } void init_live_sources(void) { #define STARTUP_TRIES 100 int j,n,b; for (j = 0; j != n_interfaces; ++j) pci[j]->nblocks = 0; for (n = 0; n != STARTUP_TRIES; ++n) { for (j = 0; j != n_interfaces; ++j) { /* Get block for each i/f */ read_live_block(j); } for (b = j = 0; j != n_interfaces; ++j) if (pci[j]->nblocks != 0) ++b; if (b == n_interfaces) break; } if (n == STARTUP_TRIES) log_msg(LOG_ERR, 98, "init_live_sources(): couldn't get data from interface(s)"); meter_start_time = pci[0]->card_ts; /* Set to min Dag timestamp */ for (j = 1; j != n_interfaces; ++j) { if (pci[j]->card_ts <= meter_start_time) meter_start_time = pci[j]->card_ts; } last_ts = meter_start_time; last_t_sec = DAGT_S(last_ts); printf("init_live_sources(): first blocks read\n"); fflush(stdout); } int init_interface(struct interface_info *pi) /* Dag direct */ { char buf[150], *crl_info_string; int len, ifd, flags; if (strncmp(pi->name, "/dev/", 5) != 0) { live_source = 0; /* User wants to read trace file(s) */ trace_count = trace_interval; crl_n_count = 0; sprintf(buf, "%d -i%s (Dag trace file)", trace_interval, /* NeMaC uses this for its collection interval */ pi->name); len = strlen(buf); crl_info_string = (char *)malloc(len+1); strcpy(crl_info_string, buf); crl_info = crl_info_string; /* NeMaC writes this to a #CoralReef record */ } if ((ifd = open(pi->name, O_RDONLY)) < 0) { log_msg(LOG_ERR, 0, "iif: open(%s): %s\n", pi->name, strerror(errno)); return 0; /* Fail */ } if (live_source) { if ((flags = fcntl(ifd, F_GETFL, 0)) < 0) { log_msg(LOG_ERR, 0, "iif: getfl(%s): fcntl get failed\n", pi->name); return 0; /* Fail */ } flags |= O_NONBLOCK; if (fcntl(ifd, F_SETFL, flags) < 0) { log_msg(LOG_ERR, 0, "iif: nonblck(%s): fcntl set failed\n", pi->name); return 0; /* Fail */ } } if ((pi->d_buf = (struct dag_rec *)malloc( sizeof(struct dag_rec)*DAG_BUF_SZ)) == NULL) { close(ifd); log_msg(LOG_ERR, 99, "Not enough memory for NeTraMet %s buffer!", pi->name); } pi->last_errno = 0; pi->fd = ifd; return 1; /* OK */ } unsigned int pkt_counts(int reset) { int j; struct interface_info *pi; if (reset) { npackets_org = pkts; lostpackets_org = drops; } pkts = drops = 0L; /* Get total packets seen and dropped */ for (j = 0; j != n_interfaces; ++j) { pi = pci[j]; pkts += pi->ipackets; drops += pi->idrops; } npackets = pkts - npackets_org; lostpackets = drops - lostpackets_org; return 1; } /* ===== End of Dag direct interface code ===== */ #else /* !NF_CISCO_DATA && !CR_DATA && !LFAP_METER && !ADSL_DATA && !DAG_DIRECT */ /* ===== Interface code using libpcap to get packet headers ===== */ #define PCAPWAITMS 50 /* ms to wait on packet-header-block reads */ struct ether_hdr null_e_hdr = { /* For PPP callback */ 0,0,0,0,0,0, 0,0,0,0,0,0, 0 }; /* ----- Following fddi code is from tcpdump's print-fddi.c ----- */ #ifdef __GNUC__ /* These defines from interface.h .. */ #define inline __inline #ifndef __dead #define __dead volatile #endif #else #define inline #define __dead #endif #include /* * Some FDDI interfaces use bit-swapped addresses. */ #if defined(ultrix) || defined(__alpha) int fddi_bitswap = 0; #else int fddi_bitswap = 1; #endif /* * FDDI support for tcpdump, by Jeffrey Mogul [DECWRL], June 1992 * * Based in part on code by Van Jacobson, which bears this note: * * NOTE: This is a very preliminary hack for FDDI support. * There are all sorts of wired in constants & nothing (yet) * to print SMT packets as anything other than hex dumps. * Most of the necessary changes are waiting on my redoing * the "header" that a kernel fddi driver supplies to bpf: I * want it to look like one byte of 'direction' (0 or 1 * depending on whether the packet was inbound or outbound), * two bytes of system/driver dependent data (anything an * implementor thinks would be useful to filter on and/or * save per-packet, then the real 21-byte FDDI header. * Steve McCanne & I have also talked about adding the * 'direction' byte to all bpf headers (e.g., in the two * bytes of padding on an ethernet header). It's not clear * we could do this in a backwards compatible way & we hate * the idea of an incompatible bpf change. Discussions are * proceeding. * * Also, to really support FDDI (and better support 802.2 * over ethernet) we really need to re-think the rather simple * minded assumptions about fixed length & fixed format link * level headers made in gencode.c. One day... * * - vj */ #define FDDI_HDRLEN (sizeof(struct fddi_header)) static u_char fddi_bit_swap[] = { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, }; /* Extract src, dst addresses */ static inline void extract_fddi_addrs( const struct fddi_header *fddip, unsigned char *fsrc, unsigned char *fdst) { register int i; if (fddi_bitswap) { /* * bit-swap the fddi addresses (isn't the IEEE standards * process wonderful!) then convert them to names. */ for (i = 0; i < 6; ++i) fdst[i] = fddi_bit_swap[fddip->fddi_dhost[i]]; for (i = 0; i < 6; ++i) fsrc[i] = fddi_bit_swap[fddip->fddi_shost[i]]; } else { memcpy(fdst, fddip->fddi_dhost, 6); memcpy(fsrc, fddip->fddi_shost, 6); } } /* ----- End of fddi code from tcpdump's print-fddi.c ----- */ #define get_short(a) (a[0] << 8 | a[1]) void fddi_callback(struct interface_info *user, struct pcap_pkthdr *h, u_char *p) { const struct fddi_header *fp = (struct fddi_header *)p; int caplen = h->caplen; struct ether_hdr ehdr; #ifdef COPY_STRUCTS struct llc llc; #else struct llc *llc; #endif struct pkt pp; unsigned int ether_type, lsap; if (user->if_delta_set == 0) { /* First timestamp for this interface */ user->if_time_delta = h->ts; tv_sub(user->if_time_delta, meter_start_time); user->if_delta_set = 1; } if (--user->sample_count == 0) { /* Process only 1 pkt per SampleRate */ user->sample_count = user->SampleRate; if (caplen < FDDI_HDRLEN) return; /* Too short! */ /* Get the FDDI addresses into a canonical form */ if (adj_reqd) /* Don't do this unless we have to! */ extract_fddi_addrs(fp, ehdr.source, ehdr.dest); if ((fp->fddi_fc & FDDIFC_CLFF) == FDDIFC_LLC_ASYNC) { caplen -= FDDI_HDRLEN; if (caplen < sizeof(struct llc)) return; /* Too short! */ p += FDDI_HDRLEN; #ifdef COPY_STRUCTS /* tcpdump does this; don't know why */ memcpy((char *)&llc, (char *)p, sizeof(llc)); lsap = llc.dsap << 8 | llc.ssap; if (lsap == 0xAAAA) { /* SNAP packet */ ether_type = get_short(llc.ctl.snap_ether.snap_ethertype); p += sizeof(struct llc); } else ether_type = 0; /* Other 802.2 packet */ #else /* This works, at least for Solaris 2.4 */ llc = (struct llc *)p; lsap = llc->dsap << 8 | llc->ssap; if (lsap == 0xAAAA) { /* SNAP packet */ ether_type = get_short(llc->ctl.snap_ether.snap_ethertype); p += sizeof(struct llc); } else ether_type = 0; /* Other 802.2 packet */ #endif pp.p_len = h->len; /* Bug fix from Dylan Hall, 31 May 00 */ if (handle_pkt(&pp, ether_type,lsap, (unsigned char *)&ehdr, p, caplen - sizeof(struct llc))) { pp.Low.Interface = pp.High.Interface = user->nbr; /* NETFLOW uses Low.Interface for ntm_interface */ pp.Low.AdjType = pp.High.AdjType = AT_FDDI; pp.arrival_time = h->ts; /* struct timeval */ tv_sub(pp.arrival_time, user->if_time_delta); pkt_monitor(&pp); if (pp.arrival_time.tv_sec != last_t_sec) { last_t_sec = pp.arrival_time.tv_sec; one_second_process(); } } } } } void ether_callback(struct interface_info *user, struct pcap_pkthdr *h, u_char *p) { struct ether_hdr *ethp; struct llc *llcp; struct pkt pp; unsigned int ether_type, lsap; int len, j; if (user->if_delta_set == 0) { /* First timestamp for this interface */ user->if_time_delta = h->ts; tv_sub(user->if_time_delta, meter_start_time); user->if_delta_set = 1; } if (--user->sample_count == 0) { /* Process only 1 pkt per SampleRate */ user->sample_count = user->SampleRate; ethp = (struct ether_hdr *)p; ether_type = ntohs(ethp->type); p += sizeof(struct ether_hdr); if (ether_type <= 1500) { /* 802.3 packet */ llcp = (struct llc *)p; lsap = llcp->dsap << 8 | llcp->ssap; if (lsap == 0xAAAA) { /* SNAP packet */ ether_type = get_short(llcp->ctl.snap_ether.snap_ethertype); p += sizeof(struct llc); } else ether_type = 0; /* Other 802.2 packet */ } else lsap = 0; /* Blue book packet */ pp.p_len = h->len; /* Bug fix from Dylan Hall, 31 May 00 */ if (handle_pkt(&pp, ether_type,lsap, (unsigned char *)ethp, p, h->caplen - sizeof(struct ether_hdr))) { pp.Low.Interface = pp.High.Interface = user->nbr; /* NETFLOW uses Low.Interface for ntm_interface */ pp.Low.AdjType = pp.High.AdjType = user->adjtype; pp.arrival_time = h->ts; /* struct timeval */ tv_sub(pp.arrival_time, user->if_time_delta); pkt_monitor(&pp); if (pp.arrival_time.tv_sec != last_t_sec) { last_t_sec = pp.arrival_time.tv_sec; one_second_process(); } } } } void ppp_callback(struct interface_info *user, struct pcap_pkthdr *h, u_char *p) { struct ether_hdr *ethp; struct llc *llcp; struct pkt pp; unsigned int ether_type, lsap; int len, j; if (user->if_delta_set == 0) { /* First timestamp for this interface */ user->if_time_delta = h->ts; tv_sub(user->if_time_delta, meter_start_time); user->if_delta_set = 1; } if (--user->sample_count == 0) { /* Process only 1 pkt per SampleRate */ user->sample_count = user->SampleRate; pp.p_len = h->len; /* Bug fix from Dylan Hall, 31 May 00 */ if (handle_pkt(&pp, 0x0800,0, /* Assume it's an IP packet */ (unsigned char *)&null_e_hdr, p, h->caplen)) { pp.Low.Interface = pp.High.Interface = user->nbr; /* NETFLOW uses Low.Interface for ntm_interface */ pp.Low.AdjType = pp.High.AdjType = AT_PPP; pp.arrival_time = h->ts; /* struct timeval */ tv_sub(pp.arrival_time, user->if_time_delta); pkt_monitor(&pp); if (pp.arrival_time.tv_sec != last_t_sec) { last_t_sec = pp.arrival_time.tv_sec; one_second_process(); } } } } void interface_read(struct interface_info *pi) { pcap_read(pi->pd, -1, pi->callback, (u_char *)pi); } int init_interface(struct interface_info *pi) /* libpcap */ { char *interface, errbuf[PCAP_ERRBUF_SIZE]; pcap_t *pd; int type; #define PCAPWAITMS 50 if (pi->name[0] == '\0') if (!(interface = pcap_lookupdev(errbuf))) { log_msg(LOG_ERR, 0, "pcap_lookupdevice(): %s\n",errbuf); return 0; /* Fail */ } else strcpy(pi->name,interface); if ((pd = pcap_open_live(pi->name, SNAPSIZE, 1, PCAPWAITMS, errbuf)) == NULL) { log_msg(LOG_ERR, 0, "pcap_open_live(%s): %s\n", pi->name,errbuf); return 0; /* Fail */ } pi->pd = pd; pi->fd = pd->fd; switch (type = pcap_datalink(pd)) { case DLT_EN10MB: case DLT_IEEE802: #if CYGWIN32 case DLT_EN100MB: #endif pi->adjtype = AT_ETHERNET; pi->callback = (pcap_handler)ether_callback; break; #if CYGWIN32 case DLT_PPP_WIN32: /* WinDump dummies up a MAC header (!) */ pi->adjtype = AT_PPP; pi->callback = (pcap_handler)ether_callback; break; #endif case DLT_RAW: case DLT_SLIP_BSDOS: case DLT_PPP_BSDOS: pi->adjtype = AT_PPP; pi->callback = (pcap_handler)ppp_callback; break; case DLT_FDDI: pi->adjtype = AT_FDDI; pi->callback = (pcap_handler)fddi_callback; break; default: log_msg(LOG_ERR, 0, "pcap bad link type 0x%x!\n", type); pcap_close(pd); return 0; /* Fail */ } return 1; /* OK */ } unsigned int pkt_counts(int reset) { int j; struct interface_info *pi; struct pcap_stat ps; char errbuf[PCAP_ERRBUF_SIZE]; if (reset) { npackets_org = pkts; lostpackets_org = drops; } pkts = drops = 0L; /* Get total packets seen and dropped */ for (j = 0; j != n_interfaces; ++j) { pi = pci[j]; if (pcap_stats(pi->pd,&ps)) { log_msg(LOG_ERR, 0, "pcap_stats(%d): %s\n",pi->nbr,errbuf); return 0; } pkts += ps.ps_recv; drops += ps.ps_drop; pi->ipackets = ps.ps_recv; pi->idrops = ps.ps_drop; } npackets = pkts - npackets_org; lostpackets = drops - lostpackets_org; return 1; } #endif /* !NETFLOW */ /* ===== End of libpcap interface code ===== */ unsigned long iface_info(int if_nbr, int which) { struct interface_info *pi; pi = pci[if_nbr-1]; if (which == IFACE_LOST) return pi->idrops + pi->noflowpackets; else if (which == IFACE_RATE) return pi->SampleRate; else return 0; } void bump_noflowpkts(int if_nbr) { ++noflowpackets; ++pci[if_nbr-1]->noflowpackets; /* 1-org */ } void start_uptime_clock(void) { #if !DAG_DIRECT struct timeval tv; # if CR_DATA tv = last_ts; /* Use time from trace file */ # else gettimeofday(&tv, (struct timezone *)0); # endif meter_start_time = tv; #endif } Bit32 uptime_s(void) /* seconds*/ { #if DAG_DIRECT long long tsec = last_ts - meter_start_time; return DAGT_S(tsec); #else struct timeval tv; # if CR_DATA tv = last_ts; /* Use time from trace file */ # else gettimeofday(&tv, (struct timezone *)0); # endif tv_sub(tv, meter_start_time); return tv.tv_sec; #endif } Bit32 uptime_cs(void) /* centiseconds */ { #if DAG_DIRECT long long tsec = last_ts - meter_start_time; Bit32 s = DAGT_S(tsec); Bit32 us = DAGT_US(tsec); return s*100 + us/10000; #else struct timeval tv; # if CR_DATA tv = last_ts; /* Use time from trace file */ # else gettimeofday(&tv, (struct timezone *)0); # endif tv_sub(tv, meter_start_time); return tv.tv_sec*100 + tv.tv_usec/10000; #endif } #if DAG_DIRECT double centiseconds(long long nt) { long long t = nt - meter_start_time; return ((double)DAGT_S(t))*100.0 + ((double)DAGT_US(t))/10000.0; #else double centiseconds(struct timeval nt) { tv_sub(nt, meter_start_time); return ((double)nt.tv_sec)*100.0 + ((double)nt.tv_usec)/10000.0; #endif } #if DAG_DIRECT double microseconds(long long nt) { long long t = nt - meter_start_time; return ((double)DAGT_S(t))*1000000.0 + (double)DAGT_US(t); #else double microseconds(struct timeval nt) { tv_sub(nt, meter_start_time); return ((double)nt.tv_sec)*1000000.0 + (double)nt.tv_usec; #endif } void show_meter_time() { char msg[60], *ts; ts = ctime((time_t *)&meter_start_time); sprintf(msg,"%lu seconds since %c%c%c%c:%c%c", uptime_s(), ts[11],ts[12],ts[14],ts[15], ts[17],ts[18]); display_msg(0,msg); } extern int errno; int snmp_dump_packet = 0; #define MXINTERFACES 4 char interface_name[SZ_IFNAME*MXINTERFACES+1] = ""; /* Interface name (used by met_vars.c) */ char r_community[SZ_IFNAME+1], w_community[SZ_IFNAME+1]; /* Help screen. Add any new command line options here */ void print_help(void) { fprintf(stderr, "\nUsage: NeTraMet [OPTION]...\n"); fprintf(stderr, "A meter for network traffic flows:\n\n" /* " -d\n" */ " -i IFN \t Specify interface to monitor (maximum of 4)\n" " -k \t Disable keyboard input processing\n" " -l \t Use length field from IP headers\n" " -m PRT \t Use port PRT to communicate with readers/managers\n" #if NF_OCX_BGP " -o \t Use 'owner' ASNs instead of 'next-hop'\n" #endif #ifdef LFAP_METER " -p1 \t Display flow info received from a CCE\n" " -p2 \t Display messages sent between CCE and FAS (LfapMet)\n" " -p4 \t Display raw messages (hexdump) received from CCE\n" " -p8 \t log flow info received from a CCE in file " "'LfapMet.logflowstats'\n" " -p16 \t log messages sent between CCE and FAS in file " "'LfapMet.logmessages'\n" " -p32 \t log raw messages (hexdump) received from CCE in file" "'LfapMet.loghexmessages' \n\t\t (useful for debugging)\n" " \t -p options can be combined, e.g:\n" " \t -p63 enables all\n" " \t -p9 enables display & logging of flow info\n" " -R RSC \t Set snmp read community for CCEs\n" #endif " -r RSC \t Set snmp read community to RSC\n" " -s \t Disable screen display\n" " -w WSC \t Set snmp write community to WSC\n" " -D \t Run NeTraMet as a daemon (superset of -k -s)\n" "\n \t\t Memory allocation (defaults)\n" " -f fff \t fff flows (%lu)\n" " -u rrr \t rrr rUles (%lu)\n" " -a aaa \t aaa pAcket data blocks (%lu)\n" " -t ttt \t ttt IP sTreams (%lu)\n" " -b bbb \t bbb stream data Blocks (%lu)\n" " -v ddd \t ddd distributions (%lu)\n" " -e eee \t eee distrib Events (%lu)\n" "\n" "A meter reader can collect flow data from many meters, and each meter\n" "may have its data retrieved by serveral meter readers. Traffic flows\n" "of interest are defined by user-specified sets of rules\n" "\n" "For more information see the NeTraMet Reference Manual and User Guide\n" "\n" "Report bugs to netramet@auckland.ac.nz\n", mxflows, mxrules, mxstr_blocks, mxstreams, mxdistribs, mxdistevents, mxpktdatas); } void check_struct_sizes(void) { /* Using Bit16 and Bit32 instead of arrays of Bit8 requires us to keep these things correctly aligned in memory. gcc pads structures to multiples of 4 bytes, but other compilers might not, hence this check */ #if 0 struct stream s; printf("size(pktsnap.h):\n" " address=%d, pkt_key=%d\n", sizeof(union address), sizeof(struct pkt_key)); printf("size(flowhash.h):\n" " stream_name=%d, key=%d, flow_key=%d, rule=%d\n", sizeof(struct stream_name), sizeof(struct key), sizeof(struct flow_key), sizeof(struct rule)); printf(" struct stream: offset of sfn = %d\n", (IntFromPtr)&s.sfn - (IntFromPtr)&s.next_hc); if (sizeof(struct pkt_key) % 4 != 0 || sizeof(struct stream_name) % 4 != 0 || sizeof(struct key) % 4 != 0 || sizeof(struct flow_key) % 4 != 0 ) { printf("sizeof(struct) not a multiple of 4 bytes <<<<<\n"); exit(-1); } #endif } int main(int argc, char *argv[]) { int arg,c, sd, s_n, daemon, bgp_result, k; char *ap; struct sockaddr_in me; struct utsname name; int dag_type = 0; if (argc < 2) { /* Help Screen when called with no args */ print_help(); check_struct_sizes(); exit(-1); } mxflows = DFMXFLOWS; mxrules = DFMXRULES; mxstreams = DFMXSTREAMS; mxstr_blocks = DFMXSTRBLOCKS; mxdistribs = DFMXDISTRIBS; mxdistevents = DFMXDISTEVENTS; mxpktdatas = DFMXPKTDATS; signal(SIGINT, sigint_handler); signal(SIGTERM, sigint_handler); #if CR_DATA use_ip_length = 1; /* Use IP header lengths */ coral_set_api(CORAL_API_CELL); /* Allows config file with proto rules */ #elif DAG_DATA || DAG_DIRECT use_ip_length = 1; /* Use IP header lengths */ #endif printf("%s\n",version_descr); display_enabled = kb_enabled = 1; /* Enabled by default */ daemon = 0; /* Default (CMU) communities are: 0 = "public", 1 = "proxy", 2 = "private", 3 = "regional", 4 = "core" We only allow "public" and "private" by default */ communities[1] = communities[3] = communities[4] = ""; for (n_interfaces = 0; n_interfaces != MXINTERFACES; ++n_interfaces) pci[n_interfaces] = NULL; s_n = 1; n_interfaces = 0; for (c = 0, arg = 1; arg < argc; ++arg) { if (argv[arg][0] == '-') { ap = argv[arg]+2; switch (argv[arg][1]) { case 'a': if (*ap == '\0') ap = argv[++arg]; mxpktdatas = atoi(ap); break; case 'b': if (*ap == '\0') ap = argv[++arg]; mxstr_blocks = atoi(ap); break; case 'd': snmp_dump_packet++; break; case 'e': if (*ap == '\0') ap = argv[++arg]; mxdistevents = atoi(ap); break; case 'f': if (*ap == '\0') ap = argv[++arg]; mxflows = atoi(ap); break; #if DAG_DIRECT case 'g': /* -g dag type, default = 0 (atm) */ if (*ap == '\0') ap = argv[++arg]; dag_type = atoi(ap); if (dag_type < TT_ATM || dag_type > TT_POS) log_msg(LOG_ERR, 1, "Dag type (-g) not 0 (ATM), 1 (Ether) or 2 (PoS)\n"); break; #endif case 'i': /* -i name of interface to monitor */ if (*ap == '\0') ap = argv[++arg]; pci[n_interfaces] = (struct interface_info *)calloc( sizeof(struct interface_info), 1); strncpy(pci[n_interfaces]->name,ap,SZ_IFNAME); pci[n_interfaces]->name[SZ_IFNAME] = '\0'; #if DAG_DIRECT pci[n_interfaces]->dag_type = dag_type; #endif pci[n_interfaces]->nbr = n_interfaces+1; /* 1-org */ ++n_interfaces; break; case 'k': kb_enabled = 0; /* -k to disable keyboard */ break; case 'l': /* Use 'Total Length' field from IP datagrams */ use_ip_length = 1; break; case 'm': if (*ap == '\0') ap = argv[++arg]; au_snmp_port = atoi(ap); break; case 'n': if (*ap == '\0') ap = argv[++arg]; if (ap != NULL) s_n = atoi(ap); else s_n = 1; if (s_n < 1) s_n = 1; break; #if NF_OCX_BGP case 'o': use_owner_asns++; break; #endif #ifdef LFAP_METER case 'p': if (*ap == '\0') ap = argv[++arg]; if (ap!=NULL) lfap_info_level = atoi(ap); else lfap_info_level = LFAP_INFLVL_FLOW|LFAP_INFLVL_MSG; if (lfap_info_level < 0) lfap_info_level = 0; /* else if (lfap_info_level > 15) lfap_info_level = 15; */ break; #endif case 'r': if (*ap == '\0') ap = argv[++arg]; /* -r to set read community */ strncpy(r_community,ap,SZ_IFNAME); r_community[SZ_IFNAME] = '\0'; communities[0] = r_community; break; case 's': display_enabled = 0; /* -s to disable screen */ break; case 't': if (*ap == '\0') ap = argv[++arg]; mxstreams = atoi(ap); break; case 'u': if (*ap == '\0') ap = argv[++arg]; mxrules = atoi(ap); break; case 'v': if (*ap == '\0') ap = argv[++arg]; mxdistribs = atoi(ap); break; case 'w': if (*ap == '\0') ap = argv[++arg]; /* -w to set write community */ strncpy(w_community,ap,SZ_IFNAME); w_community[SZ_IFNAME] = '\0'; communities[2] = w_community; break; #if CR_DATA case 'C': /* CoralReef option (other than source) */ if (*ap == '\0') ap = argv[++arg]; if (coral_config_command(ap) < 0) exit(-1); break; #endif case 'D': if (!trace_interval) { display_enabled = kb_enabled = 0; daemon++; } else log_msg(LOG_WARNING, 0, "-D options reqires -T0 to run as daemon!"); break; #if CR_DATA || DAG_DIRECT case 'N': /* Stop after N*T s of tracefile. 0 => stop at EOF */ if (*ap == '\0') ap = argv[++arg]; crl_n_max = atoi(ap); break; #endif #ifdef LFAP_METER case 'R': if (*ap == '\0') ap = argv[++arg]; strncpy(lfap_community,ap,SZ_IFNAME); lfap_community[SZ_IFNAME] = '\0'; break; #endif #if CR_DATA case 'S': /* CoralReef source */ if (*ap == '\0') ap = argv[++arg]; pci[n_interfaces] = (struct interface_info *)calloc( sizeof(struct interface_info), 1); strncpy(pci[n_interfaces]->name,ap,SZ_IFNAME); pci[n_interfaces]->name[SZ_IFNAME] = '\0'; pci[n_interfaces]->nbr = n_interfaces+1; /* 1-org */ pci[n_interfaces]->crl_src = coral_new_source(ap); if (pci[n_interfaces]->crl_src == NULL) { printf("Failed to open coral source %s\n", ap); exit(-1); } ++n_interfaces; break; #endif #if CR_DATA || DAG_DIRECT case 'T': /* Collection interval (seconds) for trace file. T0 (default) implies 'use live source(s)' */ if (*ap == '\0') ap = argv[++arg]; trace_interval = atoi(ap); break; #endif default: log_msg(LOG_ERR, 1, "Invalid option: -%c\n", argv[arg][1]); } } else log_msg(LOG_ERR, 1, "Option not preceded by -\n"); } if (daemon) { /* User asked to run NeTraMet as daemon */ switch (k = 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 */ } #if NF_OCX_BGP InitSubnet(); bgp_result = read_bgp_file("bgp.txt"); if (bgp_result) { log_msg(LOG_ERR, 1, "read_bgp_file failed, bgp_result=%d\n", bgp_result); } #endif /* Set up connections */ sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) log_msg(LOG_ERR, 1, "Couldn't get socket"); me.sin_family = AF_INET; me.sin_addr.s_addr = INADDR_ANY; me.sin_port = htons(SNMP_PORT); if (bind(sd, (struct sockaddr *)&me, sizeof(me)) != 0) log_msg(LOG_ERR, 2, /* SNMP_PORT defined in snmplib/snmp.h */ "Couldn't bind port %d: %s", SNMP_PORT,strerror(errno)); if (n_interfaces == 0) { pci[0] = (struct interface_info *)calloc(sizeof(struct interface_info),1); pci[0]->nbr = n_interfaces = 1; } for (c = 0; c != n_interfaces; ++c) pci[c]->SampleRate = pci[c]->sample_count = s_n; #ifdef LFAP_METER if (!start_lfap(&name)) return 0; /* Fail */ #endif init_monitor(n_interfaces); init_snmp(); uname(&name); ap = interface_name; k = sizeof(interface_name); for (c = 0; c != n_interfaces; ++c) { if (!init_interface(pci[c])) exit(3); if (strlen(pci[c]->name)+3 < k) { if (c != 0) ap = strmov(ap,", "); ap = strmov(ap,pci[c]->name); k -= strlen(pci[c]->name)+2; } } *ap = '\0'; printf("Running on %s", name.nodename); #if defined(NETFLOW) printf(", port(s) %s\n", interface_name); #else printf(", interface(s) %s\n", interface_name); #endif fflush(stdout); setuid(getuid()); receive(sd,n_interfaces); } void zero_pkt_stats() { struct tms tbuf; srealtime = realtime_org = times(&tbuf); sproctime = proctime_org = tbuf.tms_utime+tbuf.tms_stime; /* tms_cutime, tms_cstime give times for child processes */ pc_noflowpkts = noflowpackets; pkt_counts(1); /* Reset origins */ min_idle1000 = 1000L; stats_time = spackets = 0L; max_pkt_rate = 0; clear_pkt_stats = 0; } int chart_interface = 0; /* Use SampleRate from first interface */ unsigned int bkgi; /* Seconds before next run of backgound process */ #define BKG_INTERVAL 30 /* Seconds */ int gci; #if CR_DATA || DAG_DIRECT unsigned int mtrlogi = 10; /* Seconds before next run of meter log process */ #define LOG_INTERVAL 300 /* Seconds (5 min) */ /* #define LOG_INTERVAL 900 /* Seconds (15 min) */ #endif void one_second_process(void) { int j, x; clock_t rt,pt; struct tms tbuf; unsigned long idle1000, pd; unsigned char p; struct mgr_rec *mip; struct rdr_rec *cip; char msg[100]; #if CR_DATA || DAG_DIRECT int i; char *mp, ibuf[40]; #endif #ifdef UX_TESTING printf("."); #endif #if CR_DATA if (request_stop) stop_meter(0); #endif if (clear_pkt_stats) zero_pkt_stats(); else { ++stats_time; realtime = times(&tbuf); proctime = tbuf.tms_utime+tbuf.tms_stime; if (realtime != srealtime) { idle1000 = 1000 - (proctime-sproctime)*1000/(realtime-srealtime); if (idle1000 < min_idle1000) min_idle1000 = idle1000; } pkt_counts(0); pd = (npackets-spackets)*pci[chart_interface]->SampleRate; if (pd > max_pkt_rate) max_pkt_rate = pd; srealtime = realtime; sproctime = proctime; spackets = npackets; } #if NEW_ATR check_rates(uptime_s()); /* uptime in seconds */ #endif if (--gci == 0) { #ifdef UX_TESTING printf("g"); #endif garbage_collect(1); /* Routine incremental collection */ gci = gc_interval; } #if CR_DATA || DAG_DIRECT if (--mtrlogi == 0) { /* Write regular meter log entry */ int cpb, mxcpb; for (mp = msg, mxcpb = i = 0; i != n_interfaces; ++i) { cpb = pci[i]->nblocks == 0 ? 0 : pci[i]->tcells/pci[i]->nblocks; sprintf(ibuf, "%d: %lu badts, %lu cpb, %lu_%lu^%lu, ", i, pci[i]->badtspkts, cpb, pci[i]->nblocks, pci[i]->mx_empty, pci[i]->mx_full); pci[i]->tcells = pci[i]->nblocks = pci[i]->mx_empty = pci[i]->mx_full = 0; if (cpb > mxcpb) mxcpb = cpb; mp = strmov(mp, ibuf); } mp[-2] = '\0'; log_msg(LOG_WARNING, 0, msg); show_stream_hash(0); mtrlogi = LOG_INTERVAL; if (mxcpb < TARGET_CPB) { /* Should we adjust the select time? */ if (select_us < MX_SELECT_US) { select_us *= 2; #if DAG_DIRECT select_time = US_DAGT(select_us); #elif CR_DATA select_tv.tv_usec = select_us; #endif log_msg(LOG_WARNING, 0, "--* select_us = %d", select_us); } } else if (mxcpb >= 4*TARGET_CPB) { if (select_us > MN_SELECT_US) { select_us /= 2; #if DAG_DIRECT select_time = US_DAGT(select_us); #elif CR_DATA select_tv.tv_usec = select_us; #endif log_msg(LOG_WARNING, 0, "--/ select_us = %d", select_us); } } } #endif if (--bkgi == 0) { /* Check % of flows active */ #ifdef UX_TESTING printf("b\n"); #endif p = (unsigned long)active_flows()*100/(mxflows); if (p > FloodMark && FloodMode != TV_TRUE && FloodMark > 0 && FloodMark < 100) { FloodMode = TV_TRUE; /* Stop creating new flow records */ log_msg(LOG_WARNING, 0, "Meter in Flood mode"); } else for (x = 0; x != sizeof(mi)/sizeof(struct mgr_rec); ++x) { mip = &mi[x]; if (mip->mi_Status != RS_ACTIVE || mip->mi_RunningStandby == TV_TRUE) continue; else if (p > mip->mi_HighWaterMark && mip->mi_HighWaterMark > 0 && mip->mi_HighWaterMark < 100 && mip->mi_StandbyRuleSet != mip->mi_CurrentRuleSet && (mip->mi_StandbyRuleSet != 0 && ri[mip->mi_StandbyRuleSet-1].ri_Status == RS_ACTIVE) ) { open_rule_set(mip->mi_CurrentRuleSet, 0); /* Close */ open_rule_set(mip->mi_StandbyRuleSet, 1); /* Open */ mip->mi_RunningStandby = TV_TRUE; log_msg(LOG_WARNING, 0, "Manager %d running Standby", x+1); } else if (p > HighWaterMark*2/3) more_garbage(); } pd = uptime_cs(); for (x = 0; x != sizeof(ci)/sizeof(struct rdr_rec); ++x) { cip = &ci[x]; if (cip->ci_Status != RS_ACTIVE || cip->ci_Timeout == 0) continue; if ((pd - cip->ci_LastTime)/100 > cip->ci_Timeout) cip->ci_Status = RS_UNUSED; /* Timed out */ } bkgi = BKG_INTERVAL; } #ifdef UX_TESTING fflush(stdout); #endif } handle_keyboard() { char kb_buf[25]; int ch; if (fgets(kb_buf, sizeof kb_buf, stdin) == NULL) return; /* Error or EOF */ if ((ch = kb_buf[0]) == 27 && kb_buf[1] == 27) /* ESC ESC */ stop_meter(0); switch (tolower(ch)) { case 'v': printf("\n%s\n",version_descr); break; #if CR_DATA case '*': if (coral_start_all() < 0) /* Also synchronises all sources */ log_msg(LOG_ERR, 0, "coral_start failed"); break; #endif default: handle_kb(ch); break; } } void snmp_read_sp(int sd); /* Handles meter's SNMP requests */ #if CR_DATA || DAG_DIRECT int non_live_source(int sd) { fd_set fdset; struct timeval t; int j, width, count; #define DEBUG_TRACE_STATE 0 #if DEBUG_TRACE_STATE int last_state = -1; #endif #if CR_DATA int last_trace_count; #elif DAG_DIRECT init_live_sources(); /* Read first block, set meter_start_time */ #endif width = STDIN_FILENO; /* Determine size of fd_set */ if (sd > width) width = sd; ++width; FD_ZERO(&fdset); t.tv_sec = 0; for (;;) { #if DEBUG_TRACE_STATE if (trace_state != last_state) { printf("Main loop: state=%d, crl_n_count=%d, last_t_sec=%d" ", last_ts=%d.%06d\n", trace_state, crl_n_count, last_t_sec, last_ts.tv_sec, last_ts.tv_usec); last_state = trace_state; } #endif t.tv_usec = 250000; /* 0.25 s */ switch (trace_state) { /* Reading from trace file */ case CT_WAIT_RULES: /* met_vars will change to CT_RULES_READY */ break; case CT_RULES_READY: #if CR_DATA interface_read_trace(pci[0]); /* Get timestamp for beginning of first interval */ last_t_sec = last_ts.tv_sec; meter_start_time.tv_sec = last_ts.tv_sec-1; /* Don't let uptime start at -1 */ #endif trace_state = CT_WAIT_FLOWS_READ; break; case CT_PROCESS_FLOWS: #if CR_DATA t.tv_usec = 20000; /* 20 ms */ last_trace_count = trace_count; do { if (!interface_read_trace(pci[0])) { trace_state = CT_TRACE_EOF; /* No more blocks */ break; } } while (trace_count != 0); if (trace_state == CT_TRACE_EOF) break; /* Let NeMaC do EOF processing (i.e. last reading) */ #elif DAG_DIRECT dag_merge(); /* Return at end of block */ if (pci[waiting_live_source]->ncells == 0) { /* Block now empty */ if (read_live_block(waiting_live_source) == 0) { trace_state = CT_TRACE_EOF; /* No more blocks */ break; } } #endif if (trace_count == 0) { /* Reached -T interval */ /* show_memory(); /* Show memory every -T interval */ /* show_stream_hash(); */ trace_count = trace_interval; if (crl_n_max) { /* Stop after -N meter readings */ if (++crl_n_count == crl_n_max) { trace_state = CT_TRACE_EOF; /* Dummy up EOF */ } else trace_state = CT_WAIT_FLOWS_READ; } else trace_state = CT_WAIT_FLOWS_READ; } break; case CT_WAIT_FLOWS_READ: /* NeMaC will change to CT_PROCESS_FLOWS */ break; case CT_TRACE_EOF: /* NeMaC will change to CT_SHUTDOWN */ break; case CT_SHUTDOWN: stop_meter(0); break; } FD_SET(sd, &fdset); if (kb_enabled) FD_SET(STDIN_FILENO, &fdset); /* stdin */ count = select(width, &fdset, 0, 0, &t); if (count > 0) { if (FD_ISSET(sd, &fdset)) snmp_read_sp(sd); if (kb_enabled && FD_ISSET(STDIN_FILENO, &fdset)) /* stdin */ handle_keyboard(); } else switch (count) { case 0: break; case -1: if (errno == EINTR) continue; else log_msg(LOG_ERR, 0, "select returned %d, %s", count, strerror(errno)); return 0; default: log_msg(LOG_ERR, 0, "select returned %d", count); return 0; } } return 1; } #endif /* CR_DATA || DAG_DIRECT */ int receive(int sd,int n_interfaces) { fd_set fdset; int j, width, count; struct timeval t; #if defined (NETFLOW) || CR_DATA || LFAP_METER time_t now,prev; #endif #if DAG_DIRECT int i_count = 0; #endif /* printf("running receive():\n"); fflush(stdout); */ zero_pkt_stats(); gci = gc_interval; /* Garbage collect interval */ bkgi = BKG_INTERVAL; /* Background interval */ gc_f = (mxflows)/100; /* Flow indexes to check */ if (gc_f < 4) gc_f = 4; #if DAG_DIRECT select_time = US_DAGT(select_us); #endif #if CR_DATA || DAG_DIRECT if (!live_source) return non_live_source(sd); #endif width = STDIN_FILENO; /* Determine size of fd_set */ if (sd > width) width = sd; #if !CYGWIN32 for (j = 0; j != n_interfaces; ++j) if (pci[j]->fd > width) width = pci[j]->fd; #endif ++width; #if defined(NETFLOW) || LFAP_METER time(&prev); #endif #if CR_DATA init_live_sources(); /* Read some cells from each interface */ coral_merge(); /* Return when ready for next block */ #elif DAG_DIRECT init_live_sources(); /* Read some cells from each interface */ dag_merge(); /* Return when ready for next block */ #endif for (;;) { FD_ZERO(&fdset); t.tv_sec = 0; #if CR_DATA if (read_live_block(waiting_live_source)) coral_merge(); /* Return when ready for next block */ else if (request_stop) stop_meter(0); t.tv_usec = select_us; #elif DAG_DIRECT /* !CR_DATA */ if (read_live_block(waiting_live_source)) dag_merge(); /* Return when ready for next block */ t.tv_usec = select_us; #elif CYGWIN32 /* !DAG_DATA */ t.tv_usec = 15000; /* 15 ms */ for (j = 0; j != n_interfaces; ++j) /* Read interfaces every cycle */ interface_read(pci[j]); /* Can block for several seconds !!!!!! */ #elif LFAP_METER /* LFAP_METER */ t.tv_usec = 250000; /* 0.25 s */ for (j = 0; j != n_interfaces; ++j) { tick(pci[j]); if (pci[j]->fd != -1) { FD_SET(pci[j]->fd,&fdset); if (pci[j]->fd >= width) width = pci[j]->fd + 1; } } FD_SET(lfap_server_socket,&fdset); if (lfap_server_socket >= width) width = lfap_server_socket + 1; #else /* Unix */ t.tv_usec = 250000; /* 0.25 s */ for (j = 0; j != n_interfaces; ++j) FD_SET(pci[j]->fd,&fdset); #endif FD_SET(sd, &fdset); if (kb_enabled) FD_SET(STDIN_FILENO, &fdset); /* stdin */ count = select(width, &fdset, 0, 0, &t); if (count > 0) { #if LFAP_METER if (FD_ISSET(lfap_server_socket, &fdset)) { printf("client wants to connect.\n"); /* Apparently someone knocking on my door... find a free interface */ for (j = 0; j < n_interfaces; ++j) { printf("trying i/f %i.\n",j); if (pci[j]->fd == -1) { printf("i/f %i is free.\n",j); /* Found a free one */ pci[j]->fd = accept(lfap_server_socket,NULL,NULL); pci[j]->fasinfo.conn = pci[j]->fd; j = n_interfaces + 1; } } if (j == n_interfaces) { int sock; LFAPHeader *lfap; LFAP_IE_Header* ie; printf("No interfaces left !\n"); /* Dirty hack to refuse connections, need this in LFAPv5 ? */ sock = accept(lfap_server_socket, NULL, NULL); /* Assume it is the right one ... */ lfap = newLFAPMessage(LFAP_OPCODE_AR, LFAP_STATUS_SUCCESS, 1); /* Fill in command code IE */ ie = newIE(LFAP_IE_COMMAND_CODE,4); putIEDword(ie, (Bit32)LFAP_CMD_CONNECTION_ACCEPTED, 0); addIE(&lfap, ie); free(ie); sendLFAPMessage(lfap, sock); free(lfap); close(sock); } } #endif #if !CYGWIN32 && !CR_DATA for (j = 0; j != n_interfaces; ++j) { # if LFAP_METER if(pci[j]->fd != -1) { if (FD_ISSET(pci[j]->fd, &fdset)) interface_read(pci[j]); } # elif DAG_DIRECT if (FD_ISSET(pci[j]->fd, &fdset)) { printf(">>> select returned from Dag i/f %d\n", j); fflush(stdout); } # else /* !LFAP_METER */ if (FD_ISSET(pci[j]->fd, &fdset)) interface_read(pci[j]); # endif } #endif if (FD_ISSET(sd, &fdset)) snmp_read_sp(sd); if (kb_enabled && FD_ISSET(STDIN_FILENO, &fdset)) /* stdin */ handle_keyboard(); } else switch (count) { case 0: break; case -1: if (errno == EINTR) continue; else log_msg(LOG_ERR, 0, "select returned %d (not EINTR)", count); return 0; default: log_msg(LOG_ERR, 0, "select returned %d", count); return 0; } #if defined(NETFLOW) || LFAP_METER time(&now); if (now != prev) { one_second_process(); prev = now; } #endif } return 1; } Bit32 snmp_peer_addr; /* IPv4 address of SNMP client */ void snmp_read_sp(int sd) { struct sockaddr_in from; int length, out_length, fromlength, count; Bit8 packet[1500], outpacket[1520]; /* Leave some space just in case */ char snmp_peer_name[50]; /* Name of host which sent the snmp request */ fromlength = sizeof from; length = recvfrom(sd, packet, 1500, 0, (struct sockaddr *)&from, &fromlength); snmp_peer_addr = from.sin_addr.s_addr; strcpy(snmp_peer_name,inet_ntoa(from.sin_addr)); if (length == -1) { log_msg(LOG_ERR, 0, "snmp_read_sp() recvfrom failed: %s", strerror(errno)); return; } if (snmp_dump_packet) { printf("received %d bytes from %s:\n", length, snmp_peer_name); for (count = 0; count < length; count++) { printf("%02X ", packet[count]); if ((count % 16) == 15) printf("\n"); else if ((count % 4) == 3) printf(" "); } printf("\n\n"); } out_length = 1500; if (snmp_agent_parse(packet, length, outpacket, &out_length, from.sin_addr.s_addr)) { if (snmp_dump_packet) { printf("sent %d bytes to %s:\n", out_length, snmp_peer_name); for (count = 0; count < out_length; count++) { printf("%02X ", outpacket[count]); if ((count % 16) == 15) printf("\n"); } printf("\n\n"); } if (sendto(sd, (char *)outpacket, out_length, 0, (struct sockaddr *)&from, sizeof(from)) < 0) { log_msg(LOG_ERR, 0, "snmp_read_sp() sendto failed: %s", strerror(errno)); } } }