/*   
*     ARP
*/
#include "shared.h"
#include "config.h"
#include <string.h>
#include "funcdef.h"

static void _near cacheupdate(u_char _near *, u_long);
extern u_long _fastcall n_clicks(void);

/************************************************************************
*
*   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.
*
*/
extern struct real_data shared;
u_char etherall[DADDLEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

union arpbuff {
	struct	arp	arp;
	u_char	data[AMAXLEN];
};
union arpbuff arpbuffer;
#define	arpbuf	arpbuffer.arp

void
arpinit()
{
	memcpy(shared.xmitarp.d.me, shared.myether, DADDLEN);
	shared.xmitarp.d.type = EARP;		/* 0x0806 is ARP type */
	shared.xmitarp.hrd = htons(HTYPE);	/*  Ether = 1, swabbed  */
	shared.xmitarp.pro = htons(ARPPRO);  /* IP protocol = 0x0800, swabbed */
	shared.xmitarp.hln = DADDLEN;		/* Ethernet hardware length */
	shared.xmitarp.pln = 4;			/* IP length = 4 */
	shared.xmitarp.spa = shared.myip;	/* sender's IP addr */
	memcpy(shared.xmitarp.sha, shared.myether, DADDLEN); /* sender's hardware addr */
	memcpy(shared.xmitarp.tha, etherall, DADDLEN); /* target hardware addr */
	if (shared.myip)
		sendarp(etherall, shared.myip, htons(ARPREQ));
}

void _near
sendarp(thaptr, tipnum, op)
u_char _near *thaptr;
u_long	tipnum;
u_int	op;
{
	if (shared.arpqueued)
		return;
	shared.arpqueued = 1;
	(void) memcpy(shared.xmitarp.tha, thaptr, DADDLEN);
	(void) memcpy(shared.xmitarp.d.dest, thaptr, DADDLEN);
	shared.xmitarp.tpa = tipnum;		/* requester's IP address */
	shared.xmitarp.op = op;
}

/*************************************************************************
* cacheupdate
*  We just received an ARP, or reply to ARP and need to add the information
*  to the cache.
*/
static void _near
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 < NARPC; i++) {
		if (ipn == shared.arpc[i].ip)
			break;
		if (shared.arpc[i].tm < timer) {
			timer = shared.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 == NARPC) {
		i = found;
		shared.arpc[found].ip = ipn;
	}
	(void) memcpy(shared.arpc[i].hrd, hrdptr, DADDLEN);
	shared.arpc[i].tm = n_clicks();
}

/************************************************************************
*  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()
{

	if (arpbuf.spa == shared.myip)
		return;

/*
*  check packet's desired IP address translation to see if it wants
*  me to answer.
*/
	switch (ntohs(arpbuf.op)) {
	    case ARPREQ:
		if (arpbuf.tpa == shared.myip) { 
			cacheupdate(arpbuf.sha, arpbuf.spa);
			sendarp(arpbuf.sha, arpbuf.spa, htons(ARPREP));
		}
		break;
#ifdef notdef
/*
*  Check for a RARP reply.  If present, set my ip address
*/
	    case RARPR:
		if (!memcmp(arpbuf.tha, shared.myether, DADDLEN)) {
			/* copy in the dest ip address as my ip address */
			arp.spa = arpbuf.tpa;
			blankip.i.ipsource = arpbuf.tpa;
			ipout.i.ipsource = arpbuf.tpa;
		}
		break;
#endif
/* 
*  Check for a reply that I probably asked for.
*/
	    case ARPREP:
		if (arpbuf.tpa == shared.myip && arpbuf.hrd == htons(HTYPE) &&
		    arpbuf.hln == DADDLEN && arpbuf.pln == 4 ) {
			cacheupdate(arpbuf.sha, arpbuf.spa);
		}
		break;
	}
}
