RCS_ID_C="$Id: amiga_cstat.c,v 1.21 1993/11/06 23:51:22 ppessi Exp $";
/*
 * Copyright (c) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
 *                    Helsinki University of Technology, Finland.
 *                    All rights reserved.
 *
 * HISTORY
 * $Log: amiga_cstat.c,v $
 * Revision 1.21  1993/11/06  23:51:22  ppessi
 * Added Berkeley licence. Added route tree printing routines.
 * Changed some allocation routines.
 *
 * Revision 1.20  1993/08/06  08:55:02  jraja
 *  Set the state field in connections list to 0 for udp.
 *
 * Revision 1.19  1993/08/01  22:16:16  ppessi
 * Fixed gcc compatiblity problem in getsockets()
 *
 * Revision 1.18  1993/06/04  11:16:15  jraja
 * Fixes for first public release.
 *
 * Revision 1.17  1993/05/31  23:32:18  jraja
 * getsockets() fixed to work.
 *
 * Revision 1.16  1993/05/29  21:01:49  jraja
 * Changed return values to symbolc RETURN_XXXX,
 * fixed icmp hist retrieval function.
 *
 * Revision 1.15  1993/05/17  01:07:47  ppessi
 * Changed RCS version.
 *
 * Revision 1.14  1993/05/05  16:09:58  puhuri
 * Fixes for final demo.
 *
 * Revision 1.13  93/05/03  18:52:25  18:52:25  puhuri (Markus Peuhkuri)
 * Changed name from amiga_nettrace.c. Add read icmp_stat, again fixed format
 * string and changed order in what things are done.
 * 
 * Revision 1.12  93/05/03  17:09:03  17:09:03  puhuri (Markus Peuhkuri)
 * fixed formatstring.
 * 
 * Revision 1.11  93/04/28  19:25:07  19:25:07  puhuri (Markus Peuhkuri)
 * Fixed format string of getsockets() output.
 * 
 * Revision 1.10  93/04/25  03:03:48  03:03:48  jraja (Jarno Tapio Rajahalme)
 * Added type to bsd_malloc(), added bsd_free() calls and checks for
 * return value for allocation of mem, changed aligned_bcopy_consts to structure
 * copies, changed sbcnt to mbcnt to conform with the socket structure,
 * added .s_addr's to the printf() call.
 * 
 * Revision 1.9  93/04/23  21:01:59  21:01:59  puhuri (Markus Peuhkuri)
 * Fixed includes, Add code to check that every socket fits in buffer
 * and to alloc more memory.
 * 
 * Revision 1.8  93/04/23  00:28:17  00:28:17  ppessi (Pekka Pessi)
 * getvalue() moved into amiga_config.c
 * 
 * Revision 1.7  93/04/21  19:07:20  19:07:20  puhuri (Markus Peuhkuri)
 * Now copies wanted parts of structure. UNFINISHED.
 * 
 * Revision 1.6  93/04/06  15:15:36  15:15:36  jraja (Jarno Tapio Rajahalme)
 * Changed spl function return value storage to spl_t,
 * changed bcopys and bzeros to aligned and/or const when possible,
 * added inclusion of conf.h to every .c file.
 * 
 * Revision 1.5  93/03/05  21:11:05  21:11:05  jraja (Jarno Tapio Rajahalme)
 * Fixed includes (again).
 * 
 * Revision 1.4  93/03/05  03:25:55  03:25:55  ppessi (Pekka Pessi)
 * Compiles with SASC. Initial test version.
 * 
 * Revision 1.3  93/02/26  19:53:54  19:53:54  puhuri (Markus Peuhkuri)
 * Fixsed includes, few typos, add*toa functions and removed #define conflict
 * 
 * Revision 1.2  93/02/25  19:55:28  19:55:28  puhuri (Markus Peuhkuri)
 * Add real variable & other information to get
 */

/*
 * Copyright (c) 1983, 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <conf.h>

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/synch.h>
#include <sys/malloc.h>
#include <sys/syslog.h>

#include <kern/amiga_includes.h>
#include <kern/amiga_rexx.h>
#include <kern/amiga_config.h>

#include <dos/rdargs.h>

#if __SASC
#include <proto/dos.h>
#elif __GNUC__
#include <inline/dos.h>
#endif

#include <net/route.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>

long count = 0;

int ultoa(unsigned long ul,char *buffer)
{
  static char buf[10];
  char *p;
  int len;
  
  p = buf;
  *p='\0';

  do {
    *++p = (ul % 10)+'0';
  } while (ul /= 10);

  len = p - buf;

  while (*buffer++ = *p--)
    ;
  return(len);
}

int ltoa(long l, char *buffer)
{
  int len=0;

  if(l<0){
    *buffer++='-';
    l=-l;
    len++;
  }
 return (len + ultoa((unsigned long)l, buffer));
}

/*
 * Allocate big enough buffer for reply
 */
LONG CS_Alloc(struct CSource *reply, size_t size)
{
  /* do we have enough space? */
  if (reply->CS_Length < size) { 
    char *buffer;
    if ((buffer = bsd_malloc(size, M_TEMP, 0)) == NULL){
      return 0; 
    }
    /*
     * Old buffer will be freed by caller
     */
    reply->CS_Buffer = buffer;
    reply->CS_Length = size;
  }

  return 1;
}

/*
 * getsockets(): a reply statics for all sockets 
 *
 */
struct printsocket {
  struct in_addr  inp_faddr;	/* u_long, far addr */
  u_short         inp_fport;
  struct in_addr  inp_laddr;	/* u_long, local addr */
  u_short         inp_lport;
  short           so_type;	/* SOCK_STREAM, _DGRAM... */
  u_short         so_rcv_sb_cc;	/* Recv queue size */
  u_short         so_snd_sb_cc;	/* Send queue */
  short           inp_ppcb_t_state; /* State of TCP connection */
};

LONG getsockets(struct CSource *args, UBYTE **errstrp, struct CSource *res)
{
  int i, count = 0;
  struct inpcb *pcb;
  struct printsocket *pps, *mem;
  spl_t s = splnet();		/* Critical section starts here */

  /* Count number of connections */
  for(pcb = udb.inp_next; pcb != &udb ; pcb = pcb->inp_next)
    ++count;
  for(pcb = tcb.inp_next; pcb != &tcb ; pcb = pcb->inp_next)
    ++count;

  if (count == 0) {		/* return now if nothing to print */
    splx(s);
    return RETURN_OK;
  }

  /* Allocate memory */
  mem = (struct printsocket *)
    bsd_malloc(sizeof(struct printsocket) * (count), M_TEMP, 0);

  if (mem == NULL) {
    splx(s);
    *errstrp = ERR_MEMORY;
    return RETURN_FAIL;
  }

  /* Proto recv-q send-q laddr lport faddr fport state */
  /*  1   1   4  1   4  1  8  1  4  1   8 1   4 1 1   1 =  42 chars */
#define STATLEN 42
  if (!CS_Alloc(res, STATLEN * count + 1)) {
    /* Allocation failed, free printsocket memory */
    splx(s);
    bsd_free(mem, M_TEMP);
    *errstrp = ERR_MEMORY;
    return RETURN_FAIL;
  }

  /* Copy information, TCP first.. */
  for(pcb = tcb.inp_next, pps = mem; pcb != &tcb; pcb = pcb->inp_next, ++pps){
    pps->inp_faddr = pcb->inp_faddr;
    pps->inp_fport = pcb->inp_fport;
    pps->inp_laddr = pcb->inp_laddr;
    pps->inp_lport = pcb->inp_lport;
    pps->so_type = pcb->inp_socket->so_type;
    pps->so_rcv_sb_cc = pcb->inp_socket->so_rcv.sb_cc;
    pps->so_snd_sb_cc = pcb->inp_socket->so_snd.sb_cc;
    pps->inp_ppcb_t_state = ((struct tcpcb *)(pcb->inp_ppcb))->t_state;
  }
  /* ...then UDP */
  for(pcb = udb.inp_next; pcb != &udb; pcb = pcb->inp_next, ++pps){
    pps->inp_faddr = pcb->inp_faddr;
    pps->inp_fport = pcb->inp_fport;
    pps->inp_laddr = pcb->inp_laddr;
    pps->inp_lport = pcb->inp_lport;
    pps->so_type = pcb->inp_socket->so_type;
    pps->so_rcv_sb_cc = pcb->inp_socket->so_rcv.sb_cc;
    pps->so_snd_sb_cc = pcb->inp_socket->so_snd.sb_cc;
    pps->inp_ppcb_t_state = 0;	/* NO state for UDP */
  }

  splx(s);			/* Critical section completed now */

  /*
   * Print all socket entries 
   */
  for(i = 0; i < count; i++)
    csprintf(res, "%lc %04lx %04lx %08lx %04lx %08lx %04lx %1lx%s",
	     mem[i].so_type == SOCK_STREAM ? 't': 'u',
	     mem[i].so_rcv_sb_cc, mem[i].so_snd_sb_cc,
	     mem[i].inp_laddr.s_addr, mem[i].inp_lport,
	     mem[i].inp_faddr.s_addr, mem[i].inp_fport,
	     mem[i].inp_ppcb_t_state,
	     (i < count - 1) ? " " : "");

#if DIAGNOSTIC			/* check for overrun */
  if (res->CS_CurChr >= res->CS_Length)
    log(LOG_ERR, "getsockets(): buffer overwritten by %ld bytes\n", 
	res->CS_CurChr - res->CS_Length + 1);
#endif

  /*
   * free mem
   */
  bsd_free(mem, M_TEMP);

  return RETURN_OK;
}

/*
 * Get ICMP history profiles 
 */
#include <netinet/icmp_var.h>
#include <netinet/ip_icmp.h>

LONG read_icmphist(struct CSource *args, UBYTE **errstrp, struct CSource *res)
{
  int i;
  UBYTE *p = res->CS_Buffer;

  for(i = 0;i <= ICMP_MAXTYPE; i++){
    p += ultoa(icmpstat.icps_outhist[i], p);
    *p++=' ';
  }

  for(i = 0;i <= ICMP_MAXTYPE; i++){
    p += ultoa(icmpstat.icps_inhist[i], p);
    *p++ = ' ';
  }
  *--p = '\0';

  res->CS_CurChr = p - res->CS_Buffer;
  return RETURN_OK;
}

/*
 * Get routing tables 
 */
#include <net/route.h>
#include <net/if.h>

#define DB(x) ;

/* Address families supported */
extern STRPTR KW_Protocols;

/* What is stored in the route entry */
#define NORMAL 0
#define MASK   1
#define DUPED  2

/* Our recursion depth in route tree. 32 is # of bits in IP address */
/* Other protocols may require deeper stack */
#define MAX_ROUTE_TREE_DEPTH 32

struct printroute {
  struct sockaddr pr_dest;
  struct sockaddr pr_via;
  u_long          pr_use;
  struct ifnet   *pr_ifp;
  short           pr_refcnt;
  u_short         pr_flags;
  u_short         pr_what;
};

static void cstat_rtentry(struct rtentry *rt, struct printroute *pr);
static int  cstat_rtree(struct radix_node *rn, struct printroute *pr);
static void cstat_rtflags(struct CSource *res, char *format, register int f);

LONG getroutes(struct CSource *args, UBYTE **errstrp, struct CSource *res)
{
  UBYTE Buffer[KEYWORDLEN];
  int af, newroutes, routes = 0;
  struct radix_node_head *rnh;
  struct printroute *mem, *pr;
  spl_t s;

  /* 
   * Parse address family 
   *
   * Match the  against the template. The af is the index of
   * the matching keyword, if one is found.
   */
  if (ReadItem(Buffer, sizeof(Buffer), args) <= 0) {
    *errstrp = ERR_SYNTAX;
    return RETURN_ERROR;
  }
  if ((af = FindArg(KW_Protocols, Buffer)) < 0) {
    res->CS_CurChr = 0;
    csprintf(res, ERR_ILLEGAL_VAR, "getroutes", Buffer);
    *errstrp = res->CS_Buffer;
    return RETURN_WARN;
  }

  DB(log(LOG_DEBUG, "getroutes: getting routes for protocol %s (%ld)", 
	 Buffer, af));

  s = splnet();			/*                         Critical section */

  /* Count entries in tables */
  for (rnh = radix_node_head; rnh ; rnh = rnh->rnh_next) {
    if (af == AF_UNSPEC || af == rnh->rnh_af) {
      newroutes = cstat_rtree(rnh->rnh_treetop, NULL);
      if (newroutes < 0) {
	splx(s);
	*errstrp = (UBYTE *)"Route tree overflow.\n";
	return RETURN_FAIL;
      }
      routes += newroutes;
    }
  }

  DB(log(LOG_DEBUG, "getroutes: found %ld routes", routes));

  /* Allocate memory for entries */
  pr = mem = (struct printroute *)
    bsd_malloc(sizeof(struct printroute) * (routes), M_TEMP, 0);

  if (mem == NULL) {
    splx(s);
    *errstrp = ERR_MEMORY;
    return RETURN_FAIL;
  }

  /* Fill in printroute table */
  for (rnh = radix_node_head; rnh ; rnh = rnh->rnh_next) {
    if (af == AF_UNSPEC || af == rnh->rnh_af) {
      pr += cstat_rtree(rnh->rnh_treetop, pr);
#if DIAGNOSTIC
      if (pr > mem + routes) {
	UBYTE *msg = "cstat_rtree(): found more entries than counted!\n";
	splx(s);
	bsd_free(mem, M_TEMP);
	log(LOG_ERR, msg);
	*errstrp = msg;
	return RETURN_FAIL;
      }
#endif
    }
  }
  splx(s);			/*                  End of critical section */
  
  /* Reply line has format
   * protocol route gateway flags refcount used device/unit + separation 
   * 2  +     8  +  8  +    4  +  4  +     8  + 32  +  10  + 10  = 90 chars
   */
  if (!CS_Alloc(res, 90 * routes)) {
    /* Allocation failed, free printroute memory */
    bsd_free(mem, M_TEMP);
    *errstrp = ERR_MEMORY;
    return RETURN_FAIL;
  }

  /* cstat_rtree_print */
  for (pr = mem; routes-- > 0 ; pr++) {
    csprintf(res, "%02lx %08lx %08lx ",
	     pr->pr_dest.sa_family,
	     ((struct sockaddr_in *)&pr->pr_dest)->sin_addr.s_addr,
	     ((struct sockaddr_in *)&pr->pr_via)->sin_addr.s_addr);
    cstat_rtflags(res, "%-8.8s ", pr->pr_flags);
    csprintf(res, "%04lx %08lx ", pr->pr_refcnt, pr->pr_use);
    if (pr->pr_ifp) {
      csprintf(res, "%s/%ld ", pr->pr_ifp->if_name, pr->pr_ifp->if_unit);
    } else {
      csprintf(res, "none ");
    }
  }

  /* free printsocket structures */
  bsd_free(mem, M_TEMP);

  return RETURN_OK;
}

/*
 * cstat_rtree(): get a route tree to a table
 */
static int 
cstat_rtree(struct radix_node *rn, struct printroute *pr)
{
  short routes = 0;
  short stack_pointer = 0;
  struct radix_node *stack[MAX_ROUTE_TREE_DEPTH]; 
  
 again:
  if (rn->rn_b < 0) {
    if ((rn->rn_flags & RNF_ROOT) == 0) {
      if (pr) {
	cstat_rtentry((struct rtentry*)rn, pr);
	pr++;
      }
      routes++;
    }
    if (rn = rn->rn_dupedkey)
      goto again;
  } else {
    if (rn->rn_r) {
      if (stack_pointer >= MAX_ROUTE_TREE_DEPTH - 1)
	return -1;
      stack[stack_pointer++] = rn->rn_r;
    }
    /* tail recursion removal */
    if (rn = rn->rn_l) {
      goto again;
    }
  }

  /* recursion removal */
  if (stack_pointer-- > 0) {
    rn = stack[stack_pointer];
    goto again;
  }

  DB(log(LOG_DEBUG, "rtree: found %ld routes", routes));
  return routes;
}

/*
 * Copy rtentry into printroute
 *      assumes that rtentry and pr are valid
 */
static void
cstat_rtentry(struct rtentry *rt, struct printroute *pr)
{
  /* These should allow variable sized sockaddrs */
  bcopy(rt_key(rt), &pr->pr_dest, sizeof(pr->pr_dest));
  bcopy(rt->rt_gateway, &pr->pr_via, sizeof(pr->pr_via));

  /* */
  pr->pr_use    = rt->rt_use;
  pr->pr_refcnt = rt->rt_refcnt;
  pr->pr_ifp    = rt->rt_ifp;
  pr->pr_flags  = rt->rt_flags;
  if (rt->rt_nodes->rn_dupedkey)
    pr->pr_what = DUPED;
  else
    pr->pr_what = NORMAL;
}

/*
 * Definitions for showing gateway flags.
 */
struct bits {
	short	b_mask;
	char	b_val;
} bits[] = {
	{ RTF_UP,	'U' },
	{ RTF_GATEWAY,	'G' },
	{ RTF_HOST,	'H' },
	{ RTF_DYNAMIC,	'D' },
	{ RTF_MODIFIED,	'M' },
	{ RTF_CLONING,	'C' },
	{ RTF_XRESOLVE,	'X' },
	{ RTF_LLINFO,	'L' },
	{ RTF_REJECT,	'R' },
	{ 0 }
};

static void
cstat_rtflags(struct CSource *res, char *format, register int f)
{
  char name[33], *flags;
  register struct bits *p = bits;
  for (flags = name; p->b_mask; p++)
    if (p->b_mask & f)
      *flags++ = p->b_val;
  *flags = '\0';
  csprintf(res, format, name);
}
