/* 1515, Sat 7 Jul 01 (NZT) FLOWHASH.C: RTFM Meter, using hash tables for counts Copyright (C) 1992-2002 by Nevil Brownlee, CAIDA | University of Auckland */ /* * $Log: flowhash.c,v $ * Revision 1.1.1.2.2.14 2002/02/23 01:57:26 nevil * Moving srl examples to examples/ directory. Modified examples/Makefile.in * * Revision 1.1.1.2.2.12 2001/05/24 02:19:49 nevil * LfapMet implemented by Remco Poortinga. * MinPDUs implemented by Nevil. * * Revision 1.1.1.2.2.10 2000/12/28 00:48:13 nevil * Count unrequested DNS responses as LostPDUs * * Revision 1.1.1.2.2.8 2000/10/26 23:23:58 nevil * Write log_msg messages to meter.log file * * Revision 1.1.1.2.2.7 2000/08/08 19:44:49 nevil * 44b8 release * * Revision 1.1.1.2.2.5 2000/06/06 03:38:17 nevil * Combine NEW_ATR with TCP_ATR, various bug fixes * * Revision 1.1.1.2.2.2 2000/01/12 02:57:08 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.2.1 1999/11/29 00:17:24 nevil * Make changes to support NetBSD on an Alpha (see version.history for details) * * Revision 1.1.1.2 1999/10/03 21:06:22 nevil * *** empty log message *** * * Revision 1.1.1.1.2.17 1999/09/29 22:29:14 nevil * Changes (mainly changing // to /* comments) for Irix * * Revision 1.1.1.1.2.16 1999/09/29 02:42:47 nevil * Fix problems discovered on PC meters * - Use NETRAMET compile-time variable to leave out C++ declarations * - Clean up mistakes in porting OCX_BGP code from meter_pc to meter_oc * * Revision 1.1.1.1.2.15 1999/09/24 02:58:37 nevil * Polish up code to get rid of warning messages from Borland (DOS) compiler. * Make manager PeerAddress buffers NSAP_ADDR_LEN bytes long. * Add asn_lookup variable - only call FindSubnet if ruleset uses ASNs. * * Revision 1.1.1.1.2.14 1999/09/22 05:38:40 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.13 1999/09/14 00:45:26 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.12 1999/05/26 02:41:37 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.11 1999/05/18 03:36:26 nevil * Implement IPv6 in NeTraMet, and its manager/collectors. * - This is controlled by the V6 #define * - NeTraMet recognises v6 packets and fishes through their extension * headers until it finds the actual payload. * - NeMaC et al display v6 addresses in the fom specified by RFC 2373 * - fd_util and fd_extract allow colons in addresses (for defining tags) * * Revision 1.1.1.1.2.10 1999/05/17 00:11:53 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). * */ #if HAVE_CONFIG_H #include #endif #define DEBUG /* Required if any of the following are set */ #define PME_TRACE 0 /* Trace rules during pattern match */ #define PM_DEBUG 0 #define CR_TRACE 0 #define PP_DEBUG 0 #define PP_DEBUG_XX 0 #define DNS_ROOT_DEBUG 0 #define DNS_ROOT_DEBUG_2 0 #define STREAM_CHAIN_DEBUG 0 #define SCC_CHECK 0 #define DIST_DBG 0 #define RS_CH_DEBUG 0 #define V6_TESTV4 0 /* Turns IP4 addresses into ::IP4 addresses */ #define noSTACK_CHECK /* Keep an eye on pattern & return stack extents */ #define noGC_TEST /* Monitor garbage collector */ #define noMATCH_TRACE /* Testing, testing .. */ #define noCOMPARE_TRACE #define noCOUNT_TRACE #define noP_TRACE #define noGC_DEBUG #define noTESTING #define noGC_TEST1 #define noH_TEST #define TCP_TESTING 0 #define TCP_TRACE 0 #define noDECNET #include #include #include #include #if NEW_ATR #include #endif #if !defined(DOS) # if HAVE_MALLOC_H # include # endif #include #include #include "../bgp/types/types.h" /* Needed here for subnet.h */ #else #include #include #include #include "..\intel\byteswap.h" # ifdef DEBUG # include # endif #endif /* DOS */ #include "ausnmp.h" #include "asn1.h" #include "snmp.h" #include "snmpimpl.h" #include #include #include "pktsnap.h" #define EXTFLOW #include "flowhash.h" #include "decnet.h" #include "met_vars.h" #if NEW_ATR #define MX_PQ_LEN_TCP 100 /* Enough for a big window */ #define MX_PQ_LEN_OTHER 50 /* No windows in UDP or ICMP */ #define STR_TO_MULTIPLIER 10.0 /* For 'LostPackets' */ #endif #if TCP_PKT_TRACE # define TRACE_SEQ_DECR 0 # define TRACE_NEG_BLP 0 # define BLP_FRACT 0.50 FILE *pt_file; # define ptf_title "ntm.trace" void print_pkt_trace(FILE *f, char *id, /* Identifies invocation */ struct stream *sfp, char *fmt, ...); /* Supporting detail */ #endif #ifdef DEBUG void quack(int x) { } /* Use for a debugger break point */ void paddr(Bit32 a); void daddr(Bit32 a); void dport(Bit16 a); void fdaddr(FILE *f, Bit32 a); void fdport(FILE *f, Bit16 a); void pkey(char *msg, struct flow *n); #endif #if PM_DEBUG int pm_dbg = 0; char dbg_msg[80]; #endif #if NF_OCX_BGP # if !defined(DOS) /* Unix meter */ # include "../bgp/integrat/readbgp.h" # include "../bgp/integrat/subnet.h" # else /* OCX or DOS */ # include "..\integrat\readbgp.h" # include "..\integrat\subnet.h" # endif #endif void dump_flow(struct flow *fp) { Bit32 pa; log_msg(LOG_WARNING, -1, " flow %u (%x), Ruleset=%d, PeerAddrType=%d, TransAddrType=%d," " FlowKind=%d, FlowClass=%d", flow_nbr(fp),fp, fp->FlowRuleSet, fp->fk.PeerAddrType, fp->fk.TransAddrType, fp->fk.FlowKind, fp->fk.FlowClass); #if QBYTE_PEER pa = ntohl(fp->fk.Low.PeerAddress.qbyte); #else pa = ntohl(fp->fk.Low.PeerAddress.peer32[0]); #endif log_msg(LOG_WARNING, -1, " Source Interface=%d, PeerAddress=%d.%d.%d.%d, TransAddress=%u", fp->fk.Low.Interface, pa >> 24, (pa >> 16) & 0xFF, (pa >> 8) & 0xFF, pa & 0xFF, ntohs(fp->fk.Low.Interface), ntohs(fp->fk.Low.TransAddress)); #if QBYTE_PEER pa = ntohl(fp->fk.High.PeerAddress.qbyte); #else pa = ntohl(fp->fk.High.PeerAddress.peer32[0]); #endif log_msg(LOG_WARNING, -1, " Dest Interface=%d, PeerAddress=%d.%d.%d.%d, TransAddress=%u", pa >> 24, (pa >> 16) & 0xFF, (pa >> 8) & 0xFF, pa & 0xFF, ntohs(fp->fk.High.Interface), ntohs(fp->fk.High.TransAddress)); } void dump_bp(struct pkt *bp) { Bit32 pa; log_msg(LOG_WARNING, -1, "pkt %x, PeerAddrType=%d, TransAddrType=%d", bp, bp->PeerAddrType, bp->TransAddrType); #if QBYTE_PEER pa = ntohl(bp->Low.PeerAddress.qbyte); #else pa = ntohl(bp->Low.PeerAddress.peer32[0]); #endif log_msg(LOG_WARNING, -1, " Source PeerAddress=%d.%d.%d.%d, TransAddress=%u", pa >> 24, (pa >> 16) & 0xFF, (pa >> 8) & 0xFF, pa & 0xFF, ntohs(bp->Low.TransAddress)); #if QBYTE_PEER pa = ntohl(bp->High.PeerAddress.qbyte); #else pa = ntohl(bp->High.PeerAddress.peer32[0]); #endif log_msg(LOG_WARNING, -1, " Dest PeerAddress=%d.%d.%d.%d, TransAddress=%u", pa >> 24, (pa >> 16) & 0xFF, (pa >> 8) & 0xFF, pa & 0xFF, ntohs(bp->High.TransAddress)); } void dump_sp_key(struct stream *sp) { Bit32 pa; struct stream_name *sfn = &sp->sfn; log_msg(LOG_WARNING, -1, "stream %x: Ruleset=%d, TransAddrType=%d", sp, sfn->RuleSet, sfn->TransType); #if QBYTE_PEER pa = ntohl(sfn->SrcPeerAddr); #else pa = ntohl(sfn->SrcPeerAddr[0]); #endif log_msg(LOG_WARNING, -1, " Source PeerAddress=%d.%d.%d.%d, TransAddress=%u", pa >> 24, (pa >> 16) & 0xFF, (pa >> 8) & 0xFF, pa & 0xFF, ntohs(sfn->SrcTransAddr)); #if QBYTE_PEER pa = ntohl(sfn->DestPeerAddr); #else pa = ntohl(sfn->DestPeerAddr[0]); #endif log_msg(LOG_WARNING, -1, " Dest PeerAddress=%d.%d.%d.%d, TransAddress=%u", pa >> 24, (pa >> 16) & 0xFF, (pa >> 8) & 0xFF, pa & 0xFF, ntohs(sfn->DestTransAddr)); } #if STREAM_CHAIN_DEBUG int mx_chain_len = 0; int check_flow_hash_chains(char *msg) { int x, n, rv = 0; struct flow **sf; struct flow *f, *lf; for (x = 0; x != fthashmod; ++x) { sf = &flow_tbl_htp->hash_ent[x]; if ((f = sf[0]) == NULL) { lf = NULL; /* Empty hash chain */ } else { n = 0; do { lf = f; if (++n > mx_chain_len) { printf("check_f_s_h(%s): hash %d has > %d flows <<<<\n", msg, x, ++mx_chain_len); if (n > 15) rv = 1; } } while ((f = lf->ht_next) != (struct flow *)sf); f = NULL; /* Loop stops with lf -> last flow in chain */ } } return rv; } void dump_stream_chain(struct stream_data *std, char *msg) { struct stream *sp, *lsp; int n = 0; printf("dump_s_c(%s): std=%x sq=%x", msg, std, sp = std->sq); if (sp == NULL) printf("->0"); else do { sp = sp->next_sp; printf("->%u", sp); } while (sp != NULL && ++n <= 10); printf(" sq_tail=%x", sp = std->sq_tail); if (sp == NULL) printf("=>0"); else do { sp = sp->prev_sp; printf("=>%x", sp); } while (sp != NULL && ++n <= 20); printf("\n"); fflush(stdout); } void dump_flow_key(struct flow_key *fkp, int hash, char *msg) { int j; printf("flow_key(%s) fkp=%x, hash=%d: PeerAddrType=%d, TransAddrType=%d\n" " FlowClass=%d, FlowKind=%d, MeterId=%d, DSCP=%d\n", msg, fkp, hash, fkp->PeerAddrType, fkp->TransAddrType, fkp->FlowClass, fkp->FlowKind,fkp-> MeterId, fkp->DSCodePoint); printf(" Low: "); for (j = 0; j != sizeof(struct key); ++j) printf(" %02x", ((Bit8 *)(&fkp->Low))[j]); printf("\n High: "); for (j = 0; j != sizeof(struct key); ++j) printf(" %02x", ((Bit8 *)(&fkp->High))[j]); printf("\n"); } int check_stream_chains(struct flow *fp, char *msg) { /* Consistency check for a flow's streams */ struct stream_data *sdp; struct stream *fsp, *rsp; char buf[100]; int k, nas, rv; if ((sdp = fp->stdata) == NULL) return 1; nas = sdp->active_streams; fsp = sdp->sq; rsp = sdp->sq_tail; for (rv = 1, k = 0; ; ++k) { if (fsp == NULL || rsp == NULL) { if (fsp != rsp) { printf("s_c_c(%s): fsp=%x != rsp=%x\n", msg, fsp, rsp); quack(1001); rv = 0; } break; } fsp = fsp->next_sp; rsp = rsp->prev_sp; } if (k != nas) { printf("s_c_c(%s): k=%u != nas=%u\n", msg, k, nas); for (k=0, fsp = sdp->sq; fsp != NULL; fsp = fsp->next_sp) { printf(" %d:%x", k++, fsp); } printf("\n"); rv = 0; quack(1002); } return rv; } void check_all_stream_chains(int rs, char *msg) { Bit32 x; struct flow *fp; char buf[100]; int k; sprintf(buf, "c_a_s_c(%s)", msg); x = ri[rs-1].ri_flow_chain; for (fp = find_flow(x); fp != NULL; ) { if (fp->stdata) { if (!check_stream_chains(fp, buf)) exit(123); } x = fp->rs_next; fp = x != 0 ? find_flow(x) : NULL; } } int check_stream_hash_chains(char *msg) { int x; struct stream **sha; struct stream *sp, *lsp, *isp; for (x = 0; x != sfhashmod; ++x) { sha = &sfht[x]; /* Address of stream hash table entry */ if ((sp = *sha) == NULL) lsp = NULL; /* Empty hash chain */ else { isp = sp; for (;;) { if (sp->next_hc == NULL) { printf("check_stream_h_c(%s): x=%d, isp=%x, lsp=%x, sp=%x\n", msg, x, isp, lsp, sp); return 0; } lsp = sp; if ((sp = lsp->next_hc) == (struct stream *)sha) { sp = NULL; break; /* Loop stops with lsp -> last stream in chain */ } } } } return 1; } #endif /* STREAM_CHAIN_DEBUG */ void free_flow(struct flow *q) /* Free memory allocated for a flow */ /* Caution: must call mark_idle() before calling free_flow() <<< */ { #if NEW_ATR struct distribution *d, *nd; struct pktdata *p, *np; struct stream_data *sdp; if (q->stdata != NULL) { sdp = q->stdata; q->stdata = NULL; /* Don't let it point into stream_data free list! */ terminate_all_streams(q, sdp); /* May update distribs! */ free_st_data(sdp); } for (d = q->distrib_list; d != NULL; d = nd) { nd = d->next; free_dist(d); } #endif /* NEW_ATR */ /* q->FlowRuleSet = 0; /* Now it doesn't look like part of an rs chain! */ /* Don't need this, it's done in mark_idle(). Nevil, 15 May 99 */ } #if NEW_ATR static int complained_about_distribs = 0; struct distribution *get_dist(void) /* Get an unused distribution */ { struct distribution *d; if (free_distribs != NULL) { d = free_distribs; free_distribs = free_distribs->next; --n_free_distribs; return d; } else { if (!complained_about_distribs) { log_msg(LOG_WARNING, 0, "Not enough distributions, increase -v <<<\n"); complained_about_distribs = 1; } return NULL; } } void free_dist(struct distribution *d) /* Return distribution to free list */ { switch (d->selector) { case FTTOBITRATE: case FTFROMBITRATE: case FTTOPDURATE: case FTFROMPDURATE: unlink_distrib_from_event(d); break; } d->next = free_distribs; free_distribs = d; ++n_free_distribs; complained_about_distribs = 0; } static int complained_about_events = 0; struct event *get_event(void) /* Get an unused event */ { struct event *e; if (free_events != NULL) { e = free_events; free_events = free_events->next; --n_free_events; return e; } else { if (!complained_about_events) { log_msg(LOG_WARNING, 0, "Not enough dist events, increase -e <<<\n"); complained_about_events = 1; } return NULL; } } void free_event(struct event *e) /* Return event to free list */ { e->next = free_events; free_events = e; ++n_free_events; complained_about_events = 0; } void schedule_event(struct event *ev) { struct event *eqp, *lqp; lqp = &eventq; eqp = eventq.next; for ( ; eqp != NULL; lqp = eqp, eqp = eqp->next) { if (ev->t <= eqp->t) break; } lqp->next = ev; ev->next = eqp; return; } static int complained_about_pktdata = 0; struct pktdata *get_pktdata(void) /* Get an unused pktdata */ { struct pktdata *pd; if (free_pkt_dat != NULL) { pd = free_pkt_dat; free_pkt_dat = free_pkt_dat->next; --n_free_pktdata; ++PktsCreated; return pd; } else { if (!complained_about_pktdata) { log_msg(LOG_WARNING, 0, "Not enough packet data blocks, increase -a <<<\n"); complained_about_pktdata = 1; } return NULL; } } void free_pktdata(struct pktdata *pd) /* Return pktdata to free list */ { pd->next = free_pkt_dat; free_pkt_dat = pd; ++n_free_pktdata; ++PktsRecovered; complained_about_pktdata = 0; } int make_distrib_list(struct flow *f); void bump_dist( /* Add point y into distrib d */ struct distribution *d, double y); double inv_scale(struct distribution *d, Bit16 j); /* limit -> value */ Bit32 scale(struct distribution *d, double y); void compute_rate(struct distribution *d) { struct flow *f; counter64 r; double delta; f = d->fp; switch (d->selector) { case FTTOBITRATE: subtr64(r, f->UpOctets,f->LastUpOctets); assign64(f->LastUpOctets, f->UpOctets); delta = (doublefrom64(r))*8.0; /* Bits */ break; case FTFROMBITRATE: subtr64(r, f->DownOctets,f->LastDownOctets); assign64(f->LastDownOctets, f->DownOctets); delta = (doublefrom64(r))*8.0; /* Bits */ break; case FTTOPDURATE: subtr64(r, f->UpPDUs,f->LastUpPDUs); assign64(f->LastUpPDUs, f->UpPDUs); delta = doublefrom64(r); /* Packets */ break; case FTFROMPDURATE: subtr64(r, f->DownPDUs,f->LastDownPDUs); assign64(f->LastDownPDUs, f->DownPDUs); delta = doublefrom64(r); /* Packets */ break; } bump_dist(d, delta/((double)d->RateInterval)); } void check_rates(Bit32 now) { struct event *eqp; struct distribution *d; for (;;) { eqp = eventq.next; if (eqp == NULL || eqp->t > now) return; eventq.next = eqp->next; /* Remove from event queue */ if ((d = eqp->dp) == NULL) /* No distributions queued */ free_event(eqp); else { do { compute_rate(d); d = d->nrate; } while (d != NULL); eqp->t += eqp->interval; schedule_event(eqp); } } } void link_distrib_to_event(struct distribution *d) { struct event *eqp; struct distribution *dq; for (eqp = eventq.next; eqp != NULL; eqp = eqp->next) { if (eqp->interval == d->RateInterval) break; } if (eqp == NULL) { /* Make new event for this interval */ if ((eqp = get_event()) != NULL) { eqp->dp = NULL; eqp->interval = d->RateInterval; eqp->t = uptime_s() + eqp->interval; schedule_event(eqp); } } if (eqp != NULL) { /* Add distrib to head of event chain */ /* Was eqp == NULL, fixed Sat 3 Jun 00 */ d->nrate = eqp->dp; eqp->dp = d; } } #if DIST_DBG void print_dist_chain(struct event *ep) { struct distribution *dq; for (dq = ep->dp; dq != NULL; dq = dq->nrate) printf("%d@%lu ", dq->fp->fk.PeerAddrType, dq); printf("\n"); } #endif void unlink_distrib_from_event(struct distribution *d) { struct event *eqp; struct distribution *dq, *lq; for (eqp = eventq.next; eqp != NULL; eqp = eqp->next) { if (eqp->interval == d->RateInterval) break; } #if DIST_DBG printf("unlink_distrib(%lu): ", d); /* $$$ */ print_dist_chain(eqp); #endif if (eqp == NULL) { scpos(0,scr_lrow); log_msg(LOG_ERR, 0, "No event for %ds rate distribution <<<", d->RateInterval); return; } for (lq = NULL, dq = eqp->dp; dq != d; lq = dq, dq = dq->nrate) { if (dq == NULL) { scpos(0,scr_lrow); log_msg(LOG_ERR, 0, "Couldn't find rate distribution <<<"); return; } } if (lq == NULL) eqp->dp = d->nrate; else lq->nrate = d->nrate; #if DIST_DBG printf(" "); /* $$$ */ print_dist_chain(eqp); #endif } static int complained_about_str_blocks = 0; struct stream_data *get_st_data(void) /* Get unused st_data */ { struct stream_data *sh; if (free_str_blocks != NULL) { sh = free_str_blocks; free_str_blocks = free_str_blocks->next; --n_free_str_blocks; memset(sh, 0, sizeof(struct stream_data)); return sh; } else { if (!complained_about_str_blocks) { log_msg(LOG_WARNING, 0, "Not enough stream data blocks, increase -b <<<\n"); complained_about_str_blocks = 1; } return NULL; } } void free_st_data(struct stream_data *sh) /* Return st_data to free list */ { sh->next = free_str_blocks; free_str_blocks = sh; ++n_free_str_blocks; complained_about_str_blocks = 0; } static int complained_about_streams = 0; struct stream *get_stream(void) { struct stream *sf; if (free_streams != NULL) { sf = free_streams; free_streams = free_streams->next_sp; --n_free_streams; ++StreamsCreated; return sf; } else { if (!complained_about_streams) { log_msg(LOG_WARNING, 0, "Not enough stream space, increase -t <<<\n"); complained_about_streams = 1; } return NULL; } } void free_stream(struct stream *sp) { sp->next_sp = free_streams; free_streams = sp; ++n_free_streams; ++StreamsRecovered; complained_about_streams = 0; } #endif /* NEW_ATR */ #ifdef DEBUG FILE *logfl; int logf_nbr = 1; void open_log() { char fn[30]; sprintf(fn,"flows.%03d",logf_nbr++); logfl = fopen(fn, "w"); } void paddr(Bit32 a) { fprintf(logfl,"%u.%u.%u.%u", a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); } void daddr(Bit32 a) { printf("%u.%u.%u.%u", a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); } void dport(Bit16 a) { printf("%u", a); } void fdaddr(FILE *f, Bit32 a) { fprintf(f, "%u.%u.%u.%u", a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); } void fdport(FILE *f, Bit16 a) { fprintf(f, "%u",a); } void pkey(char *msg, struct flow *f) { if (!logfl) open_log(); fprintf(logfl,"%s: %lu %u\n {", msg,f, f->fk.PeerAddrType); #if QBYTE_PEER paddr(f->fk.Low.PeerAddress.qbyte); fprintf(logfl," & "); #else paddr(f->fk.Low.PeerAddress.peer32[0]); fprintf(logfl," & "); #endif paddr(*(Bit32 *)Masks[f->fk.Low.PeerMaskVal].Val); fprintf(logfl,"} -> {"); #if QBYTE_PEER paddr(f->fk.High.PeerAddress.qbyte); fprintf(logfl," & "); #else paddr(f->fk.High.PeerAddress.peer32[0]); fprintf(logfl," & "); #endif paddr(*(Bit32 *)Masks[f->fk.High.PeerMaskVal].Val); fprintf(logfl,"}\n"); } #endif #define DRT_SIZE 3 /* Default rule set: 3 rules */ #ifdef OLD_CT #define DCT_SIZE 1 /* 1 count */ #endif struct rule default_rule_table[DRT_SIZE] = { /* Value, Selector, Mask, JumpIndex, Action */ { # if CLNS {AT_DUMMY,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, # elif V6 || FULL_IPX {AT_DUMMY,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, # else /* v4 */ {AT_DUMMY,0,0,0,0,0}, # endif FTLOWPEERTYPE, 1, 0, RA_IGNORE}, /* SourcePeerType & 255 = dummy: Ignore, 0; # 1 */ { # if CLNS {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, # elif V6 || FULL_IPX {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, # else /* v4 */ {0,0,0,0,0,0}, # endif RS_NULL, 0, 3, RA_GOTOACT}, /* Null & 0 = 0: GotoAct, Next; # 2 */ { # if CLNS {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, # elif V6 || FULL_IPX {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, # else /* v4 */ {0,0,0,0,0,0}, # endif FTLOWPEERTYPE, 1, 1, RA_COUNTPKT} /* SourcePeerType & 255 = 0: CountPkt, 1; # 3 */ }; Bit32 p2primes[] = { /* Primes just below powers of two */ 7, 13, 31, 61, 127, 251, 509, 1021, /* 0 .. 7 1024 */ 2039, 4093, 8191, 16381, 32749, 65521, /* 8 .. 13 65536 */ 131071, 262139, 524287, 1048573}; /* 14 .. 17 1048576 */ #if 0 int primes[22] = { /* Two for Trans bytes + enough for NSAP bytes */ 211, 251, 113, 139, 173, 227, 179, 193, 181, 131, 67, 73, 83, 97, 103, 127, 149, 23, 53, 89, 59, 47}; #endif Bit32 primes[22] = { /* Two for Trans bytes + enough for NSAP bytes */ 97241, 98467, 97171, 95881, 96953, 95027, 93559, 95089, 96289, 97039, 97301, 99761, 95803, 94873, 95131, 96457, 91867, 94007, 97501, 99961, 96337, 95383}; #define prime_0 95027 #define prime_1 93559 #define prime_2 95089 #define prime_3 96289 #define prime_4 97039 #define prime_5 97301 #define prime_6 99761 #define prime_7 95803 #define prime_8 94873 #define prime_9 95131 #define prime_10 96457 #define prime_11 91867 #define prime_12 94007 #define prime_13 97501 #define prime_14 99961 #define prime_15 96337 #define prime_16 95383 Bit32 compute_rule_hash(int attrib, union address *rvp) { /* Must compute these **exactly** as for hashed compare in match */ #if !QBYTE_PEER Bit32 hash; int j; #endif switch (attrib) { case FTLOWPEERADDRESS: case FTHIPEERADDRESS: #if QBYTE_PEER return rvp->qbyte*prime_0; #else for (j = hash = 0; j !=(PEER_ADDR_LEN+3)/4; ++j) hash += rvp->peer32[j]*primes[j]; return hash; #endif case FTLOWPEERTYPE: case FTHIPEERTYPE: return rvp->byte*prime_1; case FTLOWTRANSADDRESS: case FTHITRANSADDRESS: return rvp->trans*prime_2; case FTLOWTRANSTYPE: case FTHITRANSTYPE: return rvp->byte*prime_3; case FTLOWADJACENTADDRESS: case FTHIADJACENTADDRESS: return rvp->ma.ms4*prime_4 + rvp->ma.ls2*prime_5; case FTSOURCECLASS: case FTDESTCLASS: case FTFLOWCLASS: return rvp->byte*prime_9; case FTSOURCEKIND: case FTDESTKIND: case FTFLOWKIND: return rvp->byte*prime_10; case FTLOWINTERFACE: case FTHIINTERFACE: return rvp->byte*prime_7; case FTLOWADJACENTTYPE: case FTHIADJACENTTYPE: return rvp->byte*prime_6; case FTDSCODEPOINT: return rvp->byte*prime_8; #if NF_ASN_ATT case FTLOWROUTEASN: case FTHIROUTEASN: return rvp->byte*prime_11; case FTLOWROUTEPREFIX: case FTHIROUTEPREFIX: return rvp->byte*prime_12; #endif #if NF_OTHER_ATT case FTMETERID: return rvp->byte*prime_13; #endif } } int optimise_rule_table(int rs) { struct rtinf_rec *rip = &ri[rs-1]; Bit16 crs, j, k, g, h, x, l, nht; /* Nbr of rule_hash_tbls */ Bit32 ths, /* Sum of rule_hash_tbl sizes */ rhss; /* Total rule_hash sequence size */ struct rule *crt, *rp, *hp; Bit16 *rs_rule_hash; struct rule_hash_tbl *c_rh; Bit32 hash; char msg[60]; crt = rip->ri_rule_table; crs = rip->ri_Size; #ifdef H_TEST scpos(0,scr_lrow); printf("\nRT: crs=%u, RULE_ADDR_LEN=%d\n", crs,RULE_ADDR_LEN); #endif hp = &crt[0]; hp->hash_tbl_index = 1; for (j = 1; j != crs; ++j) { /* Mark match break points */ rp = &crt[j]; rp->push_rule = ( /* For build_search_key */ (rp->RuleAction != RA_PUSHPKTTO) && (rp->RuleAction != RA_PUSHPKTTOACT) && (rp->RuleAction != RA_COUNTPKT) ); rp->hash_tbl_index = rp->RuleSelector != hp->RuleSelector || rp->RuleMaskVal != hp->RuleMaskVal; hp = rp; } for (j = 0; j != crs; ++j) { /* Mark goto break points */ rp = &crt[j]; rp->hash_link = 0; k = rp->RuleAction; if (k == RA_PUSHTO /* OK to gotoact in middle of compare group */ || k == RA_PUSHPKTTO || k == RA_GOTO || k == RA_GOSUB || k == RA_ASSIGN || k == RA_POPTO ) { hp = &crt[rp->RuleJumpIndex-1]; hp->hash_tbl_index = 2; } } #ifdef H_TEST scpos(0,scr_lrow); printf("rule index link MARKED\n"); for (j = 0; j != crs; ++j) { rp = &crt[j]; printf("%3d %5d %5d %d ", j,rp->hash_tbl_index,rp->hash_link, rp->RuleSelector); daddr((Bit8 *)Masks[rp->RuleMaskVal].Val); printf("\n"); } #endif nht = ths = 0; hp = &crt[0]; k = 1; for (j = 1; j != crs; ++j) { /* Find compare groups */ rp = &crt[j]; if ((g = rp->hash_tbl_index) == 0) ++k; /* Same group */ if (g != 0 || j == crs-1) { /* End of a group */ if (k >= MNCGRPSZ && hp->RuleSelector != RS_NULL) { for (h = 0; h != sizeof(p2primes)/sizeof(Bit32)-1 && p2primes[h] <= (Bit32)k; ++h) ; /* h = next prime above k */ if (p2primes[h] > MXRHTSZ) h = MXRHTSZ; else h = p2primes[h]; hp->hash_tbl_index = k; /* Remember group and */ hp->hash_link = h; /* hash table sizes */ ++nht; ths += h; } else hp->hash_tbl_index = 0; hp = rp; k = 1; } rp->hash_tbl_index = 0; /* Last row can't start a group */ } rhss = 1 + nht*2 + ths; /* rule_hash set size (Bit16s) */ if (rhss > 0xFFFF) { log_msg(LOG_ERR, 0, msg,"Ruleset '%s': rhss = %ul (> 65535)", rip->ri_Name, rhss); return 0; /* Fail */ } #ifdef H_TEST printf("rule index link GROUPED\n"); for (j = 0; j != crs; ++j) { rp = &crt[j]; printf("%3d %5d %5d\n", j, rp->hash_tbl_index,rp->hash_link); } #endif if (!alloc_rulespace(rs, 0, rhss)) /* Alloc rule hash space */ return 0; /* No room for rule hash table! */ rs_rule_hash = rip->ri_rule_hash; rs_rule_hash[0] = 0; k = 1; rp = &crt[j = 0]; for ( ; nht != 0; --nht, k += 2+h) { c_rh = (struct rule_hash_tbl *)&rs_rule_hash[k]; while (rp->hash_tbl_index == 0) { ++rp; ++j; } g = rp->hash_tbl_index; /* Group size */ h = rp->hash_link; /* Hash table size */ rp->hash_tbl_index = k; /* Index of hash_tbl in rule_hash */ rp->hash_link = 0; c_rh->group_last = j+g; c_rh->hashmod = h; for (x = 0; x != h; ++x) c_rh->hash_ent[x] = 0; for ( ; g != 0; --g) { /* Build rule hash tables */ hash = compute_rule_hash(rp->RuleSelector, &rp->RuleMatchedValue) % h; if ((l = c_rh->hash_ent[hash]) == 0) c_rh->hash_ent[hash] = j+1; /* First rule with this hash */ else { do { hp = &crt[l-1]; l = hp->hash_link; } while (l != 0); hp->hash_link = j+1; /* Add to end of hash chain */ } ++rp; ++j; } } #ifdef H_TEST printf("Rule Hash Tables ..\n\nrule index n_rules n_hash n_zero\n\n"); for (j = 0; j != crs; ++j) { rp = &crt[j]; if ((k = rp->hash_tbl_index) == 0) continue; c_rh = (struct rule_hash_tbl *)&rs_rule_hash[k]; h = c_rh->hashmod+1; for (g = 0, hash = 0; hash != h; ++hash) if (c_rh->hash_ent[hash] == 0) ++g; printf("%5d%5d %5d%5d%5d\n ", j+1, k, c_rh->group_last-j+1, h, g); printf("\n"); } #endif return 1; } void empty_hash_chain(int rs) /* Unlink flows for ruleset rs */ { Bit32 k; struct flow **sf; struct flow *fp, *lfp; struct flow *chp, *ctp; /* Could walk the rs chain,rather than linear searching the whole whole table! But we don't shut down rule sets very often! */ for (k = 0; k != fthashmod; ++k) { sf = &flow_ht[k]; /* Address of hash table entry */ if ((fp = *sf) != NULL) { chp = ctp = NULL; /* New chain for remaining active flows */ do { lfp = fp; fp = lfp->ht_next; if (lfp->FlowRuleSet == rs) mark_orphan(lfp); else { if (chp == NULL) chp = ctp = lfp; else { ctp->ht_next = lfp; ctp = lfp; } } } while (fp != (struct flow *)sf); if (chp != NULL) { ctp->ht_next = fp; *sf = chp; } else { /* No remaining active flows */ *sf = NULL; --n_hash_ents; } } } } #if PME_TRACE #define MXTRACE 250 #define MXTRACE_MSGS 5 /* Per rule set */ static Bit16 ruletrace[MXTRACE], rtx; static int tr_msgs_left[MXRTBLS]; #endif void open_rule_set(int n, int open_rs) { int lrx,rrx, k, p; Bit16 rsx; struct rule *rp; if (n == 0) return; /* 0 = no rule set to run */ if (ri[n-1].ri_Status != RS_ACTIVE) return; if (open_rs) { if (++ri[n-1].ri_running != 1) /* Already running */ return; else { if (running_rts == 0) /* Starting first ruleset */ running_rts = n; else { for (rrx = running_rts; ; ) { if (ri[rrx-1].next_running_rt == 0) break; rrx = ri[rrx-1].next_running_rt; } ri[rrx-1].next_running_rt = n; } ri[n-1].next_running_rt = 0; #if PME_TRACE tr_msgs_left[n-1] = MXTRACE_MSGS; #endif } } else { /* Close ruleset */ if (--ri[n-1].ri_running != 0) /* Still running */ return; for (lrx = 0, rrx = running_rts; rrx != n; ) { lrx = rrx; rrx = ri[rrx-1].next_running_rt; } if (lrx == 0) running_rts = ri[rrx-1].next_running_rt; else ri[lrx-1].next_running_rt = ri[rrx-1].next_running_rt; ri[n-1].next_running_rt = 0; empty_hash_chain(n); } c_rt = ri[n-1].ri_rule_table; c_rsz = ri[n-1].ri_Size; for (rsx = 0; rsx != c_rsz; ++rsx) { rp = &c_rt[rsx]; if (rp->RuleSelector == FTLOWPEERTYPE || rp->RuleSelector == FTHIPEERTYPE) { p = rp->RuleMatchedValue.rule[0]; if (rp->RuleAction == RA_PUSHPKTTO || /* All peer types */ rp->RuleAction == RA_PUSHPKTTOACT || rp->RuleAction == RA_COUNTPKT) for (k = 0; k != sizeof(proto_reqd)/sizeof(int); ++k) { if (open_rs) ++proto_reqd[k]; else --proto_reqd[k]; } else if (p <= MX_PROTOCOLS && *proto_names[p] != '\0') { /* Was & above! Sebastian Zander, 6 Jan 2000 */ if (open_rs) ++proto_reqd[p]; /* Specific peer type */ else --proto_reqd[p]; } } if ((rp->RuleSelector == FTLOWADJACENTADDRESS || rp->RuleSelector == FTHIADJACENTADDRESS) || ((rp->RuleAction == RA_ASSIGN || rp->RuleAction == RA_ASSIGNACT) && (rp->RuleMatchedValue.rule[0] == FTLOWADJACENTADDRESS || rp->RuleMatchedValue.rule[0] == FTHIADJACENTADDRESS)) ) if (open_rs) ++adj_reqd; else --adj_reqd; #if NF_OCX_BGP if (rp->RuleSelector == FTLOWROUTEASN || rp->RuleSelector == FTHIROUTEASN) if (open_rs) ++asn_lookup; else --asn_lookup; #endif } if (adj_reqd) { for (k = 0; k != sizeof(proto_reqd)/sizeof(int); ++k) if (proto_reqd[k]) return; /* At least one protocol specified */ for (k = 0; k != sizeof(proto_reqd)/sizeof(int); ++k) { if (open_rs) ++proto_reqd[k]; /* All peer types */ else --proto_reqd[k]; } } } #ifdef GC_DEBUG int dup_flow(struct flow *f, char *msg) { Bit32 x; for (x = 253; x <= mxflows; ++x) if (flow_ix[x-1] != NULL) { scpos(0,scr_lrow); printf(" flow_ix[%d]=%lu !!\n", x,flow_ix[x-1]); } for (x = 1; x <= mxflows; ++x) if (flow_ix[x-1] == f) { scpos(0,scr_lrow); printf("Flow %lu is flow_ix[%d], msg=%s\n", f,x, msg); exit(0); } return 0; } #endif Bit32 rsc_n_searches, rsc_mx_range, rsc_av_range, /* Flow index range searched */ rsc_mx_tests, rsc_av_tests; /* Nbr of TimeFilter tests */ # if RS_CH_DEBUG void check_rs_chain(int rs, char *msg) { Bit32 fx; int count; for (count = 0, fx = ri[rs-1].ri_flow_chain; fx != 0; fx = fa[fx-1].rs_next) { if (++count > ri[rs-1].ri_FlowRecords) break; } if (count != ri[rs-1].ri_FlowRecords) printf("check_rs_chain(%d, %s): count=%d, FlowRecords=%d\n", rs,msg, count, ri[rs-1].ri_FlowRecords); } # endif void mark_idle(Bit32 fx) { struct flow *fp = &fa[fx-1]; fp->FlowRuleSet = 0; /* Mark idle */ if (free_flow_tail == 0) free_flow_head = fx; else fa[free_flow_tail-1].rs_next = fx; /* Add to tail of free chain */ free_flow_tail = fx; fp->rs_next = 0; /* End marker for rs chain */ } Bit32 alloc_flow_nbr(void) /* Allocate nbr for a new a flow */ { Bit32 fx; if (already_complained) /* We know there are no free flows */ return 0; /* already_complained reset by set_LastTime() */ if (free_flow_head == 0) { /* No idle flows: force garbage collection */ if (garbage_collect(0)) already_complained = 0; } if (free_flow_head != 0) { fx = free_flow_head; free_flow_head = fa[fx-1].rs_next; /* Take from head of free chain */ if (free_flow_head == 0) free_flow_tail = 0; return fx; } if (already_complained) return 0; more_garbage(); /* Increase garbage collecting rate */ # if defined(DOS) scpos(0,scr_lrow); printf("Recovering flows too slow !!!"); # else log_msg(LOG_WARNING, 0, "Recovering flows too slow !!!\n"); # endif already_complained = 1; return 0; } Bit32 number_flow() /* Allocate a flow_nbr */ { Bit32 start_ix = empty_ix; if (already_complained) /* We know there are no free flows */ return 0; /* already_complained reset by set_LastTime() */ do { if (++empty_ix > mxflows) /* empty_ix goes from 1 to MXFLOWS */ empty_ix = 1; /* Flows go from 1 to mxflows too */ if (flow_idle(empty_ix)) { # ifdef GC_DEBUG printf("nbr_flow(%lu) -> %u\n", fp,empty_ix); # endif return empty_ix; /* 1-org */ } } while (empty_ix != start_ix); if (garbage_collect(0)) { /* No unused flows: force garbage collection */ already_complained = 0; return empty_ix = gcf_ix; /* 1-org */ } if (already_complained) return 0; more_garbage(); /* Increase garbage collecting rate */ #if defined(DOS) scpos(0,scr_lrow); printf("Recovering flows too slow !!!"); #else printf("Recovering flows too slow !!!\n"); #endif already_complained = 1; return 0; } struct flow *find_flow(Bit32 x) /* Find flow with flow_nbr x */ { if (x > mxflows || x < 1) { log_msg(LOG_ERR, 0, "Invalid flow number (%d)", x); return NULL; } #if FLOW_INDEX return flow_ix[x-1]; #else return &fa[x-1]; #endif } void more_garbage() { int max_gc_f; gc_interval >>= 1; /* Decrease garbage collection interval */ if (gc_interval < 2) { gc_interval = 2; max_gc_f = (mxflows)/7; gc_f <<= 1; /* Increase nbr of flows checked each collection */ if (gc_f > max_gc_f) gc_f = max_gc_f; } } int garbage_collect(int incremental) { int f,e; Bit32 s_gcf_ix; Bit32 GCtime; struct rule *rp; struct flow *fp, *np, *tp, *ltp; #ifdef GC_DEBUG int k; #endif Bit32 rs_first_flow; struct flow *pf; int rs; e = gc_f; f = gc_f/4; #ifdef GC_TEST1 scpos(0,scr_lrow); printf("GC: gcf_ix=%u, e=%d, f=%d\n", gcf_ix,e,f); #endif s_gcf_ix = gcf_ix; do { if (++gcf_ix > mxflows) /* gcf_ix goes from 1 to MXFLOWS */ gcf_ix = 1; if (flow_idle(gcf_ix)) { /* Unused flow index */ if (incremental && --e == 0) return 1; continue; } #if FLOW_INDEX fp = flow_ix[gcf_ix-1]; #else fp = &fa[gcf_ix-1]; #endif GCtime = ri[fp->FlowRuleSet-1].ri_gc_time; #ifdef GC_TEST1 printf(" GCtime=%lu, FlowRuleSet=%u, gcf_ix=%lu\n", GCtime,fp->FlowRuleSet,gcf_ix); #endif if (fp->ntm_LastTime <= GCtime) { /* Inactive, recover it */ #ifdef GC_TEST1 printf(" f=%d, gcf_ix=%d, ntm_LastTime=%lu\n", f,gcf_ix,fp->ntm_LastTime); #endif if (!flow_orphan(fp)) { /* Remove from hash chain */ #ifdef GC_DEBUG scpos(0,scr_lrow); printf("GC: set=%u, ix=%u, fp=%lu, np=%lu\n", fp->FlowRuleSet,gcf_ix, fp,fp->ht_next); k = 0; #endif np = fp->ht_next; tp = fp; do { ltp = tp; #ifdef GC_DEBUG printf(" k=%d, ltp=%lu, tp=%lu\n", ++k, ltp,ltp->ht_next); if (tp->ht_next == NULL || k > 10) exit(0); /* Infinite loop - stop */ #endif } while ((tp = tp->ht_next) != fp); if (ltp == np) { /* Single flow in hash chain */ mark_orphan(ltp); --n_hash_ents; } else ltp->ht_next = np; mark_orphan(fp); /* Added 15 May 99, Nevil */ } --ri[fp->FlowRuleSet-1].ri_FlowRecords; rs = fp->FlowRuleSet; rs_first_flow = ri[rs-1].ri_flow_chain; if (gcf_ix == rs_first_flow) { /* First flow of chain */ ri[rs-1].ri_flow_chain = fp->rs_next; } else { /* Find previous link in ruleset chain */ pf = fp; do { --pf; } while (pf->FlowRuleSet != rs); pf->rs_next = fp->rs_next; } # if RS_CH_DEBUG check_rs_chain(rs, "GC"); # endif mark_idle(gcf_ix); #ifdef GC_DEBUG printf("\ngarbage_collect ..\n"); #endif free_flow(fp); --nflows; ++FlowsRecovered; if (!incremental) return 1; /* Forced collection succeeded, flow index gcf_ix is now free */ #ifdef GC_TEST scpos(0,scr_lrow); printf("Flow %d recovered\n", gcf_ix); #endif } else { /* Not inactive */ #if NEW_ATR if (fp->stdata != NULL && /* Flow has >= 1 stream attribute */ fp->stdata->pp_match_reqd && /* Packet-pair matching required */ (fp->fk.PeerAddrType == AT_IP4 # if V6 || fp->fk.PeerAddrType == AT_IP6 # endif ) && fp->fk.TransAddrType == PT_TCP) /* It's a TCP flow */ check_tcp_idle_streams(fp, 1); #endif /* NEW_ATR */ } if (incremental && --f == 0) return 1; } while (gcf_ix != s_gcf_ix); return 0; /* Failed to recover any flows */ } #define MIGNORE 2 /* match() result */ #define MSUC 1 #define MFAIL 0 #define MFWD 1 /* count() parameter */ #define MREV 0 struct search_result { struct flow **sf; /* Address of hash table entry */ struct flow *lf; /* Last flow in hash chain */ }; int match(int forward, struct pkt *bp, struct pkt_key *from, struct pkt_key *to); void build_search_key(Bit32 *search_hash, struct flow_key *key, struct pkt *bp, struct pkt_key *Low, struct pkt_key *High); struct flow *current(struct search_result*s_r, Bit32 hash, struct flow_key *search_key); struct flow *create(struct search_result *s_r, struct flow_key *search_key, struct pkt *bp); void count(struct flow *fp, int flow_order, struct pkt *bp); Bit16 p_stack[RSTKSIZ]; /* Pattern stack */ /* Was int p_stack. 22 Apr 98 */ int a_stack[RSTKSIZ]; /* Attribute values for pattern stack */ int p_s_depth; Bit8 user_vbls[N_VBLS]; /* User variables */ Bit16 env_stack[ESTKSIZ]; /* Environment stack */ int e_s_tos; #ifdef GC_DEBUG void print_chain(struct hash_tbl *t, Bit16 hash, char *msg) { struct flow **sf; struct flow *f, *lf; sf = &t->hash_ent[hash]; /* Address of hash table entry */ scpos(0,scr_lrow); printf("Chain: t=%lu, hash=%u, sf=%lu, msg=%s\n", t,hash,sf, msg); lf = f = sf[0]; do { printf(" f=%lu\n", f); if ((lf = f) == NULL) return; } while ((f = lf->ht_next) != (struct flow *)sf); } #endif void build_search_key(Bit32 *search_hash, struct flow_key *key, struct pkt *bp, struct pkt_key *Low, struct pkt_key *High) /* Builds a key which current() will use to search the flow table */ { Bit32 hash; struct rule *rp; int pal, j, k; union address *Maskp; hash = c_rtx*prime_14; /* Include ruleset ix in search key. Nevil, 11 Sep 98 */ memset(key, 0, sizeof(struct flow_key)); /* Start with a null key */ pal = (PEER_ADDR_LEN+3)/4; /* Could be any type, start by using max */ #ifdef TESTING if (!logfl) open_log(); fprintf(logfl,"p_s_depth=%d\n", p_s_depth); #endif for (j = 0; j != p_s_depth; ++j) { rp = &c_rt[p_stack[j]-1]; Maskp = Masks[rp->RuleMaskVal].Val; #ifdef TESTING fprintf(logfl," j=%d, attrib=%d\n", j,a_stack[j]); #endif switch (a_stack[j]) { case FTLOWPEERADDRESS: key->Low.PeerMaskVal = rp->RuleMaskVal; #if QBYTE_PEER if (rp->push_rule) hash += (key->Low.PeerAddress.qbyte = rp->RuleMatchedValue.qbyte)*prime_0; else hash += (key->Low.PeerAddress.qbyte = Low->PeerAddress.qbyte & Maskp->qbyte)*prime_0; #else if (rp->push_rule) for (k = 0; k != pal; ++k) hash += (key->Low.PeerAddress.peer32[k] = rp->RuleMatchedValue.peer32[k])*primes[k]; else for (k = 0; k != pal; ++k) hash += (key->Low.PeerAddress.peer32[k] = Low->PeerAddress.peer32[k] & Maskp->peer32[k])*primes[k]; #endif break; case FTHIPEERADDRESS: key->High.PeerMaskVal = rp->RuleMaskVal; #if QBYTE_PEER if (rp->push_rule) hash += (key->High.PeerAddress.qbyte = rp->RuleMatchedValue.qbyte)*prime_0; else hash += (key->High.PeerAddress.qbyte = High->PeerAddress.qbyte & Maskp->qbyte)*prime_0; #else if (rp->push_rule) for (k = 0; k != pal; ++k) hash += (key->High.PeerAddress.peer32[k] = rp->RuleMatchedValue.peer32[k])*primes[k]; else for (k = 0; k != pal; ++k) hash += (key->High.PeerAddress.peer32[k] = High->PeerAddress.peer32[k] & Maskp->peer32[k])*primes[k]; #endif break; case FTLOWPEERTYPE: case FTHIPEERTYPE: if (rp->push_rule) hash += (key->PeerAddrType = rp->RuleMatchedValue.byte)*prime_1; else hash += (key->PeerAddrType = bp->PeerAddrType)*prime_1; pal = addr_len32[key->PeerAddrType]; /* Now we know peer type */ break; case FTLOWTRANSADDRESS: key->Low.TransMaskVal = rp->RuleMaskVal; if (rp->push_rule) hash += (key->Low.TransAddress = rp->RuleMatchedValue.trans)*prime_2; else hash += (key->Low.TransAddress = Low->TransAddress & Maskp->trans)*prime_2; break; case FTHITRANSADDRESS: key->High.TransMaskVal = rp->RuleMaskVal; if (rp->push_rule) hash += (key->High.TransAddress = rp->RuleMatchedValue.trans)*prime_2; else hash += (key->High.TransAddress = High->TransAddress & Maskp->trans)*prime_2; break; case FTLOWTRANSTYPE: case FTHITRANSTYPE: if (rp->push_rule) hash += (key->TransAddrType = rp->RuleMatchedValue.byte)*prime_3; else hash += (key->TransAddrType = bp->TransAddrType)*prime_3; break; case FTLOWADJACENTADDRESS: key->Low.AdjMaskVal = rp->RuleMaskVal; if (rp->push_rule) { hash += (key->Low.AdjAddr_ms4 = rp->RuleMatchedValue.ma.ms4)*prime_4; hash += (key->Low.AdjAddr_ls2 = rp->RuleMatchedValue.ma.ls2)*prime_5; } else { hash += (key->Low.AdjAddr_ms4 = Low->AdjAddr_ms4 & Maskp->ma.ms4)*prime_4; hash += (key->Low.AdjAddr_ls2 = Low->AdjAddr_ls2 & Maskp->ma.ls2)*prime_5; } break; case FTHIADJACENTADDRESS: key->High.AdjMaskVal = rp->RuleMaskVal; if (rp->push_rule) { hash += (key->High.AdjAddr_ms4 = rp->RuleMatchedValue.ma.ms4)*prime_4; hash += (key->High.AdjAddr_ls2 = rp->RuleMatchedValue.ma.ls2)*prime_5; } else { hash += (key->High.AdjAddr_ms4 = High->AdjAddr_ms4 & Maskp->ma.ms4)*prime_4; hash += (key->High.AdjAddr_ls2 = High->AdjAddr_ls2 & Maskp->ma.ls2)*prime_5; } break; case FTSOURCECLASS: hash += (key->Low.Class = rp->RuleMatchedValue.byte)*prime_9; break; case FTDESTCLASS: hash += (key->High.Class = rp->RuleMatchedValue.byte)*prime_9; break; case FTFLOWCLASS: hash += (key->FlowClass = rp->RuleMatchedValue.byte)*prime_9; break; case FTSOURCEKIND: hash += (key->Low.Kind = rp->RuleMatchedValue.byte)*prime_10; break; case FTDESTKIND: hash += (key->High.Kind = rp->RuleMatchedValue.byte)*prime_10; break; case FTFLOWKIND: hash += (key->FlowKind = rp->RuleMatchedValue.byte)*prime_10; break; case FTLOWINTERFACE: if (rp->push_rule) hash += (key->Low.Interface = rp->RuleMatchedValue.byte)*prime_7; else hash += (key->Low.Interface = Low->Interface); break; case FTHIINTERFACE: if (rp->push_rule) hash += (key->High.Interface = rp->RuleMatchedValue.byte)*prime_7; else hash += (key->High.Interface = High->Interface); break; case FTLOWADJACENTTYPE: if (rp->push_rule) hash += (key->Low.AdjAddrType = rp->RuleMatchedValue.byte)*prime_6; else hash += (key->Low.AdjAddrType = Low->AdjType)*prime_6; break; case FTHIADJACENTTYPE: if (rp->push_rule) hash += (key->High.AdjAddrType = rp->RuleMatchedValue.byte)*prime_6; else hash += (key->High.AdjAddrType = High->AdjType)*prime_6; break; case FTDSCODEPOINT: if (rp->push_rule) hash += (key->DSCodePoint = rp->RuleMatchedValue.byte)*prime_8; else hash += (key->DSCodePoint = bp->DSCodePoint)*prime_8; break; #if NF_ASN_ATT case FTLOWROUTEASN: key->Low.ASNMaskVal = rp->RuleMaskVal; if (rp->push_rule) hash += (key->Low.RouteASN = rp->RuleMatchedValue.trans)*prime_11; else hash += (key->Low.RouteASN = Low->nf_ASN & Maskp->trans)*prime_11; break; case FTHIROUTEASN: key->High.ASNMaskVal = rp->RuleMaskVal; if (rp->push_rule) hash += (key->High.RouteASN = rp->RuleMatchedValue.trans)*prime_11; else hash += (key->High.RouteASN = High->nf_ASN & Maskp->trans)*prime_11; break; case FTLOWROUTEPREFIX: if (rp->push_rule) hash += (key->Low.RoutePrefix = rp->RuleMatchedValue.byte)*prime_12; else hash += (key->Low.RoutePrefix = Low->nf_mask)*prime_12; break; case FTHIROUTEPREFIX: if (rp->push_rule) hash += (key->High.RoutePrefix = rp->RuleMatchedValue.byte)*prime_12; else hash += (key->High.RoutePrefix = High->nf_mask)*prime_12; break; #endif #if NF_OTHER_ATT case FTMETERID: if (rp->push_rule) hash += (key->MeterId = rp->RuleMatchedValue.byte)*prime_13; else hash += (key->MeterId = bp->ntm_interface)*prime_13; break; #endif } } #ifdef MATCH_TRACE printf("\nType,Class,Kind: %d %d %d %d %d %d %d hash=%d\n", key->PeerAddrType, key->Low.Class,key->High.Class,key->FlowClass, key->Low.Kind,key->High.Kind,key->FlowKind, hash); #endif *search_hash = hash % fthashmod; } #ifdef STACK_CHECK void dump_pat_stack(char *reason) { int j; scpos(0,24); printf("match %s: stk=", reason); for (j = 0; j != RSTKSIZ; ++j) printf("%u+%u,", a_stack[j],p_stack[j]); printf("\n LowPeer="); daddr(search_key.Low.PeerAddress); printf(", HighPeer="); daddr(search_key.High.PeerAddress); printf("\n"); } #endif #if PME_TRACE void log_PME_TRACE(void) { int ptx; for (ptx = 0; ptx <= rtx-1; ptx+=10) log_msg(LOG_INFO, 0, " %d, %d, %d, %d, %d, %d, %d, %d, %d, %d,", ruletrace[ptx], ruletrace[ptx+1], ruletrace[ptx+2], ruletrace[ptx+3], ruletrace[ptx+4], ruletrace[ptx+5], ruletrace[ptx+6], ruletrace[ptx+7], ruletrace[ptx+8], ruletrace[ptx+9]); } #endif Bit8 /* Computed attribute values for this packet */ psc,pdc,pfc, psk,pdk,pfk; int match(int forward, struct pkt *bp, struct pkt_key *from, struct pkt_key *to) { int rx, attrib, j, match, test_reqd, pal; /* Peer address length (qbytes) */ struct rule *rp; #if PME_TRACE rtx = 0; /* Initialise match trace */ #endif e_s_tos = p_s_depth = 0; test_reqd = 1; psc = pdc = pfc = psk = pdk = pfk = 0; pal = (PEER_ADDR_LEN+3)/4; for (rx = 1; rx <= c_rsz; ) { /* Search the rule table */ #if PME_TRACE if (rtx != MXTRACE) ruletrace[rtx++] = rx; else { if (tr_msgs_left[c_rtx-1] != 0) { log_msg(LOG_ERR, 0, "PME match loop, ri[%d]: rtx=%d", c_rtx, rtx); log_PME_TRACE(); --tr_msgs_left[c_rtx-1]; } quack(123); return MFAIL; } #endif rp = &c_rt[rx-1]; attrib = rp->RuleSelector; if (test_reqd && attrib != RS_NULL) { union address *Maskp = Masks[rp->RuleMaskVal].Val; if (USER_ATTRIB(attrib)) /* User variable */ attrib = user_vbls[rp->RuleSelector-FTV1]; /* Dereference it */ if (bp->PeerAddrType != AT_DUMMY) ++n_matches; if (rp->hash_tbl_index == 0) { /* Simple compare */ switch (attrib) { case FTLOWPEERADDRESS: #if QBYTE_PEER match = (from->PeerAddress.qbyte & Maskp->qbyte) == rp->RuleMatchedValue.qbyte; #else for (j = 0; j != pal; ++j) { if ((from->PeerAddress.peer32[j] & Maskp->peer32[j]) != rp->RuleMatchedValue.peer32[j]) { match = 0; break; } } match = 1; #endif break; case FTHIPEERADDRESS: #if QBYTE_PEER match = (to->PeerAddress.qbyte & Maskp->qbyte) == rp->RuleMatchedValue.qbyte; #else for (j = 0; j != pal; ++j) { if ((to->PeerAddress.peer32[j] & Maskp->peer32[j]) != rp->RuleMatchedValue.peer32[j]) { match = 0; break; } } match = 1; #endif break; case FTLOWPEERTYPE: case FTHIPEERTYPE: match = bp->PeerAddrType == rp->RuleMatchedValue.byte; if (match) pal = addr_len32[bp->PeerAddrType]; break; case FTLOWTRANSADDRESS: match = (from->TransAddress & Maskp->trans) == rp->RuleMatchedValue.trans; break; case FTHITRANSADDRESS: match = (to->TransAddress & Maskp->trans) == rp->RuleMatchedValue.trans; break; case FTLOWTRANSTYPE: case FTHITRANSTYPE: match = bp->TransAddrType == rp->RuleMatchedValue.byte; break; case FTLOWADJACENTADDRESS: match = ((from->AdjAddr_ms4 & Maskp->ma.ms4) == rp->RuleMatchedValue.ma.ms4) && ((from->AdjAddr_ls2 & Maskp->ma.ls2) == rp->RuleMatchedValue.ma.ls2); break; case FTHIADJACENTADDRESS: match = ((to->AdjAddr_ms4 & Maskp->ma.ms4) == rp->RuleMatchedValue.ma.ms4) && ((to->AdjAddr_ls2 & Maskp->ma.ls2) == rp->RuleMatchedValue.ma.ls2); break; case FTFORWARD: /* Addresses in packet order */ match = forward == rp->RuleMatchedValue.byte; break; case FTSOURCECLASS: match = (psc & Maskp->byte) == rp->RuleMatchedValue.byte; break; case FTDESTCLASS: match = (pdc & Maskp->byte) == rp->RuleMatchedValue.byte; break; case FTFLOWCLASS: match = (pfc & Maskp->byte) == rp->RuleMatchedValue.byte; break; case FTSOURCEKIND: match = (psk & Maskp->byte) == rp->RuleMatchedValue.byte; break; case FTDESTKIND: match = (pdk & Maskp->byte) == rp->RuleMatchedValue.byte; break; case FTFLOWKIND: match = (pfk & Maskp->byte) == rp->RuleMatchedValue.byte; break; case FTLOWINTERFACE: match = from->Interface == rp->RuleMatchedValue.byte; /* Kevin, 23 Nov 98 */ break; case FTHIINTERFACE: match = to->Interface == rp->RuleMatchedValue.byte; /* Kevin, 23 Nov 98 */ break; case FTLOWADJACENTTYPE: match = from->AdjType == rp->RuleMatchedValue.byte; break; case FTHIADJACENTTYPE: match = to->AdjType == rp->RuleMatchedValue.byte; break; case FTDSCODEPOINT: match = bp->DSCodePoint == rp->RuleMatchedValue.byte; break; #if NF_ASN_ATT case FTLOWROUTEASN: match = (from->nf_ASN & Maskp->trans) != rp->RuleMatchedValue.trans; break; case FTHIROUTEASN: match = (to->nf_ASN & Maskp->trans) != rp->RuleMatchedValue.trans; break; case FTLOWROUTEPREFIX: match = from->nf_mask == rp->RuleMatchedValue.byte; break; case FTHIROUTEPREFIX: match = to->nf_mask == rp->RuleMatchedValue.byte; break; #endif #if NF_OTHER_ATT case FTMETERID: match = bp->ntm_interface == rp->RuleMatchedValue.byte; break; #endif case RS_NULL: default: match = 1; /* Don't test anything */ } } else { /* Hashed compare */ union address masked_value; Bit32 hash; int ral; /* Rule address length (bytes) */ struct rule_hash_tbl *c_rh; match = 0; switch (attrib) { case FTLOWPEERADDRESS: #if QBYTE_PEER hash = (masked_value.qbyte = from->PeerAddress.qbyte & Maskp->qbyte)*prime_0; ral = 4; #else for (j = hash = 0; j != pal; ++j) hash += (masked_value.peer32[j] = from->PeerAddress.peer32[j] & Maskp->peer32[j])*primes[j]; ral = pal*4; #endif break; case FTHIPEERADDRESS: #if QBYTE_PEER hash = (masked_value.qbyte = to->PeerAddress.qbyte & Maskp->qbyte)*prime_0; ral = 4; #else for (j = hash = 0; j != pal; ++j) hash += (masked_value.peer32[j] = to->PeerAddress.peer32[j] & Maskp->peer32[j])*primes[j]; ral = pal*4; #endif break; case FTLOWPEERTYPE: case FTHIPEERTYPE: hash = (masked_value.byte = bp->PeerAddrType & Maskp->byte)*prime_1; ral = 1; break; case FTLOWTRANSADDRESS: hash = (masked_value.trans = from->TransAddress & Maskp->trans)*prime_2; ral=TRANS_ADDR_LEN; break; case FTHITRANSADDRESS: hash = (masked_value.trans = to->TransAddress & Maskp->trans)*prime_2; ral=TRANS_ADDR_LEN; break; case FTLOWTRANSTYPE: case FTHITRANSTYPE: hash = (masked_value.byte = bp->TransAddrType & Maskp->byte)*prime_3; ral = 1; break; case FTLOWADJACENTADDRESS: hash = (masked_value.ma.ms4 = from->AdjAddr_ms4 & Maskp->ma.ms4)*prime_4 + (masked_value.ma.ls2 = from->AdjAddr_ls2 & Maskp->ma.ls2)*prime_5; ral = MAC_ADDR_LEN; break; case FTHIADJACENTADDRESS: hash = (masked_value.ma.ms4 = to->AdjAddr_ms4 & Maskp->ma.ms4)*prime_4 + (masked_value.ma.ls2 = to->AdjAddr_ls2 & Maskp->ma.ls2)*prime_5; ral = MAC_ADDR_LEN; break; case FTSOURCECLASS: hash = (masked_value.byte = psc & Maskp->byte)*prime_9; ral = 1; break; case FTDESTCLASS: hash = (masked_value.byte = pdc & Maskp->byte)*prime_9; ral = 1; break; case FTFLOWCLASS: hash = (masked_value.byte = pfc & Maskp->byte)*prime_9; ral = 1; break; case FTSOURCEKIND: hash = (masked_value.byte = psk & Maskp->byte)*prime_10; ral = 1; break; case FTDESTKIND: hash = (masked_value.byte = pdk & Maskp->byte)*prime_10; ral = 1; break; case FTFLOWKIND: hash = (masked_value.byte = pfk & Maskp->byte)*prime_10; ral = 1; break; case FTLOWINTERFACE: hash = (masked_value.byte = from->Interface & Maskp->byte)*prime_7; ral = 1; break; case FTHIINTERFACE: hash = (masked_value.byte = to->Interface & Maskp->byte)*prime_7; ral = 1; break; case FTLOWADJACENTTYPE: hash = (masked_value.byte = from->AdjType & Maskp->byte)*prime_6; ral = 1; break; case FTHIADJACENTTYPE: hash = (masked_value.byte = to->AdjType & Maskp->byte)*prime_6; ral = 1; break; case FTDSCODEPOINT: hash = (masked_value.byte = bp->DSCodePoint & Maskp->byte)*prime_8; ral = 1; break; #if NF_ASN_ATT case FTLOWROUTEASN: hash = (masked_value.trans = from->nf_ASN & Maskp->trans)*prime_11; ral=TRANS_ADDR_LEN; break; case FTHIROUTEASN: hash = (masked_value.trans = to->nf_ASN & Maskp->trans)*prime_11; ral=TRANS_ADDR_LEN; break; case FTLOWROUTEPREFIX: hash = (masked_value.byte = from->nf_mask & Maskp->byte)*prime_12; ral = 1; break; case FTHIROUTEPREFIX: hash = (masked_value.byte = to->nf_mask & Maskp->byte)*prime_12; ral = 1; break; #endif #if NF_OTHER_ATT case FTMETERID: hash = (masked_value.byte = bp->ntm_interface & Maskp->byte)*prime_13; ral = 1; break; #endif case RS_NULL: default: match = 1; /* Don't test anything */ } if (!match) { c_rh = (struct rule_hash_tbl *)&c_rht[rp->hash_tbl_index]; hash %= c_rh->hashmod; #ifdef P_TRACE ptx = rx; #endif if ((rx = c_rh->hash_ent[hash]) != 0) { do { rp = &c_rt[rx-1]; if (memcmp((Bit8 *)masked_value.rule, rp->RuleMatchedValue.rule, ral) == 0) { match = 1; break; /* Matched */ } } while ((rx = rp->hash_link) != 0); } } if (match == 0) rx = c_rh->group_last; /* Not in group */ #ifdef P_TRACE printf("Hash cmp: rx=%d, att=%d, mtch=%d, m_rx=%d, act=%d, ix=%d\n", ptx,attrib, match,rx, rp->RuleAction,rp->RuleJumpIndex); #endif } } else { /* No test required */ #ifdef P_TRACE printf("No test: rx=%d, attrib=%d, action=%d, index=%d\n", rx,attrib, rp->RuleAction,rp->RuleJumpIndex); #endif test_reqd = 1; /* Didn't test for this rule */ match = 1; } if (match) { switch (rp->RuleAction) { case RA_IGNORE: #if PM_DEBUG if (c_rtx == pm_dbg) { sprintf(dbg_msg, " f=%d, rx=%d, IGNORE", forward,rx); display_msg(0,dbg_msg); } #endif return MIGNORE; case RA_RETRY: #if PM_DEBUG if (c_rtx == pm_dbg) { sprintf(dbg_msg, " f=%d, rx=%d, RETRY", forward,rx); display_msg(0,dbg_msg); } #endif return MFAIL; case RA_COUNT: case RA_COUNTPKT: if (attrib != 0 || rp->RuleMaskVal != 0) { /* Non-zero mask */ /* Count rule specified an attribute */ if (USER_ATTRIB(attrib)) /* User variable */ attrib = user_vbls[rp->RuleSelector-FTV1]; /* Deref it */ a_stack[p_s_depth] = attrib; /* Remember last matched rule */ p_stack[p_s_depth++] = rx; } #ifdef COUNT_TRACE /* if ((from->PeerAddress[2] == 4 && from->PeerAddress[3] == 33) || (to->PeerAddress[2] == 4 && to->PeerAddress[3] == 33)) */ { printf("Count: %u+%u",a_stack[0],p_stack[0]); for (j = 1; j != p_s_depth; ++j) printf(",%u+%u, ", a_stack[j],p_stack[j]); daddr(from->PeerAddress); printf(":"); dport(from->TransAddress); printf(" %s ", forward ? "->" : "<-"); daddr(to->PeerAddress); printf(":"); dport(to->TransAddress); printf("\n"); } #endif #if PM_DEBUG if (c_rtx == pm_dbg) { sprintf(dbg_msg, " f=%d, rx=%d, COUNT", forward,rx); display_msg(0,dbg_msg); } #endif return MSUC; case RA_RETURN: --e_s_tos; #ifdef STACK_CHECK if (e_s_tos < 0) { scpos(0,scr_lrow); printf("gosub oflo: depth=%d, stk=", e_s_tos); for (j = 0; j != ESTKSIZ; ++j) printf("%u,", env_stack[j]); printf("\n"); return MFAIL; } #endif #ifdef P_TRACE scpos(0,scr_lrow); printf("Return: rx=%d, attrib=%d, gosub@%d, index=%d\n", rx,rp->RuleSelector, env_stack[e_s_tos],rp->RuleJumpIndex); #endif rx = env_stack[e_s_tos] + rp->RuleJumpIndex; test_reqd = 0; /* Always return to Action */ continue; case RA_ASSIGN: case RA_ASSIGNACT: user_vbls[rp->RuleSelector-FTV1] = rp->RuleMatchedValue.byte; /* Used to be RuleJumpIndex */ /* ++rx; Bug! Nevil, 6 Mar 97 */ rx = rp->RuleJumpIndex; if (rp->RuleAction == RA_ASSIGNACT) test_reqd = 0; continue; case RA_GOSUB: case RA_GOSUBACT: env_stack[e_s_tos++] = rx; /* Save index of gosub rule */ #ifdef STACK_CHECK if (e_s_tos >= ESTKSIZ) { scpos(0,scr_lrow); printf("gosub oflo: depth=%d, stk=", e_s_tos); for (j = 0; j != e_s_tos; ++j) printf("%u,", env_stack[j]); printf("\n"); return MFAIL; } #endif rx = rp->RuleJumpIndex; if (rp->RuleAction == RA_GOSUBACT) test_reqd = 0; continue; case RA_GOTO: case RA_GOTOACT: rx = rp->RuleJumpIndex; #ifdef TESTING scpos(0,scr_lrow); printf(" GOTO %d\n", rx); #endif if (rp->RuleAction == RA_GOTOACT) test_reqd = 0; continue; case RA_PUSHTO: case RA_PUSHTOACT: if (USER_ATTRIB(attrib)) /* User variable */ attrib = user_vbls[rp->RuleSelector-FTV1]; /* Deref it */ if (attrib >= FTSOURCECLASS && attrib <= FTFLOWKIND) switch (attrib) { /* Save 'packet' computed attrib value */ case FTSOURCECLASS: psc = rp->RuleMatchedValue.byte; break; case FTDESTCLASS: pdc = rp->RuleMatchedValue.byte; break; case FTFLOWCLASS: pfc = rp->RuleMatchedValue.byte; break; case FTSOURCEKIND: psk = rp->RuleMatchedValue.byte; break; case FTDESTKIND: pdk = rp->RuleMatchedValue.byte; break; case FTFLOWKIND: pfk = rp->RuleMatchedValue.byte; break; } a_stack[p_s_depth] = attrib; /* Remember which rules matched */ p_stack[p_s_depth++] = rx; rx = rp->RuleJumpIndex; if (rp->RuleAction == RA_PUSHTOACT) test_reqd = 0; #ifdef STACK_CHECK if (p_s_depth >= RSTKSIZ) { dump_pat_stack("oflo"); return MFAIL; } #endif continue; case RA_PUSHPKTTO: case RA_PUSHPKTTOACT: if (USER_ATTRIB(attrib)) /* User variable */ attrib = user_vbls[rp->RuleSelector-FTV1]; /* Deref it */ a_stack[p_s_depth] = attrib; /* Remember which rules matched */ p_stack[p_s_depth++] = rx; rx = rp->RuleJumpIndex; if (rp->RuleAction == RA_PUSHPKTTOACT) test_reqd = 0; #ifdef STACK_CHECK if (p_s_depth >= RSTKSIZ) { dump_pat_stack("oflo"); return MFAIL; } #endif continue; case RA_POPTO: case RA_POPTOACT: --p_s_depth; /* Forget last matched rule */ rx = rp->RuleJumpIndex; if (rp->RuleAction == RA_POPTOACT) test_reqd = 0; #ifdef STACK_CHECK if (p_s_depth < 0) { dump_pat_stack("uflo"); return MFAIL; } #endif continue; default: /* Not a valid action! */ log_msg(LOG_ERR, 0, "Bad action, ruleset %d, rule %d", c_rtx,rx); #if PME_TRACE log_PME_TRACE(); #endif return MFAIL; } } else ++rx; } log_msg(LOG_ERR, 0, "Bad index, ruleset %d, rule %d", c_rtx,rx); #if PME_TRACE log_PME_TRACE(); quack(456); #endif return MFAIL; /* Not matched */ } void show_flow_hash(void) { int j, inuse_slots, entries, cl, hc_len[51], mx_cl; struct flow **sf, *f; inuse_slots = entries = 0; memset(hc_len, 0, sizeof(hc_len)); for (j = 0; j != fthashmod; ++j) { if (flow_ht[j] != NULL) { ++inuse_slots; ++entries; sf = &flow_ht[j]; f = *sf; for (cl = 1; ; ++cl, ++entries) { if ((f = f->ht_next) == (struct flow *)sf) break; } ++hc_len[cl > sizeof(hc_len)/sizeof(int) ? sizeof(hc_len)/sizeof(int)-1 : cl-1]; } } printf("current() hash performance:\n"); printf(" %d hash slots, %d in use (%.2f\%)\n", fthashmod, inuse_slots, inuse_slots*100.0/fthashmod); for (mx_cl = sizeof(hc_len)/sizeof(int)-1; mx_cl != 0; --mx_cl) if (hc_len[mx_cl] != 0) break; if (mx_cl >= 0) { ++mx_cl; printf(" %d entries in table, average %.2f entries/inuse_slot\n", entries, (float)entries/(inuse_slots == 0 ? 1 : inuse_slots)); printf(" chain lengths:"); for (j = 0; j != mx_cl; ++j) printf(" %d", hc_len[j]); printf(" (max %d)\n", mx_cl); } } struct flow *current( struct search_result *s_r, Bit32 hash, struct flow_key *search_key) { struct flow **sf; struct flow *f, *lf; Bit32 *qp, *hqp; int j; sf = &flow_ht[hash]; /* Address of hash table entry */ #ifdef MATCH_TRACE printf(" current(): cht=%Fp, sf=%Fp\n", cht,sf); #endif ++n_hash_searches; if ((f = sf[0]) == NULL) { lf = NULL; /* Empty hash chain */ ++n_hash_compares; /* Make sure compares >= searches! */ } else { do { ++n_hash_compares; for (;;) { /* Compare whole fields - data has been masked */ for (hqp = (Bit32 *)&f->fk, qp = (Bit32 *)search_key, j = 0; j != sizeof(struct flow_key)/sizeof(Bit32); ++j) { if (*hqp++ != *qp++) goto s_no_match; } return f; /* Matched */ } s_no_match: lf = f; } while ((f = lf->ht_next) != (struct flow *)sf); f = NULL; /* Loop stops with lf -> last flow in chain */ } s_r->sf = sf; s_r->lf = lf; /* Save sf and lf; we'll need them to insert a new flow */ #ifdef MATCH_TRACE printf(" current() -> sf=%Fp, lf=%Fp\n", s_r->sf,s_r->lf); #endif return f; } #if NEW_ATR int make_distrib_list( /* From pattern stack */ struct flow *f) { struct rule *rp; int j, a; struct distribution *r, *d, *ld; double N,D, UL; if (f->distrib_list != NULL) { /* $$$ */ scpos(0,scr_lrow); log_msg(LOG_ERR, 0, "Non-empty distribution list <<<"); return 0; } for (j = 0; j != p_s_depth; ++j) { if (stream_atr[a = a_stack[j]] /* Stream attribute */ && f->stdata == NULL) { if ((f->stdata = get_st_data()) != NULL) f->stdata->U = OTHER_STREAM_TIMEOUT; /* Default (cs) */ } if (dist_atr[a]) { /* Distribution-valued attribute */ #if PP_DEBUG_XX printf("make_distrib_list(1); distrib attribute a=%d\n", a); #endif rp = &c_rt[p_stack[j]-1]; if ((d = get_dist()) == NULL) return 0; memset(d, 0, sizeof(struct distribution)); d->selector = a; memcpy(d->mask_params, Masks[rp->RuleMaskVal].Val, RULE_ADDR_LEN); memcpy(d->value_params, rp->RuleMatchedValue.rule, RULE_ADDR_LEN); d->Transform = d->mask_params[0]; d->ScaleFactor = d->mask_params[1]; d->LowerLimit = (d->mask_params[2] << 8) | d->mask_params[3]; d->UpperLimit = (d->mask_params[4] << 8) | d->mask_params[5]; d->Buckets = d->value_params[0]; if (d->Buckets > MXBUCKETS) { scpos(0,scr_lrow); log_msg(LOG_ERR, 0, "Distribution with > %d buckets **", MXBUCKETS); d->Buckets = d->value_params[0] = MXBUCKETS; } d->Parameter1 = d->value_params[1]; d->Parameter2 = (d->value_params[2] << 8) | d->value_params[3]; d->Parameter3 = (d->value_params[4] << 8) | d->value_params[5]; N = d->Buckets - 1; switch (d->Transform) { case DS_LIN: D = d->UpperLimit - d->LowerLimit; d->M = N/D; break; case DS_LOG: D = log10((double)d->UpperLimit/(double)d->LowerLimit); d->M = N/D; break; case DS_DYN_REQ: d->max_val = d->lowerlim = d->LowerLimit; d->min_val = d->upperlim = d->UpperLimit; d->n_values = 0; break; } if (f->distrib_list == NULL) f->distrib_list = d; else ld->next = d; ld = d; f->distrib_bits |= 1 << d->selector-FTTOPACKETSIZE; d->fp = f; d->nrate = NULL; switch (a) { /* Special handling for particular d-v attributes */ case FTTOBITRATE: case FTFROMBITRATE: case FTTOPDURATE: case FTFROMPDURATE: if (d->RateInterval != 0) link_distrib_to_event(d); else log_msg(LOG_WARNING, 0, "Rate distribution with zero interval !!!"); break; case FTTOTCPSIZE: /* TCP attributes: */ case FTFROMTCPSIZE: /* (param1 = stream-size scale) */ case FTTOTCPTIME: /* Must build streams, */ case FTFROMTCPTIME: /* don't care about pp_type */ case FTTOTCPRATE1: case FTFROMTCPRATE1: case FTTOTCPRATE2: case FTFROMTCPRATE2: if (f->stdata != NULL) f->stdata->pp_match_reqd = 1; break; #if 0 case FTTOTRAINLENGTH: /* In train if <= UpperSize */ case FTFROMTRAINLENGTH: /* (and > LowerSize) */ #endif case FTTOTURNAROUNDTIME4: case FTFROMTURNAROUNDTIME4: break; case FTTOINTERARRIVALTIME: /* Inter-packet time attributes: */ case FTFROMINTERARRIVALTIME: /* Don't have to build streams */ case FTTOTURNAROUNDTIME1: /* Turnaround time attributes: */ case FTFROMTURNAROUNDTIME1: /* Must build streams, */ case FTTOTURNAROUNDTIME2: /* (param1 = pp_type selector) */ case FTFROMTURNAROUNDTIME2: case FTTOTURNAROUNDTIME3: case FTFROMTURNAROUNDTIME3: case FTFLOWTIME: if (f->stdata == NULL || d->PP_Type == PP_NO_TEST) break; /* No matching, just inter-packet times */ else { f->stdata->pp_match_reqd = 1; UL = /* LostPacket time (cs) */ (inv_scale(d, d->UpperLimit)/10000.0)*STR_TO_MULTIPLIER; if (UL < OTHER_STREAM_TIMEOUT) { /* Lower than default */ if (f->stdata->U == OTHER_STREAM_TIMEOUT || /* First distrib to set U */ UL > f->stdata->U) /* Max of distrib Upper values */ f->stdata->U = UL; } } if (f->fk.PeerAddrType != AT_IP4 #if V6 && f->fk.PeerAddrType != AT_IP6 #endif ) { log_msg(LOG_WARNING, 0, "PP test on non-IP flow !!!"); break; } if ((d->PP_Type & PP_TCP) == PP_TCP) { if (f->fk.TransAddrType != PT_TCP) { log_msg(LOG_WARNING, 0, "PP TCP test on non-TCP flow !!!"); break; } } else switch (d->PP_Type) { case PP_UDP_DNS: if (f->fk.Low.TransAddress != htons(WNP_DOMAIN) && f->fk.High.TransAddress != htons(WNP_DOMAIN)) { log_msg(LOG_WARNING, 0, "PP DNS test on non-DNS port (%04x->%04x) !!!", f->fk.Low.TransAddress, f->fk.High.TransAddress); break; } if (f->fk.TransAddrType != PT_UDP) { log_msg(LOG_WARNING, 0, "PP DNS test on non-UDP flow !!!"); break; } break; case PP_ICMP_ECHO: if (f->fk.TransAddrType != PT_ICMP) { log_msg(LOG_WARNING, 0, "PP ping test on non-ICMP flow !!!"); break; } break; case PP_NO_TEST: case PP_OTHER: break; case PP_ICMP_TIMSTMP: case PP_UDP_ECHO: case PP_UDP_SNMP: break; default: log_msg(LOG_WARNING, 0, "Unknown PP value for flow !!!"); break; } break; } } } return 1; } #if TCP_TESTING || TCP_PKT_TRACE || PKT_QUEUE_TEST || TCP_TAT_TRACE void print_stream_name(FILE *f, char *msg, struct stream *sp) { fprintf(f, "str(%s) ", msg); fdaddr(f, sp->sfn.SrcPeerAddr); fprintf(f, ", "); fdport(f, sp->sfn.SrcTransAddr); fprintf(f, " -> "); fdaddr(f, sp->sfn.DestPeerAddr); fprintf(f, ", "); fdport(f, sp->sfn.DestTransAddr); fprintf(f, ", TransType=%d, rs=%d: ", sp->sfn.TransAddr, sp->sfn.RuleSet); } #endif #if SCC_CHECK int check_stream_hash(struct stream *isp, struct stream *l_n_h, char *msg) { int n = 0; struct stream *sp, *lsp; Bit8 *np; int j; Bit32 hash; struct stream **sha; int sha_seen; for (np = (Bit8 *)&isp->sfn, hash = 0, j = 0; j != sizeof(isp->sfn); ++np, ++j) /* hash += *np * primes[j]; */ hash = (hash << 4) ^ (hash >> 28) ^ *np; sha = &sfht[hash % sfhashmod]; if (isp->next_hc ==(struct stream *)sha && l_n_h == isp) return; /* Just removed lone entry from hash chain */ /* if ((sp = isp) == NULL) return; */ for (sha_seen = 0, sp = isp; ; ) { if (sp->next_hc == NULL /* && !(l_n_h == isp && sp == (struct stream *)sha) */ ) { printf("check_stream_h(%s): isp=%x, lsp=%x, sp=%x\n", msg, isp, lsp, sp); return 0; } lsp = sp; if ((sp = lsp->next_hc) == isp) /* Loop stops with lsp -> last stream in chain */ break; if (sp == (struct stream *)sha) { if (sha_seen) break; /* Entry removed leaving loop from sha */ sha_seen = 1; } if (++n == 20) { printf("check_stream_h(%s): runaway next_hc chain, isp=%x\n", msg, isp); } } return 1; } #endif /* SCC_CHECK */ void terminate_stream(struct stream_data *sdp, struct stream *sp) /* Removes stream from stream hash table, frees its pktdata blocks */ { struct pktdata *pk, *npk; struct stream *np, *tp, *ltp; #if SCC_CHECK struct stream *l_n_h; #endif for (pk = sp->pq; pk != NULL; pk = npk) { npk = pk->next; /* Free stream's pktdata blocks */ if (sdp->pp_match_reqd) { /* pktdatas still in queue are considered lost! */ if (pk->direction == MFWD) ++sdp->ToLostPDUs; else ++sdp->FromLostPDUs; } free_pktdata(pk); } #if SCC_CHECK check_stream_hash(sp, NULL, "term-before"); /* ??? */ #endif np = sp->next_hc; /* Remove stream from hash chain */ tp = sp; do { ltp = tp; } while ((tp = tp->next_hc) != sp); #if SCC_CHECK l_n_h = ltp->next_hc; #endif if (ltp == np) /* Single flow in hash chain */ #if SCC_CHECK { check_stream_hash(sp, l_n_h, "term-middle"); /* ??? */ ltp->next_hc = NULL; } #else ltp->next_hc = NULL; #endif else ltp->next_hc = np; #if SCC_CHECK check_stream_hash(sp, l_n_h, "term-after"); /* ??? */ #endif } void terminate_all_streams(struct flow *fp, struct stream_data *sdp) { struct stream *sp, *lsp; #if SCC_CHECK check_stream_chains(fp, "term_all_streams: before"); #endif if ((sp = sdp->sq) != NULL) { do { lsp = sp; sp = sp->next_sp; flow_stream_stats(fp, sdp, lsp); terminate_stream(sdp, lsp); if (sp == NULL) { /* Only one stream in queue */ sdp->sq = sdp->sq_tail = NULL; } else { sdp->sq = sp; sp->prev_sp = NULL; } free_stream(lsp); } while (sp != NULL); } #if SCC_CHECK check_stream_chains(fp, "term_all_streams: after"); #endif } void flow_stream_stats(struct flow *fp, struct stream_data *std, struct stream *sp) /* Computes flow-based statistics for terminating streams */ { struct distribution *d; double delta; for (d = fp->distrib_list; d != NULL; d = d->next) { switch (d->selector) { case FTTOFLOWOCTETS: bump_dist(d, sp->ToOctets); break; case FTFROMFLOWOCTETS: bump_dist(d, sp->FromOctets); break; case FTTOFLOWPDUS: bump_dist(d, sp->ToPDUs); break; case FTFROMFLOWPDUS: bump_dist(d, sp->FromPDUs); break; case FTFLOWTIME: /* Use highest estimate for total stream time */ if (sp->ToSeen && sp->FromSeen) { delta = sp->LastFromTime > sp->LastToTime ? sp->LastFromTime : sp->LastToTime; delta -= sp->StrFirstTime; } else if (sp->ToSeen) /* Only 'to' packets seen */ delta = sp->LastToTime - sp->StrFirstTime; else if (sp->FromSeen) /* Only 'from' packets seen */ delta = sp->LastFromTime - sp->StrFirstTime; else continue; /* Didn't see any packets! */ bump_dist(d, delta); /* us */ break; } } } void tcp_stream_stats(struct flow *fp, struct stream_data *std, struct stream *sp) /* Computes TCP-based statistics for terminating streams */ { struct distribution *d; Bit32 bytes, b2; double delta; int n; #if TRACE_NEG_BLP double ToBLF, FromBLF; int bad_blp, hdr_printed; #endif #if TRACE_NEG_BLP FromBLF = ToBLF = 1.0; if (str->ToTCPSentOctets.low != 0 && str->ToTCPSeqOctets.low != 0) ToBLF = (double)str->ToTCPSentOctets.low / (double)str->ToTCPSeqOctets.low; if (str->FromTCPSentOctets.low != 0 && str->FromTCPSeqOctets.low != 0) FromBLF = (double)str->FromTCPSentOctets.low/ (double)str->FromTCPSeqOctets.low; bad_blp = ToBLF < BLP_FRACT || FromBLF < BLP_FRACT; /* if ((str->flags_seen & 0x0C) == 0x0C) /* Seen both SYNs */ hdr_printed = 0; #endif #if TRACE_NEG_BLP if (bad_blp) { if (!hdr_printed) { fprintf(pt_file, "BAD BLP: " "ToBLF=%.2f [%lu %lu], FromBLF=%.2f [%lu %lu], str=%lu <<<<<\n", ToBLF, str->ToTCPSentOctets.low, str->ToTCPSeqOctets.low, FromBLF, str->FromTCPSentOctets.low,str->FromTCPSeqOctets.low, str); hdr_printed = 1; } print_pkt_trace(pt_file, "+++", sp, "LT=%lu, sp=%lu", sp->LastTime, sp); } #endif for (d = fp->distrib_list; d != NULL; d = d->next) { switch (d->selector) { case FTTOTCPSIZE: case FTTOTCPTIME: case FTTOTCPRATE1: case FTTOTCPRATE2: bytes = sp->ToSeqOctets > sp->FromAckOctets ? sp->ToSeqOctets : sp->FromAckOctets; break; case FTFROMTCPSIZE: case FTFROMTCPTIME: case FTFROMTCPRATE1: case FTFROMTCPRATE2: bytes = sp->FromSeqOctets > sp->ToAckOctets ? sp->FromSeqOctets : sp->ToAckOctets; break; } if (bytes == 0) continue; /* No data for stream! */ if (d->LowerSize != 0 || d->UpperSize != 0) { n = scale(d, bytes); /* Divide by 10**SizeScale */ #define TCPTIME_TEST 0 #if TCPTIME_TEST if (d->selector == FTFROMTCPTIME) printf(" TCPFromTime: FromB=%lu|ToB=%lu, n=%u, L=%u|U=%u\n", bytes,b2, n, d->LowerSize,d->UpperSize); #endif if (d->LowerSize != 0 && n <= (Bit32)d->LowerSize) continue; if (d->UpperSize != 0 && n > (Bit32)d->UpperSize) continue; } /* Use highest estimate for total stream time */ if (sp->ToSeen && sp->FromSeen) { delta = sp->LastFromTime > sp->LastToTime ? sp->LastFromTime : sp->LastToTime; delta -= sp->StrFirstTime; } else if (sp->ToSeen) /* Only 'to' packets seen */ delta = sp->LastToTime - sp->StrFirstTime; else if (sp->FromSeen) /* Only 'from' packets seen */ delta = sp->LastFromTime - sp->StrFirstTime; else continue; /* Didn't see any packets! */ switch (d->selector) { case FTTOTCPSIZE: case FTFROMTCPSIZE: bump_dist(d, bytes); /* Bytes */ break; case FTTOTCPTIME: case FTFROMTCPTIME: bump_dist(d, delta); /* us */ break; case FTFROMTCPRATE1: case FTFROMTCPRATE2: if (delta == 0) delta = 1; /* Don't divide by 0 */ bump_dist(d, bytes*1000000.0/delta); /* B/s */ break; case FTTOTCPRATE1: case FTTOTCPRATE2: if (delta == 0) delta = 1; /* Don't divide by 0 */ bump_dist(d, bytes*1000000.0/delta); /* B/s */ break; } } #if TCP_TESTING print_stream_name(stdout, "fin", sp); printf(": active=%d, To %lu/%lu, From %lu/%lu", str->active_streams, str->ToTCPSentOctets.low,str->ToTCPSeqOctets.low, str->FromTCPSentOctets.low,str->FromTCPSeqOctets.low); printf("\n");) #endif } static last_ntm_LastTime = 0; void check_other_idle_streams(struct flow *fp) /* Checks timeouts for streams other than tcp pp_match. Streams time out after being idle for OTHER_STREAM_TIMEOUT cs */ { struct stream *sp, *nsp, *iq_tail; Bit32 uc, IDLE_time, pkts; struct stream_data *sdp; double av_int; Bit32 sto, inact; int keep, n_streams, n_kept, t_bkwd; if (fp->ntm_LastTime == last_ntm_LastTime) /* centiseconds */ return; /* Nothing can have aged out yet */ last_ntm_LastTime = fp->ntm_LastTime; sdp = fp->stdata; uc = sdp->U; /* Centiseconds */ if (fp->ntm_LastTime <= uc) return; /* Haven't run long enough for streams to time out yet */ IDLE_time = fp->ntm_LastTime - uc; /* centiseconds */ n_streams = n_kept = t_bkwd = 0; sp = sdp->sq; iq_tail = sdp->sq_tail; #if SCC_CHECK check_stream_chains(fp, "check_o_i_s: before"); #endif while (sp != NULL && sp != iq_tail) { ++n_streams; nsp = sp->next_sp; /* Next stream to check */ if (sp->LastTime <= IDLE_time) { /* Inactive, can we recover it? */ if (sp->FirstTime < sp->LastTime) { /* Was FirstTime > LastTime ! Nevil, 25 Feb 01 */ if ((pkts = sp->ToPDUs + sp->FromPDUs) == 0) pkts = 1; av_int = (double)(sp->LastTime - sp->FirstTime)/(double)pkts; sto = av_int*STO_MULTIPLIER; inact = fp->ntm_LastTime - sp->LastTime; if (keep = (inact < sto)) ++n_kept; } else { /* Trace files can have odd time jumps in them */ keep = 0; /* No data to improve sto estimate */ ++t_bkwd; } sdp->sq = sp->next_sp; /* Remove stream from queue */ if (sp->next_sp != NULL) sp->next_sp->prev_sp = sp->prev_sp; else sdp->sq_tail = sp->prev_sp; if (keep) { if (sdp->sq == NULL) { /* Add it to tail of queue */ sdp->sq = sp; sp->prev_sp = NULL; } else { sdp->sq_tail->next_sp = sp; sp->prev_sp = sdp->sq_tail; } sp->next_sp = NULL; sdp->sq_tail = sp; } else { /* Timed-out stream */ flow_stream_stats(fp, sdp, sp); terminate_stream(sdp, sp); --sdp->active_streams; free_stream(sp); #if SCC_CHECK check_stream_chains(fp, "check_o_i_s: after 1"); #endif } } else if (sp->LastTime > IDLE_time) break; /* Later streams can't have timed out yet */ sp = nsp; } /* printf("other_idle(): flow %x, IDLE %u, %u streams, %u kept, %u with first > last\n", fp, IDLE_time, n_streams, n_kept, t_bkwd); */ #if SCC_CHECK check_stream_chains(fp, "check_o_i_s: after 2"); #endif } void check_tcp_idle_streams(struct flow *fp, int gc_call) /* Checks timeouts for flow's IDLE and terminating streams. Called by garbage collector for *active* flows (gc_call) to check for all terminating streams */ { struct stream *sp, *nsp, *iq_tail; Bit32 uc, FIN_WAIT_time, IDLE_time, pkts; struct stream_data *sdp; double av_int; Bit32 sto, inact; /* struct distribution *d; Bit32 bytes, b2; double delta; Bit32 n; */ int term, keep; #if CR_TRACE int s_rcv = 0; #endif if (fp->ntm_LastTime == last_ntm_LastTime) /* centiseconds */ return; /* Nothing can have aged out yet */ last_ntm_LastTime = fp->ntm_LastTime; sdp = fp->stdata; uc = sdp->U; /* Centiseconds */ if (fp->ntm_LastTime <= uc) return; /* Haven't run long enough for streams to time out yet */ IDLE_time = fp->ntm_LastTime - uc; FIN_WAIT_time = fp->ntm_LastTime - TCP_STREAM_TIMEOUT; /* centiseconds */ sp = sdp->sq; iq_tail = sdp->sq_tail; while (sp != NULL && sp != iq_tail) { nsp = sp->next_sp; /* Next stream to check */ if ((term = sp->terminating && sp->LastTime <= FIN_WAIT_time) || sp->LastTime <= IDLE_time) { if (term) keep = 0; /* Terminated */ else { /* Inactive, can we recover it? */ if (sp->FirstTime < sp->LastTime) { if ((pkts = sp->ToPDUs + sp->FromPDUs) == 0) pkts = 1; av_int = (double)(sp->LastTime - sp->FirstTime)/(double)pkts; sto = av_int*STO_MULTIPLIER; inact = fp->ntm_LastTime - sp->LastTime; keep = inact < sto; } else { /* Trace files can have odd time jumps in them */ keep = 0; /* No data to improve sto estimate */ } } sdp->sq = sp->next_sp; /* Remove stream from queue */ if (sp->next_sp != NULL) sp->next_sp->prev_sp = sp->prev_sp; else sdp->sq_tail = sp->prev_sp; if (keep) { if (sdp->sq == NULL) { /* Add it to tail of queue */ sdp->sq = sp; sp->prev_sp = NULL; } else { sdp->sq_tail->next_sp = sp; sp->prev_sp = sdp->sq_tail; } sp->next_sp = NULL; sdp->sq_tail = sp; } else { /* Timed-out stream */ flow_stream_stats(fp, sdp, sp); terminate_stream(sdp, sp); --sdp->active_streams; if (sp->terminating) { if (fp->distrib_list) tcp_stream_stats(fp, sdp, sp); --sdp->terminating_streams; } free_stream(sp); #if CR_TRACE ++s_rcv; #endif } } else { if ((!gc_call && sp->LastTime > IDLE_time) || (gc_call && sp->terminating && sp->LastTime > FIN_WAIT_time)) { break; /* Later streams can't have timed out yet */ } } sp = nsp; } #if CR_TRACE if (s_rcv != 0) { if (s_rcv < 10) s_rcv = '.'; else if (s_rcv < 100) s_rcv = 'o'; else s_rcv = '*'; printf("%c", s_rcv); fflush(stdout); } #endif } int tcp_stream_update(struct flow *fp, struct stream *sp, const struct pktdata *pd) /* Updates TCP state for stream sp of flow fp. pd points to packet in stream queue (if we saved it), or to packet in free queue (if we didn't) */ { struct stream_data *sdp = fp->stdata; int flow_order = pd->direction; Bit32 tlen = pd->pi.tcp.d_len; Bit32 Bytes; # if TCP_TRACE static Bit32 sf_count = 0; # endif # if TCP_PKT_TRACE struct trace_rec *sftp; # endif #define MX_SEQ_DELTA 65535 /* Max TCP window size */ /* #define MX_SEQ_DELTA 29200 /* 20 Ethernet TCP packets */ if (pd->pi.tcp.flags & tcp_FlagRST) sp->flags_seen |= RSTseen; else if (flow_order == MREV) { if (pd->pi.tcp.flags & tcp_FlagSYN) sp->flags_seen |= FromSYNseen; if (pd->pi.tcp.flags & tcp_FlagFIN) sp->flags_seen |= FromFINseen; } else { if (pd->pi.tcp.flags & tcp_FlagSYN) sp->flags_seen |= ToSYNseen; if (pd->pi.tcp.flags & tcp_FlagFIN) sp->flags_seen |= ToFINseen; } if (!sp->terminating && (sp->flags_seen & RSTseen || (sp->flags_seen & BothFINseen) == BothFINseen)) { sp->terminating = 1; ++fp->stdata->terminating_streams; } if (!sp->terminating) { if (flow_order == MREV) { if (sp->FromSeq_seen) { if (scmp32_ge(pd->pi.tcp.exp_seq, sp->FromLastSeq)) { /* 'Normal' */ Bytes = pd->pi.tcp.exp_seq - sp->FromLastSeq; if (Bytes <= MX_SEQ_DELTA) { add_Bit32_to_counter64(sdp->FromTCPSeqOctets, Bytes); sp->FromSeqOctets += Bytes; } else { # if TRACE_SEQ_DECR print_pkt_trace(pt_file, "FROM-SEQ", sp, ": SeqBytes=%lu <<<", Bytes); # endif } sp->FromLastSeq = pd->pi.tcp.exp_seq; } else ++sdp->FromTCPDecrSeq; /* Backward jump in seq */ } else if (pd->pi.tcp.exp_seq != 0) { add_Bit32_to_counter64(sdp->FromTCPSeqOctets, tlen); /* tlen data bytes in this packet */ sp->FromSeqOctets = tlen; sp->FromLastSeq = pd->pi.tcp.exp_seq; sp->FromSeq_seen = 1; } if (sp->ToAck_seen) { if (scmp32_ge(pd->pi.tcp.ack, sp->ToLastAck)) { Bytes = pd->pi.tcp.ack - sp->ToLastAck; add_Bit32_to_counter64(sdp->ToTCPAckOctets, Bytes); sp->ToAckOctets += Bytes; } sp->ToLastAck = pd->pi.tcp.ack; } else if (pd->pi.tcp.ack != 0) { add_Bit32_to_counter64(sdp->ToTCPAckOctets, tlen); /* Assume these will be acked (!) */ sp->ToAckOctets = tlen; sp->ToLastAck = pd->pi.tcp.ack; sp->ToAck_seen = 1; } if (tlen > 0) { add_Bit32_to_counter64(sdp->FromTCPLenOctets, tlen); } #define SU_DEBUG 0 #if SU_DEBUG printf(" MREV(%u): seq=%u, len=%u, Ack=%u -> FSS=%d, FLen=%u, Fseq=%u, TAS=%d, Tack=%u\n", sdp, pd->pi.tcp.exp_seq, tlen, pd->pi.tcp.ack, sp->FromSeq_seen, sdp->FromTCPLenOctets.low, sdp->FromTCPSeqOctets.low, sp->ToAck_seen, sdp->ToTCPAckOctets.low); #endif } else { /* MFWD */ if (sp->ToSeq_seen) { if (scmp32_ge(pd->pi.tcp.exp_seq, sp->ToLastSeq)) { /* 'Normal' */ Bytes = pd->pi.tcp.exp_seq - sp->ToLastSeq; if (Bytes <= MX_SEQ_DELTA) { add_Bit32_to_counter64(sdp->ToTCPSeqOctets, Bytes); sp->ToSeqOctets += Bytes; } else { # if TRACE_SEQ_DECR print_pkt_trace(pt_file, "TO-SEQ", sp, ": SeqBytes=%lu <<<", Bytes); # endif } sp->ToLastSeq = pd->pi.tcp.exp_seq; } else ++sdp->ToTCPDecrSeq; /* Backward jump in seq */ } else if (pd->pi.tcp.exp_seq != 0) { add_Bit32_to_counter64(sdp->ToTCPSeqOctets, tlen); /* tlen data bytes in this packet */ sp->ToSeqOctets = tlen; sp->ToLastSeq = pd->pi.tcp.exp_seq; sp->ToSeq_seen = 1; } if (sp->FromAck_seen) { if (scmp32_ge(pd->pi.tcp.ack, sp->FromLastAck)) { Bytes = pd->pi.tcp.ack - sp->FromLastAck; add_Bit32_to_counter64(sdp->FromTCPAckOctets, Bytes); sp->FromAckOctets += Bytes; } sp->FromLastAck = pd->pi.tcp.ack; } else if (pd->pi.tcp.ack != 0) { add_Bit32_to_counter64(sdp->FromTCPAckOctets, tlen); sp->FromAckOctets = tlen; /* Assume these will be acked (!) */ sp->FromLastAck = pd->pi.tcp.ack; sp->FromAck_seen = 1; } if (tlen > 0) { add_Bit32_to_counter64(sdp->ToTCPLenOctets, tlen); } #if SU_DEBUG printf("MFWD(%u): seq=%u, len=%u, Ack=%u -> TSS=%d, TLen=%u, Tseq=%u, FAS=%d, Fack=%u\n", sdp, pd->pi.tcp.exp_seq, tlen, pd->pi.tcp.ack, sp->ToSeq_seen, sdp->ToTCPLenOctets.low, sdp->ToTCPSeqOctets.low, sp->FromAck_seen, sdp->FromTCPAckOctets.low); #endif } } check_tcp_idle_streams(fp, 0); # if 0 if (flow_order == MFWD) { sp->FromWindow = pd->pi.tcp.send_win = ntohs(bp->pi.tcp.th.window); pd->pi.tcp.recv_win = sp->ToWindow; } else { sp->ToWindow = pd->pi.tcp.send_win = ntohs(bp->pi.tcp.th.window); pd->pi.tcp.recv_win = sp->FromWindow; } # endif # if TCP_PKT_TRACE sftp = &sp->sft.tr[sp->sft.sf_trx]; sftp->pkt_nbr = ++sp->sft.pkt_nbr; sftp->time_int = (pd->ts - sp->sft.last_pkt_time) / 1000.0; sp->sft.last_pkt_time = pd->ts; sftp->TCPlen = tlen; sftp->flags = pd->tcp.flags; sftp->s_flags = sp->flags_seen; if (flow_order == MREV) { sftp->maxSeq = sp->FromMaxSeq; } else { sftp->s_flags |= sess_Forward; sftp->maxSeq = sp->ToMaxSeq; } sftp->Seq = ThisSeq; if (++sp->sft.sf_trx == TRACE_SZ) sp->sft.sf_trx = 0; # endif # if TCP_TRACE fprintf(stderr, "%5d %9.5f %6.3f %s %s%s len=%4d, ack=%lu, seq=%lu, flg=%04x\n", sp->sf_nbr, microseconds(bp->arrival_time)/1000000.0, (microseconds(bp->arrival_time)- (flow_order == MREV ? sp->at.LastToTime : sp->at.LastFromTime) )/1000000.0, flow_order == MREV ? "<-" : "->", flow_order != sp->at.LastDirection ? " " : "=", sp->at.TimeOKrev ? "R" : " ", tlen, ThisAck, ThisSeq, ThisFlags ); # endif } FILE *pt_file; #define ptf_title "ntm.trace" # if TCP_PKT_TRACE void print_pkt_trace(FILE *f, char *id, /* Identifies invocation */ struct stream *sfp, char *fmt, ...) /* Supporting detail */ { va_list ap; int j,k, seq, mx_seq; char *dirn; struct sf_trace *sftp = &sfp->sft; struct trace_rec *trp; print_stream_name(f, id, sfp); va_start(ap, fmt); vfprintf(f, fmt, ap); va_end(ap); fprintf(f, "\n"); for (j = sftp->sf_trx, k = 0; k != TRACE_SZ; ++k) { trp = &sftp->tr[j]; if (trp->pkt_nbr != 0 && !(trp->s_flags & sess_PktPrinted)) { seq = trp->Seq; mx_seq = trp->maxSeq; if (trp->s_flags & sess_Forward) { dirn = "->"; if (trp->s_flags & ToSYNseen) { seq -= sfp->ToFirstSeq; mx_seq -= sfp->ToFirstSeq; } } else { /* Back */ dirn = "<-"; if (trp->s_flags & FromSYNseen) { seq -= sfp->FromFirstSeq; mx_seq -= sfp->FromFirstSeq; } } fprintf(f, /* " %5d %7.3f %s %5d %c%c%c%c %lu %lu\n", */ " %02x %02x %5d %7.3f %s %5d %c%c%c%c %lu %lu\n", trp->flags, trp->s_flags, trp->pkt_nbr, trp->time_int/1000.0, dirn, trp->TCPlen, trp->flags & tcp_FlagACK ? 'A' : ' ', trp->flags & tcp_FlagRST ? 'R' : ' ', trp->flags & tcp_FlagSYN ? 'S' : ' ', trp->flags & tcp_FlagFIN ? 'F' : ' ', seq, mx_seq); trp->s_flags |= sess_PktPrinted; /* Only print each packet once */ } if (++j == TRACE_SZ) j = 0; } fflush(f); } # endif #define TCP_TAT_TRACE 0 #if TCP_TAT_TRACE void print_tcp_pktdata(struct pktdata *pd) { printf("%.3f %s %c%c%c%c%c %lu:%lu(%u) ack %lu, pkt %u\n", pd->ts/1000.0, pd->direction == MFWD ? "->" : "<-", pd->pi.tcp.flags & tcp_FlagACK ? 'A' : ' ', pd->pi.tcp.flags & tcp_FlagPUSH ? 'P' : ' ', pd->pi.tcp.flags & tcp_FlagRST ? 'R' : ' ', pd->pi.tcp.flags & tcp_FlagSYN ? 'S' : ' ', pd->pi.tcp.flags & tcp_FlagFIN ? 'F' : ' ', pd->pi.tcp.exp_seq - pd->pi.tcp.d_len, pd->pi.tcp.exp_seq, pd->pi.tcp.d_len, pd->pi.tcp.ack, pd->pi.tcp.pkt_nbr); /* pd->pi.tcp.send_win, pd->pi.tcp.recv_win); */ } void print_tcp_list(struct pktdata *pd, char *msg) { int n = 0; printf(" %s:\n",msg); for (; pd != NULL; pd = pd->next) { printf(" "); print_tcp_pktdata(pd); } } #endif /* TCP_TAT_TRACE */ #define PKT_QUEUE_TEST 0 #if PKT_QUEUE_TEST int q_len(struct pktdata *pq) { struct pktdata *fp; int n; for (n = 0, fp = pq; fp != NULL; fp = fp->next) ++n; return n; } #endif int tcp_turnaround(int *pp_type, double *delta_us, struct flow *fp, struct stream *sp, struct pktdata *tp) /* Compares incoming TCP packet with those in stream's packet queue */ { struct pktdata *pd, *lpd, *npd; int acked, resent, delete, prev_delete, overlap; Bit32 a,b,c,d; int flow_order = tp->direction; int delta_OK = 0; Bit8 TaT_OK = 0; /* tp = new packet pd = queue of old packets, kept in ascending exp_seq order For opposite-direction pkts: if ack matches exp_seq, set TaT_OK flags Delete pkts with seq <= ack For same-direction packets: Test for overlaps in seq: Overlap -> bump lostPDUs Save new pkt if it has data, SYN or FIN, i.e. if it affects exp_ack */ #if PKT_QUEUE_TEST int ql = q_len(sp->pq); if (ql != sp->pq_len) { print_stream_name(stdout, "tcp_ta(1)", sp); printf(" ql=%d, pq_len=%d\n", ql,sp->pq_len); } #endif b = tp->pi.tcp.exp_seq; a = b - tp->pi.tcp.d_len; /* For overlap test */ acked = resent = prev_delete = 0; *pp_type = PP_NO_TEST; for (pd = sp->pq; pd != NULL; ) { delete = 0; if (pd->direction != flow_order) { /* Opposite-direction packet */ if (tp->pi.tcp.ack == pd->pi.tcp.exp_seq && /* Ack matched */ !acked) { /* Only ack the oldest copy! */ if (pd->pi.tcp.flags & tcp_FlagSYN) { if ((tp->pi.tcp.flags & (tcp_FlagACK | tcp_FlagSYN)) == (tcp_FlagACK | tcp_FlagSYN) ) { TaT_OK |= PP_OK_SYNACK; } else if ((tp->pi.tcp.flags & (tcp_FlagACK | tcp_FlagRST)) == (tcp_FlagACK | tcp_FlagRST) ) { TaT_OK |= PP_OK_SYNRST; } } if (pd->pi.tcp.d_len != 0) { /* Acking sent data */ if (prev_delete) /* Acking 2nd (or later) packet of queue */ TaT_OK |= PP_OK_MULTI; else if (pd->next == NULL) /* Only one pkt in queue */ TaT_OK |= PP_OK_SINGLE; else TaT_OK |= PP_OK_INGROUP; /* Other pkts in queue */ } if (TaT_OK) /* Compute turnaround time */ *delta_us = tp->ts - pd->ts; delta_OK = 1; # if TCP_TAT_TRACE == 1 if (TaT_OK & PP_OK_SYNRST) { print_stream_name(stdout, "SYN/RST", sp); printf("\n bump %s, Tat=%.3f ms, recv_win=%d, OK=%02x\n ", TaT, TaT/1000.0, pd->pi.tcp.recv_win, TaT_OK); print_tcp_pktdata(tp); print_tcp_list(sp->pq, "TCP(match)"); printf("\n"); } # endif acked = delete = 1; /* Delete acked packet */ } else if (tp->pi.tcp.ack > pd->pi.tcp.exp_seq) delete = 1; /* Delete pkts with exp_seq < ack */ } else { /* Same-direction packet */ /* a---b New packet */ /* c---------d Old packet */ d = pd->pi.tcp.exp_seq; c = d - pd->pi.tcp.d_len; overlap = pd->pi.tcp.flags & tp->pi.tcp.flags & (tcp_FlagSYN | tcp_FlagFIN) ? /* Two SYN/FIN packets */ a <= d && b >= c : /* SYN/FIN packets may not overlap at all */ a < d && b > c; /* Data packets may overlap by one byte */ if (overlap) { # if TCP_TAT_TRACE == 2 printf("--- c=%lu,d=%lu, a=%lu,b=%lu, overlap=%d, resent=%d\n", c,d, a,b, overlap, a ==c && b == d); print_stream_name(stdout, "TCP overlap", sp); printf("\n "); print_tcp_pktdata(tp); print_tcp_list(sp->pq.packet, "Overlap queue"); printf("\n"); # endif if (b >= d) { /* Don't count 'lower-part-only' overlap */ if (!resent) { /* Only count resent packet once! */ if (pd->direction == MFWD) ++fp->stdata->ToLostPDUs; else ++fp->stdata->FromLostPDUs; resent = (a == c && b == d); } } } } if (delete) { prev_delete = 1; /* At least one packet deleted from queue */ npd = pd->next; if (pd == sp->pq) sp->pq = npd; else lpd->next = npd; --sp->pq_len; free_pktdata(pd); pd = npd; #if PKT_QUEUE_TEST ql = q_len(sp->pq); if (ql != sp->pq_len) { print_stream_name(stdout, "tcp_ta(2)", sp); printf(" ql=%d, pq_len=%d\n", ql,sp->pq_len); } #endif } else { lpd = pd; pd = pd->next; } } #if PKT_QUEUE_TEST ql = q_len(sp->pq); if (ql != sp->pq_len) { print_stream_name(stdout, "tcp_ta(3)", sp); printf(" ql=%d, pq_len=%d\n", ql,sp->pq_len); } #endif if (tp->pi.tcp.d_len != 0 || /* Enqueue packet if it affects seq */ tp->pi.tcp.flags & (tcp_FlagSYN | tcp_FlagFIN)) { if ((pd = sp->pq) == NULL) { /* Empty queue */ sp->pq = tp; tp->next = NULL; } else if (pd->direction == flow_order && tp->pi.tcp.exp_seq < pd->pi.tcp.exp_seq) { sp->pq = tp; tp->next = pd; /* Enqueue at head */ } else for (; ; pd = pd->next) { if ((npd = pd->next) == NULL) { /* Enqueue at tail */ pd->next = tp; tp->next = NULL; break; } if (pd->direction == flow_order && tp->pi.tcp.exp_seq < npd->pi.tcp.exp_seq) { pd->next = tp; tp->next = npd; break; } } if (++sp->pq_len > sp->mx_pq_len) { #if CR_TRACE printf("!"); fflush(stdout); #endif for (pd = sp->pq; pd != NULL; ) { /* Discard pkts from head of queue */ if (pd->direction == MFWD) { ++fp->stdata->ToLostPDUs; ++fp->stdata->ToPQOverflows; } else { ++fp->stdata->FromLostPDUs; ++fp->stdata->FromPQOverflows; } npd = pd->next; free_pktdata(pd); if (--sp->pq_len <= sp->mx_pq_len) break; pd = npd; } sp->pq = npd; } # if TCP_TAT_TRACE == 3 if (sp->pq_len >= MX_PQ_LEN_TCP) { print_stream_name(stdout, "TCP queue too big", sp); printf("\n "); print_tcp_pktdata(tp); print_tcp_list(sp->pq.packet, "Queue"); printf("\n"); } # endif #if PKT_QUEUE_TEST ql = q_len(sp->pq); if (ql != sp->pq_len) { print_stream_name(stdout, "tcp_ta(4)", sp); printf(" ql=%d, pq_len=%d\n ", ql,sp->pq_len); # if TCP_TAT_TRACE == 3 print_tcp_pktdata(tp); print_tcp_list(sp->pq, "Queue"); printf("\n"); # endif } #endif } else free_pktdata(tp); /* Don't keep packets which don't affect seq */ *pp_type = PP_TCP | TaT_OK; return delta_OK; } void show_stream_hash(int display) { int j, inuse_slots, entries, cl, hc_len[51], mx_cl; struct stream **sha, *sp; #define STREAM_KINDS 1 #if STREAM_KINDS double av_int; Bit32 sto, inact, pkts, keep; int str_count[50]; /* One for each FlowKind value */ int str_1m[50], str_2m[50], str_5m[50], expired[50]; memset(str_count, 0, sizeof(str_count)); memset(str_1m, 0, sizeof(str_1m)); memset(str_2m, 0, sizeof(str_2m)); memset(str_5m, 0, sizeof(str_5m)); memset(expired, 0, sizeof(expired)); #endif inuse_slots = entries = 0; memset(hc_len, 0, sizeof(hc_len)); for (j = 0; j != sfhashmod; ++j) { if (sfht[j] != NULL) { ++inuse_slots; ++entries; sha = &sfht[j]; sp = *sha; for (cl = 1; ; ++cl, ++entries) { #if STREAM_KINDS ++str_count[sp->flowp->fk.FlowKind]; if (sp->flowp->ntm_LastTime - sp->FirstTime > 6000) { if (sp->FirstTime < sp->LastTime) { if ((pkts = sp->ToPDUs + sp->FromPDUs) == 0) pkts = 1; av_int = (double)(sp->LastTime - sp->FirstTime)/(double)pkts; sto = av_int*STO_MULTIPLIER; inact = sp->flowp->ntm_LastTime - sp->LastTime; keep = inact < sto; } else { /* Trace files can have odd time jumps in them */ keep = 0; /* No data to improve sto estimate */ } if (!keep) ++expired[sp->flowp->fk.FlowKind]; } if (sp->flowp->ntm_LastTime - sp->FirstTime > 30000) ++str_5m[sp->flowp->fk.FlowKind]; /* 300 s */ else if (sp->flowp->ntm_LastTime - sp->FirstTime > 12000) ++str_2m[sp->flowp->fk.FlowKind]; /* 120 s */ else if (sp->flowp->ntm_LastTime - sp->FirstTime > 6000) ++str_1m[sp->flowp->fk.FlowKind]; /* 60 s */ #endif if ((sp = sp->next_hc) == (struct stream *)sha) break; /* Loop stops with lsp -> last stream in chain */ } ++hc_len[cl > sizeof(hc_len)/sizeof(int) ? sizeof(hc_len)/sizeof(int)-1 : cl-1]; } } if (display) { printf("find_stream_helper() hash performance:\n"); printf(" %d hash slots, %d in use (%.2f\%)\n", sfhashmod, inuse_slots, inuse_slots*100.0/sfhashmod); for (mx_cl = sizeof(hc_len)/sizeof(int)-1; mx_cl >=0; --mx_cl) if (hc_len[mx_cl] != 0) break; if (mx_cl >= 0) { ++mx_cl; printf(" %d entries in table, average %.2f entries/inuse_slot\n", entries, (float)entries/(inuse_slots == 0 ? 1 : inuse_slots)); printf(" chain lengths:"); for (j = 0; j != mx_cl; ++j) printf(" %d", hc_len[j]); printf(" (max %d)\n", mx_cl); } #if STREAM_KINDS for (mx_cl = sizeof(str_count)/sizeof(int)-1; mx_cl >=0; --mx_cl) if (str_count[mx_cl] != 0) break; if (mx_cl >= 0) { ++mx_cl; printf(" Streams per FlowKind value: count 1m 2m 5m expired\n"); for (j = 0; j != mx_cl; ++j) printf(" %2d %7d %7d %6d %6d %5d\n", j, str_count[j], str_1m[j], str_2m[j], str_5m[j], expired[j]); } #endif } else log_msg(LOG_INFO, 0, "stream_hash: %d slots, %d in use, %d entries", sfhashmod, inuse_slots, entries); } struct stream *find_stream_helper(int *make_new, struct flow *fp, int flow_order, struct pkt *bp) { struct stream_name sfn; Bit32 *qp, *hqp; int j; Bit32 hash; struct stream **sha; struct stream *sp, *lsp, *nsp; struct stream_data *sdp; #if SCC_CHECK struct stream *l_n_h; #endif sfn.TransType = bp->TransAddrType; sfn.RuleSet = fp->FlowRuleSet; sfn.rsv1 = 0; /* It's included in the hash */ if (flow_order == MREV) { #if QBYTE_PEER sfn.SrcPeerAddr = bp->High.PeerAddress.qbyte; sfn.DestPeerAddr = bp->Low.PeerAddress.qbyte; #else memcpy(sfn.SrcPeerAddr, &bp->High.PeerAddress.byte, sizeof(sfn.SrcPeerAddr)); memcpy(sfn.DestPeerAddr, &bp->Low.PeerAddress.byte, sizeof(sfn.DestPeerAddr)); #endif if (bp->TransAddrType == PT_TCP || bp->TransAddrType == PT_UDP) { sfn.SrcTransAddr = bp->High.TransAddress; sfn.DestTransAddr = bp->Low.TransAddress; } else /* No port numbers */ sfn.SrcTransAddr = sfn.DestTransAddr = 0; } else { #if QBYTE_PEER sfn.SrcPeerAddr = bp->Low.PeerAddress.qbyte; sfn.DestPeerAddr = bp->High.PeerAddress.qbyte; #else memcpy(sfn.SrcPeerAddr, &bp->Low.PeerAddress.byte, sizeof(sfn.SrcPeerAddr)); memcpy(sfn.DestPeerAddr, &bp->High.PeerAddress.byte, sizeof(sfn.DestPeerAddr)); #endif if (bp->TransAddrType == PT_TCP || bp->TransAddrType == PT_UDP) { sfn.SrcTransAddr = bp->Low.TransAddress; sfn.DestTransAddr = bp->High.TransAddress; } else /* No port numbers */ sfn.SrcTransAddr = sfn.DestTransAddr = 0; } for (qp = (Bit32 *)&sfn, hash = 0, j = 0; j != sizeof(sfn)/sizeof(Bit32); ++qp, ++j) { /* hash += *qp * primes[j]; */ hash = (hash << 4) ^ (hash >> 28) ^ *qp; /* Rotating hash performs slightly better */ } sha = &sfht[hash % sfhashmod]; /* Address of stream hash table entry */ if ((sp = *sha) == NULL) lsp = NULL; /* Empty hash chain */ else { for (;;) { for (hqp = (Bit32 *)&sp->sfn, qp = (Bit32 *)&sfn, j = 0; j != sizeof(sfn)/sizeof(Bit32); ++j) { if (*hqp++ != *qp++) goto s_no_match; } break; /* Matched */ s_no_match: lsp = sp; if ((sp = lsp->next_hc) == (struct stream *)sha) { sp = NULL; break; /* Loop stops with lsp -> last stream in chain */ } } } if (sp != NULL) /* Existing stream */ *make_new = 0; else if (*make_new) { /* New stream */ nsp = get_stream(); if (nsp != NULL) { memset(nsp, 0, sizeof(struct stream)-sizeof(sfn)); memcpy(&nsp->sfn, &sfn, sizeof(sfn)); nsp->flowp = fp; /* Remember which flow the stream belongs to! */ #if SCC_CHECK check_stream_chains(fp, "f_s_helper: before"); if (lsp != NULL) check_stream_hash(lsp, l_n_h = lsp->next_hc, "fre-help-before"); #endif if (lsp == NULL) *sha = nsp; /* Add stream to hash chain */ else lsp->next_hc = nsp; nsp->next_hc = (struct stream *)sha; #if SCC_CHECK if (lsp != NULL) check_stream_hash(nsp, l_n_h, "term-after"); #endif sdp = fp->stdata; if (sdp->sq == NULL) /* Add stream to tail of flow's stream queue */ sdp->sq = nsp; /* First stream for this flow */ else { sdp->sq_tail->next_sp = nsp; nsp->prev_sp = sdp->sq_tail; } sdp->sq_tail = nsp; ++sdp->n_streams; ++sdp->active_streams; #if SCC_CHECK check_stream_chains(fp, "f_s_helper: after"); #endif } return nsp; } return sp; } #define OTHER_COUNT_VAL 30 int other_count = OTHER_COUNT_VAL; struct stream *find_stream(struct flow *fp, int *flow_order, struct pkt *bp, double at_us) /* Given incoming packet bp, finds stream from flow's stream data */ { int rfo, make_new; struct stream *sp; struct stream_data *sdp; #if PP_DEBUG int j, new = 0; #endif #if DNS_ROOT_DEBUG || DNS_ROOT_DEBUG_2 int ifo = *flow_order; /* Save PME's order */ #endif if ((bp->invalid_addrs & (INV_PEER | INV_TRANS)) != 0) return NULL; /* Didn't get IP adresses & ports */ sdp = fp->stdata; /* This flow's stream_data */ #if 0 rfo = *flow_order == MREV ? MFWD : MREV; /* Search for reverse flow_order */ make_new = 0; sp = find_stream_helper(&make_new, fp, rfo, bp); /* Reverse FO */ if (sp != NULL) { *flow_order = rfo; } else { make_new = 1; /* OK to make new stream */ sp = find_stream_helper(&make_new, fp, *flow_order, bp); /* FO */ } #endif /* *Provided* that rulesets test carefully to establish proper direction for flows, we can safely assume that match() will tell us the correct direction for a stream match. Which means we don't have to test both directions! Oct 01 */ make_new = 1; /* OK to make new stream */ sp = find_stream_helper(&make_new, fp, *flow_order, bp); /* FO */ if (!make_new) { /* Packet for existing stream */ #if DNS_ROOT_DEBUG_2 log_msg(LOG_WARNING, -1, /* Keep log file open */ "Existing stream %x: ifo=%d, fo=%d, This flow ...", sp, ifo, *flow_order); dump_flow(fp); dump_bp(bp); dump_sp_key(sp); log_msg(LOG_WARNING, 0, ""); /* Close log file */ #endif #if SCC_CHECK check_stream_chains(fp, "f_stream: before"); #endif if (sp->prev_sp != NULL) /* Remove stream from queue */ sp->prev_sp->next_sp = sp->next_sp; else sdp->sq = sp->next_sp; if (sp->next_sp != NULL) sp->next_sp->prev_sp = sp->prev_sp; else sdp->sq_tail = sp->prev_sp; --sdp->active_streams; if (sp->flowp != fp) { log_msg(LOG_WARNING, -1, /* Keep log file open */ "Stream %x could belong to more than one flow !!", sp); log_msg(LOG_WARNING, -1, "This flow ..."); dump_flow(fp); dump_bp(bp); log_msg(LOG_WARNING, -1, "Other flow ..."); dump_flow(sp->flowp); dump_sp_key(sp); log_msg(LOG_WARNING, 0, /* Close log file */ "Meter abandoning this stream <<<<<"); terminate_stream(sdp, sp); /* Kill it (don't update distribs) */ --sdp->active_streams; free_stream(sp); /* Give back it's memory */ #if 0 "Meter won't let this flow build streams <<<<<"); */ quack(911); /* Debugger breakpoint */ free_st_data(sdp); /* Don't let this flow build streams */ fp->stdata = NULL; #endif return NULL; } #if SCC_CHECK check_stream_chains(fp, "f_stream: middle"); #endif if (sdp->sq == NULL) { /* Add it to tail of queue */ sdp->sq = sp; sp->prev_sp = NULL; } else { sdp->sq_tail->next_sp = sp; sp->prev_sp = sdp->sq_tail; } sp->next_sp = NULL; sdp->sq_tail = sp; ++sdp->active_streams; #if SCC_CHECK check_stream_chains(fp, "f_stream: after"); #endif } else /* First packet for this stream */ if (sp != NULL) { /* We did get a stream block */ #if DNS_ROOT_DEBUG_2 log_msg(LOG_WARNING, -1, /* Keep log file open */ "New stream %x: ifo=%d, fo=%d, This flow ...", sp, ifo, *flow_order); dump_flow(fp); dump_bp(bp); dump_sp_key(sp); log_msg(LOG_WARNING, 0, ""); /* Close log file */ #endif if (sp->flowp != fp) { log_msg(LOG_WARNING, -1, /* Keep log file open */ "New stream %x could belong to more than one flow !!", sp); log_msg(LOG_WARNING, -1, "This flow ..."); dump_flow(fp); dump_bp(bp); log_msg(LOG_WARNING, -1, "Other flow ..."); dump_flow(sp->flowp); dump_sp_key(sp); log_msg(LOG_WARNING, 0, /* Close log file */ "Meter abandoning this stream <<<<<"); quack(912); /* Debugger breakpoint */ terminate_stream(sdp, sp); /* Kill it (don't update distribs) */ --sdp->active_streams; free_stream(sp); /* Give back it's memory */ #if 0 "Meter won't let this flow build streams <<<<<"); free_st_data(sdp); /* Don't let this flow build streams */ fp->stdata = NULL; #endif return NULL; } if (bp->PeerAddrType == AT_IP4 # if V6 || bp->PeerAddrType == AT_IP6 # endif ) { sp->mx_pq_len = bp->TransAddrType == PT_TCP ? MX_PQ_LEN_TCP : MX_PQ_LEN_OTHER; } sp->FirstTime = fp->ntm_LastTime; sp->StrFirstTime = at_us; #if PP_DEBUG new = 1; #endif if (sdp->active_streams > sdp->mx_active_streams) sdp->mx_active_streams = sdp->active_streams; # if TCP_TRACE sp->sf_nbr = ++sf_count; # endif # if TCP_PKT_TRACE sp->sft.last_pkt_time = at_us; sp->sft.pkt_nbr = sp->sft.sf_trx = 0; memset(sp->sft.tr, 0, sizeof(sp->sft.tr)); # endif # if TCP_TESTING print_stream_name(stdout, "start", sp); printf("\n"); # endif } if (!sdp->pp_match_reqd || (bp->PeerAddrType != AT_IP4 # if V6 && bp->PeerAddrType != AT_IP6 # endif ) || bp->TransAddrType != PT_TCP) { /* Not a tcp-matched flow, try to free some idle streams */ /* Used to do this before initialising mx_pq_len !!! */ if (sdp->n_streams % 10 == 0) /* Don't do this too often */ check_other_idle_streams(fp); } # if DNS_ROOT_DEBUG log_msg(LOG_WARNING, -1, /* Keep log file open */ "Flow %d: ifo=%d, fo=%d, new=%d, sp=%x, pq_len=%d", flow_nbr(fp), ifo, *flow_order, make_new, sp, sp->pq_len); dump_sp_key(sp); log_msg(LOG_WARNING, 0, ""); /* Close log file */ # endif return sp; } #endif /* NEW_ATR */ struct flow *create(struct search_result *s_r, struct flow_key *search_key, struct pkt *bp) { struct flow *f, *ff, *lf; Bit32 fx; Bit32 rs_first_flow; struct flow *pf; #if NEW_ATR || defined(MATCH_TRACE) int j; #endif if (FloodMode == TV_TRUE) /* Running in flood mode */ return NULL; /* Don't garbage collect for every new flow! */ if ((fx = alloc_flow_nbr()) == 0) { /* Find a flow data row */ bump_noflowpkts(bp->ntm_interface); return NULL; /* No unused rows in flow table */ } f = &fa[fx-1]; #if FLOW_INDEX flow_ix[fx-1] = f; /* 1-org */ #endif ++nflows; /* Nbr of active accounting flows */ ++FlowsCreated; ++ri[c_rtx-1].ri_FlowRecords; /* 1-org */ if (f == s_r->lf) { /* Garbage collector recovered the last flow of the hash chain! (Yes, it can happen) */ if ((ff = s_r->sf[0]) == NULL) { lf = NULL; /* Empty hash chain */ } else { do { lf = ff; } while ((ff = lf->ht_next) != (struct flow *)s_r->sf); } /* Loop stops with lf -> last flow in chain */ s_r->lf = lf; } f->ht_next = (struct flow *)(s_r->sf); /* Link f into hash chain */ if (s_r->lf == NULL) { /* Empty hash chain */ s_r->sf[0] = f; ++n_hash_ents; } else (s_r->lf)->ht_next = f; rs_first_flow = ri[c_rtx-1].ri_flow_chain; /* Link f into rs chain */ if (rs_first_flow == 0) { /* First flow for this ruleset */ f->rs_next = 0; ri[c_rtx-1].ri_flow_chain = fx; } else if (fx < rs_first_flow) { /* Add to front of chain */ f->rs_next = rs_first_flow; ri[c_rtx-1].ri_flow_chain = fx; } else { /* Link into ruleset chain */ pf = f; do { --pf; } while (pf->FlowRuleSet != c_rtx); f->rs_next = pf->rs_next; pf->rs_next = fx; } # if RS_CH_DEBUG check_rs_chain(c_rtx, "create"); # endif memcpy(&f->fk, /* Copy values from search_key */ search_key, sizeof(struct flow_key)); #ifdef MATCH_TRACE printf("new flow, hash=%u : f=%Fp\n", search_hash,f); printf("Count: %u+%u",a_stack[0],p_stack[0]); for (j = 1; j != p_s_depth; ++j) printf(",%u+%u",a_stack[j],p_stack[j]); printf(": "); daddr(search_key->Low.PeerAddress); printf("->"); daddr(search_key->High.PeerAddress); printf("\n"); #endif #if NEW_ATR f->distrib_list = NULL; f->distrib_bits = 0; f->stdata = NULL; make_distrib_list(f); setzero64(f->LastUpOctets); setzero64(f->LastUpPDUs); setzero64(f->LastDownOctets); setzero64(f->LastDownPDUs); #endif /* NEW_ATR */ setzero64(f->UpOctets); setzero64(f->UpPDUs); setzero64(f->DownOctets); setzero64(f->DownPDUs); f->FlowRuleSet = c_rtx; #if NF_CISCO_DATA f->LastTime = f->FirstTime = bp->dFirst; #elif LFAP_METER f->LastTime = f->FirstTime = bp->FirstTime; #else f->FirstTime = centiseconds(bp->arrival_time); #endif return f; } #if NEW_ATR static double pwr_of_10[10] = { 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001, 0.000000001 }; Bit32 scale(struct distribution *d, double y) { double x; x = y*pwr_of_10[d->SizeScale > 9 ? 9 : d->SizeScale]; return (Bit32)floor(x); } double inv_scale(struct distribution *d, Bit16 j) { return ((double)j)/pwr_of_10[d->ScaleFactor > 9 ? 9 : d->ScaleFactor]; } void bump_dist(struct distribution *d, double y) { double x, T; int n; Bit32 ix; if (d->Transform == DS_DYN_REQ && d->n_values <= d->Buckets) { if (d->n_values == d->Buckets) { /* Too many actual values, start using bins */ Bit32 values[MXBUCKETS+1]; double R, N,D; int m, j; R = d->max_val - d->min_val; /* Leave room at both ends */ m = d->min_val - 0.125*R; /* May be negative */ d->LowerLimit = m < d->lowerlim ? d->lowerlim : m; d->UpperLimit = d->max_val + 0.25*R; N = d->Buckets - 1; D = d->UpperLimit - d->LowerLimit; d->M = N/D; #if 0 /* +-+ */ { char *strmov(char *d, char *s); /* +-+ */ char nbr[20], buf[2000], *bp; log_msg(LOG_WARNING, -1, "DD: lower=%d, upper=%d, min_val=%d, max_val=%d, Lower=%d, Upper=%d", d->lowerlim,d->upperlim, d->min_val,d->max_val, d->LowerLimit, d->UpperLimit); for (bp = buf, j = 0; j != d->Buckets; ++j) { sprintf(nbr," %d", d->counts[j]); bp = strmov(bp, nbr); } *bp = '\0'; log_msg(LOG_WARNING, -1, /* %% */ " counts=%s", buf); } #endif memcpy(values, d->counts, sizeof(d->counts)); memset(d->counts, 0, sizeof(d->counts)); for (j = 0; j != d->Buckets; ++j) { ix = values[j]; if (ix <= d->LowerLimit) n = 0; else if (ix > d->UpperLimit) n = d->Buckets; else { T = ((double)(ix - d->LowerLimit)) * d->M; n = 1 + (int)floor(T); } ++d->counts[n]; } d->n_values = d->Buckets+1; } else { /* d->n_values < d->Buckets */ ix = y*pwr_of_10[d->ScaleFactor > 9 ? 9 : d->ScaleFactor]; if (ix < d->min_val && ix >= d->lowerlim) d->min_val = ix; if (ix > d->max_val && ix <= d->upperlim) d->max_val = ix; d->counts[d->n_values++] = ix; d->non_empty = 1; return; } } x = y*pwr_of_10[d->ScaleFactor > 9 ? 9 : d->ScaleFactor]; if (x <= (double)d->LowerLimit) n = 0; else if (x > (double)d->UpperLimit) { n = d->Buckets; } else switch (d->Transform) { case DS_LIN: case DS_DYN_REQ: T = (x - (double)d->LowerLimit) * d->M; n = 1 + (int)floor(T); break; case DS_LOG: T = log10(x/(double)d->LowerLimit) * d->M; n = 1 + (int)floor(T); break; } ++d->counts[n]; d->non_empty = 1; } void other_stream_update(struct flow *fp, struct stream *sp, struct pktdata *tp) /* Checks for timed-out pktdatas, adds tp to tail of sp's pq */ { double reply_timeout; struct pktdata *pd, *lpd, *npd; reply_timeout = tp->ts - fp->stdata->U*10000.0; /* U centiseconds */ # if DNS_ROOT_DEBUG log_msg(LOG_WARNING, -1, /* Keep log file open */ "other_st_ud(): flow=%d, sp=%x, pq_len=%d, mx_len=%d, tp->ts=%f, U=%f", flow_nbr(fp), sp, sp->pq_len,sp->mx_pq_len, tp->ts, fp->stdata->U); log_msg(LOG_WARNING, 0, ""); /* Close log file */ #endif lpd = NULL; for (pd = sp->pq; pd != NULL; ) { /* Look for pkts to discard */ if (sp->pq_len >= sp->mx_pq_len || /* Too many packets in queue */ pd->ts < reply_timeout) { /* Older than upper limit */ if (pd->direction == MFWD) ++fp->stdata->ToLostPDUs; else ++fp->stdata->FromLostPDUs; # if DNS_ROOT_DEBUG log_msg(LOG_WARNING, 0, "Lost packet: flow=%d, sp=%x, pq_len=%d, mx_len=%d, r_to=%f, pd->ts=%f", flow_nbr(fp), sp, sp->pq_len,sp->mx_pq_len, reply_timeout, pd->ts); #endif if (sp->pq_len >= sp->mx_pq_len) { if (pd->direction == MFWD) ++fp->stdata->ToPQOverflows; else ++fp->stdata->FromPQOverflows; } --sp->pq_len; npd = pd->next; if (lpd == NULL) sp->pq = npd; else lpd->next = npd; free_pktdata(pd); pd = npd; } else { lpd = pd; pd = pd->next; } } if (lpd == NULL) sp->pq = tp; /* Add new pkt to tail of queue */ else lpd->next = tp; ++sp->pq_len; /* Was missing, causing pktdatas to be wrongly discarded as lost packets. Nevil, 27 Oct 00 */ tp->next = NULL; } #endif /* NEW_ATR */ #define CHECK_UPTIME_DECR 1 #if CHECK_UPTIME_DECR Bit32 lll_Time = 0, lll_good = 0, lll_bad = 0; #endif #define LOG_DNS_PKTS 0 #if LOG_DNS_PKTS FILE *dns_log = NULL; void log_dns(struct flow *fp, struct pkt *bp, double at_us) { if (dns_log == NULL) { if ((dns_log = fopen("dns.log", "w")) == NULL) log_msg(LOG_ERR, 1, "Couldn't open dns.log <<<<<"); } fprintf(dns_log, "%013.6f %d %d ", at_us/1000000.0, fp->fk.FlowKind, fp->fk.FlowClass); if (bp->pi.udp.dns.p0 & dns_resp) /* DNS response */ #if QBYTE_PEER fdaddr(dns_log, ntohl(bp->High.PeerAddress.qbyte)); #else fdaddr(dns_log, ntohl(*(Bit32 *)&bp->High.PeerAddress)); #endif else /* DNS request */ #if QBYTE_PEER fdaddr(dns_log, ntohl(bp->Low.PeerAddress.qbyte)); #else fdaddr(dns_log, ntohl(*(Bit32 *)&bp->Low.PeerAddress)); #endif fprintf(dns_log, " %04x %02x%02x\n", bp->pi.udp.dns.ident, bp->pi.udp.dns.p0,bp->pi.udp.dns.p1); } #endif void count(struct flow *fp, int flow_order, struct pkt *bp) { struct search_result *s_r; Bit32 ThisUptime; #if NEW_ATR struct stream *sp = NULL; struct pktdata *pd, *lpd; struct distribution *d; int tlen; Bit16 ThisFlags; Bit32 ThisAck, ThisSeq; double at_us, delta_us, iat_delta_us; int delta_OK = 0, /* delta_us is a valid turanaround time */ pp_type = PP_NO_TEST; /* Packet-Pair type which produced delta_us */ int in_train; #endif # if DNS_ROOT_DEBUG int matched = 0; # endif #if NF_CISCO_DATA if (flow_order == MREV) { add_Bit32_to_counter64(fp->DownOctets,bp->dOctets); add_Bit32_to_counter64(fp->DownPDUs,bp->dPkts); } else { add_Bit32_to_counter64(fp->UpOctets,bp->dOctets); add_Bit32_to_counter64(fp->UpPDUs,bp->dPkts); } if (bp->dFirst < fp->FirstTime) fp->FirstTime = bp->dFirst; if (bp->dLast > fp->LastTime) fp->LastTime = bp->dLast; fp->ntm_LastTime = uptime_cs(); /* NetFlowMet meter time! */ #elif LFAP_METER if (flow_order == MREV) { add_Bit32_to_counter64(fp->DownOctets,bp->dOctets_sent); add_Bit32_to_counter64(fp->DownPDUs,bp->dPackets_sent); add_Bit32_to_counter64(fp->UpOctets,bp->dOctets_recvd); add_Bit32_to_counter64(fp->UpPDUs,bp->dPackets_recvd); } else { add_Bit32_to_counter64(fp->UpOctets,bp->dOctets_sent); add_Bit32_to_counter64(fp->UpPDUs,bp->dPackets_sent); add_Bit32_to_counter64(fp->DownOctets,bp->dOctets_recvd); add_Bit32_to_counter64(fp->DownPDUs,bp->dPackets_recvd); } /* done in create(...); fp->FirstTime = bp->FirstTime; */ fp->LastTime = bp->LastTime; #else if (flow_order == MREV) { add_Bit32_to_counter64(fp->DownOctets,bp->p_len); incr_counter64(fp->DownPDUs); } else { add_Bit32_to_counter64(fp->UpOctets,bp->p_len); incr_counter64(fp->UpPDUs); } fp->ntm_LastTime = centiseconds(bp->arrival_time); /* Also sets LastTime (see flowhash.h) */ #endif /* NF_CISCO_DATA */ #if CHECK_UPTIME_DECR if (fp->ntm_LastTime < lll_Time) { log_msg(LOG_WARNING, 0, "LastTime jump backward: if=%d, lll=%u, Last=%u, dif=%d, good=%u, bad=%u", bp->Low.Interface, lll_Time, fp->ntm_LastTime, lll_Time-fp->ntm_LastTime, lll_good, ++lll_bad); } else ++lll_good; lll_Time = fp->ntm_LastTime; #endif #if NEW_ATR && !NF_CISCO_DATA && !LFAP_METER /* Can see individual packets */ at_us = microseconds(bp->arrival_time); if (fp->stdata != NULL && /* Flow has stream_data (>= 1 stream attrib) */ (sp = find_stream(fp, &flow_order, bp, at_us)) != NULL) { sp->LastTime = fp->ntm_LastTime; /* Time last stream pkt was seen */ if (fp->stdata->pp_match_reqd) { #if PP_DEBUG_XX log_msg(LOG_WARNING, 0, "count(1) stream to match: sp=%x, fp=%x, order=%d", sp, fp, flow_order); #endif if (bp->TransAddrType == PT_TCP) { if ((pd = get_pktdata()) != NULL) { ThisFlags = ntohs(bp->pi.tcp.th.flags); tlen = bp->pi.tcp.tcp_len - tcp_dataoffset(ThisFlags); ThisSeq = ntohl(bp->pi.tcp.th.seqnum); ThisAck = ntohl(bp->pi.tcp.th.acknum); pd->ts = at_us; pd->pkt_len = bp->p_len; pd->pp_type = PP_TCP; pd->direction = flow_order; pd->pi.tcp.ack = ThisAck; pd->pi.tcp.exp_seq = ThisSeq + tlen; if (ThisFlags & (tcp_FlagSYN | tcp_FlagFIN)) ++pd->pi.tcp.exp_seq; /* SYN and FIN consume one seq nbr */ pd->pi.tcp.d_len = tlen; pd->pi.tcp.pkt_nbr = ++sp->pkt_nbr; pd->pi.tcp.flags = ThisFlags & tcp_FlagALL; delta_OK = tcp_turnaround(&pp_type, &delta_us, fp, sp, pd); tcp_stream_update(fp, sp, pd); #if CR_TRACE if (delta_OK) { printf(" %c%02X ", flow_order == MFWD ? '+' : '-', pp_type); fflush(stdout); } #endif } } else if (bp->TransAddrType == PT_UDP) { if (bp->High.TransAddress == htons(WNP_DOMAIN) || bp->Low.TransAddress == htons(WNP_DOMAIN)) { #if PP_DEBUG_XX log_msg(LOG_WARNING, 0, " count(2) DNS pkt: sp=%x, info_sz=%d, ident=%x, info=%02x%02x", sp, bp->pktinfo_sz, bp->pi.udp.dns.ident, bp->pi.udp.dns.p0,bp->pi.udp.dns.p1); #endif #if LOG_DNS_PKTS log_dns(fp,bp,at_us); #endif pp_type = PP_UDP_DNS; if (bp->pi.udp.dns.p0 & dns_resp) { /* DNS response */ #if PP_DEBUG_XX log_msg(LOG_WARNING, 0, " count(3) DNS response: sp=%x", sp); #endif for (pd = sp->pq; pd != NULL; pd = pd->next) { if (pd->pi.udp.dns.ident == bp->pi.udp.dns.ident && pd->direction != flow_order) { delta_us = at_us - pd->ts; if (pd == sp->pq) sp->pq = pd->next; else lpd->next = pd->next; --sp->pq_len; # if DNS_ROOT_DEBUG log_msg(LOG_WARNING, 0, " --- DNS response matched, stream %x, pq_len=%d", sp, sp->pq_len); matched = 1; #endif free_pktdata(pd); delta_OK = 1; break; } lpd = pd; #if PP_DEBUG_XX if (delta_OK) log_msg(LOG_WARNING, 0," count(4) DNS match: sp=%x", sp); #endif } if (pd == NULL) { /* Unrequested response; count as lost */ if (flow_order == MFWD) ++fp->stdata->ToLostPDUs; else ++fp->stdata->FromLostPDUs; } # if DNS_ROOT_DEBUG if (!matched) log_msg(LOG_WARNING, 0, " ??? DNS response NOT matched, stream %x, pq_len=%d", sp, sp->pq_len); #endif } else { /* DNS query */ #if PP_DEBUG_XX log_msg(LOG_WARNING, 0, " count(3) DNS query: sp=%x", sp); #endif if ((pd = get_pktdata()) != NULL) { pd->ts = at_us; pd->pkt_len = bp->p_len; pd->direction = flow_order; pd->pi.udp.dns.ident = bp->pi.udp.dns.ident; # if DNS_ROOT_DEBUG log_msg(LOG_WARNING, 0, " +++ DNS request queued, stream %x, pq_len=%d,", sp, sp->pq_len); #endif other_stream_update(fp, sp, pd); } } } /* else if (fp->fk.High.TransAddress[1] != .. Other UDP pairs */ } else if (bp->TransAddrType == PT_ICMP) { #if PP_DEBUG_XX log_msg(LOG_WARNING, 0, "count(5) ICMP pkt: sp=%x, info_sz=%d, ident=%04x, sequence=%04x", sp, bp->pktinfo_sz, bp->pi.icmp.ident, bp->pi.icmp.sequence); #endif if (bp->Low.TransAddress == 0) { /* Echo response */ #if PP_DEBUG_XX if (bp->pi.icmp.sequence != 0) log_msg(LOG_WARNING, 0, " count(6) ICMP echo response: sp=%x, info_sz=%d, ident=%04x, sequence=%04x, dirn=%d", sp, bp->pktinfo_sz, bp->pi.icmp.ident, bp->pi.icmp.sequence, flow_order); quack(76); #endif pp_type = PP_ICMP_ECHO; for (pd = sp->pq; pd != NULL; pd = pd->next) { #if PP_DEBUG_XX log_msg(LOG_WARNING, 0, " count(7) ICMP queue: ident=%04x, sequence=%04x, dirn=%d", pd->pi.icmp.ident, pd->pi.icmp.sequence, pd->direction); #endif if (pd->pi.icmp.ident == bp->pi.icmp.ident && pd->pi.icmp.sequence == bp->pi.icmp.sequence && pd->direction != flow_order) { #if PP_DEBUG_XX log_msg(LOG_WARNING, 0, " count(8) ICMP echo response MATCHED <<<<<<<: sp=%x", sp); #endif delta_us = at_us - pd->ts; if (pd == sp->pq) sp->pq = pd->next; else lpd->next = pd->next; --sp->pq_len; free_pktdata(pd); delta_OK = 1; break; } lpd = pd; } } else if (bp->Low.TransAddress == 8) { /* Echo request */ #if PP_DEBUG_XX if (bp->pi.icmp.sequence != 0) log_msg(LOG_WARNING, 0, " count(9) ICMP echo request: sp=%x, info_sz=%d, ident=%04x, sequence=%04x, dirn=%d", sp, bp->pktinfo_sz, bp->pi.icmp.ident, bp->pi.icmp.sequence, flow_order); #endif pp_type = PP_ICMP_ECHO; if ((pd = get_pktdata()) != NULL) { pd->ts = at_us; pd->pkt_len = bp->p_len; pd->direction = flow_order; pd->pi.icmp.ident = bp->pi.icmp.ident; pd->pi.icmp.sequence = bp->pi.icmp.sequence; other_stream_update(fp, sp, pd); } } /* else if (fp->fk.High.TransAddress[1] == .. Other ICMP pairs */ } /* else if (bp->TransAddrType == .. other protocols */ } else { /* Not pp matching */ } } #if PP_DEBUG_XX if (fp->stdata) log_msg(LOG_WARNING, 0, " count(10): order=%d, match_rq=%d, pp_type=%d, delta_OK=%d", flow_order, fp->stdata->pp_match_reqd, pp_type, delta_OK); #endif for (d = fp->distrib_list; d != NULL; d = d->next) { /* Update distribs */ if (flow_order == MREV) { switch (d->selector) { case FTFROMPACKETSIZE: bump_dist(d, bp->p_len); break; case FTFROMINTERARRIVALTIME: if (sp != NULL && sp->FromSeen) { iat_delta_us = at_us - sp->LastFromTime; bump_dist(d, iat_delta_us); } else if (fp->stdata && fp->stdata->FromSeen) { iat_delta_us = at_us - fp->stdata->LastFromTime; bump_dist(d, iat_delta_us); } break; case FTTOTURNAROUNDTIME1: case FTTOTURNAROUNDTIME2: case FTTOTURNAROUNDTIME3: /* case FTTOTURNAROUNDTIME4: */ #if PP_DEBUG_XX if (delta_OK) log_msg(LOG_WARNING, 0, " count(11) MREV: delta_OK=%d, pp_type=%02X, d->PP_type=%02X, count=%02X", delta_OK, pp_type, d->PP_Type, d->PP_Type & pp_type ); #endif if (delta_OK) { if ((bp->TransAddrType == PT_TCP && d->PP_Type & pp_type & ~PP_TCP) || /* Bits in common */ (bp->TransAddrType != PT_TCP && d->PP_Type == pp_type)) { /* Exact match */ bump_dist(d, delta_us); } #if PP_DEBUG_XX log_msg(LOG_WARNING, 0, " count(12): counted, attrib=%d\n", d->selector); #endif } break; /* case FTFROMTRAINLENGTH: */ case FTFROMTURNAROUNDTIME4: if (d->UpperSize != 0 && bp->p_len > d->UpperSize) in_train = 0; else if (d->LowerSize != 0 && bp->p_len <= d->LowerSize) in_train = 0; else in_train = 1; if (d->last_was_in_train) { if (in_train) ++d->trainlength; else { /* End of train */ bump_dist(d, d->trainlength); d->trainlength = 0; } } d->last_was_in_train = in_train; break; case FTTCPDATA: pp_type = PP_TCP; /* ??????? Do we need this for REV as well ????? */ break; } } else { /* MFWD */ switch (d->selector) { case FTTOPACKETSIZE: bump_dist(d, bp->p_len); break; case FTTOINTERARRIVALTIME: if (sp != NULL && sp->ToSeen) { iat_delta_us = at_us - sp->LastToTime; bump_dist(d, iat_delta_us); } else if (fp->stdata && fp->stdata->ToSeen) { iat_delta_us = at_us - fp->stdata->LastToTime; bump_dist(d, iat_delta_us); } break; case FTFROMTURNAROUNDTIME1: case FTFROMTURNAROUNDTIME2: case FTFROMTURNAROUNDTIME3: /* case FTFROMTURNAROUNDTIME4: */ #if PP_DEBUG_XX if (delta_OK) log_msg(LOG_WARNING, 0, " count(13) MFWD: delta_OK=%d, pp_type=%02X, d->PP_type=%02X, count=%02X", delta_OK, pp_type, d->PP_Type, d->PP_Type & pp_type ); #endif if (delta_OK) { if ((bp->TransAddrType == PT_TCP && d->PP_Type & pp_type & ~PP_TCP) || /* Bits in common */ (bp->TransAddrType != PT_TCP && d->PP_Type == pp_type)) { /* Exact match */ bump_dist(d, delta_us); } } break; /* case FTTOTRAINLENGTH: */ case FTTOTURNAROUNDTIME4: if (d->UpperSize != 0 && bp->p_len > d->UpperSize) in_train = 0; else if (d->LowerSize != 0 && bp->p_len <= d->LowerSize) in_train = 0; else in_train = 1; if (d->last_was_in_train) { if (in_train) ++d->trainlength; else { /* End of train */ bump_dist(d, d->trainlength); d->trainlength = 0; } } d->last_was_in_train = in_train; break; } } } if (flow_order == MREV) { /* Set new LastTimes after updating distributions */ if (sp != NULL) { sp->LastFromTime = at_us; sp->FromSeen = 1; ++sp->FromPDUs; sp->FromOctets += bp->p_len; } else if (fp->stdata) { fp->stdata->LastFromTime = at_us; fp->stdata->FromSeen = 1; } } else { /* MFWD */ if (sp != NULL) { sp->LastToTime = at_us; sp->ToSeen = 1; ++sp->ToPDUs; sp->ToOctets += bp->p_len; } else if (fp->stdata) { fp->stdata->LastToTime = at_us; fp->stdata->ToSeen = 1; } } #endif /* NEW_ATR && !NF_CISCO_DATA Individual packets */ } #if 0 @@@@@@@@@ Found old stream on very first test! @@@@@@@@@@ This flow ... flow 80c6270, PeerAddrType=1, TransAddrType=17 Source PeerAddress=0.180.0.2, TransAddress=0 Dest PeerAddress=0.1.0.1, TransAddress=53 f_s_r(): stream 818cbb8 found, rev chain flow 80a5f30 flow 80a5f30, PeerAddrType=1, TransAddrType=17 Source PeerAddress=0.1.0.1, TransAddress=0 Dest PeerAddress=0.180.0.2, TransAddress=53 #endif struct pkt_key k1 = { 0,180,0,2 }, k2 = { 0,1,0,1 }; int bad_flow(struct pkt *bp) { if (bp->TransAddrType == PT_ICMP) return 0; if (memcmp(&bp->Low.PeerAddress.byte, &k1.PeerAddress, PEER_ADDR_LEN) == 0 && memcmp(&bp->High.PeerAddress.byte, &k2.PeerAddress, PEER_ADDR_LEN) == 0) return 1; if (memcmp(&bp->High.PeerAddress.byte, &k1.PeerAddress, PEER_ADDR_LEN) == 0 && memcmp(&bp->Low.PeerAddress.byte, &k2.PeerAddress, PEER_ADDR_LEN) == 0) return 1; return 0; } void pkt_monitor(struct pkt *bp) { int x, r; struct rtinf_rec *rip; struct flow_key sd_key, ds_key; Bit32 sd_hash, ds_hash; struct search_result sd_pointers, ds_pointers; struct flow *f; int bad; /* () () () () */ #if defined(DOS) if (bp->PeerAddrType == AT_DUMMY) { if (++dummypackets == 1000) { ++kilodummypackets; dummypackets = 0; } ++dummypacketrate; } #endif for (x = running_rts; x != 0; x = rip->next_running_rt) { rip = &ri[x-1]; c_rtx = x; /* Rule table index of current rule table */ c_rt = rip->ri_rule_table; /* Current Rule Table */ c_rht = rip->ri_rule_hash; /* Current Rule Hash Table */ c_rsz = rip->ri_Size; /* Nbr of rules in current rule table */ #define SK_TRACE 0 #if !SK_TRACE /* () () () () */ r = match(1, bp, &bp->Low,&bp->High); if (r == MIGNORE) continue; /* Try next rule set */ /* match() builds search_key from bp */ else if (r == MFAIL) { /* S->D Fail */ r = match(0, bp, &bp->High,&bp->Low); if (r != MSUC) continue; /* Ignore or fail */ else { build_search_key(&ds_hash,&ds_key, bp, &bp->High,&bp->Low); if ((f = current(&ds_pointers, ds_hash,&ds_key)) != NULL) count(f,MREV,bp); else { f = create(&ds_pointers, &ds_key, bp); if (f == NULL) continue; /* Coudn't create flow */ count(f,MREV,bp); } } } else { /* S->D Suc */ build_search_key(&sd_hash,&sd_key, bp, &bp->Low,&bp->High); if ((f = current(&sd_pointers, sd_hash,&sd_key)) != NULL) count(f,MFWD,bp); else { build_search_key(&ds_hash,&ds_key, bp, &bp->High,&bp->Low); if ((f = current(&ds_pointers, ds_hash,&ds_key)) != NULL) count(f,MREV,bp); else { f = create(&sd_pointers, &sd_key, bp); if (f == NULL) continue; /* Coudn't create flow */ count(f,MFWD,bp); } } } #else /* () () () () */ bad = bad_flow(bp); if (bad) { printf(">>> packet "); dump_bp(bp); quack(21); } r = match(1, bp, &bp->Low,&bp->High); if (r == MIGNORE) continue; /* Try next rule set */ else if (r == MFAIL) { /* S->D Fail */ r = match(0, bp, &bp->High,&bp->Low); if (r != MSUC) continue; /* Ignore or fail */ else { build_search_key(&ds_hash, &ds_key, bp, &bp->High, &bp->Low); if (bad) { dump_flow_key(&ds_key, ds_hash,"@@1 D->S succeed"); quack(22); } if ((f = current(&ds_pointers, ds_hash, &ds_key)) != NULL) { count(f,MREV,bp); if (bad) { printf(" count(f,MREV,bp), f=%x\n", f); quack(1); } } else { f = create(&ds_pointers, &ds_key, bp); if (bad) printf(" created f=%x\n", f); if (bad) dump_flow_key(&ds_key, ds_hash,"@@2 create D->S"); if (f == NULL) continue; /* Coudn't create flow */ { count(f,MREV,bp); if (bad) { printf(" count(f,MREV,bp), f=%x\n", f); quack(2); } } } } } else { /* S->D Suc */ build_search_key(&sd_hash, &sd_key, bp, &bp->Low, &bp->High); if (bad) dump_flow_key(&sd_key, sd_hash,"@@3 S->D succeed"); if ((f = current(&sd_pointers, sd_hash, &sd_key)) != NULL) { count(f,MFWD,bp); if (bad) { printf(" count(f,MFWD,bp), f=%x\n", f); quack(3); } } else { /* match() built S->D key; save it then build D->S key */ build_search_key(&ds_hash, &ds_key, bp, &bp->High, &bp->Low); if (bad) dump_flow_key(&ds_key, ds_hash,"@@4 not current S->D"); if ((f = current(&ds_pointers, /* Swapped-over key is in search_key */ ds_hash, &ds_key)) != NULL) { count(f,MREV,bp); if (bad) { printf(" count(f,MREV,bp), f=%x\n", f); quack(4); } } else { /* Not current(D->S); restore S->D key for create() */ if (bad) dump_flow_key(&sd_key, sd_hash,"@@5 not current D->S"); f = create(&sd_pointers, &sd_key, bp); if (bad) printf(" created f=%x\n", f); if (f == NULL) continue; /* Coudn't create flow */ { count(f,MFWD,bp); if (bad) { printf(" count(f,MFWD,bp), f=%x\n", f); quack(5); } } } } } #endif /* () () () () */ } } #define MAXPKTLEN 1526 #if !NF_CISCO_DATA && !LFAP_METER /* Front-end calls the following routines to get attrib values */ #ifdef DECNET int dn_node(Bit8 *dn_addr) { return dn_addr[1]<<8 | dn_addr[0]; } #define dn_area(node) (node >> 10) #define dn_host(node) (node & 0x3FF) #endif void unpack_dn_node(Bit8 *ap, const Bit8 *dn_addr) { Bit8 dl = dn_addr[1]; ap[0] = dl >> 2; /* DECnet area */ ap[1] = dl & 0x03; ap[2] = dn_addr[0]; /* DECnet host */ ap[3] = 0; } void unpack_at_node(Bit8 *ap, const Bit8 *at_net, const Bit8 at_node) { ap[0] = at_net[0]; ap[1] = at_net[1]; ap[2] = at_node; ap[3] = 0; } #define BAD_ADJ 1 #define BAD_PEER 2 #define BAD_TRANS 3 void zero_pkt_fields(int which, struct pkt *bp) { switch (which) { case BAD_ADJ: bp->Low.AdjAddr_ms4 = bp->High.AdjAddr_ms4 = 0; bp->Low.AdjAddr_ls2 = bp->High.AdjAddr_ls2 = 0; bp->invalid_addrs |= INV_ADJ; break; case BAD_PEER: #if QBYTE_PEER bp->Low.PeerAddress.qbyte = bp->High.PeerAddress.qbyte = 0; #else memset(&bp->Low.PeerAddress.byte, 0, PEER_ADDR_LEN+TRANS_ADDR_LEN); memset(&bp->High.PeerAddress.byte, 0, PEER_ADDR_LEN+TRANS_ADDR_LEN); #endif bp->invalid_addrs |= INV_PEER; break; case BAD_TRANS: bp->Low.TransAddress = bp->High.TransAddress = 0; bp->invalid_addrs |= INV_TRANS; break; } } #if V6 int find_v6_type(int *prot, int *hx, const Bit8 *p, const int hl) { int nh, nx; nh = p[6]; nx = 40; for (;;) { switch (nh) { case PT_6FRAG: /* Fragment */ case PT_6ESP: /* Encapsulating Security Payload (ESP) RFC 2406 */ case PT_6NONH: /* No next header */ *prot = nh; return 0; /* Can't see into this packet */ /* case PT_6ICMP: /* ICMPv6 */ default: *prot = nh; *hx = nx; return 1; /* Can see into this one */ case PT_6HOP: /* Hop-by-hop options */ case PT_6ROUT: /* Routing (Type 0) */ case PT_6DEST: /* Destination options */ case PT_6AH: /* Authentication (AH) RFC 2402 */ break; /* Keep looking */ } if (hl < nx) { *prot = -1; /* Not enough header bytes to get Next Header */ return 0; } nh = p[nx]; nx += (p[nx]+1) * 8; } } #endif void pkt_extract(struct pkt *bp, const int type, const Bit8 *hdrp, const int hl) { int prot, dtype, dx, j; union decnet *dp; #ifdef DECNET int dlen, snode, dnode, area, host; #endif #if NF_OCX_BGP Subnet *my_subnet; Bit32 my_IP4addr; Bit16 my_ASN; #endif bp->PeerAddrType = type; bp->invalid_addrs = 0; if (hl < 0) { /* Didn't get the header! */ zero_pkt_fields(BAD_ADJ, bp); return; } switch (type) { #if V6 case AT_IP6: bp->DSCodePoint = /* Traffic Class field, RFC 2460 */ (hdrp[1] >> 6) | ((hdrp[0] & 0x0F) << 2); if (hl < 8+IP6_ADDR_LEN*2) { /* Didn't get the peer addresses! */ zero_pkt_fields(BAD_PEER, bp); return; } memcpy(&bp->Low.PeerAddress,&hdrp[8],IP6_ADDR_LEN); /* IPv6 source */ memcpy(&bp->High.PeerAddress,&hdrp[24],IP6_ADDR_LEN); /* IPv6 dest */ if (!find_v6_type(&prot,&dx, hdrp,hl)) { if (prot < 0) /* Not enough bytes to determine protocol */ bp->TransAddrType = 0; else /* Can't see into packet body */ bp->TransAddrType = prot; zero_pkt_fields(BAD_TRANS, bp); return; } switch (bp->TransAddrType = prot) { case PT_UDP: case PT_TCP: if (hl < dx+4) { /* Didn't get the IP ports! */ zero_pkt_fields(BAD_TRANS, bp); return; } bp->Low.TransAddress = *(Bit16 *)&hdrp[dx]; /* Source port */ bp->High.TransAddress = *(Bit16 *)&hdrp[dx+2]; /* Dest port */ break; case PT_6ICMP: /* RFC 2463 */ case PT_ICMP: if (hl < dx+2) { /* Didn't get the type,code fields */ zero_pkt_fields(BAD_TRANS, bp); return; } bp->Low.TransAddress = htons(hdrp[dx]); /* Type field */ bp->High.TransAddress = htons(hdrp[dx+1]); /* Code field */ break; } #if NEW_ATR if (prot == PT_TCP) { if (hl < dx+4+sizeof(bp->pi.tcp.th)) { /* Didn't get the tcp header */ bp->pi.tcp.tcp_len = 0; memset(&bp->pi.tcp.th, 0, sizeof(bp->pi.tcp.th)); bp->pktinfo_sz = 0; } else { memcpy(&bp->pi.tcp.th, &hdrp[dx+4], bp->pktinfo_sz = sizeof(bp->pi.tcp.th)); bp->pi.tcp.tcp_len = ntohs(*(Bit16 *)&hdrp[2]) - dx; } } #endif /* NEW_ATR */ return; #endif case AT_IP4: bp->DSCodePoint = hdrp[1] >> 2; /* Old TOS field, RFC 2474 */ if (hl < 12+IP4_ADDR_LEN*2) { /* Didn't get the peer addresses! */ zero_pkt_fields(BAD_PEER, bp); return; } #if QBYTE_PEER bp->Low.PeerAddress.qbyte = *(Bit32 *)&hdrp[12]; /* IPv4 source */ bp->High.PeerAddress.qbyte = *(Bit32 *)&hdrp[16]; /* IPv4 dest */ #else memcpy(&bp->Low.PeerAddress,&hdrp[12],IP4_ADDR_LEN); /* IPv4 source */ memcpy(&bp->High.PeerAddress,&hdrp[16],IP4_ADDR_LEN); /* IPv4 dest */ #endif prot = bp->TransAddrType = hdrp[9]; /* IPv4 Protocol Type */ if (hdrp[6] & 0x1F != 0 || hdrp[7] != 0) { /* Fragment offset != 0 */ zero_pkt_fields(BAD_TRANS, bp); /* Can't see into fragment */ return; } dx = (hdrp[0] & 0x0F) << 2; /* HLEN */ /* Data offset. HLEN = IPv4 header length in 32-bit units */ if (prot == PT_ICMP) { if (hl < dx+2) { /* Didn't get the type,code fields */ zero_pkt_fields(BAD_TRANS, bp); return; } bp->Low.TransAddress = htons(hdrp[dx]); /* Type field */ bp->High.TransAddress = htons(hdrp[dx+1]); /* Code field */ } else { if (hl < dx+4) { /* Didn't get the IP ports! */ zero_pkt_fields(BAD_TRANS, bp); return; } bp->Low.TransAddress = *(Bit16 *)&hdrp[dx]; /* Source port */ bp->High.TransAddress = *(Bit16 *)&hdrp[dx+2]; /* Dest port */ /* In **network** order within Bit16, to match rule values */ } #if NEW_ATR if (prot == PT_ICMP) { if (hl < dx+4+sizeof(bp->pi.icmp)) { /* Didn't get enough ICMP data */ memset(&bp->pi.icmp, 0, sizeof(bp->pi.icmp)); bp->pktinfo_sz = 0; } else { memcpy(&bp->pi.icmp, &hdrp[dx+4], bp->pktinfo_sz = sizeof(bp->pi.icmp)); } } else if (prot == PT_UDP) { if (hl < dx+8+sizeof(bp->pi.udp)) { /* Didn't get enough UDP data */ memset(&bp->pi.udp, 0, sizeof(bp->pi.udp)); bp->pktinfo_sz = 0; } else { memcpy(&bp->pi.udp, &hdrp[dx+8], bp->pktinfo_sz = sizeof(bp->pi.udp)); } #if PP_DEBUG_XXX printf("\npkt_extract(2) UDP pkt: info_sz=%d, ", bp->pktinfo_sz); for (j = 0; j != bp->pktinfo_sz; ++j) printf(" %02x", ((char *)&bp->pi.udp)[j]); printf("\n"); #endif } else if (prot == PT_TCP) { if (hl < dx+4+sizeof(bp->pi.tcp.th)) { /* Didn't get the tcp header */ bp->pi.tcp.tcp_len = 0; memset(&bp->pi.tcp.th, 0, sizeof(bp->pi.tcp.th)); bp->pktinfo_sz = 0; } else { memcpy(&bp->pi.tcp.th, &hdrp[dx+4], bp->pktinfo_sz = sizeof(bp->pi.tcp.th)); bp->pi.tcp.tcp_len = (hdrp[2] << 8 | hdrp[3]) - dx; } } #endif /* NEW_ATR */ #if NF_OCX_BGP #define get32(a) ((a[0] << 8 | a[1]) << 8 | a[2]) << 8 | a[3] if (asn_lookup) { my_IP4addr = get32(bp->Low.PeerAddress); if ((my_subnet = FindSubnet(ntohl(my_IP4addr))) != NULL) { my_ASN = use_owner_asns ? my_subnet->src_as : my_subnet->exit_as; bp->Low.nf_ASN[0] = my_ASN >> 8; bp->Low.nf_ASN[1] = my_ASN & 0xff; bp->Low.nf_mask = bits_in_mask(my_subnet->mask); } else bp->Low.nf_ASN[0] = bp->Low.nf_ASN[1] = 0; my_IP4addr = get32(bp->High.PeerAddress); if ((my_subnet = FindSubnet(ntohl(my_IP4addr))) != NULL) { my_ASN = use_owner_asns ? my_subnet->src_as : my_subnet->exit_as; bp->High.nf_ASN[0] = my_ASN >> 8; bp->High.nf_ASN[1] = my_ASN & 0xff; bp->High.nf_mask = bits_in_mask(my_subnet->mask); } else bp->High.nf_ASN[0] = bp->High.nf_ASN[1] = 0; } #endif return; case AT_NOVELL: bp->TransAddrType = hdrp[5]; /* XNS packet type */ if (hl < 18+IPX_ADDR_LEN) { /* Didn't get the peer addresses! */ zero_pkt_fields(BAD_PEER, bp); return; } memcpy(&bp->High.PeerAddress,&hdrp[6],IPX_ADDR_LEN); /* IPX dest net */ memcpy(&bp->Low.PeerAddress,&hdrp[18],IPX_ADDR_LEN); /* IPX soure net */ if (hl < 30) { /* Didn't get the IPX sockets! */ zero_pkt_fields(BAD_TRANS, bp); return; } bp->High.TransAddress = *(Bit16 *)&hdrp[16]; /* IPX dest socket */ bp->Low.TransAddress = *(Bit16 *)&hdrp[28]; /* IPX source socket */ return; case AT_ETHERTALK: if (hl < 10) { /* Didn't get the peer addresses! */ zero_pkt_fields(BAD_PEER, bp); return; } unpack_at_node(&bp->Low.PeerAddress.byte, &hdrp[6],hdrp[9]); /* AT source */ unpack_at_node(&bp->High.PeerAddress.byte, &hdrp[4],hdrp[8]); /* AT source */ if (hl < 13) { /* Didn't get the AT sockets! */ zero_pkt_fields(BAD_TRANS, bp); return; } bp->TransAddrType =hdrp[12]; /* AT DDP protocol type */ bp->Low.TransAddress = (Bit16)hdrp[11]; /* AT source socket */ bp->High.TransAddress = (Bit16)hdrp[10]; /* AT dest socket */ return; case AT_CLNS: bp->TransAddrType = hdrp[5] & 0x1F; /* CLNS packet type */ bp->Low.TransAddress = bp->High.TransAddress = 0; memcpy(&bp->High.PeerAddress,&hdrp[11],j = hdrp[10]); /* Dest */ if (j < NSAP_ADDR_LEN) memset(&bp->High.PeerAddress + j, 0, NSAP_ADDR_LEN-j); memcpy(&bp->Low.PeerAddress,&hdrp[12+j],dx = hdrp[11+j]); /* Source */ if (dx < NSAP_ADDR_LEN) memset(&bp->Low.PeerAddress + dx, 0, NSAP_ADDR_LEN-dx); return; case AT_DECNET: bp->Low.TransAddress = bp->High.TransAddress = 0; dtype = hdrp[2]; /* DECnet packet type */ if (dtype != 0x81) dp = (union decnet *)&hdrp[3]; else { dtype = hdrp[3]; dp = (union decnet *)&hdrp[4]; } switch (bp->TransAddrType = dtype & 0x0F) { case 0x06: /* Data */ case 0x0E: /* Data + discard flag */ unpack_dn_node(&bp->Low.PeerAddress.byte, dp->d.src_dn_addr); unpack_dn_node(&bp->High.PeerAddress.byte, dp->d.dest_dn_addr); #ifdef DECNET dnode = dn_node(dp->d.dest_dn_addr); snode = dn_node(dp->d.src_dn_addr); fprintf(logfl,"Data to %d.%d from %d.%d\n", dn_area(dnode),dn_host(dnode), dn_area(snode),dn_host(snode) ); #endif break; case 0x07: /* Level 1 routing */ unpack_dn_node(&bp->Low.PeerAddress.byte, dp->l1r.src_dn_addr); #ifdef DECNET snode = dn_node(dp->l1r.src_dn_addr); fprintf(logfl,"Level 1 routing from %d.%d\n", dn_area(snode),dn_host(snode)); #endif break; case 0x09: /* Level 2 routing */ unpack_dn_node(&bp->Low.PeerAddress.byte, dp->l2r.src_dn_addr); #ifdef DECNET snode = dn_node(dp->l2r.src_dn_addr); fprintf(logfl,"Level 2 routing from %d.%d\n", dn_area(snode),dn_host(snode)); #endif break; case 0x0B: /* Router hello */ unpack_dn_node(&bp->Low.PeerAddress.byte, dp->rh.src_dn_addr); #ifdef DECNET snode = dn_node(dp->rh.src_dn_addr); dnode = dn_node(dp->rh.rtr_dn_addr); fprintf(logfl,"Router hello from %d.%d, other router %d.%d\n", dn_area(snode),dn_host(snode), dn_area(dnode),dn_host(dnode) ); #endif break; case 0x0D: /* Endnode hello */ unpack_dn_node(&bp->Low.PeerAddress.byte, dp->eh.src_dn_addr); #ifdef DECNET snode = dn_node(dp->eh.src_dn_addr); dnode = dn_node(dp->eh.rtr_dn_addr); fprintf(logfl,"Endnode hello from %d.%d, designated router %d.%d\n", dn_area(snode),dn_host(snode), dn_area(dnode),dn_host(dnode) ); #endif break; default: /* Unknown DECnet type */ scpos(0,scr_lrow); printf("\nDN pkt type %02x: ", dtype); for (j=0; j != 16; ++j) printf(" %02x",hdrp[j]); printf("\n"); for (j=16; j != 34; ++j) printf(" %02x",hdrp[j]); printf("\n"); #ifdef TESTING if (!logfl) open_log(); fprintf(logfl, "\nDN pkt type %02x: ", dtype); for (j=17; j != 34; ++j) fprintf(logfl," %02x",hdrp[j]); fprintf(logfl, "\n"); #endif } return; } } #define putshort(a,n) \ a[0] = (n >> 8) & 0xFF; a[1] = n & 0xFF; void other_extract(struct pkt *bp, const Bit16 ethertype, const Bit16 lsap) { bp->PeerAddrType = AT_OTHER; memset(&bp->Low.PeerAddress.byte, 0, sizeof(bp->Low.PeerAddress)); memset(&bp->High.PeerAddress.byte, 0, sizeof(bp->High.PeerAddress)); bp->Low.PeerAddress.trans = htons((Bit16)ethertype); /* Blue Book type */ bp->High.PeerAddress.trans = htons((Bit16)lsap); /* 802.2 LSAP */ bp->TransAddrType = 0; bp->Low.TransAddress = bp->High.TransAddress = 0; } int handle_pkt(struct pkt *pp, const Bit16 ether_type, const Bit16 lsap, const Bit8 *ethp, const Bit8 *p, const int pl) { pp->PeerAddrType = AT_DUMMY; #if V6 && V6_TESTV4 memset(pp->Low.PeerAddress,0,PEER_ADDR_LEN); memset(pp->High.PeerAddress,0,PEER_ADDR_LEN); #endif switch (ether_type) { case 0x0800: #if V6 && V6_TESTV4 if (proto_reqd[AT_IP6]) { pkt_extract(pp, AT_IP4, p,pl); if (use_ip_length) pp->p_len = p[2] << 8 | p[3]; memcpy(&pp->Low.PeerAddress[12],&pp->Low.PeerAddress[0],4); memcpy(&pp->High.PeerAddress[12],&pp->High.PeerAddress[0],4); memset(pp->Low.PeerAddress,0,4); memset(pp->High.PeerAddress,0,4); pp->PeerAddrType = AT_IP6; } else if (proto_reqd[AT_IP4]) { pkt_extract(pp, AT_IP4, p,pl); if (use_ip_length) pp->p_len = p[2] << 8 | p[3]; } #elif V6 if ((p[0] & 0xF0) == 0x40 && proto_reqd[AT_IP4]) { pkt_extract(pp, AT_IP4, p,pl); if (use_ip_length) pp->p_len = p[2] << 8 | p[3]; } else if ((p[0] & 0xF0) == 0x60 && proto_reqd[AT_IP6]) { pkt_extract(pp, AT_IP6, p,pl); if (use_ip_length) pp->p_len = p[4] << 8 | p[5]; } #else if (proto_reqd[AT_IP4]) { pkt_extract(pp, AT_IP4, p,pl); if (use_ip_length) pp->p_len = p[2] << 8 | p[3]; } #endif break; case 0x809B: if (proto_reqd[AT_ETHERTALK]) pkt_extract(pp, AT_ETHERTALK, p,pl); break; case 0x8137: if (proto_reqd[AT_NOVELL]) pkt_extract(pp, AT_NOVELL, p,pl); break; case 0x6003: if (proto_reqd[AT_DECNET]) pkt_extract(pp, AT_DECNET, p,pl); break; case 0x0000: /* 802.3 packet other than SNAP */ switch (lsap) { case 0xFFFF: if (proto_reqd[AT_NOVELL]) pkt_extract(pp, AT_NOVELL, p,pl); break; /* Novell "Raw 802.2" doesn't use LLC */ #ifdef CLNS case 0xFEFE: /* CLNS */ if (proto_reqd[AT_CLNS]) pkt_extract(pp, AT_CLNS, p+2,pl-2); break; /* ISO 8473-1 uses 1-org, byte 0 is LLC control byte */ #endif case 0xE0E0: /* Novell 802.2 */ if (proto_reqd[AT_NOVELL]) pkt_extract(pp, AT_NOVELL, p+3,pl-3); break; /* case 0xF0F0: NetBIOS */ } } if (proto_reqd[AT_OTHER] && pp->PeerAddrType == AT_DUMMY) other_extract(pp, ether_type,lsap); if (pp->PeerAddrType != AT_DUMMY && adj_reqd) { pp->Low.AdjAddr_ms4 = *(Bit32 *)ðp[6]; /* Source */ pp->Low.AdjAddr_ls2 = *(Bit16 *)ðp[10]; pp->High.AdjAddr_ms4 = *(Bit32 *)ðp[0]; /* Dest */ pp->High.AdjAddr_ls2 = *(Bit16 *)ðp[4]; } return pp->PeerAddrType != AT_DUMMY; } #endif /* NF_CISCO_DATA */ #ifdef DOS void save_time(void) { s_tod_h = tod_h; s_tod_m = tod_m; s_tod_s = tod_s; elapsed_sec = 0; } #endif char *u_fmt(char *buf, Bit32 n) { if (n < 1024) sprintf(buf, "[%u B]", n); else if (n < 1024*1024) sprintf(buf, "[%u KB]", (n+512)/1024); else sprintf(buf, "[%u MB]", (n+512*1024)/(1024*1024)); return buf; } void show_memory(void) { char msg[90], nb[30]; int t, r,rh, f,fh, s,sd, d,de, p; r = mxrules*sizeof(struct rule); rh = mxrulehash*sizeof(Bit16); f = mxflows*sizeof(struct flow); fh = fthashmod*sizeof(struct flow *); #if NEW_ATR s = mxstreams*sizeof(struct stream) + sfhashmod*sizeof(struct stream *); sd = mxstr_blocks*sizeof(struct stream_data); d = mxdistribs*sizeof(struct distribution); de = mxdistevents*sizeof(struct event); p = mxpktdatas*sizeof(struct pktdata); #else s = sd = 0; d = de = p = 0; #endif t = r+rh + f+fh + s+sd + d+de + p; display_msg(1,"Memory Usage Info .."); sprintf(msg, "Rules: %lu max, %lu free %s", mxrules, mxrules-inuse_rules, u_fmt(nb,r)); display_msg(0,msg); sprintf(msg, "RuleHash: %lu max, %lu free %s", mxrulehash, mxrulehash-inuse_rule_hash, u_fmt(nb,rh)); display_msg(0,msg); sprintf(msg, "Flows: %lu max (%d B/flow) %s", mxflows, sizeof(struct flow), u_fmt(nb,f)); display_msg(0,msg); sprintf(msg, "FlowHash: %lu slots %s", fthashmod, u_fmt(nb,fh)); display_msg(0,msg); #if NEW_ATR sprintf(msg, "TCP streams: %lu max, %lu free %s", mxstreams, n_free_streams, u_fmt(nb,s)); display_msg(0,msg); sprintf(msg, "Flows with streams: %lu alloc, %lu free (%d B/str) %s", mxstr_blocks, n_free_str_blocks, sizeof(struct stream_data), u_fmt(nb,sd)); display_msg(0,msg); sprintf(msg, "Distribs: %lu max, %lu free (%d B/dist) %s", mxdistribs, n_free_distribs, sizeof(struct distribution), u_fmt(nb,d)); display_msg(0,msg); sprintf(msg, "Distrib Events: %lu alloc, %lu free %s", mxdistevents, n_free_events, u_fmt(nb,de)); display_msg(0,msg); sprintf(msg, "Packet data: %lu alloc, %lu free (%d B/pkt) %s", mxpktdatas, n_free_pktdata, sizeof(struct pktdata), u_fmt(nb,p)); display_msg(0,msg); #endif #if defined(DOS) sprintf(msg, "malloc(): %lu bytes free", coreleft()); display_msg(0,msg); #else sprintf(msg, "Total allocated data: %s", u_fmt(nb,t)); display_msg(0,msg); #endif } #if NEW_ATR void show_new_attrib(void) { char msg[60]; display_msg(1,"New Attribute Info .."); sprintf(msg, "Streams (-t): %lu in use, %lu max", mxstreams-n_free_streams, mxstreams); display_msg(0,msg); sprintf(msg," %lu recovered", StreamsRecovered); display_msg(0,msg); if (stats_time != 0) { sprintf(msg," Creating %.03f stream/s", (double)StreamsCreated/stats_time); display_msg(0,msg); sprintf(msg," Recovering %.03f stream/s", (double)StreamsRecovered/stats_time); display_msg(0,msg); } sprintf(msg, "Packet data (-a): %lu in use, %lu max", mxpktdatas-n_free_pktdata, mxpktdatas); display_msg(0,msg); sprintf(msg," %lu recovered", PktsRecovered); display_msg(0,msg); if (stats_time != 0) { sprintf(msg," Creating %.03f pktdata/s", (double)PktsCreated/stats_time); display_msg(0,msg); sprintf(msg," Recovering %.03f pktdata/s", (double)PktsRecovered/stats_time); display_msg(0,msg); } } #endif void show_chains(int reset) { char msg[60]; display_msg(1,"Ruleset Chain Info .."); sprintf(msg, "%d tests", rsc_n_searches); display_msg(0,msg); if (rsc_n_searches == 0) return; sprintf(msg, "Range: %lu max, %lu av", rsc_mx_range, rsc_av_range/rsc_n_searches); display_msg(0,msg); sprintf(msg, "Tests: %lu max, %lu av", rsc_mx_tests, rsc_av_tests/rsc_n_searches); display_msg(0,msg); if (reset) { rsc_n_searches = rsc_mx_range = rsc_av_range = rsc_mx_tests = rsc_av_tests = 0; } } Bit32 active_flows(void) { Bit32 af,j; struct flow *fp; Bit32 GCtime; for (af = 0, j = 1; j <= mxflows; ++j) { /* Flow indexes from 1 to MXFLOWS */ if (flow_idle(j)) continue; #if FLOW_INDEX fp = flow_ix[j-1]; #else fp = &fa[j-1]; #endif GCtime = ri[fp->FlowRuleSet-1].ri_gc_time; if (fp->ntm_LastTime > GCtime) ++af; /* Can't be recovered */ } return af; } void show_flow_table(void) { char msg[60]; Bit32 f; display_msg(1,"Flow Table Info .."); sprintf(msg, "InactTime %d, Flood %d", InactivityTimeout,FloodMark); display_msg(0,msg); sprintf(msg, "Flows: %lu active, %u used", active_flows(), nflows); display_msg(0,msg); sprintf(msg," %lu recovered (GC: %u s, %u flow)", FlowsRecovered, gc_interval,gc_f); display_msg(0,msg); if (stats_time != 0) { f = FlowsCreated*1000L/stats_time; sprintf(msg," Creating %lu.%03lu flow/s", f/1000,f%1000); display_msg(0,msg); f = FlowsRecovered*1000L/stats_time; sprintf(msg," Recovering %lu.%03lu flow/s", f/1000,f%1000); display_msg(0,msg); } } void show_protocols(void) { int j; char msg[60]; display_msg(1,"Protocols being metered .."); sprintf(msg," %3.3s %s", adj_reqd ? "Yes" : " No", "Adjacent"); display_msg(0,msg); #if NF_OCX_BGP sprintf(msg," %3.3s %s", asn_lookup ? "Yes" : " No", "ASN lookup"); display_msg(0,msg); #endif for (j = 0; j != sizeof(proto_reqd)/sizeof(int); ++j) { if (proto_names[j][0] != '\0') { sprintf(msg," %2d %3.3s %s", j, proto_reqd[j] ? "Yes" : " No", proto_names[j]); display_msg(0,msg); } } } void zero_stats(int show_msg) { FlowsCreated = FlowsRecovered = #if NEW_ATR PktsCreated = PktsRecovered = StreamsCreated = StreamsRecovered = #endif n_matches = n_hash_compares = n_hash_searches = 0L; clear_pkt_stats = 1; if (show_msg) { #ifdef DOS scpos(0,scr_lrow); printf("Statistics Zeroed"); #else display_msg(0,"Statistics Zeroed"); #endif } } void show_stats(void) { int i,j; double aps; Bit32 apb, kdp; char msg[60]; #ifdef DOS if (stats_time == 0 || npackets == 0 || kilodummypackets == 0 || mindummyrate == 0) #else if (stats_time == 0 || npackets == 0) #endif return; /* Avoid divides by zero */ display_msg(1,"Meter Statistics .."); aps = (double)npackets/(double)stats_time; #ifdef DOS apb = (t_backlog*10+5L)/(stats_time*10L); sprintf(msg,"Av pkt/s %u, av pkt backlog %u", aps,apb); display_msg(0,msg); sprintf(msg,"Max pkt/s %u, max pkt backlog %u", max_pkt_rate,max_pkt_backlog); display_msg(0,msg); if (kilodummypackets != 0) { if (dummypackets >= 500) ++kilodummypackets; i = kilodummypackets*1000L/(kilodummypackets+npackets/1000L); j = (mindummyrate*10000L+5L)/((mindummyrate+mdpacketrate)*10L); sprintf(msg,"Idle time av %u.%u, min %u.%u %", i/10,i%10, j/10,j%10); display_msg(0,msg); } #else sprintf(msg,"Av pkt/s %lu, max pkt/s %u", aps,max_pkt_rate); display_msg(0,msg); #endif sprintf(msg,"%lu flows active (max %lu)",active_flows(),mxflows); display_msg(0,msg); sprintf(msg,"%u masks in table (max %u)", n_masks,MXMASKS); display_msg(0,msg); i = (n_matches*100L+5L)/(npackets*10L); j = (n_hash_searches*100L+5L)/(npackets*10L); sprintf(msg,"%u.%u rules/pkt, %u.%u searches/pkt", i/10,i%10, j/10,j%10); display_msg(0,msg); if (n_hash_searches != 0) { i = (n_hash_compares*100L+5L)/(n_hash_searches*10L); sprintf(msg,"%u.%u compares/search", i/10,i%10); display_msg(0,msg); sprintf(msg,"%u hash slots, %u in use, ", fthashmod,n_hash_ents); } } void show_rulesets(void) { int c,j,t; struct rtinf_rec *rip; char buf[80]; display_msg(1,"Ruleset Table .."); for (c = t = j = 0; j != sizeof(ri)/sizeof(struct rtinf_rec); ++j) { rip = &ri[j]; if (rip->ri_Status != RS_ACTIVE && rip->ri_Status != RS_NOTINSERVICE ) continue; ++c; if (t == 0) { display_msg(0," Rdy Flows Rules Name Owner"); t = 1; } sprintf(buf, "%2d %c %5lu %5u %s %s", j+1, /* 1-org */ rip->ri_Status == RS_ACTIVE ? 'T' : 'F', rip->ri_FlowRecords, rip->ri_Size, rip->ri_Name, rip->ri_Owner); display_msg(0,buf); } if (c == 0) display_msg(0,"No ruleset rows active"); } void show_managers(void) { int c,j,t; struct mgr_rec *mip; char buf[80]; display_msg(1,"Manager Table .."); for (c = t = j = 0; j != sizeof(mi)/sizeof(struct mgr_rec); ++j) { mip = &mi[j]; if (mip->mi_Status != RS_ACTIVE) continue; ++c; if (t == 0) { display_msg(0," Crnt Stby HWM RSby Owner"); t = 1; } sprintf(buf, "%2d %2d %2d %3d %c %s", j+1, /* 1-org */ mip->mi_CurrentRuleSet, mip->mi_StandbyRuleSet, mip->mi_HighWaterMark, mip->mi_RunningStandby == TV_TRUE ? 'T' : 'F', mip->mi_Owner); display_msg(0,buf); } if (c == 0) display_msg(0,"No manager rows active"); } void show_readers(void) { int c,j,t; struct rdr_rec *cip; Bit32 now; char buf[80]; display_msg(1,"Meter Reader Table .."); now = uptime_cs(); for (c = t = j = 0; j != sizeof(ci)/sizeof(struct rdr_rec); ++j) { cip = &ci[j]; if (cip->ci_Status != RS_ACTIVE) continue; ++c; if (t == 0) { display_msg(0," Rsx Last Prev Owner"); t = 1; } sprintf(buf, "%2d %2d %5lu %5lu %s", j+1, /* 1-org */ cip->ci_RuleSet, (now - cip->ci_LastTime)/100L, (now - cip->ci_PrevTime)/100L, cip->ci_Owner); display_msg(0,buf); } if (c == 0) display_msg(0,"No reader rows active"); } void init_monitor(int n_interfaces) { int j; #ifdef DOS set_tod(); save_time(); /* For screen display */ #endif start_uptime_clock(); if ((fa = (struct flow *)malloc( sizeof(struct flow)*mxflows)) == NULL) log_msg(LOG_ERR, 1, "Not enough memory for flow table!"); for (j = 0; j != mxflows; ++j) fa[j].FlowRuleSet = 0; for (j = 0; j != mxflows-1; ++j) fa[j].rs_next = j+2; /* 1-org */ fa[mxflows-1].rs_next = 0; free_flow_head = 1; free_flow_tail = mxflows; rsc_n_searches = rsc_mx_range = rsc_av_range = rsc_mx_tests = rsc_av_tests = 0; #if FLOW_INDEX if ((flow_ix = (struct flow **)malloc( sizeof(struct flow *)*mxflows)) == NULL) log_msg(LOG_ERR, 2, "Not enough memory for flow index!"); for (j = 0; j != mxflows; ++j) flow_ix[j] = NULL; #endif nflows = 0; /* No accounting flows in use yet */ gcf_ix = empty_ix = mxflows; /* Flow 1 will be the first allocated */ fthashmod = mxflows; /* Doesn't need to be mutually prime */ #if 0 for (j = sizeof(p2primes)/sizeof(Bit32)-1; j != 8 && p2primes[j] > mxflows; --j) ; fthashmod = p2primes[j]; /* Min fthashmod = p2primes[8] (2039) */ #endif flow_ht = (struct flow **)malloc(fthashmod*sizeof(struct flow *)); if (flow_ht == NULL) log_msg(LOG_ERR, 3, "Not enough memory for flow hash table!"); memset(flow_ht, 0, fthashmod*sizeof(struct flow *)); if ((rule_space = (struct rule *)malloc( sizeof(struct rule)*mxrules)) == NULL) log_msg(LOG_ERR, 4, "Not enough memory for rulesets!"); inuse_rules = 0; mxrulehash = 1 + mxrules*3/2; if ((rule_hash_space = (Bit16 *)malloc( sizeof(Bit16)*mxrulehash)) == NULL) log_msg(LOG_ERR, 5, "Not enough memory for rule hashes!"); inuse_rule_hash = 0; #if NEW_ATR if ((sfa = (struct stream *)malloc( sizeof(struct stream)*mxstreams)) == NULL) log_msg(LOG_ERR, 6, "Not enough memory for streams!"); free_streams = sfa; n_free_streams = mxstreams; for (j = 0; j != mxstreams-1; ++j) sfa[j].next_sp = &sfa[j+1]; sfa[mxstreams-1].next_sp = NULL; sfhashmod = mxstreams; /* Doesn't need to be mutually prime */ #if 0 for (j = sizeof(p2primes)/sizeof(Bit32)-1; j != 8 && p2primes[j] > mxstreams; --j) ; sfhashmod = p2primes[j]; #endif sfht = (struct stream **)malloc(sfhashmod*sizeof(struct stream *)); if (sfht == NULL) log_msg(LOG_ERR, 7, "Not enough memory for stream hash table!"); memset(sfht, 0, sfhashmod*sizeof(struct stream *)); if ((strb = (struct stream_data *)malloc( sizeof(struct stream_data)*mxstr_blocks)) == NULL) log_msg(LOG_ERR, 8, "Not enough memory for stream data blocks!"); free_str_blocks = strb; n_free_str_blocks = mxstr_blocks; for (j = 0; j != mxstr_blocks-1; ++j) strb[j].next = &strb[j+1]; strb[mxstr_blocks-1].next = NULL; # if TCP_PKT_TRACE pt_file = fopen(ptf_title, "a"); # endif if ((distribs = (struct distribution *)malloc( sizeof(struct distribution)*mxdistribs)) == NULL) log_msg(LOG_ERR, 9, "Not enough memory for distributions!"); free_distribs = distribs; n_free_distribs = mxdistribs; for (j = 0; j != mxdistribs-1; ++j) distribs[j].next = &distribs[j+1]; distribs[mxdistribs-1].next = NULL; if ((events = (struct event *)malloc( sizeof(struct event)*mxdistevents)) == NULL) log_msg(LOG_ERR, 10, "Not enough memory for distrib events!"); free_events = events; n_free_events = mxdistevents; for (j = 0; j != mxdistevents-1; ++j) events[j].next = &events[j+1]; events[mxdistevents-1].next = NULL; if ((packet_data = (struct pktdata *)malloc( sizeof(struct pktdata)*mxpktdatas)) == NULL) log_msg(LOG_ERR, 11, "Not enough memory for packet data blocks!"); free_pkt_dat = packet_data; n_free_pktdata = mxpktdatas; for (j = 0; j != mxpktdatas-1; ++j) packet_data[j].next = &packet_data[j+1]; packet_data[mxpktdatas-1].next = NULL; #endif /* NEW_ATR */ memset(ri, 0, sizeof(dist_atr)); memset(ri, 0, sizeof(stream_atr)); for (j = 0; ; ++j) { if (attribs[j].attr == 0) break; dist_atr[attribs[j].attr] = attribs[j].distrib; stream_atr[attribs[j].attr] = attribs[j].stream; } memset(ri, 0, sizeof(ri)); memset(rs_rdr, 0, sizeof(rs_rdr)); /* For nifty */ /* Create a ruleset info entry for the default rule table */ ri[0].ri_rule_table = (struct rule *)default_rule_table; ri[0].ri_Size = DRT_SIZE; optimise_rule_table(1); /* Alloc rule hash */ memcpy(ri[0].ri_Owner, "NeTraMet", 8); memcpy(ri[0].ri_Name, "1", 1); /* For compatibility with v3 */ ri[0].ri_TimeStamp = 1; ri[0].ri_Status = RS_ACTIVE; tbl_size[2] = n_interfaces; /* Interface table */ memset(mi, 0, sizeof(mi)); /* Manager info */ for (j = 1; j != sizeof(mi)/sizeof(struct mgr_rec); ++j) mi[j].mi_RunningStandby = TV_FALSE; memcpy(mi[0].mi_Owner, "NeTraMet", 8); mi[0].mi_CurrentRuleSet = 1; mi[0].mi_RunningStandby = TV_FALSE; mi[0].mi_TimeStamp = 1; mi[0].mi_Status = RS_ACTIVE; for (j = 0; j != sizeof(proto_reqd)/sizeof(int); ++j) proto_reqd[j] = 0; adj_reqd = 0; open_rule_set(1, 1); /* Open set 1 */ memset(ci, 0, sizeof(ci)); /* Reader (collector) info */ } void show_help(void) { display_msg(0,"Copyright (C) 1992-2000"); display_msg(0," Nevil Brownlee"); display_msg(0," ITSS Technology Development"); display_msg(0," The University of Auckland"); display_msg(0,""); display_msg(0,"Keyboard commands .."); display_msg(0,""); display_msg(0," e: show mEter reader table"); display_msg(0," a: show mAnager table"); display_msg(0," u: show rUleset table"); display_msg(0," f: show Flow Table"); display_msg(0," p: show Protocols"); display_msg(0," s: show Statistics"); display_msg(0," v: show meter Version"); #if defined(DOS) display_msg(0," z: Zero statistics"); # if !(OCX_NTM || OC3_NTM) display_msg(0," b: show Bad packet counts"); # endif display_msg(0," m: show Memory usage"); #endif if (kb_enabled) { display_msg(0,""); display_msg(0,"Esc: stop metering, exit NeTraMet"); } } void handle_kb(int ch) { int c; switch (ch) { case 'a': show_managers(); break; #if NEW_ATR case 'd': show_new_attrib(); break; #endif case 'C': case 'c': show_chains(ch == 'C'); break; case 'e': show_readers(); break; case 'h': show_flow_hash(); break; case 'H': show_stream_hash(1); break; case 'm': show_memory(); break; case 'p': show_protocols(); break; case 'u': show_rulesets(); break; case 's': show_stats(); break; case 't': show_meter_time(); break; #if PM_DEBUG case '\\': printf("pm_debug: "); #ifdef DOS c = getch(); #else c = getchar(); #endif if (c == '\\') { pm_dbg = 0; printf("ri[] off"); } else { c -= '0'; pm_dbg = c == 0 ? 10 : c; printf("ri[%d]", pm_dbg); } break; #endif case 'z': zero_stats(1); break; case '?': show_help(); break; default: break; } }