
#include <types.h>
#include "ether.h"
	
/* flags for the nipinfo structure: */
#define NI_NETADDR	0x01
#define NI_NETMASK	0x02
#define NI_BROADCAST	0x04
#define NI_LOWEST	0x08
#define NI_HIGHEST	0x10
#define NI_RECOMMEND	0x20
#define NI_GATEWAY	0x40

extern unss cksum();

/* actual packet format */

struct nip_pkt {
	struct et_hdr	np_et;	/* with 2 bytes of pad at the front */
	byte		np_source[6];
	unss		np_checksum;
	unss		np_opcode;
	unss		np_version;
	unsl		np_netaddress;
	unsl		np_netmask;
	unsl		np_broadcast;
	unsl		np_lowest;
	unsl		np_highest;
	unsl		np_recommend;
	unsl		np_gateway;
};


/* Opcodes */


/* ethernet type code for NIP */
#ifdef ibm032
#define ETHERTYPE_NIP	0x0801
#define NIP_REQUEST	1
#define NIP_RESPONSE	2

#define NIP_VERSION	1
#else
#define ETHERTYPE_NIP	0x0108
#define NIP_REQUEST	0x100
#define NIP_RESPONSE	0x200

#define NIP_VERSION	0x100
#endif

static struct nip_pkt pkt;
static char in_buf[2048];

/* XXX this doesn't belong here */

void bzero (ptr, len)
	register byte *ptr;
	register int len;
{
	while (len--) *ptr++ = 0;
}
	

struct nip_pkt *nip_get()
{
	register int i, len;
	register struct nip_pkt *ipkt;

	bzero(&pkt, sizeof(pkt));
	etadcpy(etmyaddr, pkt.np_source);
	etadcpy(etmyaddr, pkt.np_et.et_src);
	etadcpy(etbroad, pkt.np_et.et_dst);
	printf("NIP\n");
	
	pkt.np_et.et_type = ETHERTYPE_NIP;
	pkt.np_opcode = NIP_REQUEST;
	pkt.np_version = NIP_VERSION;
	
	pkt.np_checksum = ~cksum(pkt.np_source, (sizeof (struct nip_pkt) - sizeof (struct et_hdr))>>1, 0);
	printf("checksum is %x\n", pkt.np_checksum);
	
	for (;;) {
		printf("About to send..\n");
		et_write (pkt.np_et.et_dst, sizeof(pkt)-2);
		printf("Sent..\n");
		
		for (i=0; i < 0xfff; i++) {
			if ((len = et_read (in_buf, 2048)) >= (sizeof (pkt) - 2)) {
				printf ("got reply: %d bytes\n", len);
				ipkt = (struct nip_pkt *) (in_buf - 2);
				if (ipkt->np_et.et_type != ETHERTYPE_NIP) {
					printf("Bad ether type\n");
					continue;
				}
				if ((ipkt->np_version != NIP_VERSION)) {
					printf("bogus version %x %x\n",
					       ipkt->np_version,
					       NIP_VERSION);
					continue;
				}
				if ((ipkt->np_opcode != NIP_RESPONSE)) {
					printf("bogus opcode\n");
					continue;
				}
				if ((~cksum ((unsigned short *) ipkt->np_source,
					    (sizeof (struct nip_pkt) - sizeof (struct et_hdr))>>1, 0)
				     != 0)) {
					printf("bogus cksum; rcksum %x\n", ipkt->np_checksum);
					ipkt->np_checksum = 0;
					printf("ccksum %x\n", ~cksum((unsigned short *) ipkt->np_source,
								     (sizeof (struct nip_pkt) - sizeof (struct et_hdr))>>1, 0));
				}
				goto got_reply;
			}
		}
		printf("Timeout\n");
		continue;
	got_reply:
		printf ("netaddress %x\n", ipkt->np_netaddress);
		printf ("netmask %x\n", ipkt->np_netmask);
		printf ("broadcast %x\n", ipkt->np_broadcast);
		printf ("gateway %x\n", ipkt->np_gateway);
		printf ("lowest %x\n",  ipkt->np_lowest);
		printf ("highest %x\n",  ipkt->np_highest);
		printf ("recommend %x\n", ipkt->np_recommend);

		return (ipkt);
	}
}

char eth[6];

nip_verify (address, defend)
	unsl address;
	int defend;
{
	struct adr adr;
	struct adr *padr2 = (struct adr *)(in_buf - 2);
	int i, j, cc;
	
	printf("verifying address %x\n", address);
	bzero (&adr, sizeof(adr));
	etadcpy (etbroad, adr.ar_et.et_dst);
	etadcpy (etmyaddr, adr.ar_et.et_src);
	adr.ar_et.et_type = ETHER_ARP;
	adr.ar_hd = ETHER_HTYPE;
	adr.ar_pro = ETHER_IP;
	adr.ar_hln = ETHER_HLEN;
	adr.ar_pln = 4;
	adr.ar_op = ARP_REQ;
	etadcpy(etmyaddr, adr.ar_sha);
	ipadcpy (&address, adr.ar_tpa);
	if (defend)
		ipadcpy (&address, adr.ar_spa);
	
	for (i=0 ; i < 5; i++) {
		et_write (adr.ar_et.et_dst, sizeof (struct adr)-2);
		for (j=0; j < 50; j++) {
			if (defend)
				cc = net_read (padr2, sizeof(in_buf));
			else
				cc = et_read (padr2->ar_et.et_dst,
					      sizeof(in_buf));
			if (cc = 0) continue;
			if (padr2->ar_et.et_type != ETHER_ARP) continue;
			if (padr2->ar_op != ARP_REP) continue;
			if (bcmp(padr2->ar_spa, &address, 4)) continue;
			printf ("not verified\n");
			return 0;
		}
	}
	printf("address not in use.\n");
	return 1;
}

nip_set_address()
{
	register struct nip_pkt *np;
	long address, low, high;
	long count = 0;
	
	np = nip_get();
#ifdef BROKEN_NIP_SVR
	low = swabl(np->np_lowest);
	high = swabl(np->np_highest);
#else
	low = np->np_lowest;
	high = np->np_highest;
#endif
	if (np->np_recommend) {
#ifdef BROKEN_NIP_SVR
		address = np->np_recommend;
#else
		address = swabl(np->np_recommend);
#endif
		if (nip_verify (address, 0)) goto gotit;
	}
lostit:
	low = swabl(low);
	high = swabl(high);
	for (; count < high - low; count++) {
		address = hash (etmyaddr, low, high, count);
		if (nip_verify (address, 0)) goto gotit;
	}
	printf("Unable to find an address to use.\n");
	asm ("halt");
gotit:
	printf("Chose address %x\n", address);
	in_me = address;
	if (!nip_verify (address, 1)) {
		printf ("Somebody else did, too\n");
		in_me = 0;
		goto lostit;
	}
	printf("On the air.\n");
}

hash(pa, low, high, x)
	byte	*pa;
	long low, high;
	int	x;
{
	unsl tmp;
	/*
	 * Hash based on the manufacturer-assigned
	 * half of the ethernet address.
	 */
	tmp = pa[3] + x * pa[4] + pa[5];
	if (pa[4] == 0)
		tmp += x;
	tmp %= high - low;
	return swabl(low + tmp);
}
