
/****************************************************************************/
/*                                                                          */
/*      NNstat -- Internet Statistics Collection Package                    */
/*                                                                          */
/*            Written by: Bob Braden & Annette DeSchon                      */
/*            USC Information Sciences Institute                            */
/*            Marina del Rey, California                                    */
/*                                                                          */
/*      Copyright (c) 1991 University of Southern California.               */
/*      All rights reserved.                                                */
/*                                                                          */
/*      Redistribution and use in source and binary forms are permitted     */
/*      provided that the above copyright notice and this paragraph are     */
/*      duplicated in all such forms and that any documentation,            */
/*      advertising materials, and other materials related to such          */
/*      distribution and use acknowledge that the software was              */
/*      developed by the University of Southern California, Information     */
/*      Sciences Institute.  The name of the University may not be used     */
/*      to endorse or promote products derived from this software           */
/*      without specific prior written permission.                          */
/*      THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR        */
/*      IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      */
/*      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR          */
/*      PURPOSE.                                                            */
/*                                                                          */
/****************************************************************************/

static char rcsid[]=
  "$Header: /tmp_mnt/r/jove_staff3/mogul/alpha/code/NNstat/RCS/sobjbp.c,v 1.4 1993/08/31 22:49:35 mogul Exp $";
 
/*
 * CHANGES:
 *   Rel 3.0 
 *      ISI: Add List_BP()
 *      DEC/ISI: Little-endian changes    
 *      ISI:
 *         o Add binary version BP2. 
 *         o Add new optional parm: max capture length.
 *         o Keep time in MS (if XPARSE is defined)
 *         o Record into local file in binary 
 *         o Use minimum fixed-sized slots, instead of MAX_BPSIZE slots.
 *         o Change XDR encoding in *incompatible* fashion.
 *         o Do not defer allocation of buffer to first packet.
 *         o Fix memory leak.
 *  Aug93/DECWRL: Alpha/OSF port.  XDR is a pointer type!
 */
 
/*
 *   Internet Statistics Facility -- Generic Operation Routines
 *
 *    This module defines a set of operations on a particular class
 *    of Statistical Objects.  In the spirit of object-oriented
 *    programming and information-hiding, these routines are
 *    the only ones that know about the internal structre of the
 *    Statistical Object (SOBJ) data structures.
 *
 */
 
/*
 *
 *          BP, BP2 Object Classes ==   BINARY PACKETS Operation
 *
 *    An object of the BP[2] class maintains a circular buffer of equal-
 *    sized slots containing binary text  (e.g., complete packet headers 
 *    selected by filter(s)).  BP2 is binary, and concatenates two fields
 *    into one slot.
 * 
 *    Reading the object returns the saved binary text, starting
 *    from the oldest.  
 *
 *    Operations on BP[2] class objects:
 *
 *       SOBJ *Make_BP( object-name, &parmlist, nparms, datalen )
 *       SOBJ *Make_BP2( object-name, &parmlist, naprms, datalen1, datalen2 )
 *
 *       boolean Write_BP( (SOBJ *) SOBJ-ptr, &value, len)
 *       boolean Write_BP2( (SOBJ *) SOBJ-ptr, &value1, len1, &value2, len2)
 *
 *       Delete_BP( (SOBJ *) SOBJ-ptr )
 *
 *       Clear_BP( (SOBJ *) SOBJ-ptr )
 *
 *       Read_BP( fd, (SOBJ *) SOBJ-ptr )
 *
 *       List_BP( fd, OBJp )
 *
 */
 
#include <stdio.h>
#include <sys/types.h>
#include "stat.h"
#include "sobj.h"

#define MAX_BPSIZE  256  /* This is only required to be larger than snaplen */

    /* Object area */

#define BPA struct stat_xarea
BPA {
    SOBJ   bpa_sobj;         /* SOBJ = standard root of area */
    
    u_char bpa_wrapped;      /* Boolean: true once wraps first time */
    u_char bpa_pktsize;      /* Max packet size within slot */
    short  bpa_slotsize;     /* Size of one slot */
    int    bpa_slotno;       /* Number of slots */
    
    char  *bpa_nxtslot;      /* ptr to next slot to be written */
    char  *bpa_segp;         /* Ptr to circular buffer segment */
    char  *bpa_segendp;      /* Ptr to 1+end of buffer */
} ;

    /* Format of circular buffer slot -- slots have fullword boundary,
     *    for Sparc, and packet data is short-aligned, to make bcopy faster.
     *    Slot length is maintained to allow parsing of a binary file written
     *    directly from circular buffer.
     */ 
#define BPE struct bpe_entry 
BPE {
    long   bpe_time ;     /* Timestamp when collected */
    u_char bpe_slotlen;   /* Length of slot */
    u_char bpe_pktlen ;   /* Actual length of data saved in slot */
    char   bpe_packet[MAX_BPSIZE] ; /* Packet saved */
        /* Note: this is max possible slot size.  Actual slot size is
         * determined by 2nd parm, if present, or from type(s) of
         * invoking fields.
         */    
} ;

    /*  XDR transmission length -- 
     *      N is number of slots
     *      L is sum over slots of ALIGN(data length) 
     */
#define BPr_SIZE(N, L) (1+2*(N))*sizeof(u_int32)+L

struct BP_parms {  /* parm format */
    int32 bpp_maxno ; /* required: max number of slots */
    int32 bpp_maxlen; /* optional: maximum capture length */
} ;

    
SOBJ *Make_BP(), *Make_BP2() ;
void Clear_BP(), Delete_BP(), List_BP();
boolean Write_BP(), Write_BP2(), Read_BP();

    /* Transfer Vector 
     *   for external access to these routines
     */
GENERICOP BPop =  {Make_BP,  Write_BP,  Read_BP, Clear_BP, Delete_BP, List_BP};
GENERICOP BP2op = {Make_BP2, Write_BP2, Read_BP, Clear_BP, Delete_BP, List_BP};

#ifdef XPARSE
extern long CurrTimeMS;
#endif

SOBJ *Make_BP(objname, parmp, nparms, datalen)
    char *objname;
    struct BP_parms *parmp ;
    int datalen, nparms;
    {
    return(Make_BP2(objname, parmp, nparms, datalen, 0));
}
    

SOBJ *Make_BP2(objname, parmp, nparms, datalen1, datalen2)
    char *objname;
    struct BP_parms *parmp ;
    int datalen1, datalen2, nparms;
    {
    register BPA *bpap ;
    int size, vsize, dlen = datalen1+datalen2 ;   
    char *bufp;

    if (nparms < 1) {
        SOBJ_error = "BINPKT: Parameter error";
        return(NULL);
    }
    vsize = (dlen > MAX_BPSIZE)? MAX_BPSIZE : dlen;
    
         /* optional second parm: limit length */
    if (nparms == 2 && vsize > ntohl(parmp->bpp_maxlen))
        vsize = ntohl(parmp->bpp_maxlen);
                 
    if ((bpap = (BPA *) malloc( sizeof ( BPA ))) == NULL) {
        SOBJ_error = "BINPKT: Memory overflow" ;
        return(NULL) ;
    }
    bzero(bpap, sizeof(BPA)) ; 
    bpap->bpa_pktsize = vsize;
    bpap->bpa_slotsize =  ALIGN(sizeof(BPE)+vsize-MAX_BPSIZE+1);   
    bpap->bpa_slotno = ntohl(parmp->bpp_maxno);            

            /* Allocate circular buffer */ 
    size = bpap->bpa_slotsize * bpap->bpa_slotno;  
    if ((bufp  = (char *) malloc(size)) == NULL) 
        {
        free(bpap); 
        SOBJ_error = "BINPKT: too large";
        return(NULL) ;
    }
    bpap->bpa_segp =  bpap->bpa_nxtslot = bufp;
    bpap->bpa_segendp = bufp + size;
    return(&bpap->bpa_sobj) ; 
}   /* Make_BP2() */  
     
     
boolean Write_BP( bpap, Valuep, L)
    register BPA *bpap ;
    char *Valuep ;
    int L;
    {
    return(Write_BP2( bpap, Valuep, L, NULL, 0));
}


boolean Write_BP2( bpap, Valuep1, L1, Valuep2, L2) 
    register BPA *bpap ;
    char *Valuep1, *Valuep2 ;
    int L1, L2;
    {
    register BPE *bpep;
    register int L = L1 + L2;
    
    bpep = (BPE *) bpap->bpa_nxtslot;
#ifdef XPARSE    
    bpep->bpe_time = CurrTimeMS;
#else
    bpep->bpe_time = CurrTime;
#endif
    bpep->bpe_slotlen = bpap->bpa_slotsize;
    if (L1 > bpap->bpa_pktsize) 
        bcopy(Valuep1, bpep->bpe_packet, bpep->bpe_pktlen = bpap->bpa_pktsize);
       
    else  {
        bcopy(Valuep1, bpep->bpe_packet,bpep->bpe_pktlen = L1);
        if (L2) 
            bcopy(Valuep2, bpep->bpe_packet+L1, 
                (bpep->bpe_pktlen +=
                  (L <= bpap->bpa_pktsize)? L2: bpap->bpa_pktsize - L1));
    }
    
    if ((bpap->bpa_nxtslot += bpap->bpa_slotsize) >= bpap->bpa_segendp) {
        bpap->bpa_wrapped = 1 ;
        bpap->bpa_nxtslot = bpap->bpa_segp ;
    }   
    return(WRRET_OK) ;          
}  /* Write_BP2() */
  
        
void Delete_BP( bpap )
    BPA *bpap ;
    {
    free(bpap->bpa_segp);  /* free circular buffer */
    free(bpap) ;           /* and control area */
}


void Clear_BP( bpap )
    register BPA *bpap ;
    {           
    bpap->bpa_nxtslot = bpap->bpa_segp; 
    bpap->bpa_wrapped = 0;      
}


/*
 *  Read_BP: Called locally by statspy to read object
 *
 */    
boolean Read_BP(fd, bpap)
    XDR *fd ;
    register BPA  *bpap ;
    {
    register char *bpep, *last;
    register int length = 0;
    int count;
    int islog = (ISTTY(fd));
    OBJ xobj;
    
    SetupXRep(&xobj, bpap);
    
    last = (bpap->bpa_wrapped)?  bpap->bpa_segendp : bpap->bpa_nxtslot;
    count= (last - bpap->bpa_segp)/bpap->bpa_slotsize;
    
        /* Have to determine total length, first */ 
    if (bpep = bpap->bpa_segp)
        while (bpep < last)  {
            length += ALIGN(((BPE *)bpep)->bpe_pktlen);
            bpep += bpap->bpa_slotsize;
        }
      
    if (ISTTY(fd) && logfp != stdout) {
        /* SPECIAL CASE: WRITE ENTIRE BUFFER IN BINARY INTO LOCAL FILE */
        if (write(fileno(logfp), bpap->bpa_segp, last-bpap->bpa_segp) < 0) {
            perror("Write binary file");
            return(FALSE);
        }
        printf("Wrote buffer to file\n");
        return(TRUE);
    }
     
    if (!PutReadResp(fd, BPr_SIZE(count, length), bpap)) return(FALSE);
    if (ISTTY(fd))
        fprintf(logfp, "  # of first packet= %d\n", 
                                         xobj.obj_state.totalno - count+1);
   
    else if (!xdr_int(fd, &count)) 
            return(FALSE);
            
    if (bpap->bpa_wrapped) {
        for (bpep = bpap->bpa_nxtslot ;  bpep < bpap->bpa_segendp;
                              bpep += bpap->bpa_slotsize)
            if (!ReadBP(fd, bpep, &xobj, islog))
                return(FALSE) ;
    }
    if (bpep = bpap->bpa_segp)
        while (bpep < bpap->bpa_nxtslot)  {
            if (!ReadBP(fd, bpep, &xobj, islog))
                return(FALSE) ;
            bpep += bpap->bpa_slotsize; 
        }
    return(TRUE);  
}  /* Read_BP() */ 


#define OUTSIZE 256

/*
 *   List_BP: Called by collect & rspy to display results of remote read.
 *
 */
void
List_BP(fd, OBJp)
    XDR *fd;
    OBJ *OBJp;
    {   
    int count, j;
    BPE bpe;
    char out[OUTSIZE];
    int fno = fileno(logfp);
    extern boolean isrspy;
    boolean ischar = (!isrspy || (logfp == stdout));
     
    count = get_long(fd, out, "  #packets saved");
    
    if (ischar) {
        log(out, logfp);
        fprintf(logfp, "  # of first packet= %d\n", 
                                           OBJp->obj_state.totalno-count+1);
    }
        
    for (j=0;j<count;j++) {
        if (!ReadBP(fd, &bpe, OBJp, ischar)) {
            printf(" BAD ReadBP! ");
            break;
        }        
        
           /* SPECIAL CASE: WRITE IN BINARY INTO LOCAL FILE */
        if (!ischar) {
            if (write(fno, &bpe, bpe.bpe_slotlen) < 0) {
                perror("Write binary file");
                return;
            }
        }
    }
} /* List_BP() */
    
           
    /*
     *  ReadBP() -- Common I/O routine for bin-pkt class read
     *
     */
boolean ReadBP(fd, bpep, objp, islog)
    XDR *fd;
    register BPE *bpep;
    OBJ  *objp;
    int   islog;
{
    BPE  *cp = bpep;
    int   len = bpep->bpe_slotlen;
    char  out[256];

    if (fd) {
       if (!xdr_bytes(fd, &cp, &len, sizeof(BPE)))
             /* Send/recv entire slot unchanged... */
          return(FALSE);
    }
        
    len = bpep->bpe_pktlen;
    if (islog) {
        /***
        deltat =  objp->obj_state.currtime - bpep->bpe_time;
        sprintf(out, " @-%dsec >> ", deltat);
        ***/       
        sprintf(out, " @%d MS>> ", bpep->bpe_time);
        if (objp->obj_state.datatype == DTYP_packet) {
            strcat(out, "\n");
            log(out, logfp);
            if (logfp)
                hex_print(logfp, bpep->bpe_packet, len);
            else  return(FALSE);
        }
        else {
            if (len > objp->obj_state.dleng && objp->obj_state.dleng2) {
                print_val(endof(out), bpep->bpe_packet, 
                          objp->obj_state.datatype, objp->obj_state.dleng);
                strcat(out, " ");
                print_val(endof(out), &bpep->bpe_packet[objp->obj_state.dleng], 
                          objp->obj_state.datatyp2, 
                          len - objp->obj_state.dleng);
            }
            else {
                print_val(endof(out), cp, objp->obj_state.datatype, len);
            }
            strcat(out, "\n");
            log(out, logfp);
        }
    }
    return(TRUE);
} /* ReadBP */
            
