/* table.c - ATMARP table */
 
/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
 

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/atm.h>

#include "atm.h"
#include "atmd.h"

#include "table.h"


#define COMPONENT "TABLE"


const char *entry_state_name[] = { "NONE","RESOLV","INVALID","VALID" };


ENTRY *alloc_entry(int svc)
{
    ENTRY *entry;

    entry = alloc_t(ENTRY);
    entry->state = as_none;
    entry->svc = svc;
    entry->ip = 0;
    entry->addr = NULL;
    entry->flags = 0;
    entry->timer = NULL;
    entry->vccs = NULL;
    entry->itf = NULL;
    return entry;
}


ENTRY *lookup_ip(const ITF *itf,unsigned long ip)
{
    ENTRY *walk;

    for (walk = itf->table; walk; walk = walk->next)
	if (walk->ip == ip) break;
    return walk;
}


ENTRY *lookup_addr(const ITF *itf,const struct sockaddr_atmsvc *addr)
{
    ENTRY *walk;

    for (walk = itf->table; walk; walk = walk->next)
	if (walk->addr && atm_equal(walk->addr,addr,0,0)) break;
    return walk;
}


ENTRY *lookup_incoming(const struct sockaddr_atmsvc *addr)
{
    ENTRY *walk;

    for (walk = unknown_incoming; walk; walk = walk->next)
	if (walk->addr && atm_equal(walk->addr,addr,0,0)) break;
    return walk;
}


static FILE *out_file = NULL;
static int out_error = 0;


static void output(const char *fmt,...)
{
    va_list ap;

    va_start(ap,fmt);
    if (!out_file) vdiag(COMPONENT,DIAG_DEBUG,fmt,ap);
    else if (vfprintf(out_file,fmt,ap) < 0 || putc('\n',out_file) < 0)
	    out_error = 1;
    va_end(ap);
}


static void dump_vcc(VCC *vcc)
{
    struct sockaddr_atmsvc addr;
    char buffer[MAX_ATM_ADDR_LEN+1];
    int size;

    size = sizeof(addr);
    if (getpeername(vcc->fd,(struct sockaddr *) &addr,&size) < 0) {
	diag(COMPONENT,DIAG_ERROR,"getpeername: %s",strerror(errno));
	strcpy(buffer,"<getsocknam error>");
    }
    else {
#if 0
	int i;

	for (i = 0; i < size; i++)
	    printf("%02X ",((unsigned char *) &addr)[i]);
	printf("\n");
#endif
	if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) &addr,
	  pretty) < 0) strcpy(buffer,"<atm2text error>");
    }
    output("  %s%s",buffer,vcc->connecting ? ", connecting" :
      !vcc->entry->svc ? "" : vcc->active ? " (active)" : " (passive)");
}


static void dump_entries(ENTRY *list)
{
    static const char *flag_name[] = {
	"???",	"com", "PERM", "PUBL",			/* 0x0001-0x0008 */
	"trailers", "netmask", "dontpub", "magic",	/* 0x0010-0x0080 */
	"???", "???", "???", "???",			/* 0x0100-0x0800 */
	"NULL", "ARPSRV", "DEFQOS" };			/* 0x1000-0x8000 */
    /* lower case flags are not used by ATMARP */
    ENTRY *entry;
    VCC *vcc;
    char addr_buf[MAX_ATM_ADDR_LEN+1];
    char qos_buf[MAX_ATM_QOS_LEN+1];
    char tmp[100]; /* large enough for all flags */
    unsigned char *ipp;
    int i;

    for (entry = list; entry ; entry = entry->next) {
	if (!entry->addr) strcpy(addr_buf,"<none>");
	else if (atm2text(addr_buf,MAX_ATM_ADDR_LEN+1,
	      (struct sockaddr *) entry->addr,pretty) < 0)
		strcpy(addr_buf,"<error>");
	ipp = (unsigned char *) &entry->ip;
	*tmp = 0;
	for (i = 0; i < 16; i++)
	    if (entry->flags & (1 << i)) {
		if (*tmp) strcat(tmp,",");
		strcat(tmp,flag_name[i]);
	    }
	output("IP %d.%d.%d.%d, state %s, addr %s, flags 0x%x<%s>",ipp[0],
	  ipp[1],ipp[2],ipp[3],entry_state_name[entry->state],addr_buf,
	  entry->flags,tmp);
	if (entry->itf && !qos_equal(&entry->itf->qos,&entry->qos)) {
	    if (qos2text(qos_buf,sizeof(qos_buf),&entry->qos,0) < 0)
		strcpy(qos_buf,"<error>");
		output("  QOS: %s",qos_buf);
	}
	for (vcc = entry->vccs; vcc; vcc = vcc->next) dump_vcc(vcc);
    }
}


static void dump_itf(ITF *itf)
{
    unsigned char *ipp;
    char buf[MAX_ATM_QOS_LEN+1];

    if (qos2text(buf,sizeof(buf),&itf->qos,0) < 0) strcpy(buf,"<error>");
    ipp = (unsigned char *) &itf->local_ip;
    output("----- Itf %d (%d.%d.%d.%d) -----",itf->number,ipp[0],ipp[1],
      ipp[2],ipp[3]);
    output("Default QOS: %s",buf);
    dump_entries(itf->table);
}


void dump_all(void)
{
    ITF *itf;

    if (!debug) {
	out_file = fopen(TMP_DUMP_FILE,"w");
	out_error = 0;
    }
    for (itf = itfs; itf; itf = itf->next) dump_itf(itf);
    output("----- Unknown incoming connections -----");
    dump_entries(unknown_incoming);
    output("----- End of dump -----");
    if (out_file) {
	if (fclose(out_file) < 0) out_error = 1;
	if (!out_error) rename(TMP_DUMP_FILE,DUMP_FILE);
	unlink(TMP_DUMP_FILE);
    }
}
