/* ARP commands
 * Copyright 1991 Phil Karn, KA9Q
 *
 * Mods by G1EMM
 *
 * Mods by SM6RPZ
 * 1992-05-28 - Added interface to "arp add ..."-command.
 * 1992-07-26 - Small cosmetic changes here and there.
 */
#include "global.h"
#include "ctype.h"
#include "commands.h"
#include "mbuf.h"
#include "timer.h"
#include "internet.h"
#include "ip.h"
#include "arp.h"
#include "rspf.h"
#include "domain.h"
#include "session.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: arpcmd.c,v 1.18 2001/05/06 16:32:54 brian Exp $";
#endif

static int Arp_Sort = 1;		/* set Initial sort mode */
int Maxarpq = 5;
char ArpDupcall[AXALEN];
int ArpExpire = ARPLIFE;


static int doarpsort (int argc, char *argv[], void *p);
static void make_arp_string (struct arp_tab * ap, char *buf);
static int doarpadd (int argc, char *argv[], void *p);
static int doarpdrop (int argc, char *argv[], void *p);
static int doarpflush (int argc, char *argv[], void *p);
static int doarppoll (int argc, char *argv[], void *p);
static int doarpeaves (int argc, char *argv[], void *p);
static int doarpexpire (int argc, char *argv[], void *p);
static int doarpqueue (int argc, char *argv[], void *p);
static int doarpdupcall (int argc, char *argv[], void *p);
static void dumparp (void);


#ifdef CATALOG
#include "catalog.h"

#define CAT arpcmd_catalog

#define addusage	__STR(0)
#define noiface		__STR(1)
#define exptime		__STR(2)
#define dropusage	__STR(3)
#define dumpstr		__STR(4)
#define dumpheader	__STR(5)

#else /* CATALOG */
static const char addusage[] = "Usage: arp %s <hostid> ether|ax25|netrom|arcnet|mac <hardware addr> <iface>\n";
static const char noiface[] = "No such interface %s\n";
static const char exptime[] = "Expire time for arp entries (in sec.)";
static const char dropusage[] = "Usage: arp drop <hostid> ether|ax25|netrom|arcnet|mac <iface>\n";
static const char dumpstr[] = "received %u badtype %u bogus addr %u reqst in %u replies %u reqst out %u\n";
static const char dumpheader[] = "IP addr         Type           Time Q Address           Interface\n";

#endif /* CATALOG */


static const char publishstr[] = "publish";
static const char addstr[] = "add";
static const char netromstr[] = "netrom";
static const char strCR[] = "%s\n";
static const char maxqueue[] = "Max queue";
static const char unknownstr[] = "[unknown]         ";
static const char publishedstr[] = "(published)";


static struct cmds Arpcmds[] =
{
	{ "add",	doarpadd,	0, 4, "arp add <hostid> ether|ax25|netrom|arcnet|mac <hardware addr> <iface>"},
	{ "drop",	doarpdrop,	0, 3, "arp drop <hostid> ether|ax25|netrom|arcnet|mac <iface>"},
	{ "dupcall",	doarpdupcall,	0, 0, NULLCHAR},
	{ "eaves",	doarpeaves,	0, 0, NULLCHAR},
	{ "expire",	doarpexpire,	0, 0, NULLCHAR},
	{ "flush",	doarpflush,	0, 0, NULLCHAR},
	{ "maxq",	doarpqueue,	0, 0, NULLCHAR},
	{ "poll",	doarppoll,	0, 0, NULLCHAR},
	{ "publish",	doarpadd,	0, 4, "arp publish <hostid> ether|ax25|netrom|arcnet|mac <hardware addr> <iface>"},
	{ "sort",	doarpsort,	0, 0, NULLCHAR},
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR}
};


const char *Arptypes[] =
{
	"NET/ROM",
	"10 Mb Ethernet",
	"3 Mb Ethernet",
	"AX.25",
	"Pronet",
	"Chaos",
	"",
	"Arcnet",
	"Appletalk"
};




int
doarp (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	if (argc < 2) {
		dumparp ();
		return 0;
	}
	return subcmd (Arpcmds, argc, argv, p);
}


static int
doarpadd (argc, argvv, p)
int argc;
char *argvv[];
void *p OPTIONAL;
{
char const **argv = (char const **) argvv;
int16 hardware;
uint32 addr;
char *hwaddr;
struct arp_tab *ap;
struct arp_type *at;
int pub = 0;
struct iface *iface;

	if (argv[0][0] == 'p')	/* Is this entry published? */
		pub = 1;
	if ((addr = resolve (argv[1])) == 0) {
		tprintf (Badhost, argv[1]);
		return 1;
	}
	if (argc == 4 && tolower (argv[2][0]) != 'n') {
		tprintf (addusage, pub ? publishstr : addstr);
		return -1;
	}
	/* This is a kludge. It really ought to be table driven */
	switch (tolower (argv[2][0])) {
		case 'n':	/* Net/Rom pseudo-type */
			argc = 5;
			argv[4] = netromstr;	/* Force use of netrom interface */
			hardware = ARP_NETROM;
			break;
		case 'e':	/* "ether" */
			hardware = ARP_ETHER;
			break;
		case 'a':	/* "ax25" */
			switch (tolower (argv[2][1])) {
				case 'x':
					hardware = ARP_AX25;
					break;
				case 'r':
					hardware = ARP_ARCNET;
					break;
				default:
					tprintf (addusage, pub ? publishstr : addstr);
					return -1;
			}
			break;
		case 'm':	/* "mac appletalk" */
			hardware = ARP_APPLETALK;
			break;
		default:
			tprintf (addusage, pub ? publishstr : addstr);
			return -1;
	}
	if ((iface = if_lookup (argv[4])) == NULLIF) {
		tprintf (noiface, argv[4]);
		return 1;
	}
	/* If an entry already exists, clear it */
	if ((ap = arp_lookup (hardware, addr, iface)) != NULLARP)
		arp_drop (ap);

	at = &Arp_type[hardware];
	if (at->scan == NULLFP ((char *, const char *))) {
		tputs ("Attach device first\n");
		return 1;
	}
	/* Allocate buffer for hardware address and fill with remaining args */
	hwaddr = mallocw ((unsigned) at->hwalen);
	/* Destination address */
	(void) (*at->scan) (hwaddr, argv[3]);
	ap = arp_add (addr, hardware, hwaddr, pub, iface);	/* Put in table */
	free (hwaddr);		/* Clean up */
	stop_timer (&ap->timer);/* Make entry permanent */
	set_timer (&ap->timer, 0L);
#ifdef  RSPF
	rspfarpupcall (addr, hardware, NULLIF);	/* Do a RSPF upcall */
#endif /* RSPF */
	return 0;
}


static int
doarpeaves (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return dosetflag (argc, argv, p, ARP_EAVESDROP, 0);
}


static int
doarppoll (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return dosetflag (argc, argv, p, ARP_KEEPALIVE, 0);
}


static int
doarpdupcall (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	char tmp[AXBUF];

	if (argc < 2) {
		tprintf (strCR, pax25 (tmp, ArpDupcall));
		return 0;
	}
	if (argv[1][0]) {
		if (setcall (ArpDupcall, argv[1]) == -1)
			return -1;
	} else
		*ArpDupcall = 0;
	return 0;
}


static int
doarpqueue (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return setint (&Maxarpq, maxqueue, argc, argv);
}


static int
doarpexpire (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return setint (&ArpExpire, exptime, argc, argv);
}


/* Remove an ARP entry */
static int
doarpdrop (argc, argvv, p)
int argc;
char *argvv[];
void *p OPTIONAL;
{
char const **argv = (char const **) argvv;
int16 hardware;
uint32 addr;
struct arp_tab *ap;
struct iface *iface;

	if ((addr = resolve (argv[1])) == 0) {
		tprintf (Badhost, argv[1]);
		return 1;
	}
	if (argc == 3 && tolower (argv[2][0]) != 'n') {
		tputs (dropusage);
		return -1;
	}
	/* This is a kludge. It really ought to be table driven */
	switch (tolower (argv[2][0])) {
		case 'n':
			argc = 4;
			argv[3] = netromstr;	/* Force use of netrom interface */
			hardware = ARP_NETROM;
			break;
		case 'e':	/* "ether" */
			hardware = ARP_ETHER;
			break;
		case 'a':	/* "ax25" */
			switch (tolower (argv[2][1])) {
				case 'x':
					hardware = ARP_AX25;
					break;
				case 'r':
					hardware = ARP_ARCNET;
					break;
				default:
					tputs (dropusage);
					return -1;
			}
			break;
		case 'm':	/* "mac appletalk" */
			hardware = ARP_APPLETALK;
			break;
		default:
			tputs (dropusage);
			return -1;
	}
	if ((iface = if_lookup (argv[3])) == NULLIF) {
		tprintf (noiface, argv[3]);
		return 1;
	}
	if ((ap = arp_lookup (hardware, addr, iface)) == NULLARP)
		return -1;
	arp_drop (ap);
	return 0;
}


/* Flush all automatic entries in the arp cache */
static int
doarpflush (argc, argv, p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
register struct arp_tab *ap;
struct arp_tab *aptmp;
int i;

	for (i = 0; i < HASHMOD; i++) {
		for (ap = Arp_tab[i]; ap != NULLARP; ap = aptmp) {
			aptmp = ap->next;
			if (dur_timer (&ap->timer) != 0)
				arp_drop (ap);
		}
	}
	return 0;
}


/* Dump ARP table */
static void
dumparp ()
{
register int i, j, k, flow_tmp;
register struct arp_tab *ap;
char *temp;

	flow_tmp = Current->flowmode;
	Current->flowmode = 1;

	tprintf (dumpstr, Arp_stat.recv, Arp_stat.badtype, Arp_stat.badaddr, Arp_stat.inreq,
		 Arp_stat.replies, Arp_stat.outreq);

	for (i = 0, j = 0; i < HASHMOD; i++)
		for (ap = Arp_tab[i]; ap != (struct arp_tab *) NULL; ap = ap->next, j++) ;

	if (j) {

		tputs (dumpheader);

		temp = mallocw ((unsigned) j * 80);

		for (i = 0, k = 0; i < HASHMOD; i++) {
			for (ap = Arp_tab[i]; ap != (struct arp_tab *) NULL; ap = ap->next, k += 80)
				make_arp_string (ap, &temp[k]);
		}

		if (Arp_Sort)
			qsort (temp, (size_t) j, 80, (int (*)(const void *, const void *)) strcmp);

		for (i = 0, k = 4; i < j; i++, k += 80) {
			tputs (&temp[k]);
			if (tputc ('\n') == EOF)
				break;
		}
		free (temp);
	}
	Current->flowmode = flow_tmp;
}


static void
make_arp_string (ap, buf)
register struct arp_tab *ap;
char *buf;
{
char e[128];
int a = 0;
char *name;

	if (DTranslate && (name = resolve_a (ap->ip_addr, !DVerbose)) != NULLCHAR) {
		strcpy (buf, name);
		a += 4;
		free (name);
	} else
		a = SPRINTF ((buf, "%4.4s", inet_ntobos (ap->ip_addr)));

	a += SPRINTF ((&buf[a], "%-15.15s ", inet_ntoa (ap->ip_addr)));
	a += SPRINTF ((&buf[a], "%-14.14s ", smsg (Arptypes, NHWTYPES, (unsigned) ap->hardware)));
	a += SPRINTF ((&buf[a], "%4ld ", read_timer (&ap->timer) / 1000L));

	if (ap->state == ARP_PENDING)
		a += SPRINTF ((&buf[a], "%1.1u ", len_q (ap->pending)));
	else
		a += SPRINTF ((&buf[a], "  "));

	if (ap->state == ARP_VALID) {
		if (Arp_type[ap->hardware].format != NULL)
			(void) (*Arp_type[ap->hardware].format) (e, ap->hw_addr);
		else
			e[0] = '\0';
		a += SPRINTF ((&buf[a], "%-17.17s ", e));
	} else
		a += SPRINTF ((&buf[a], unknownstr));

	if (ap->iface)
		a += SPRINTF ((&buf[a], "%-6.6s ", ap->iface->name));

	if (ap->pub)
		a += SPRINTF ((&buf[a], publishedstr));

	return;
}


/* Sort ARP dump */
static int
doarpsort (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return setbool (&Arp_Sort, "ARP Sort flag", argc, argv);
}
