/* $Header$
 *
 * Verify that an address is not in use (uses ARP)
 */

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <sys/ioctl.h>
#include <net/nit.h>
#include <net/nip.h>

#define BUFSIZE 4096
#define ERROR -1
#define ARPTIMES 3
#define WAITTIME 1
#define FAIL 1
#define OK 0


verify(s, ni)
int	s;
struct nipinfo *ni;
{
    int i, rfds, xfds, count;
    struct sockaddr sa;
    struct nit_ioc nic;
    struct ether_header *eh;
    struct ether_arp *ea;
    struct nit_hdr *nh;
    unsigned char pkt[sizeof(*nh) + sizeof(*eh) + sizeof(*ea)];
    struct timeval starttime, tmout;
    struct timezone tz;

    nic.nioc_bufspace = BUFSIZE;
    nic.nioc_chunksize = sizeof(struct nit_hdr) +
      sizeof(struct ether_header) + sizeof(struct ether_arp);
    nic.nioc_typetomatch = ETHERTYPE_ARP;
    nic.nioc_snaplen = sizeof(struct ether_arp) + sizeof(struct ether_header);
    nic.nioc_flags = 0;
    if (ioctl(s, SIOCSNIT, &nic) < 0) {
	perror("ioctl");
	return(ERROR);
    }

    nh = (struct nit_hdr *) &pkt[0];
    eh = (struct ether_header *) &pkt[sizeof(*nh)];
    ea = (struct ether_arp *) &pkt[sizeof(*nh) + sizeof(*eh)];

    for (count = 0; count < ARPTIMES; count++) {
	/* Compose and send request */
	bzero(&pkt[0], sizeof(pkt));
	ea->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
	ea->ea_hdr.ar_pro = htons(ETHERTYPE_IP);
	ea->ea_hdr.ar_hln = sizeof(struct ether_addr);
	ea->ea_hdr.ar_pln = sizeof(struct in_addr);
	ea->ea_hdr.ar_op = htons(ARPOP_REQUEST);
	ea->arp_sha[0] = 0;
	ea->arp_sha[1] = 0;
	ea->arp_sha[2] = 0;
	ea->arp_sha[3] = 0;
	ea->arp_sha[4] = 0;
	ea->arp_sha[5] = 0;
	*(long *) &(ea->arp_tpa[0]) = ni->ni_recommend;
	sa.sa_family = AF_UNSPEC;
	eh = (struct ether_header *) &(sa.sa_data[0]);
	for (i = 0; i < 6; i++)
	  eh->ether_dhost[i] = -1;
	eh->ether_type = ETHERTYPE_ARP;
	if (sendto(s, ea, sizeof(*ea), 0, &sa, sizeof(sa)) < 0) {
	    perror("sendto");
	    return(ERROR);
	}

	/* wait for replies */
	gettimeofday(&starttime, &tz);
	rfds = xfds = (1 << s);
	while (1) {
	    gettimeofday(&tmout, &tz);
	    if (tmout.tv_sec >= starttime.tv_sec + WAITTIME)
	      break;
	    tmout.tv_sec = starttime.tv_sec + WAITTIME - tmout.tv_sec;
	    if (select(s+1, &rfds, NULL, &xfds, &tmout) == 0)
	      break;

	    /* get & validate packet */
	    i = read(s, pkt, sizeof(pkt));

	    if ((nh->nh_state != NIT_CATCH) ||
		(nh->nh_datalen < sizeof(*eh) + sizeof(*ea)) ||
		(ea->arp_hrd != htons(ARPHRD_ETHER)) ||
		(ea->arp_pro != htons(ETHERTYPE_IP)) ||
		(ea->arp_hln != sizeof(struct ether_addr)) ||
		(ea->arp_pln != sizeof(struct in_addr)) ||
		(ea->arp_op != htons(ARPOP_REPLY)) ||
		(*(u_long *) &(ea->arp_spa[0]) != ni->ni_recommend))
	      continue;

/*	    printf("sender: %d.%d.%d.%d %02x%02x%02x%02x%02x%02x\n",
		   ea->arp_spa[0],ea->arp_spa[1],ea->arp_spa[2],ea->arp_spa[3],
		   ea->arp_sha[0],ea->arp_sha[1],ea->arp_sha[2],
		   ea->arp_sha[3],ea->arp_sha[4],ea->arp_sha[5]);
	    printf("target: %d.%d.%d.%d %02x%02x%02x%02x%02x%02x\n",
		   ea->arp_tpa[0],ea->arp_tpa[1],ea->arp_tpa[2],ea->arp_tpa[3],
		   ea->arp_tha[0],ea->arp_tha[1],ea->arp_tha[2],
		   ea->arp_tha[3],ea->arp_tha[4],ea->arp_tha[5]);
*/
	    return(FAIL);
	}
    }
    return(OK);
}
