/* 1655, Thu 21 Jan 99

   NM_DISPLAY.C:  Display routines for nm_rc

   Copyright (C) 1992-2002 by Nevil Brownlee,
   CAIDA | University of Auckland */

/*
 * $Log: nm_display.c,v $
 * Revision 1.1.1.2.2.10  2002/02/23 01:57:18  nevil
 * Moving srl examples to examples/ directory.  Modified examples/Makefile.in
 *
 * Revision 1.1.1.2.2.6  2000/08/08 19:44:43  nevil
 * 44b8 release
 *
 * Revision 1.1.1.2.2.4  2000/06/06 03:38:09  nevil
 * Combine NEW_ATR with TCP_ATR, various bug fixes
 *
 * Revision 1.1.1.2.2.1  1999/11/29 00:17:21  nevil
 * Make changes to support NetBSD on an Alpha (see version.history for details)
 *
 * Revision 1.1.1.2  1999/10/03 21:06:15  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.4  1999/05/18 03:36:25  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.3  1999/01/27 04:26:13  nevil
 * Minor corrections to fix compiler warnings
 *
 * Revision 1.1.1.1.2.2  1999/01/20 04:01:33  nevil
 * Implementation of TCP attributes, part 4
 *
 * Revision 1.1.1.1.2.1  1999/01/08 01:38:28  nevil
 * Distribution file for 4.3b7
 *
 * Revision 1.1.1.1  1998/11/16 03:57:27  nevil
 * Import of NeTraMet 4.3b3
 *
 * Revision 1.1.1.1  1998/11/16 03:22:00  nevil
 * Import of release 4.3b3
 *
 * Revision 1.1.1.1  1998/10/28 20:31:24  nevil
 * Import of NeTraMet 4.3b1
 *
 * Revision 1.1.3.2  1998/10/18 23:44:06  nevil
 * Added Nicolai's patches, some 'tidying up' of the source
 *
 * Revision 1.1.3.1  1998/10/13 02:48:18  nevil
 * Import of Nicolai's 4.2.2
 *
 * Revision 1.1.1.1  1998/08/24 12:09:28  nguba
 * NetraMet 4.2 Original Distribution
 *
 * Revision 1.3  1998/05/07 04:28:53  rtfm
 * Implement NetFlowMet, the Cisco NetFlow RTFM meter
 */

#if HAVE_CONFIG_H
#include <ntm_conf.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#include "ausnmp.h"

#include "asn1.h"
#include "snmp.h"
#include "snmpimpl.h"
#include "snmpapi.h"
#include "snmpclnt.h"
#include "mib.h"

#include "nmc.h"
#include "nmc_c64.h"

extern int show_names;  /* 1 => ask DNS for names */

char *sdisplay_peertype(char *d, unsigned int peertype)
{
   static char *ptypes[] = {
         "", "ip4", "ip6", "cln",    "",  /*  0 ..  4 */
         "", "oth",    "",    "",    "",  /*  5 ..  9 */
         "", "ipx", "at ", "dec" };       /* 10 .. 13 */
   char buf[20];
   if (peertype > 13 || *ptypes[peertype] == '\0') {
      sprintf(buf, "%u", peertype);
      return strmov(d, buf);
      }
   else return strmov(d, ptypes[peertype]);
   }

char *sdisplay_transtype(char *d,
   unsigned int peertype, unsigned int transtype)
{
   char buf[20];
   switch (peertype) {
   case AT_IP4:  
#if V6
   case AT_IP6:  
#endif
      switch(transtype) {
      case 1: return strmov(d,"icmp");
      case 6: return strmov(d,"tcp ");
     case 17: return strmov(d,"udp ");
     case 89: return strmov(d,"ospf");
     default: sprintf(buf,"%u",transtype);
         }
      break;
   case AT_NOVELL:  switch(transtype) {
      case 4: return strmov(d,"px  ");
      case 5: return strmov(d,"spx ");
     case 17: return strmov(d,"ncp ");
     default: sprintf(buf,"%u",transtype);
         }
      break;
   case AT_ETHERTALK:  switch(transtype) {
      case 1: return strmov(d,"rtmr");
      case 2: return strmov(d,"nbp ");
      case 3: return strmov(d,"atp ");
      case 4: return strmov(d,"ep ");
      case 5: return strmov(d,"rtmq");
      case 6: return strmov(d,"zip ");
      case 7: return strmov(d,"adsp");
     default: sprintf(buf,"%u",transtype);
         }
      break;
   default:  sprintf(buf,"%u",transtype);
      }
   return strmov(d,buf);
   }

char *sdisplay_transaddr(char *d,
   unsigned int peertype, unsigned char *a)
{
   unsigned int p = a[0] << 8 | a[1];
   char buf[20];
   switch (peertype) {
   case AT_IP4:
#if V6
   case AT_IP6:  
#endif
      switch(p) {
      case 20: return strmov(d,"ftpdata");
      case 21: return strmov(d,"ftp");
      case 23: return strmov(d,"telnet");
      case 25: return strmov(d,"smtp");
      case 53: return strmov(d,"dns");
      case 70: return strmov(d,"gopher");
      case 79: return strmov(d,"finger");
      case 80: return strmov(d,"www");
     case 110: return strmov(d,"pop");
     case 113: return strmov(d,"imap");
     case 119: return strmov(d,"nntp");
     case 123: return strmov(d,"ntp");
     case 138: return strmov(d,"nbio");  /* netBIOS */
     case 161: return strmov(d,"snmp");
     case 513: return strmov(d,"login");
    case 1515: return strmov(d,"printer");
    case 2049: return strmov(d,"nfs");
    case 6000: return strmov(d,"xwin");
      default: sprintf(buf,"%u",p);
         }
      break;
   case AT_NOVELL:  switch(p) {
    case 1105: return strmov(d,"ncp");
    case 1106: return strmov(d,"sap");
    case 1107: return strmov(d,"rip");
    case 1109: return strmov(d,"netbios");
    case 1110: return strmov(d,"diag");
      default: sprintf(buf,"%u",peertype);
         }
      break;
   default: sprintf(buf,"%u",p);
      }
   return strmov(d,buf);
   }

char *sdisplay_number(char *d, unsigned long x)
{
   char buf[20], *prefix = "";
   if (x > 999) {
      prefix = "k";  x = (x+500)/1000;
      if (x > 999) {
         prefix = "M";  x = (x+500)/1000;
         if (x > 999) {
            prefix = "G";  x = (x+500)/1000;
	    }
         }
      sprintf(buf,"%3lu%s",x,prefix);
      }
   else sprintf(buf,"%4lu",x);
   return strmov(d,buf);
   }

char *sdisplay_timeinterval(char *d, unsigned long t)
{
   char buf[20], *unit = "s";
   t = (t+50)/100;  /* Seconds */
   if (t > 59) {
      unit = "m";  t = (t+30)/60;
      if (t > 59) {
         unit = "h";  t = (t+30)/60;
         }
      }
   sprintf(buf,"%3lu%s",t,unit);
   return strmov(d,buf);
   }

int get_name(char *buf, char *addr)
{  /* Not sure how to do v6 name lookups! */
   struct in_addr ia;
   struct hostent *hp;
   strncpy((char *)&ia.s_addr,addr,4);  /* Must be longword-aligned! */
   if ((hp = gethostbyaddr((char *)&ia.s_addr,4,AF_INET)) == NULL)
      return 0;
   strcpy(buf,hp->h_name);
   return 1;
   }

char *sdisplay_attrib(char *d,
   struct flow_info *fp, unsigned char col,
   struct meter_status *ms)
{
   char pal = fp->LowPeerType > MX_PROTOCOLS ? MAC_ADDR_LEN 
      : addr_len[fp->LowPeerType];
   char buf[250], *bp;
   struct hostent *hp;
   Bit16 asn;

   switch(col) {
   case FTFLOWINDEX:  /* Used to synchronise column blobs */
      sprintf(buf,"%u",fp->FlowIndex);
      break;
   case FTFLOWSTATUS:
      sprintf(buf,"%u",fp->FlowStatus);
      break;
   case FTLOWINTERFACE:
      sprintf(buf,"%u",fp->LowInterface);
      break;
   case FTLOWADJACENTTYPE:
      sprintf(buf,"%u",fp->LowAdjType);
      break;
   case FTLOWADJACENTADDRESS:
#if defined(NETFLOW)
      return sfmt_address(d,fp->LowAdjAddress,ADJ_ADDR,
         fp->LowAdjType == AT_IP4 ? IP4_ADDR_LEN : MAC_ADDR_LEN);
#else
      return sfmt_address(d,fp->LowAdjAddress,ADJ_ADDR,MAC_ADDR_LEN);
#endif
   case FTLOWADJACENTMASK:
      return sfmt_address(d,fp->LowAdjMask,ADJ_ADDR,MAC_ADDR_LEN);
   case FTLOWPEERTYPE:
      return sdisplay_peertype(d,fp->LowPeerType);
   case FTLOWPEERADDRESS:
      if (show_names && (fp->LowPeerType == AT_IP4
#if V6
            || fp->LowPeerType == AT_IP6
#endif
            )) {
         if (get_name(buf,(char *)fp->LowPeerAddress))
            return strmov(d,buf);
         }
      return sfmt_address(d,fp->LowPeerAddress,PEER_ADDR,pal);
   case FTLOWPEERMASK:
      return sfmt_address(d,fp->LowPeerMask,PEER_ADDR,pal);
   case FTLOWTRANSTYPE:
      return sdisplay_transtype(d,fp->LowPeerType,fp->LowTransType);
   case FTLOWTRANSADDRESS:
      return sdisplay_transaddr(d,fp->LowPeerType,fp->LowTransAddress);
   case FTLOWTRANSMASK:
      return sfmt_address(d,fp->LowTransMask,TRANS_ADDR,TRANS_ADDR_LEN);
   case FTHIINTERFACE:
      sprintf(buf,"%u",fp->HighInterface);
      break;
   case FTHIADJACENTTYPE:
      sprintf(buf,"%u",fp->HighAdjType);
      break;
   case FTHIADJACENTADDRESS:
#if defined(NETFLOW)
      return sfmt_address(d,fp->HighAdjAddress,ADJ_ADDR,
         fp->HighAdjType == AT_IP4 ? IP4_ADDR_LEN : MAC_ADDR_LEN);
#else
      return sfmt_address(d,fp->HighAdjAddress,ADJ_ADDR,MAC_ADDR_LEN);
#endif
   case FTHIADJACENTMASK:
      return sfmt_address(d,fp->HighAdjMask,ADJ_ADDR,MAC_ADDR_LEN);
   case FTHIPEERTYPE:
      return sdisplay_peertype(d,fp->LowPeerType);
   case FTHIPEERADDRESS:
      if (show_names && (fp->LowPeerType == AT_IP4
#if V6
            || fp->LowPeerType == AT_IP6
#endif
            )) {
         if (get_name(buf,(char *)fp->HighPeerAddress))
            return strmov(d,buf);
         }
      return sfmt_address(d,fp->HighPeerAddress,PEER_ADDR,pal);
   case FTHIPEERMASK:
      return sfmt_address(d,fp->HighPeerMask,PEER_ADDR,pal);
   case FTHITRANSTYPE:
      return sdisplay_transtype(d,fp->LowPeerType,fp->HighTransType);
   case FTHITRANSADDRESS:
      return sdisplay_transaddr(d,fp->LowPeerType,fp->HighTransAddress);
   case FTHITRANSMASK:
      return sfmt_address(d,fp->HighTransMask,TRANS_ADDR,TRANS_ADDR_LEN);
   case FTRULESET:
      sprintf(buf,"%u",fp->FlowRuleSet);
      break;

#if defined(NETFLOW)
   case FTMETERID:
      sprintf(buf,"%u",fp->MeterId);
      break;
   case FTLOWROUTEASN:
      asn = ntohs(*((unsigned short *)fp->LowRouteASN));
      sprintf(buf,"%u", asn);
      break;
   case FTLOWROUTEPREFIX:
      sprintf(buf,"%u",fp->LowRoutePrefix);
      break;
   case FTHIROUTEASN:
      asn = ntohs(*((unsigned short *)fp->HighRouteASN));
      sprintf(buf,"%u",asn);
      break;
   case FTHIROUTEPREFIX:
      sprintf(buf,"%u",fp->HighRoutePrefix);
      break;
#endif

   case FTUPOCTETS:
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
      bp = sdisplay_number(buf,fp->FwdBytes);
#else
      bp = sdisplay_number(buf,fp->FwdBytes.low);
#endif
      strcpy(bp,"B");
      break;
   case FTUPPDUS:
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
      return sdisplay_number(d,fp->FwdPackets);
#else
      return sdisplay_number(d,fp->FwdPackets.low);
#endif
   case FTDOWNOCTETS:
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
      bp = sdisplay_number(buf,fp->BackBytes);
#else
      bp = sdisplay_number(buf,fp->BackBytes.low);
#endif
      strcpy(bp,"B");
      break;
   case FTDOWNPDUS:
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
      return sdisplay_number(d,fp->BackPackets);
#else
      return sdisplay_number(d,fp->BackPackets.low);
#endif
   case FTFIRSTTIME:
      return sdisplay_timeinterval(d,ms->uptime - fp->FirstTime);
   case FTLASTTIME:
      sprintf(buf,"%lu",fp->LastTime);
      break;
   case FTSOURCECLASS:
      sprintf(buf,"%u",fp->SourceClass);
      break;
   case FTDESTCLASS:
      sprintf(buf,"%u",fp->DestClass);
      break;
   case FTFLOWCLASS:
      sprintf(buf,"%u",fp->FlowClass);
      break;
   case FTSOURCEKIND:
      sprintf(buf,"%u",fp->SourceKind);
      break;
   case FTDESTKIND:
      sprintf(buf,"%u",fp->DestKind);
      break;
   case FTFLOWKIND:
      sprintf(buf,"%u",fp->FlowKind);
      break;
   case FTDSCODEPOINT:
      sprintf(buf,"%u",fp->DSCodePoint);
      break;

#if NEW_ATR
   case FTTCPDATA:
      return sfmt_tcp_data(d, fp->sfd);
#endif  /* NEW_ATR */

   default:
      sprintf(buf,"*");  /* Unknown attribute */
      }
   return strmov(d,buf);
   }
