/*   
*     ARP
*/
#include "pcdefs.h"
#include "mbuf.h"
#include "protocol.h"
#include "data.h"
#include "config.h"
#include "funcdef.h"

static void _near _fastcall cacheupdate(u_char _near *, u_long);
static void _near queuearp(u_char _near *, u_long, u_int);

/************************************************************************
*
*   Address Resolution Protocol handling.  This can be looked at as
*   Ethernet-dependent, but the data structure can handle any ARP
*   hardware, with minor changes here.
*
*/
int _near
sendarp(thaptr, tipnum, op)
u_char _near *thaptr;
u_long	tipnum;
u_int	op;
{

	(void) memcpy((u_char _near *)arp.tha, thaptr, DADDLEN);
	if (op == htons(RARPQ)) {
	    arp.d.type = ERARP;
	    (void) memcpy((u_char _near *)arp.d.dest, etherall, DADDLEN);
	} else
	    (void) memcpy((u_char _near *)arp.d.dest, thaptr, DADDLEN);
	arp.tpa = tipnum;		/* requester's IP address */
	arp.op = op;
	return pkxmit((u_char far *)&arp, sizeof(arp));
}

void _near
queuearp(thaptr, tipnum, op)
u_char _near *thaptr;
u_long	tipnum;
u_int	op;
{

	if (arp_queued)
		return;
	(void) memcpy((u_char _near *)arp.tha, thaptr, DADDLEN);
	(void) memcpy((u_char _near *)arp.d.dest, thaptr, DADDLEN);
	arp.tpa = tipnum;		/* requester's IP address */
	arp.op = op;
	arp_queued = 1;
}

/*************************************************************************
* cacheupdate
*  We just received an ARP, or reply to ARP and need to add the information
*  to the cache.
*/
static void _near _fastcall
cacheupdate(hrdptr, ipn)
u_char	_near *hrdptr;
u_long	ipn;
{
	u_int	i;
	u_int	found;
	u_long	timer;

/*
* linear search to see if we already have this entry
*/
	timer = 0x7fffffffL;
	for (i = 1; i < NPORTS; i++) {
		if (ipn == arpc[i].ip)
			break;
		if (arpc[i].tm < timer) {
			timer = arpc[i].tm;
			found = i;
		}
	}

/*
*  if that IP number is not already here, take the oldest entry.
*  If it is already here, update the info and reset the timer.
*  These were pre-initialized to 0, so if any are blank, they will be
*  taken first because they are faked to be oldest.
*/
	if (i == NPORTS) {
		i = found;
		arpc[found].ip = ipn;
	}
	(void) memcpy((u_char _near *)arpc[i].hrd, hrdptr, DADDLEN);
	arpc[i].tm = n_clicks();
}

/*************************************************************************
*  cachelook
*   look up information in the cache
*   returns the cache entry number for the IP number given.
*   Returns -1 on no valid entry, also if the entry present is too old.
*/
u_char _near * _near _fastcall
cachelook(ipn)
u_long	ipn;
{
	int	i;
	static u_long	arptime = 0L;

	for (i = 0; i < NPORTS; i++) {
		if (ipn == arpc[i].ip && 
		    arpc[i].tm + CACHETO > n_clicks()) {
			return((u_char _near *)arpc[i].hrd);
		}
	}
/*
*  no valid entry, send an ARP
*/
	/* check time limit */
	if (n_clicks() >= arptime) {
		/* put out a broadcast request */
		(void) sendarp(etherall, ipn, htons(ARPREQ));
		arptime = n_clicks() + ARPTO;
	}
	return(NNULL);
}

/************************************************************************
*  interpret ARP packets
*   Look at incoming ARP packet and make required assessment of usefulness,
*   check to see if we requested this packet, clear all appropriate flags.
*/
void _near
arpinterpret()
{

/*
*  check packet's desired IP address translation to see if it wants
*  me to answer.
*/
	switch (ntohs(arpbuf.op)) {
	    case ARPREQ:
		if (!Scon.myip)
		    return;
		if (arpbuf.tpa == Scon.myip) { 
			(void) cacheupdate(arpbuf.sha, arpbuf.spa);
			(void) queuearp(arpbuf.sha, arpbuf.spa, htons(ARPREP));
		}
		break;
/*
*  Check for a RARP reply.  If present, set my ip address
*/
	    case RARPR:
		if (Scon.myip)
		    return;
		if (!memcmp(arpbuf.tha, myether, DADDLEN)) {
			/* copy in the dest ip address as my ip address */
			Scon.myip = arpbuf.tpa;
			arp.spa = arpbuf.tpa;
			blankip.i.ipsource = arpbuf.tpa;
			ipout.i.ipsource = arpbuf.tpa;
		}
		break;
/* 
*  Check for a reply that I probably asked for.
*/
	    case ARPREP:
		if (!Scon.myip)
		    return;
		if (arpbuf.tpa == Scon.myip && arpbuf.hrd == htons(HTYPE) &&
		    arpbuf.hln == DADDLEN && arpbuf.pln == 4 ) {
			(void) cacheupdate(arpbuf.sha, arpbuf.spa);
		}
		break;
	    default:
dfputs("I don't know what it is\r\n");
	}
}
