/* 1102, Sun 4 Mar 01 (PST) METER_UX.C: The AU Internet Accouting Meter mainline Copyright (C) 1992-2001 by Nevil Brownlee, CAIDA | University of Auckland */ /* * $Log: meter_ux.c,v $ * 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 ADSL_DATA 1 #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 */ #include #include #include #if HAVE_SYS_SELECT_H #include #endif #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" #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]; unsigned char type[2]; /* Length or protocol type */ }; #define SZ_IFNAME 32 struct interface_info { u_char nbr; /* Interface number (for Source/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; 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; #else /* libpcap */ # if CR_DATA coral_source_t *crl_src; coral_iface_t *iface; int ncells, nblocks; coral_atm_cell_t *cellp; int first_block; struct timeval card_ts; /* For next cell */ # if CORAL_TIME_OFFSET struct timeval ts_offset; /* Accumulated time error from card */ # endif u_int badtspkts; /* Packets with bad timestamps */ # else 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 */ /* 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; \ } #if CORAL_TIME_OFFSET /* 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; \ } #endif static unsigned long npackets_org = 0L, lostpackets_org = 0L, pkts = 0L, drops = 0L; struct timeval meter_start_time; char *strmov(char *d, char *s) { while (*s != '\0') *d++ = *s++; return d; } #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,&fr1->srcaddr,IP4_ADDR_LEN); memcpy(pp.High.PeerAddress,&fr1->dstaddr,IP4_ADDR_LEN); pp.TransAddrType = fr1->prot; memcpy(pp.Low.TransAddress, &fr1->srcport, TRANS_ADDR_LEN); memcpy(pp.High.TransAddress, &fr1->dstport, TRANS_ADDR_LEN); 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 */ memset(pp.Low.AdjAddress,0,MAC_ADDR_LEN); pp.High.AdjType = AT_IP4; memcpy(pp.High.AdjAddress,&fr1->nexthop,IP4_ADDR_LEN); memset(&pp.High.AdjAddress[IP4_ADDR_LEN],0,MAC_ADDR_LEN-IP4_ADDR_LEN); memset(pp.Low.nf_ASN,0,TRANS_ADDR_LEN); memset(pp.High.nf_ASN,0,TRANS_ADDR_LEN); 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,&fr5->srcaddr,IP4_ADDR_LEN); memcpy(pp.High.PeerAddress,&fr5->dstaddr,IP4_ADDR_LEN); pp.TransAddrType = fr5->prot; memcpy(pp.Low.TransAddress, &fr5->srcport, TRANS_ADDR_LEN); memcpy(pp.High.TransAddress, &fr5->dstport, TRANS_ADDR_LEN); 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; pp.Low.AdjType = 0; /* NetFlow attributes */ memset(pp.Low.AdjAddress,0,MAC_ADDR_LEN); #if 0 /* RTFM meter can't match S->D and D->S without having both */ pp.High.AdjType = AT_IP4; /* addresses for EVERY packet */ memcpy(pp.High.AdjAddress,&fr5->nexthop,IP4_ADDR_LEN); memset(&pp.High.AdjAddress[IP4_ADDR_LEN],0,MAC_ADDR_LEN-IP4_ADDR_LEN); #else pp.High.AdjType = 0; /* NetFlow attributes */ memset(pp.High.AdjAddress,0,MAC_ADDR_LEN); #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 memcpy(pp.Low.nf_ASN, &fr5->src_as, TRANS_ADDR_LEN); memcpy(pp.High.nf_ASN, &fr5->dst_as, TRANS_ADDR_LEN); 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) { 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 ===== */ int live_source = 0; int waiting_live_source = -1; static struct timeval last_ts = {0,0}; /* Used by uptime() to get Coralreef times */ static Bit32 last_t_sec; #define MX_TS_JUMP 2000000 /* 2 seconds */ int tv_diff_us(struct timeval x, struct timeval y) { /* Returns x-y microseconds */ 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; } # if LOG_BAD_TIMESTAMPS int mxbadt = 1000; # endif int restart_interface(int s) { int r; coral_source_t *i_src; if ((r = coral_stop(i_src = pci[s]->crl_src)) != 0) { log_msg(LOG_ERR, 0, "restart_int(%d): stop failed, result=%d", s,r); return 0; /* Fail */ } if ((r = coral_close(i_src)) != 0) { log_msg(LOG_ERR, 0, "restart_int(%d): close failed, result=%d", s,r); return 0; /* Fail */ } if ((r = coral_open(i_src)) != 0) { log_msg(LOG_ERR, 0, "restart_int(%d): open failed, result=%d", s,r); return 0; /* Fail */ } if ((r = coral_start(i_src)) != 0) { log_msg(LOG_ERR, 0, "restart_int(%d): start failed, result=%d", s,r); return 0; /* Fail */ } log_msg(LOG_ERR, 0, "restart_int(%d): successful Coral restart", s); return 1; } #define MX_CRB_FAILS 4000 /* 5 ms waits * 20 seconds */ int crb_fails = 0, last_crb_err = 0; #define MX_EAGAINS 60000 /* 5 ms waits * 300 seconds */ /* #define MX_EAGAINS 12000 /* 5 ms waits * 60 seconds */ int eagains = 0; #if CORAL_BUFFER_TRACE #define MX_BT_RECORDS 10 int bt_recs = 0; #define NORMAL_LOGRECS 20 int coral_logging = NORMAL_LOGRECS; #endif int save_errno = 0; /* Last errno from read_live_block() */ int coral_to_us = 5000; /* 5 ms */ int read_live_block(int s) { struct timeval t, last_t; coral_iface_t *iface; coral_blk_info_t *binfop; coral_atm_cell_t *cellp; int ncells, ts_delta; #if CORAL_BUFFER_TRACE char *mp, msg[200], ibuf[50]; int i; #endif t.tv_sec = 0; t.tv_usec = coral_to_us; if ((iface = coral_read_block(pci[s]->crl_src, &binfop, &cellp, &t)) == NULL) { if ((save_errno = errno) != EAGAIN) { if (save_errno != last_crb_err || crb_fails == MX_CRB_FAILS) { printf("coral_read_block(%d) failed, errno=%d, count=%d", s, save_errno, crb_fails); fflush(stdout); log_msg(LOG_ERR, 0, "coral_read_block(%d) failed, errno=%d, count=%d", s, save_errno, crb_fails); } last_crb_err = save_errno; if (++crb_fails > MX_CRB_FAILS) { if (!restart_interface(s)) exit(999); /* Couldn't restart interface; give up now */ } return 0; /* Try again */ } if (++eagains == MX_EAGAINS) { log_msg(LOG_ERR, 0, "coral_read_block(%d) too many EAGAINs"); if (!restart_interface(s)) exit(911); /* Couldn't restart interface; give up now */ eagains = 0; } last_crb_err = crb_fails = 0; coral_to_us = 20000; /* 20 ms */ return 0; /* Try again */ } else { eagains = 0; coral_to_us = 5000; /* Normal value, 5 ms */ pci[s]->iface = iface; pci[s]->ncells = ncells = ntohl((binfop)->cell_count); pci[s]->cellp = cellp; ++pci[s]->nblocks; pci[s]->ipackets += ncells; /* ???? For now, we're only collecting 1 cell per pkt */ pci[s]->idrops += ntohl((binfop)->cells_lost); last_t = pci[s]->card_ts; CORAL_TIMESTAMP_TO_TIMEVAL(iface, coral_cell_time(iface, cellp), &t); #if CORAL_TIME_OFFSET tv_sub(t, pci[s]->ts_offset); #endif ts_delta = tv_diff_us(t, last_t); if ((ts_delta < 0 || ts_delta > MX_TS_JUMP) && !pci[s]->first_block) { ++pci[s]->badtspkts; /* Bad timestamp from card */ printf( "*** read_live_block(%d): " "ncells=%u, last_t=%u.%06u, t=%u.%06u\n", s, ncells, last_t.tv_sec,last_t.tv_usec, t.tv_sec,t.tv_usec); #if LOG_BAD_TIMESTAMPS log_msg(LOG_ERR, 0, "*** read_live_block(%d): " "ncells=%u, last_t=%u.%06u, t=%u.%06u", s, ncells, last_t.tv_sec,last_t.tv_usec, t.tv_sec,t.tv_usec); if (--mxbadt <= 0) { quack(54321); } #endif } else pci[s]->card_ts = t; if (pci[s]->first_block) { printf( "+++ read_live_block(%d): first_block, " "ncells=%u, t=%u.%06u\n", s, ncells, t.tv_sec,t.tv_usec); #if LOG_BAD_TIMESTAMPS log_msg(LOG_ERR, 0, "+++ read_live_block(%d): first_block, " "ncells=%u, t=%u.%06u", s, ncells, t.tv_sec,t.tv_usec); if (--mxbadt <= 0) { quack(54321); } #endif pci[s]->first_block = 0; } #if CORAL_BUFFER_TRACE if (coral_logging != 0) { --coral_logging; for (mp = msg, i = 0; i != n_interfaces; ++i) { sprintf(ibuf, "%d: cells=%u, t=%u.%06u, ", i, pci[i]->ncells, pci[i]->card_ts.tv_sec, pci[i]->card_ts.tv_usec); mp = strmov(mp, ibuf); } mp[-2] = '\0'; log_msg(LOG_ERR, 0, "=== block(%d): %s", s, msg); } #endif return 1; } } void init_live_sources(void) { int j; for (j = 0; j != n_interfaces; ++j) { /* Get block for each source */ pci[j]->first_block = 1; while (!read_live_block(j)) ; printf("init_live_sources(%d) first block read\n", j); fflush(stdout); } } void empty_live_buffers(int n) /* Read n blocks from each interface */ { int j, k; for (j = 0; j != n_interfaces; ++j) { /* Get blocks for each source */ for (k = 0; ; ++k) { pci[j]->first_block = 1; coral_to_us = 20000; /* 20 ms */ if (!read_live_block(j)) break; } log_msg(LOG_ERR, 0, "--- empty_live_buffers(%d): %d blocks", n, k); } #if 0 for (j = n*n_interfaces, k = 1; j != 0; ++k) { for (i = 0; i != n_interfaces; ++i) { pci[i]->first_block = 1; coral_to_us = 20000; /* 20 ms */ if (read_live_block(i)) --j; } } for (n = j = 0; j != n_interfaces; ++j) { /* Do ONE read on each block */ pci[j]->first_block = 1; coral_to_us = 20000; /* 20 ms */ if (read_live_block(j)) ++n; } log_msg(LOG_ERR, 0, "--- empty_live_buffers(): %d interface skips", n); #endif } int next_time_ok(int if_n, struct timeval bad_t) /* Number of good cell-time pairs (up to 3) */ { coral_atm_cell_t *cellp; struct timeval t; int n, t_delta, r; n = pci[if_n]->ncells; r = 0; t = bad_t; cellp = pci[if_n]->cellp; if (--n != 0) { bad_t = t; cellp += coral_cell_size(pci[if_n]->iface); CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, coral_cell_time(pci[if_n]->iface, cellp), &t); t_delta = tv_diff_us(t, bad_t); if (t_delta >= 0 && t_delta < MX_TS_JUMP) ++r; if (--n != 0) { bad_t = t; cellp += coral_cell_size(pci[if_n]->iface); CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, coral_cell_time(pci[if_n]->iface, cellp), &t); t_delta = tv_diff_us(t, bad_t); if (t_delta >= 0 && t_delta < MX_TS_JUMP) ++r; if (--n != 0) { bad_t = t; cellp += coral_cell_size(pci[if_n]->iface); CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, coral_cell_time(pci[if_n]->iface, cellp), &t); t_delta = tv_diff_us(t, bad_t); if (t_delta >= 0 && t_delta < MX_TS_JUMP) ++r; } } } return r; } #define MIN_CELL_BITE 500 /* Tune this so that meter can catch up without loosing too many cells */ int find_non_bitten_time(int if_n, struct timeval save_t) /* Look for a 'good' timestamp succeeding save_t */ { coral_atm_cell_t *cellp; struct timeval t; int n,save_n, t_delta, r; save_n = n = pci[if_n]->ncells; r = 0; cellp = pci[if_n]->cellp; while (--n != 0) { cellp += coral_cell_size(pci[if_n]->iface); CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, coral_cell_time(pci[if_n]->iface, cellp), &t); t_delta = tv_diff_us(t, save_t); if (t_delta >= 0 && t_delta < MX_TS_JUMP) { /* Good timestamp */ if (n > MIN_CELL_BITE) { /* Help Coral/NeTraMet catch up */ pci[if_n]->ncells = n - MIN_CELL_BITE; pci[if_n]->cellp = cellp + coral_cell_size(pci[if_n]->iface)*MIN_CELL_BITE; CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, coral_cell_time(pci[if_n]->iface, pci[if_n]->cellp), &t); pci[if_n]->card_ts = t; r = 1; } break; } } #if LOG_BAD_TIMESTAMPS quack(54321); /* Try to see what's happening here */ log_msg(LOG_ERR, 0, "*** find_non_bitten_time(%d): bad_cells=%u, " "cells_left=%u, (CELL_BITE=%u), save_t=%u.06%u, t=%u.%06u", if_n, save_n-n, n, MIN_CELL_BITE, save_t.tv_sec,save_t.tv_usec, t.tv_sec,t.tv_usec); #endif return r; } void interface_read_live(void) { struct timeval ts, mints, save_t; int j, minj, ts_delta, nto; coral_pkt_buffer_t outer, hdr; union atm_hdr atm_h; const Bit8 *pay; llcsnap_t *snap_h; unsigned int lsap; struct pkt pmp; /* To be passed to pkt_monitor() */ struct ether_hdr ehdr; #if CORAL_TIME_OFFSET struct timeval t_jump; #endif 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) { mints = ts; minj = j; } else if (ts.tv_sec < mints.tv_sec) { mints = ts; minj = j; } } last_ts = mints; if (--pci[minj]->sample_count == 0) { /* Process only 1 pkt per SampleRate */ pci[minj]->sample_count = pci[minj]->SampleRate; if (coral_cell_to_pkt(pci[minj]->iface, pci[minj]->cellp, &outer) /* ATM data in outer.buf. Could unpack it ourselves (as we did for RFC1483 to get ethertype and lsap), but we'd have to do it for all ATM encapsulations! */ && coral_get_payload(&outer, &hdr) >= 0) { /* get_payload() strips ATM encapsulation, as per .cfg file */ if (adj_reqd) { /* Don't do this unless we have to! */ atm_h.ui = ntohl(*(Bit32 *)coral_cell_header( pci[minj]->iface, pci[minj]->cellp)); /* 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; } if (handle_pkt(&pmp, 0x0800, 0xAAAA, (Bit8 *)&ehdr, /* Will need to work out what eth_type/lsap should be */ hdr.buf, hdr.caplen)) { pmp.Low.Interface = pmp.High.Interface = minj+1; /* 1-org */ pmp.Low.AdjType = pmp.High.AdjType = AT_AAL5; pmp.arrival_time = mints; /* struct timeval */ pkt_monitor(&pmp); } } } if (last_ts.tv_sec > last_t_sec) { last_t_sec = last_ts.tv_sec; one_second_process(); /* NeTraMet housekeeping */ /* Note this happens at the time seen by the interface, we may get several seconds worth of data in a buffer */ } if (--pci[minj]->ncells == 0) { waiting_live_source = minj; return; /* Keep outer block alive */ } else { save_t = mints; pci[minj]->cellp += coral_cell_size(pci[minj]->iface); CORAL_TIMESTAMP_TO_TIMEVAL(pci[minj]->iface, coral_cell_time(pci[minj]->iface, pci[minj]->cellp), &ts); #if CORAL_TIME_OFFSET tv_sub(ts, pci[minj]->ts_offset); #endif ts_delta = tv_diff_us(ts, save_t); if (ts_delta < 0 || ts_delta > MX_TS_JUMP) { ++pci[minj]->badtspkts; /* Bad timestamp from card */ printf("*** interface_read_live(%d): " "ncells=%u, save_t=%u.%06u, ts=%u.%06u\n", minj, pci[minj]->ncells, save_t.tv_sec,save_t.tv_usec, ts.tv_sec,ts.tv_usec); log_msg(LOG_ERR, 0, "*** interface_read_live(%d): " "ncells=%u, save_t=%u.%06u, ts=%u.%06u", minj, pci[minj]->ncells, save_t.tv_sec,save_t.tv_usec, ts.tv_sec,ts.tv_usec); nto = next_time_ok(minj, ts); log_msg(LOG_ERR, 0, "!!! interface_read_live(%d): " "Next %d cell-time pairs OK, %s", minj, nto, nto == 3 ? "time shift detected" : "bad time ignored"); if (nto != 3) { if (--mxbadt <= 0) { quack(54321); } } else { /* Dag time shift detected */ #if CORAL_TIME_OFFSET t_jump = ts; tv_sub(t_jump, save_t); tv_add(pci[minj]->ts_offset, t_jump); tv_sub(ts, pci[minj]->ts_offset); #endif #if CORAL_BUFFER_TRACE coral_logging = NORMAL_LOGRECS; #endif empty_live_buffers(5); /* Skip 5 blocks */ # if 0 pci[minj]->card_ts = ts; /* Try accepting the shift (just to see what happens) */ log_msg(LOG_ERR, 0, "!!! interface_read_live(%d): " " tail-biting detected, %u cells abandoned", minj, pci[minj]->ncells+1); pci[minj]->idrops += pci[minj]->ncells+1; pci[minj]->first_block = 1; waiting_live_source = minj; return; /* Get the next buffer for this interface */ /* When this happened, CoralReef got into a resource contention argument with Unix, leading to NeTraMet filling disk with error messages. Better to just throw away the rest of this buffer. */ if (!find_non_bitten_time(minj, save_t)) { waiting_live_source = minj; return; /* Couldn't recover, read next block */ } # endif } } else pci[minj]->card_ts = ts; } } } #if 0 /* $$$$$$ */ int interface_read_live(struct interface_info *pi) /* Returns 0 if no cells left in buffer */ { coral_iface_t *iface; coral_blk_info_t *binfop; coral_atm_cell_t *cellp; struct timeval t; int ncells, src_nbr; struct timeval this_ts; union atm_hdr atm_h; const Bit8 *pay; llcsnap_t *snap_h; unsigned int lsap; struct pkt pmp; /* To be passed to pkt_monitor() */ struct ether_hdr ehdr; int result; #if 0 /* $$$$$ */ t.tv_sec = 0; t.tv_usec = 5000; /* 5 ms */ if ((iface = coral_read_block_all(&binfop, &cellp, &t)) == NULL) { #endif if ((iface = coral_read_cell_all(&binfop, &cellp, &t)) == NULL) { if (errno != EAGAIN) log_msg(LOG_ERR, 0, "coral_read_block_all() failed, errno=%d", errno); return 0; } else { src_nbr = coral_source_get_number(coral_interface_get_src(iface)); ncells = ntohl((binfop)->cell_count); ++pci[src_nbr]->cells_read; if (pci[src_nbr]->cells_read == 1) { /* Starting new buffer */ printf("*%d(%lu) ", src_nbr, ncells); fflush(stdout); pci[src_nbr]->ipackets += ncells; /* ???? For now, we're only collecting 1 cell per pkt */ pci[src_nbr]->idrops += ntohl((binfop)->cells_lost); result = 1; } if (pci[src_nbr]->cells_read == ncells) { /* Handling last cell */ pci[src_nbr]->cells_read = 0; result = 0; } if (pci[src_nbr]->card_ts.tv_sec == 0) { CORAL_TIMESTAMP_TO_TIMEVAL(iface, coral_cell_time(iface, cellp), &last_ts); pci[src_nbr]->card_start = last_ts; } else last_ts = pci[src_nbr]->card_ts; /* meter_start_time = pci[src_nbr]->card_start; */ /* $$$$$ for ( ; ncells != 0; --ncells, cellp += coral_cell_size(iface)) { */ { if (--pci[src_nbr]->sample_count == 0) { /* Process only 1 pkt per SampleRate */ pci[src_nbr]->sample_count = pci[src_nbr]->SampleRate; CORAL_TIMESTAMP_TO_TIMEVAL(iface, coral_cell_time(iface, cellp), &this_ts); if (this_ts.tv_sec >= last_ts.tv_sec && this_ts.tv_sec - last_ts.tv_sec < 3) last_ts = this_ts; /* Looks OK */ else { /* Bad timestamp from point card. Don't use it! */ ++pci[src_nbr]->badtspkts; /* printf("!"); fflush(stdout); */ /* $$$$$ continue; */ return 1; } { /* RFC 1483 (SNAP over AAL5) */ if (adj_reqd) { /* Don't do this unless we have to! */ atm_h.ui = ntohl(*(Bit32 *)coral_cell_header(iface, cellp)); /* 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; } pay = coral_cell_payload(iface, cellp); snap_h = (llcsnap_t *)pay; lsap = snap_h->llc_dsap << 8 | snap_h->llc_ssap; if (lsap == 0xAAAA) { pmp.p_len = 0; /* use_ip_length will get length from IP header */ if (handle_pkt(&pmp, ntohs(snap_h->snap_type), lsap, (Bit8 *)&ehdr, pay+sizeof(llcsnap_t), coral_cell_size(iface)-sizeof(llcsnap_t))) { pmp.Low.Interface = pmp.High.Interface = src_nbr+1; /* 1-org */ pmp.Low.AdjType = pmp.High.AdjType = AT_AAL5; /* ATM over AAL5 */ pmp.arrival_time = last_ts; /* struct timeval */ pkt_monitor(&pmp); } } } /* RFC 1483 */ if (last_ts.tv_sec > last_t_sec) { last_t_sec = last_ts.tv_sec; one_second_process(); /* NeTraMet housekeeping */ /* Note this happens at the time seen by the interface, we may get several seconds worth of data in a buffer */ } } } pci[src_nbr]->card_ts = last_ts; } return result; } #endif /* $$$$$$ */ #define CR_PKT_DEBUG 0 #if CR_PKT_DEBUG int p_count = 0; #endif 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 */ static int mem_shown = 0; void interface_read_trace(struct interface_info *pi) { coral_iface_t *iface; coral_pkt_result_t pkt_result; coral_interval_result_t interval_result; coral_pkt_buffer_t payload; int src_nbr, if_nbr; union atm_hdr atm_h; llcsnap_t *snap_h; struct pkt pmp; /* To be passed to pkt_monitor() */ struct ether_hdr ehdr; #if 0 Bit8 *bp; int j; char *hp,*pp; #endif if ((iface = coral_read_pkt(&pkt_result, &interval_result)) == NULL) { if (errno == 0) { /* End of trace file or stopped device */ --trace_count; /* Tell receive() loop we're at interval end */ if (!mem_shown) show_memory(); mem_shown = 1; trace_state = CT_TRACE_EOF; } else log_msg(LOG_ERR, 0, "coral_read_pkt() failed, errno=%d", errno); return; } else { src_nbr = coral_source_get_number(coral_interface_get_src(iface)); if (pkt_result.packet != NULL) { /* We have a packet */ #if 0 switch (pkt_result.header->protocol) { case CORAL_DLT_ATM_RFC1483: hp = "RFC1483"; break; case CORAL_DLT_ATM_AAL5: hp = "AAL5"; break; case CORAL_DLT_LANE_IEEE8023: case CORAL_DLT_EN10MB: case CORAL_DLT_IEEE802: case CORAL_DLT_GIGX: hp = "OTHER"; break; default: hp = "unknown"; break; } printf("count=%d, header=%s (%d),", nnbb_count, hp, pkt_result.header->protocol); bp = (Bit8 *)pkt_result.header->buf; for (j = 0; j != 16; ++j) printf(" %02x", bp[j]); switch (pkt_result.packet->protocol) { case CORAL_DLT_ATM_RFC1483: pp = "RFC1483"; break; case CORAL_DLT_ATM_AAL5: pp = "AAL5"; break; case CORAL_DLT_LANE_IEEE8023: case CORAL_DLT_EN10MB: case CORAL_DLT_IEEE802: case CORAL_DLT_GIGX: pp = "OTHER"; break; default: pp = "unknown"; break; } printf("\n packet=%s (%d),", pp, pkt_result.packet->protocol); bp = (Bit8 *)pkt_result.packet->buf; for (j = 0; j != 16; ++j) printf(" %02x", bp[j]); printf("\n"); #endif CORAL_TIMESTAMP_TO_TIMEVAL(iface, pkt_result.timestamp, &last_ts); if (--pci[src_nbr]->sample_count == 0) { /* Process only 1 pkt per SampleRate */ pci[src_nbr]->sample_count = pci[src_nbr]->SampleRate; switch (pkt_result.packet->protocol) { case CORAL_DLT_ATM_RFC1483: atm_h.ui = ntohl(*(Bit32 *)&pkt_result.header->buf[0]); snap_h = (llcsnap_t *)pkt_result.packet->buf; if (coral_get_payload(pkt_result.packet, &payload) < 0) { log_msg(LOG_INFO, 0, "coral_get_payload failed"); return; } #if CR_PKT_DEBUG if (++p_count <= 10) { if_nbr = coral_interface_get_number(iface); printf("pkt %d, if_nbr=%d, timestamp=%d|%d, atm=%08x, " "vpvc=%06x, type=%04x, len=%d\n", p_count, if_nbr, last_ts.tv_sec,last_ts.tv_usec, atm_h, atm_h.h.vpvc, ntohs(snap_h->snap_type), payload.caplen); bp = (Bit8 *)payload.buf; for (j = 0; j != 16; ++j) printf(" %02x", bp[j]); printf("\n"); } #endif 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; } pmp.p_len = 0; /* use_ip_length will get length from IP header */ if (handle_pkt(&pmp, ntohs(snap_h->snap_type),0xAAAA, (unsigned char *)&ehdr, (Bit8 *)payload.buf, payload.caplen)) { pmp.Low.Interface = pmp.High.Interface = src_nbr+1; /* 1-org */ pmp.Low.AdjType = pmp.High.AdjType = AT_AAL5; /* ATM over AAL5 */ pmp.arrival_time = last_ts; /* struct timeval */ pkt_monitor(&pmp); } break; case CORAL_DLT_ATM_AAL5: case CORAL_DLT_LANE_IEEE8023: case CORAL_DLT_EN10MB: case CORAL_DLT_IEEE802: case CORAL_DLT_GIGX: log_msg(LOG_ERR, 0, "unimplemented protocol"); return; default: log_msg(LOG_ERR, 0, "unknown protocol"); break; } if (last_ts.tv_sec != last_t_sec) { last_t_sec = last_ts.tv_sec; one_second_process(); /* NeTraMet housekeeping */ } } } else if (interval_result.stats == NULL) { /* A Coral interval has begun */ last_ts = interval_result.begin; } else { /* A Coral interval has ended */ last_ts = interval_result.end; pci[src_nbr]->ipackets += interval_result.stats->pkts_recv; pci[src_nbr]->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); --trace_count; /* Tell receive() loop we're at interval end */ } } } char *cs_type(int cst) { switch (cst) { case CORAL_TYPE_FILE: return "CoralReef file"; case CORAL_TYPE_PCAP: return "tcpdump 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"; default: return "Bad cs_type"; } } int init_interface(struct interface_info *pi) { 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; 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_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 */ 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); meter_start_time.tv_sec = min_ct; meter_start_time.tv_usec = 0; } if (!live_source) { if ((ir = coral_read_pkt_init(NULL, NULL, 1)) != 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 ADSL_DATA /* ===== ADSL Dag code ===== */ struct adsl_rec { long long ts; /* Dag timestamp */ 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 0x00000090 0x3935403f 0x00d0590d 0x325b8864 0x11000066 0x05ae0021 0x450005ac 0x42964000 0x4006d73e 0x0a64034a 0x0ac80302 0x08830014 0x0abf8a22 0x00545b09 0x50107edc 0xb8ce0000 0xecfe1d19 0xa5da1b3c 0x3c334000 0x0001121a 0x8be23cfa 0x3683523c #endif #include unsigned long last_t_sec = 0; void handle_adsl_record(struct interface_info *pi, struct adsl_rec *a_buf) { struct timeval tv; struct pkt pp; int dx; 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; pp.arrival_time = tv; pp.p_len = a_buf->ip_hdr[2] << 8 | a_buf->ip_hdr[3]; pp.Low.Interface = pp.High.Interface = (a_buf->atm_hdr >> 24)+1; pp.PeerAddrType = AT_IP4; /* Dummy up an IP packet */ memcpy(pp.Low.PeerAddress,&a_buf->ip_hdr[12],IP4_ADDR_LEN); memcpy(pp.High.PeerAddress,&a_buf->ip_hdr[16],IP4_ADDR_LEN); pp.TransAddrType = a_buf->ip_hdr[9]; dx = (a_buf->ip_hdr[0] & 0x0F) << 2; /* HLEN */ memcpy(pp.Low.TransAddress, &a_buf->ip_hdr[dx], TRANS_ADDR_LEN); memcpy(pp.High.TransAddress, &a_buf->ip_hdr[dx+2], TRANS_ADDR_LEN); pp.DSCodePoint = a_buf->ip_hdr[1] >> 2; /* Old TOS field, RFC 2474 */ if (adj_reqd) { pp.Low.AdjType = pp.High.AdjType = 0; memset(pp.Low.AdjAddress,0,3); memcpy(&pp.Low.AdjAddress[3], (Bit8 *)&a_buf->atm_hdr + 1, 3); memcpy(&pp.High.AdjAddress, &pp.Low.AdjAddress, MAC_ADDR_LEN); } 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(): errno=%d\n", 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) { 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, errno=%d\n", pi->name, errno); exit(1); } } } else if ((fd = open(pi->name, O_RDONLY)) < 0) { log_msg(LOG_ERR, 0, "adsl_open(%s): errno=%d\n", pi->name, 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 */ } setuid(getuid()); 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 ===== */ #else /* !NF_CISCO_DATA && !CR_DATA && !ADSL_DATA */ /* ===== Interface code using libpcap to get packet headers ===== */ unsigned long last_t_sec = 0; struct ether_hdr null_e_hdr = { /* For PPP callback */ 0,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 = get_short(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) { char *interface, errbuf[PCAP_ERRBUF_SIZE]; pcap_t *pd; int type; 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, 250, errbuf)) == NULL) { log_msg(LOG_ERR, 0, "pcap_open_live(%s): %s\n", pi->name,errbuf); return 0; /* Fail */ } setuid(getuid()); 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 /* ===== 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) { struct timeval tv; gettimeofday(&tv, (struct timezone *)0); meter_start_time = tv; } Bit32 uptime_s(void) /* seconds*/ { 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; } Bit32 uptime_cs(void) /* centiseconds */ { 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; } double centiseconds(struct timeval nt) { tv_sub(nt, meter_start_time); return ((double)nt.tv_sec)*100.0 + ((double)nt.tv_usec)/10000.0; } double microseconds(struct timeval nt) { tv_sub(nt, meter_start_time); return ((double)nt.tv_sec)*1000000.0 + (double)nt.tv_usec; } 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 " -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); } 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; mxflows = DFMXFLOWS; mxrules = DFMXRULES; mxstreams = DFMXSTREAMS; mxstr_blocks = DFMXSTRBLOCKS; mxdistribs = DFMXDISTRIBS; mxdistevents = DFMXDISTEVENTS; mxpktdatas = DFMXPKTDATS; if (argc < 2) { /* Help Screen when called with no args */ print_help(); exit(-1); } #if CR_DATA trace_interval = 10; /* Default 10s */ 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; 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'; 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]; s_n = atoi(ap); if (s_n < 1) s_n = 1; break; #if NF_OCX_BGP case 'o': use_owner_asns++; 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 '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; 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 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; case 'T': /* Collection interval for CoralReef trace file */ 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 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) { perror("bind"); exit(2); } 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; 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 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 */ } 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 unsigned int mtrlogi = 10; /* Seconds before next run of meter log process */ #define LOG_INTERVAL 900 /* Seconds (15 min) */ char ifmsg[100]; #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 int i; char *mp, ibuf[40]; #endif #ifdef UX_TESTING printf("."); #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 if (--mtrlogi == 0) { /* Write regular meter log entry */ for (mp = msg, i = 0; i != n_interfaces; ++i) { sprintf(ibuf, "%d: %lu blks, %lu badts, ", i, pci[i]->nblocks, pci[i]->badtspkts); mp = strmov(mp, ibuf); } mp[-2] = '\0'; log_msg(LOG_WARNING, 0, msg); mtrlogi = LOG_INTERVAL; } #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 } void stop_meter(void) { display_msg(1,"Shutting down"); #if CR_DATA coral_stop_all(); coral_close_all(); #endif exit(0); } 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) /* ESC */ stop_meter(); 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; } } receive(int sd,int n_interfaces) { fd_set fdset; int j, width, count; struct timeval t; #if defined(NETFLOW) || CR_DATA time_t now,prev; #endif #if CR_DATA int last_state = -1; /* ### */ int last_trace_count; #endif 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; 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) time(&prev); #endif for (;;) { FD_ZERO(&fdset); t.tv_sec = 0; t.tv_usec = 250000; /* 0.25 s */ for (j = 0; j != n_interfaces; ++j) FD_SET(pci[j]->fd,&fdset); FD_SET(sd, &fdset); if (kb_enabled) FD_SET(STDIN_FILENO, &fdset); /* stdin */ count = select(width, &fdset, 0, 0, &t); if (count > 0) { #if !CYGWIN32 && !CR_DATA for (j = 0; j != n_interfaces; ++j) { if (FD_ISSET(pci[j]->fd, &fdset)) interface_read(pci[j]); } #endif if (FD_ISSET(sd, &fdset)) snmp_read(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 -1; default: log_msg(LOG_ERR, 0, "select returned %d", count); return -1; } #if defined(NETFLOW) time(&now); if (now != prev) { one_second_process(); prev = now; } #endif } } Bit32 snmp_peer_addr; /* IPv4 address of SNMP client */ snmp_read(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) perror("recvfrom"); 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) { perror("sendto"); return 0; } } }