/*
 * Public Release 3
 * 
 * $Id: krt_ifread_ioctl.c,v 1.2 1997/07/17 22:42:50 chopps Exp $
 */

/*
 * ------------------------------------------------------------------------
 * 
 * Copyright (c) 1996, 1997 The Regents of the University of Michigan
 * All Rights Reserved
 *  
 * Royalty-free licenses to redistribute GateD Release
 * 3 in whole or in part may be obtained by writing to:
 * 
 * 	Merit GateDaemon Project
 * 	4251 Plymouth Road, Suite C
 * 	Ann Arbor, MI 48105
 *  
 * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE
 * UNIVERSITY OF MICHIGAN AND MERIT DO NOT WARRANT THAT THE
 * FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR
 * THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. The Regents of the
 * University of Michigan and Merit shall not be liable for
 * any special, indirect, incidental or consequential damages with respect
 * to any claim by Licensee or any third party arising from use of the
 * software. GateDaemon was originated and developed through release 3.0
 * by Cornell University and its collaborators.
 * 
 * Please forward bug fixes, enhancements and questions to the
 * gated mailing list: gated-people@gated.merit.edu.
 * 
 * ------------------------------------------------------------------------
 * 
 * Copyright (c) 1990,1991,1992,1993,1994,1995 by Cornell University.
 *     All rights reserved.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * GateD is based on Kirton's EGP, UC Berkeley's routing
 * daemon	 (routed), and DCN's HELLO routing Protocol.
 * Development of GateD has been supported in part by the
 * National Science Foundation.
 * 
 * ------------------------------------------------------------------------
 * 
 * Portions of this software may fall under the following
 * copyrights:
 * 
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms are
 * permitted provided that the above copyright notice and
 * this paragraph are duplicated in all such forms and that
 * any documentation, advertising materials, and other
 * materials related to such distribution and use
 * acknowledge that the software was developed by the
 * University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote
 * products derived from this software without specific
 * prior written permission.  THIS SOFTWARE IS PROVIDED
 * ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */


#define	INCLUDE_IOCTL
#define	INCLUDE_IF
#define	INCLUDE_ISO_VAR
#include "include.h"
#ifdef	PROTO_INET
#include "inet.h"
#endif	/* PROTO_INET */
#ifdef	PROTO_ISO
#include "iso.h"
#endif	/* PROTO_ISO */
#include "krt.h"
#include "krt_var.h"


#if	IFNAMSIZ > IFL_NAMELEN
error - IFL_NAMELEN not compatible with IFNAMESIZ
#endif

/* Make some assumptions about interface MTU */
static inline mtu_t
krt_mtu __PF1(state, flag_t)
{
    switch (BIT_TEST(state, IFS_POINTOPOINT|IFS_LOOPBACK|IFS_BROADCAST)) {
    case IFS_POINTOPOINT:
	/* Compressed SLIP interfaces use 256, it can't really hurt to specify it */
	/* too low. */
	return 256;

    case IFS_LOOPBACK:
	/* Loopback interfaces use more */
	return 1536;
	
    case IFS_BROADCAST:
	/* Assume an Ethernet */
	return 1500;
    }

    /* A safe assumption? */
    return 576;
}


int
krt_ifread __PF1(save_task_state, flag_t)
{
    u_int indx = 0;
    caddr_t limit;
    struct ifreq *ifrp;
    if_link *ifl = (if_link *) 0;
#ifdef	PROTO_ISO
    static struct iso_ifreq iso_ifreq;
    static struct ifreq *ifr = (struct ifreq *) &iso_ifreq;
#else	/* PROTO_ISO */
    static struct ifreq ifreq;
    static struct ifreq *ifr = &ifreq;
#endif	/* PROTO_ISO */
    register task *tp = krt_task;
    static int s_in = -1;
#ifdef	ISOPROTO_RAW
    static int s_iso = -1;
#endif	/* ISOPROTO_RAW */

    if (tp->task_socket < 0) {
	return EBADF;
    }

    if (!task_send_buffer) {
	/* We need a send buffer */

	task_alloc_send(tp, task_pagesize);
    }
    
    do {
	int rc;
	size_t size;

#if	defined(USE_STREAMIO) && !defined(SUNOS5_0)
	struct strioctl si;
 
	si.ic_cmd = SIOCGIFCONF;
	si.ic_timout = 0;
	si.ic_len = task_send_buffer_len;
	si.ic_dp = (caddr_t) task_send_buffer;
    
	NON_INTR(rc, ioctl(tp->task_socket, I_STR, &si));

	limit = si.ic_dp + (size = si.ic_len);

#else	/* defined(USE_STREAMIO) && !defined(SUNOS5_0) */
	static struct ifconf ifconf_req;

	ifconf_req.ifc_buf = (caddr_t) task_send_buffer;
	ifconf_req.ifc_len = task_send_buffer_len;

#if defined(SUNOS5_0) || defined(HPSTREAMS)
	NON_INTR(rc, ioctl(tp->task_socket, 
			   SIOCGIFCONF, 
			   &ifconf_req));
#else	/* SUNOS5_0 || HPSTREAMS */
	rc = task_ioctl(tp->task_socket,
			(u_long) SIOCGIFCONF,
			(void_t) &ifconf_req,
			ifconf_req.ifc_len);
#endif	/* SUNOS5_0 || HPSTREAMS */

	limit = ifconf_req.ifc_buf + (size = ifconf_req.ifc_len);

#endif	/* defined(USE_STREAMIO) && !defined(SUNOS5_0) */
	if (rc != 0) {
	    /* Something is wrong */
	    
	    if (errno == EFAULT) {
		/* Hopefully this means that the buffer is not big enough */
		
		continue;
	    }

	    trace_log_tp(tp,
			 0,
			 LOG_ERR,
			 ("krt_ifread: ioctl SIOCGIFCONF: %m"));
	    return errno;
	} else if (size
		   && task_send_buffer_len - size > sizeof *ifr) {
	    /* It appears we have read the complete list */

	    break;
	}
    } while (task_alloc_send(tp, task_send_buffer_len << 1), TRUE) ;

    trace_tp(tp,
	     TR_KRT_IFLIST,
	     TRC_NL_BEFORE,
	     ("krt_iflist: SIOCGIFCONF returns %u bytes",
	      limit - (caddr_t) task_send_buffer));

    /* Tell the IF code that we are passing complete knowledge */
    if_conf_open(tp, TRUE);
    
#define	ifr_size(x) ((unix_socksize(&(x)->ifr_addr) > sizeof((x)->ifr_addr)) ? \
    	sizeof(*(x)) + unix_socksize(&(x)->ifr_addr) - sizeof((x)->ifr_addr) : sizeof(*(x)))
#define	ifr_bump(x) ((struct ifreq *) ((void_t) ((caddr_t) (x) + ifr_size(x))))

    for (ifrp = (struct ifreq *) task_send_buffer;
	 (caddr_t) ifrp < limit;
	 ifrp = ifr_bump(ifrp)) {
	if_info ifi;
	int s = -1;
	u_long siocgifdstaddr = 0;
#ifdef	SIOCGIFNETMASK
	u_long siocgifnetmask = 0;
#endif	/* SIOCGIFNETMASK */
	u_long siocgifbrdaddr = 0;
	char if_name[IFNAMSIZ+1];

	if (TRACE_TP(tp, TR_KRT_IFLIST)) {
	    int size = unix_socksize(&ifrp->ifr_addr);
	    const char *cp = trace_value(task_domain_bits, ifrp->ifr_addr.sa_family);
	    
	    tracef("krt_ifread: name %.*s  length %u  family %u",
		   IFNAMSIZ, ifrp->ifr_name,
		   size,
		   ifrp->ifr_addr.sa_family);
	    if (cp) {
		tracef("(%s)",
		       cp);
	    }

	    switch (ifrp->ifr_addr.sa_family) {
#ifdef	PROTO_INET
	    case AF_INET:
	    {
		struct sockaddr_in *sinp = (struct sockaddr_in *) ((void_t) &ifrp->ifr_addr);
		    
		tracef("  port %u  addr %A",
		       ntohs(sinp->sin_port),
		       sockbuild_in(0, sinp->sin_addr.s_addr));
	    }
		break;
#endif	/* PROTO_INET */

#ifdef	PROTO_ISO
	    case AF_ISO:
	    {
		struct sockaddr_iso *siso = (struct sockaddr_iso *) &ifrp->ifr_addr;
		byte *dp = (byte *) siso->siso_pad;

		tracef("  addr %A",
		       siso->siso_addr.isoa_genaddr,
		       siso->siso_addr.isoa_len);

		if (siso->siso_plen) {
		    tracef("  psel %A",
			   sockbuild_ll(0, dp, siso->siso_plen));
		    dp += siso->siso_plen;
		}
		if (siso->siso_slen) {
		    tracef("  ssel %A",
			   sockbuild_ll(0, dp, siso->siso_slen));
		    dp += siso->siso_slen;
		}
		if (siso->siso_tlen) {
		    tracef("  tsel %A",
			   sockbuild_ll(0, dp, siso->siso_tlen));
		}
	    }
		break;
#endif	/* PROTO_ISO */

#ifdef	SOCKADDR_DL
	    case AF_LINK:
	    {
		struct sockaddr_dl *sdl = (struct sockaddr_dl *) &ifrp->ifr_addr;
		byte *dp = (byte *) sdl->sdl_data;
		
		tracef("  index %u  type %u",
		       sdl->sdl_index,
		       sdl->sdl_type);
		if (sdl->sdl_nlen) {
		    tracef("  name %.*s",
			   sdl->sdl_nlen, dp);
		    dp += sdl->sdl_nlen;
		}
		if (sdl->sdl_alen) {
		    tracef("  addr %A",
			   sockbuild_ll(0, dp, sdl->sdl_alen));
		    dp += sdl->sdl_alen;
		}
		if (sdl->sdl_slen) {
		    tracef("  selector %A",
			   sockbuild_ll(0, dp, sdl->sdl_slen));
		}
	    }
		break;
#endif	/* SOCKADDR_DL */
		
	    default:
		tracef("  addr %A",
		       sockbuild_ll(0,
				    (byte *) ifrp->ifr_addr.sa_data,
				    (size_t) (size - ((byte *) ifrp->ifr_addr.sa_data - (byte *) &ifrp->ifr_addr))));
	    }
	    trace_only_tp(tp,
			  TRC_NL_BEFORE,
			  (NULL));
	}

	bzero((caddr_t) &ifi, sizeof (ifi));

	bcopy(ifrp->ifr_name, if_name, IFNAMSIZ);
	if_name[IFNAMSIZ] = (char) 0;
#if defined(SUNOS5_0) || defined(HPSTREAMS)
        {
	    register char *cp = index(if_name, ':');

	    /* Remove the :n extension from the name */
	    if (cp) {
		*cp = (char) 0;
	    }
	}
#endif	/* SUNOS5_0 || HPSTREAMS */

	if (!ifl
	    || strncmp(if_name, ifl->ifl_name, IFNAMSIZ)) {
	    flag_t state;
	    metric_t metric;
	    mtu_t mtu;

	    /* And save for ioctls */
	    (void) strncpy(ifr->ifr_name, ifrp->ifr_name, IFNAMSIZ);

#if defined(SUNOS5_0) || defined(HPSTREAMS)
	    /* State, MTU and metric are associated with addresses, not the interface */
	    state = mtu = metric = 0;

#else	/* SUNOS5_0 || HPSTREAMS */
	    /* Get interface flags */
	    bzero ((caddr_t) &ifr->ifr_ifru, sizeof (ifr->ifr_ifru));
	    if (task_ioctl(tp->task_socket,
			   (u_long) SIOCGIFFLAGS,
			   (char *) ifr, 
			   sizeof (*ifr)) < 0) {
		trace_log_tp(tp,
			     0,
			     LOG_ERR,
			     ("krt_ifread: %s: ioctl SIOCGIFFLAGS: %m",
			      ifr->ifr_name));
		goto Continue;
	    }
	    state = krt_if_flags(ifr->ifr_flags);

	    /* Get a resonable default */
	    mtu = krt_mtu(state);

#ifdef	SIOCGIFMTU
	    /* Get interface MTU */
	    bzero ((caddr_t) &ifr->ifr_ifru, sizeof (ifr->ifr_ifru));
	    if (task_ioctl(tp->task_socket,
			   (u_long) SIOCGIFMTU,
			   (char *) ifr,
			   sizeof (*ifr)) < 0) {
#ifdef	notdef
		trace_tp(tp,
			 TR_ALL,
			 ("krt_ifread: %s: ioctl SIOCGIFMTU: %m",
			  ifr->ifr_name));
#endif	/* notdef */
	    } else {
		mtu = ifr->KRT_IFR_MTU;
	    }
#endif	/* SIOCGIFMTU */

	    /* Get interface metric */
#if	defined(SIOCGIFMETRIC) && defined(ifr_metric)
	    bzero ((caddr_t) &ifr->ifr_ifru, sizeof (ifr->ifr_ifru));
	    if (task_ioctl(tp->task_socket,
			   (u_long) SIOCGIFMETRIC,
			   (caddr_t)
			   ifr,
			   sizeof (*ifr)) < 0) {
		trace_log_tp(tp,
			     0,
			     LOG_ERR,
			     ("krt_ifread: %s: ioctl SIOCGIFMETRIC: %m",
			      ifr->ifr_name));
		goto Continue;
	    }
	    metric = (ifr->ifr_metric >= 0) ? ifr->ifr_metric : 0;
#else	/* defined(SIOCGIFMETRIC) && defined(ifr_metric) */
	    metric = 0;
#endif	/* defined(SIOCGIFMETRIC) && defined(ifr_metric) */
#endif	/* SUNOS5_0 || HPSTREAMS */


	    {
#ifdef	SOCKADDR_DL
		register struct ifreq *ifrl = ifrp;

		/* Search for an AF_LINK entry because it has */
		/* the index and sometimes the physical address. */
		/* On ``normal'' systems this is the first address, */
		/* but on DEC OSF/1 systems the AF_LINK has been */
		/* observed not to be. */
		for (ifrl = ifrp;
		     (caddr_t) ifrl < limit;
		     ifrl = ifr_bump(ifrl)) {
		    if (strncmp(ifrl->ifr_name, ifrp->ifr_name, IFNAMSIZ)) {
			/* Did not find one in the records for this interface */

			ifrl = (struct ifreq *) 0;
			break;
		    }

		    if (ifrl->ifr_addr.sa_family == AF_LINK) {
			/* Found the AF_LINK entry */
		    
			break;
		    }
		}

		if (ifrl && ((caddr_t) ifrl < limit)) {
		    struct sockaddr_dl *sdl = (struct sockaddr_dl *) &ifrl->ifr_addr;
		    sockaddr_un *lladdr;

		    if (sdl->sdl_alen) {
			/* We have an address */

			lladdr = sockbuild_ll(krt_type_to_ll(sdl->sdl_type),
					      (byte *) (sdl->sdl_data + sdl->sdl_nlen),
					      (size_t) sdl->sdl_alen);
		    } else {
			/* This system may not supply link-level addresses, */
			/* or this interface may not have one.  Just to be sure */
			/* try to look one up */

			lladdr = krt_lladdr(ifr);
		    }

		    ifl = ifl_addup(tp,
				    ifl_locate_index(sdl->sdl_index),
				    (indx = sdl->sdl_index),
				    state,
				    metric,
				    mtu,
				    sdl->sdl_data,
				    (size_t) sdl->sdl_nlen,
				    lladdr);
		} else {
#endif	/* SOCKADDR_DL */
		    size_t n_len = strlen(if_name);
		
		    ifl = ifl_addup(tp,
				    ifl_locate_name(if_name, n_len),
				    ++indx,
				    state,
				    metric,
				    mtu,
				    if_name,
				    n_len,
				    krt_lladdr(ifr));
		}
#ifdef	SOCKADDR_DL
	    }
#endif	/* SOCKADDR_DL */

	    if (TRACE_TP(tp, TR_KRT_IFLIST)) {
		trace_only_tp(tp,
			      0,
			      ("krt_ifread: %s index %d address %A",
			       ifl->ifl_name,
			       ifl->ifl_index,
			       ifl->ifl_addr));
		trace_only_tp(tp,
			      0,
			      ("krt_ifread: %s     metric %u mtu %u state <%s>",
			       ifl->ifl_name,
			       ifl->ifl_metric,
			       ifl->ifl_mtu,
			       trace_bits(if_state_bits, ifl->ifl_state)));
	    }
#if defined(SUNOS5_0) || defined(HPSTREAMS)
	} else {
	    /* Always copy the interface name to get address selector */

	    (void) strncpy(ifr->ifr_name, ifrp->ifr_name, IFNAMSIZ);
#endif	/* SUNOS5_0 || HPSTREAMS */
	}

	bzero((caddr_t) &ifi, sizeof (ifi));
	ifi.ifi_link = ifl;

	/* Reject unknown families */
	switch (ifrp->ifr_addr.sa_family) {
#ifdef	PROTO_INET
	case AF_INET:
	    break;
#endif	/* PROTO_INET */

#ifdef	PROTO_ISO
	case AF_ISO:
	    break;
#endif	/* PROTO_ISO */

	default:
	    /* Bogus address */
	    goto Continue;
	}
	    
	/* Copy the interface address */
	ifi.ifi_addr_local = ifi.ifi_addr = sockdup(sock2gated(&ifrp->ifr_addr, unix_socksize(&ifrp->ifr_addr)));

#if defined(SUNOS5_0) || defined(HPSTREAMS)
	/* Get interface flags */
	bzero ((caddr_t) &ifr->ifr_ifru, sizeof (ifr->ifr_ifru));
	if (task_ioctl(tp->task_socket,
		       (u_long) SIOCGIFFLAGS,
		       (char *) ifr,
		       sizeof (*ifr)) < 0) {
	    trace_log_tp(tp,
			 0,
			 LOG_ERR,
			 ("krt_ifread: %s: ioctl SIOCGIFFLAGS: %m",
			  ifr->ifr_name));
	    goto Continue;
	}
	ifi.ifi_state = krt_if_flags(ifr->ifr_flags);
	if (BIT_TEST(ifi.ifi_state, IFS_LOOPBACK)) {
	    /* Indicate this is a loopback interface */

	    BIT_SET(ifl->ifl_state, IFS_LOOPBACK);
	}

	/* Get interface MTU */
	bzero ((caddr_t) &ifr->ifr_ifru, sizeof (ifr->ifr_ifru));
	if (task_ioctl(tp->task_socket,
		       (u_long) SIOCGIFMTU,
		       (char *) ifr,
		       sizeof (*ifr)) < 0) {
#ifdef	notdef
	    trace_log_tp(tp,
			 0,
			 LOG_INFO,
			 ("krt_ifread: %s: ioctl SIOCGIFMTU: %m",
			  ifr->ifr_name));
#endif	/* notdef */

	    /* Figure out a default */
	    ifi.ifi_mtu = krt_mtu(ifi.ifi_state);
	} else {
	    ifi.ifi_mtu = ifr->KRT_IFR_MTU;
	}

	/* Get interface metric */
	bzero ((caddr_t) &ifr->ifr_ifru, sizeof (ifr->ifr_ifru));
	if (task_ioctl(tp->task_socket,
		       (u_long) SIOCGIFMETRIC,
		       (caddr_t) ifr,
		       sizeof (*ifr)) < 0) {
	    trace_log_tp(tp,
			 0,
			 LOG_ERR,
			 ("krt_ifread: %s: ioctl SIOCGIFMETRIC: %m",
			  ifr->ifr_name));
	    goto Continue;
	}
	ifi.ifi_metric = (ifr->ifr_metric >= 0) ? ifr->ifr_metric : 0;
#else	/* SUNOS5_0 || HPSTREAMS */
	/* Inherit parameters from physical interface */

	ifi.ifi_state = ifl->ifl_state;
	ifi.ifi_metric = ifl->ifl_metric;
	ifi.ifi_mtu = ifl->ifl_mtu;
#endif	/* SUNOS5_0 || HPSTREAMS */

	/* What we do next depends on the family */
	switch (ifrp->ifr_addr.sa_family) {
#ifdef	PROTO_INET
	case AF_INET:
	    /* Specify the right socket for this family */
	    if (s_in < 0) {
		BIT_RESET(task_state, TASKS_INIT|TASKS_TEST);
		s_in = task_floating_socket(tp,
					    task_get_socket(tp, PF_INET, SOCK_DGRAM, 0),
					    "ifread.inet");
		task_state = save_task_state;
	    }
	    s = s_in;
	    if (BIT_TEST(ifi.ifi_state, IFS_POINTOPOINT)) {
		siocgifdstaddr = (u_long) SIOCGIFDSTADDR;
	    }
#ifdef	SIOCGIFNETMASK
	    if (!BIT_TEST(ifi.ifi_state, IFS_LOOPBACK)) {
		siocgifnetmask = (u_long) SIOCGIFNETMASK;
#endif	/* SIOCGIFNETMASK */
	    }
	    if (BIT_TEST(ifi.ifi_state, IFS_BROADCAST)) {
		siocgifbrdaddr = (u_long) SIOCGIFBRDADDR;
	    }
	    break;
#endif	/* PROTO_INET */

#ifdef	PROTO_ISO
	case AF_ISO:
	    /* Specify the right socket for this family */
	    if (s_iso < 0) {
		BIT_RESET(task_state, TASKS_INIT|TASKS_TEST);
		s_iso = task_floating_socket(tp,
					     task_get_socket(tp, PF_ISO, SOCK_DGRAM, 0),
					     "ifread.iso");
		task_state = save_task_state;
	    }
	    s = s_iso;
	    if (BIT_TEST(ifi.ifi_state, IFS_POINTOPOINT)) {
		siocgifdstaddr = SIOCGIFDSTADDR_ISO;
	    } else if (!BIT_TEST(ifi.ifi_state, IFS_LOOPBACK)) {
		siocgifnetmask = SIOCGIFNETMASK_ISO;
	    }
	    break;
#endif	/* PROTO_ISO */

	default:
	    goto Continue;
	}

	if (siocgifdstaddr) {
	    /* Get the destination address for point-to-point interfaces */

	    size_t a_len;

	    bzero ((caddr_t) &ifr->ifr_ifru, sizeof (ifr->ifr_ifru));
	    if (task_ioctl(s,
			   siocgifdstaddr,
			   (caddr_t) ifr,
			   sizeof (*ifr)) < 0) {
		trace_log_tp(tp,
			     0,
			     LOG_ERR,
			     ("krt_ifread: %s: ioctl SIOCGIFDSTADDR: %m",
			      ifr->ifr_name));
		goto Continue;
	    }
	    if ((a_len = unix_socksize(&ifr->ifr_dstaddr))
		&& (ifi.ifi_addr = sock2gated(&ifr->ifr_dstaddr, a_len))) {
		ifi.ifi_addr = sockdup(ifi.ifi_addr);
	    } else {
		ifi.ifi_addr = (sockaddr_un *) 0;
	    }
	}
#ifdef	SIOCGIFNETMASK
	if (siocgifnetmask) {
	    bzero ((caddr_t) &ifr->ifr_ifru, sizeof (ifr->ifr_ifru));
	    if (task_ioctl(s,
			   siocgifnetmask,
			   (caddr_t) ifr,
			   sizeof (*ifr)) < 0) {
		trace_log_tp(tp,
			     0,
			     LOG_ERR,
			     ("krt_ifread: %s: ioctl SIOCGIFNETMASK: %m",
			      ifr->ifr_name));
		goto Continue;
	    }
#ifdef	SOCKET_LENGTHS
	    /* A zero mask would have a length of zero */
	    if (ifr->ifr_addr.sa_len < 2) {
		ifr->ifr_addr.sa_len = 2;	/* Enough for address and family */
	    }
	    /* Masks don't have an address family specified */
	    if (ifr->ifr_addr.sa_family == AF_UNSPEC
		&& ifi.ifi_addr) {
		ifr->ifr_addr.sa_family = socktype(ifi.ifi_addr);
	    }
#endif	/* SOCKET_LENGTHS */
	    /* Convert the mask */
	    ifi.ifi_netmask = sock2gated(&ifr->ifr_addr, unix_socksize(&ifr->ifr_addr));

	    if (ifi.ifi_netmask) {
		/* We have a mask, get pointer to right one */
		ifi.ifi_netmask = mask_locate(ifi.ifi_netmask);
	    }
	}
#else	/* SIOCGIFNETMASK */
	ifi.ifi_netmask = (sockaddr_un *) 0;
#endif	/* SIOCGIFNETMASK */

	/* Get the broadcast address for broadcast interfaces */
	if (siocgifbrdaddr) {
#ifdef SIOCGIFBRDADDR
/* Some systems (SunOS 3.x where x > 2) do not define ifr_broadaddr */
#ifndef	ifr_broadaddr
#define	ifr_broadaddr	ifr_addr
#endif	/* ifr_broadaddr */
	    bzero ((caddr_t) &ifr->ifr_ifru, sizeof (ifr->ifr_ifru));
	    if (task_ioctl(s,
			   siocgifbrdaddr,
			   (caddr_t) ifr,
			   sizeof (*ifr)) < 0) {
		trace_log_tp(tp,
			     0,
			     LOG_ERR,
			     ("krt_ifread: %s: ioctl SIOGIFBRDADDR: %m",
			      ifr->ifr_name));
		goto Continue;
	    }
	    ifi.ifi_addr_broadcast = sock2gated(&ifr->ifr_broadaddr, unix_socksize(&ifr->ifr_broadaddr));
	    if (ifi.ifi_addr_broadcast) {
		ifi.ifi_addr_broadcast = sockdup(ifi.ifi_addr_broadcast);
	    } else {
		trace_log_tp(tp,
			     0,
			     LOG_ERR,
			     ("krt_ifread: no broadcast address for %A (%s)",
			      ifi.ifi_addr_local,
			      ifrp->ifr_name));
		goto Continue;
	    }
#else	/* !SIOCGIFBRDADDR */
	    /* Assume a 4.2 based system with a zeros broadcast */
	    ifi.ifi_addr_broadcast = (sockaddr_un *) 0;
#endif	/* SIOCGIFBRDADDR */
	}

	if (TRACE_TP(tp, TR_KRT_IFLIST)) {
	    tracef("krt_ifread: %s %A",
		   ifl->ifl_name,
		   ifi.ifi_addr_local);
	    if (ifi.ifi_netmask) {
		tracef("/%A",
		       ifi.ifi_netmask);
	    }
	    if (ifi.ifi_addr_broadcast) {
		tracef(" -> %A",
		       ifi.ifi_addr_broadcast);
	    } else if (ifi.ifi_addr != ifi.ifi_addr_local) {
		tracef(" -> %A",
		       ifi.ifi_addr);
	    }
	    trace_only_tp(tp,
			  0,
			  (NULL));
	    trace_only_tp(tp,
			  0,
			  ("krt_ifread: %s     metric %u mtu %u state <%s>",
			   ifl->ifl_name,
			   ifi.ifi_metric,
			   ifi.ifi_mtu,
			   trace_bits(if_state_bits, ifi.ifi_state)));
	}
	
	/* And add the interface */
	if_conf_addaddr(tp, &ifi);
	continue;

    Continue:
	/* Free any allocated addresses */
	ifi_addr_free(&ifi);
    }

    trace_tp(tp,
	     TR_KRT_IFLIST,
	     0,
	     (NULL));
    
    if_conf_close(tp, FALSE);

    return 0;
}
