/*      $NetBSD: getif.c,v 1.6 2003/08/18 05:39:52 itojun Exp $ */

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: getif.c,v 1.6 2003/08/18 05:39:52 itojun Exp $");
#endif

/*
* getif.c : get an interface structure
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/param.h>

#if defined(SUNOS) || defined(SVR4)
#include <sys/sockio.h>
#endif
#ifdef  SVR4
#include <sys/stropts.h>
#endif

#include <net/if.h>                             /* for struct ifreq */
#include <netinet/in.h>

#ifndef NO_UNISTD
#include <unistd.h>
#endif
#include <syslog.h>
#include <errno.h>
#include <assert.h>

#include "getif.h"
#include "report.h"

#ifdef  __bsdi__
#define BSD 43
#endif

static struct ifreq ifreq[10];  /* Holds interface configuration */
static struct ifconf ifconf;    /* points to ifreq */

static int nmatch(u_char *, u_char *);

/* Return a pointer to the interface struct for the passed address. */
struct ifreq *
getif(int s, struct in_addr *addrp)
                                                       /* socket file descriptor */
                                       /* destination address on interface */
{
       int maxmatch;
       int len, m, incr;
       struct ifreq *ifrq, *ifrmax;
       struct sockaddr_in *sip;
       char *p;

       /* If no address was supplied, just return NULL. */
       if (!addrp)
               return (struct ifreq *) 0;

       /* Get the interface config if not done already. */
       if (ifconf.ifc_len == 0) {
#ifdef  SVR4
               /*
                * SysVr4 returns garbage if you do this the obvious way!
                * This one took a while to figure out... -gwr
                */
               struct strioctl ioc;
               ioc.ic_cmd = SIOCGIFCONF;
               ioc.ic_timout = 0;
               ioc.ic_len = sizeof(ifreq);
               ioc.ic_dp = (char *) ifreq;
               m = ioctl(s, I_STR, (char *) &ioc);
               ifconf.ifc_len = ioc.ic_len;
               ifconf.ifc_req = ifreq;
#else   /* SVR4 */
               ifconf.ifc_len = sizeof(ifreq);
               ifconf.ifc_req = ifreq;
               m = ioctl(s, SIOCGIFCONF, (caddr_t) & ifconf);
#endif  /* SVR4 */
               if ((m < 0) || (ifconf.ifc_len <= 0)) {
                       report(LOG_ERR, "ioctl SIOCGIFCONF");
                       return (struct ifreq *) 0;
               }
       }
       maxmatch = 7;                           /* this many bits or less... */
       ifrmax = (struct ifreq *) 0;/* ... is not a valid match  */
       p = (char *) ifreq;
       len = ifconf.ifc_len;
       while (len > 0) {
               ifrq = (struct ifreq *) p;
               if (ifrq->ifr_addr.sa_family == AF_INET) {
                       sip = (struct sockaddr_in *) &ifrq->ifr_addr;
                       m = nmatch((u_char *)addrp, (u_char *)&(sip->sin_addr));
                       if (m > maxmatch) {
                               maxmatch = m;
                               ifrmax = ifrq;
                       }
               }
               /* XXX - Could this be just #ifndef IFNAMSIZ instead? -gwr */
#if (BSD - 0) < 43
               /* BSD not defined or earlier than 4.3 */
               incr = sizeof(*ifrq);
#else /* NetBSD */
               incr = ifrq->ifr_addr.sa_len + IFNAMSIZ;
#endif /* NetBSD */

               p += incr;
               len -= incr;
       }

       return ifrmax;
}

/*
* Return the number of leading bits matching in the
* internet addresses supplied.
*/
static int
nmatch(u_char *ca, u_char *cb)
                                               /* ptrs to IP address, network order */
{
       u_int m = 0;                            /* count of matching bits */
       u_int n = 4;                            /* bytes left, then bitmask */

       /* Count matching bytes. */
       while (n && (*ca == *cb)) {
               ca++;
               cb++;
               m += 8;
               n--;
       }
       /* Now count matching bits. */
       if (n) {
               n = 0x80;
               while (n && ((*ca & n) == (*cb & n))) {
                       m++;
                       n >>= 1;
               }
       }
       return (m);
}

/*
* Local Variables:
* tab-width: 4
* c-indent-level: 4
* c-argdecl-indent: 4
* c-continued-statement-offset: 4
* c-continued-brace-offset: -4
* c-label-offset: -4
* c-brace-offset: 0
* End:
*/