#ifndef lint
static char *RCSid = "$Header: /usr/src/ecn/getethers/RCS/if.c,v 1.4 92/05/08 14:19:29 davy Exp $";
#endif

/*
 * if.c - routines to check a system's ethernet interfaces.
 *
 * David A. Curry
 * Purdue University
 * Engineering Computer Network
 * davy@ecn.purdue.edu
 * November, 1991
 *
 * $Log:	if.c,v $
 * Revision 1.4  92/05/08  14:19:29  davy
 * Fix from David Ferbrache (ferbrache@ajaz.rsre.mod.uk) for passing over
 * non-Ethernet links.
 * 
 * Revision 1.3  92/05/08  14:15:58  davy
 * Made portable to 4.3BSD, from Peter Shipley (shipley@tfs.com).
 * 
 * Revision 1.2  92/05/08  13:06:36  davy
 * Added vendor name printing from Peter Shipley (shipley@tfs.com).
 * 
 * Revision 1.1  91/11/27  10:56:31  davy
 * Initial revision
 * 
 */

#ifdef sun
/*
 *  This is a Sun with a NIT device.
 */

#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/file.h>
#include <net/if.h>
#include <net/nit_if.h>
#include <netinet/if_ether.h>
#include <string.h>
#include <stdio.h>
#include "defs.h"

extern int		verbose;
extern HostInfo		*hosts;
extern unsigned long	HostCount;

/*
 * check_if - check our ethernet interfaces and find the one that's attached
 *	      to the network in addr.
 */

check_if(addr)
struct in_addr addr;
{
	int n, s, lna;
	char buf[BUFSIZ];
	struct ifreq ifr;
	struct ifconf ifc;
	char *vendor_name();
	struct sockaddr *sad;
	struct sockaddr_in *sin;
	register struct ifreq *ifrp;

	/*
	 * Need a socket...
	 */
	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		error("socket");
		return(-1);
	}

	ifc.ifc_buf = buf;
	ifc.ifc_len = sizeof(buf);

	/*
	 * Get the list of configured interfaces.
	 */
	if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {
		error("ioctl: SIOCGIFCONF");
		close(s);

		return(-1);
	}

	close(s);

	/*
	 * For each interface...
	 */
	ifrp = ifc.ifc_req;
	for (n = ifc.ifc_len/sizeof(struct ifreq); n > 0; n--, ifrp++) {
		bcopy((char *) ifrp, (char *) &ifr, sizeof(struct ifreq));

		/*
		 * Need a new socket.
		 */
		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			error("socket");
			return(-1);
		}

		/*
		 * Get the address of this interface.
		 */
		if (ioctl(s, SIOCGIFADDR, (char *) &ifr) < 0) {
			error("ioctl: SIOCGIFADDR");
			close(s);
			
			continue;
		}

		close(s);

		sin = (struct sockaddr_in *) &ifr.ifr_addr;

		/*
		 * If it's not the one we need, bag it.
		 */
		if ((sin->sin_addr.s_addr & DEFAULTMASK) != addr.s_addr)
			continue;

		/*
		 * Save the internet address, and keep the host byte.
		 * We are assuming a netmask of 255.255.255.0 here.
		 */
		lna = inet_lnaof(sin->sin_addr) & ~DEFAULTMASK;
		hosts[lna].hl_inet = strdup(inet_ntoa(sin->sin_addr));

		/*
		 * Now we need the NIT, to get the ethernet address.
		 */
		if ((s = open("/dev/nit", O_RDONLY)) < 0) {
			error("open: /dev/nit");
			return(-1);
		}

		/*
		 * Bind the nit to this interface.
		 */
		if (ioctl(s, NIOCBIND, (char *) ifrp) < 0) {
			error("ioctl: NIOCBIND");
			close(s);

			return(-1);
		}

		/*
		 * Get the address.
		 */
		if (ioctl(s, SIOCGIFADDR, (char *) ifrp) < 0) {
			error("ioctl: SIOCGIFADDR");
			close(s);

			continue;
		}

		close(s);

		/*
		 * Save the ethernet address.
		 */
		sad = (struct sockaddr *) &ifrp->ifr_addr;
		hosts[lna].hl_ether = strdup(ether_ntoa((struct ether_addr *)
							sad->sa_data));
		hosts[lna].hl_vname = vendor_name((struct ether_addr *)
						  sad->sa_data);

		return(lna);
	}

	return(-1);
}

#else
/*
 *  Not a Sun with a NIT device.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/if_ether.h>
#include <string.h>
#include <netdb.h>
#include "defs.h"

extern int		verbose,
			large_subnets;
extern HostInfo		*hosts;
extern unsigned long	HostCount,
			CurrentSize;

static char	buffer[BUFSIZ];

int
get_if_info (addr, netmask, address)
struct in_addr	*addr,
		*netmask,
		*address;
{
    int			s, ifcount, ifnum;
    struct ifreq	ifr,
			*ifrp;
    struct ifconf	ifc;
    
    if ((s = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
	perror ("socket");
	close (s);
	return (-1);
    }

	ifc.ifc_buf = buffer;
	ifc.ifc_len = sizeof (buffer);

	if (ioctl (s, SIOCGIFCONF, &ifc)) {
	    perror ("ioctl (SIOCGIFCONF)");
	    close (s);
	    return (-1);
	}

	ifcount = ifc.ifc_len / sizeof (struct ifreq);

	for (ifrp = ifc.ifc_req, ifnum = 0;
	     ifnum < ifcount;
	     ifrp++, ifnum++) {

	    memcpy (&ifr, &ifrp[ifnum], sizeof (struct ifreq));
	    
	    if (verbose) {
		fprintf (stderr,
			 "Checking interface[%d]: %s\n",
			 ifnum, ifr.ifr_name);
	    }

	    if (ioctl (s, SIOCGIFADDR, &ifr)) {
		perror ("ioctl (SIOCGIFADDR)");
		continue;
	    }

	    address->s_addr =
	    ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;

	    if (ioctl (s, SIOCGIFNETMASK, &ifr)) {
		perror ("ioctl (SIOCGIFNETMASK)");
		continue;
	    }

	    netmask->s_addr =
	    ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;

	    if ((address->s_addr & netmask->s_addr)
		== (addr->s_addr & netmask->s_addr)) {

		return (0);
	    }
	}

	close (s);
	netmask->s_addr = address->s_addr = 0;

	return (-1);
}

check_if(addr)
struct in_addr addr;
{
	int		lna;
	static struct in_addr
			netmask,
			address;
	struct hostent	*hp;
	struct in_addr	*myaddr;
	static char	MyHostname[64];
	char	*tmp;

	(void) gethostname (MyHostname, sizeof(MyHostname));
	hp = gethostbyname(MyHostname);

	if (get_if_info (&addr, &netmask, &address) < 0) {
	    goto The_Easy_Way;
	}

	HostCount = ~ntohl (netmask.s_addr);

	if (!large_subnets && (HostCount > ~DEFAULTMASK)) {
	    netmask.s_addr = htonl (DEFAULTMASK);
	    HostCount = ~DEFAULTMASK;
	}

	if (CurrentSize < HostCount) {
	    if (CurrentSize > MAXHOST) {
		free (hosts);
	    }
	    hosts = (HostInfo *) calloc (HostCount, sizeof (HostInfo));
	    CurrentSize = HostCount;
	}

	lna = inet_lnaof (address) & HostCount;

	if (hp) {
	    hosts[lna].hl_name = strdup (hp->h_name);
	}
	else {
	    hosts[lna].hl_name = "";
	}

	hosts[lna].hl_inet = strdup (inet_ntoa (address));
	hosts[lna].hl_ether = "?:?:?:?:?:?";
	hosts[lna].hl_vname = "???";

	return (lna);

The_Easy_Way:	    /*	Easier but the one above is the more orthodox */

	if (!hp) {
	    return (-1);
	}

	myaddr = (struct in_addr *) hp->h_addr_list[0];

	while (myaddr->s_addr &&
	       (myaddr->s_addr & DEFAULTMASK) != addr.s_addr) {
	    myaddr++;
	    lna = inet_lnaof (myaddr->s_addr) & ~DEFAULTMASK;
	}

	if (!myaddr->s_addr) {
	    return (-1);
	}

	hosts[lna].hl_inet = strdup (inet_ntoa (myaddr->s_addr));
	hosts[lna].hl_name = strdup (hp->h_name);
	hosts[lna].hl_ether = "???";
	hosts[lna].hl_vname = "???";
	return (lna);
}

#endif
