/*
 * Public Release 3
 * 
 * $Id: krt_lladdr_kmem.c,v 1.2 1997/07/17 22:42:59 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_KVM
#define	INCLUDE_CTYPE
#define	INCLUDE_ETHER
#define	INCLUDE_IF
#include "include.h"
#include "krt.h"
#include "krt_var.h"


u_long krt_ifnet;

/*
 *	Default krt_lladdr, poke around in the kernel
 */

/*
 * Obtain physical address of the interface by peeking in the kernel.
 * This *hack* assumes that the interface uses an arpcom structure where
 * the physical address resides immediately after the ifnet structure.
 */
#ifdef	KRT_IF_NETBSD
sockaddr_un *
krt_lladdr __PF1(ifr, struct ifreq *)
{
    struct ifnet_head ifnet;
    struct ifnet *ifp;
    int unit = 0;
    size_t namelen;

    if (!kd) {
	return (sockaddr_un *) 0;
    }
    
    /* Get length of name and fetch unit */
    {
	register char *sp = ifr->ifr_name;
	char *lp = ifr->ifr_name + IFNAMSIZ;

	while (isalpha(*sp)) {
	    sp++;
	}

	namelen = sp - ifr->ifr_name;

	do {
	    unit = (unit * 10) + (*sp - '0');
	} while (*++sp && sp < lp) ;
    }

    if (KVM_READ(kd,
		 krt_ifnet,
		 &ifnet,
		 sizeof (ifnet)) < 0) {
	trace_log_tp(krt_task,
		     0,
		     LOG_ERR,
		     ("krt_lladdr: reading ifnet for %.*s: %s",
		      IFNAMSIZ, ifr->ifr_name,
		      KVM_GETERR(kd, "kvm_read error")));
	return (sockaddr_un *) 0;
    }

    for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) {
	struct arpcom arpcom;

	/* Read ifnet */
	if (KVM_READ(kd,
		     ifp,
		     &arpcom,
		     sizeof (arpcom)) < 0) {
	    trace_log_tp(krt_task,
			 0,
			 LOG_ERR,
			 ("krt_lladdr: reading arpcom for %.*s: %s",
			  IFNAMSIZ, ifr->ifr_name,
			  KVM_GETERR(kd, "kvm_read error")));
	    break;
	}
	ifp = &arpcom.ac_if;

	if (!ifp->if_addrlist.tqh_first
	    || strncmp(ifp->if_xname, ifr->ifr_name, IFNAMSIZ) != 0) {
	    /* Not the one we want */

	    continue;
	}

	if (BIT_TEST(ifp->if_flags, IFF_BROADCAST)
#ifdef	IFT_OTHER
	    && krt_type_to_ll(ifp->if_type) == LL_8022
#endif	/* IFT_OTHER */
	    ) {
	    /* Assume broadcast nets have 802.2 addresses */

	    return sockbuild_ll(LL_8022,
				(byte *) arpcom.ac_enaddr,
#ifdef	KRT_RT_SOCK
				(size_t) ifp->if_addrlen
#else	/* KRT_RT_SOCK */
#ifndef	ETHER_ADDRLEN
#define	ETHER_ADDRLEN	6
#endif	/* ETHER_ADDRLEN */
				ETHER_ADDRLEN
#endif	/* KRT_RT_SOCK */
				);
	}
    }

    return (sockaddr_un *) 0;
}

#else	/* KRT_IF_NETBSD */

sockaddr_un *
krt_lladdr __PF1(ifr, struct ifreq *)
{
    struct ifnet *ifp, *ifnet;
    int unit = 0;
    size_t namelen;

    if (!kd) {
	return (sockaddr_un *) 0;
    }
    
    /* Get length of name and fetch unit */
    {
	register char *sp = ifr->ifr_name;
	char *lp = ifr->ifr_name + IFNAMSIZ;

	while (isalpha(*sp)) {
	    sp++;
	}

	namelen = sp - ifr->ifr_name;

	do {
	    unit = (unit * 10) + (*sp - '0');
	} while (*++sp && sp < lp) ;
    }

    if (KVM_READ(kd,
		 krt_ifnet,
		 &ifnet,
		 sizeof (ifnet)) < 0) {
	trace_log_tp(krt_task,
		     0,
		     LOG_ERR,
		     ("krt_lladdr: reading ifnet for %.*s: %s",
		      IFNAMSIZ, ifr->ifr_name,
		      KVM_GETERR(kd, "kvm_read error")));
	return (sockaddr_un *) 0;
    }

    for (ifp = ifnet; ifp; ifp = ifp->if_next) {
	struct arpcom arpcom;
	char name[IFNAMSIZ];

	/* Read ifnet */
	if (KVM_READ(kd,
		     ifp,
		     &arpcom,
		     sizeof (arpcom)) < 0) {
	    trace_log_tp(krt_task,
			 0,
			 LOG_ERR,
			 ("krt_lladdr: reading arpcom for %.*s: %s",
			  IFNAMSIZ, ifr->ifr_name,
			  KVM_GETERR(kd, "kvm_read error")));
	    break;
	}
	ifp = &arpcom.ac_if;

	if (!ifp->if_addrlist
	    || !ifp->if_name
	    || ifp->if_unit != unit) {
	    /* Not the one we want */

	    continue;
	}
	
	/* And interface name */
	if (KVM_READ(kd,
		     ifp->if_name,
		     name,
		     sizeof name) < 0) {
	    trace_log_tp(krt_task,
			 0,
			 LOG_ERR,
			 ("krt_lladdr: reading interface name for %.*s: %s",
			  IFNAMSIZ, ifr->ifr_name,
			  KVM_GETERR(kd, "kvm_read error")));
	    break;
	}
	ifp->if_name = name;	

	if (!isalpha(name[0])) {
	    /* Something is terribly wrong! */
	    break;
	}

	if (BIT_TEST(ifp->if_flags, IFF_BROADCAST)
#ifdef	IFT_OTHER
	    && krt_type_to_ll(ifp->if_type) == LL_8022
#endif	/* IFT_OTHER */
	    ) {
	    /* Assume broadcast nets have 802.2 addresses */

	    if (unit == ifp->if_unit
		&& !strncmp(ifp->if_name, ifr->ifr_name, namelen)) {
		/* This is the one we want */

		return sockbuild_ll(LL_8022,
				    (byte *) arpcom.ac_enaddr,
#ifdef	KRT_RT_SOCK
				    (size_t) ifp->if_addrlen
#else	/* KRT_RT_SOCK */
#ifndef	ETHER_ADDRLEN
#define	ETHER_ADDRLEN	6
#endif	/* ETHER_ADDRLEN */
				    ETHER_ADDRLEN
#endif	/* KRT_RT_SOCK */
				    );
	    }
	}
    }

    return (sockaddr_un *) 0;
}
#endif	/* KRT_IF_NETBSD */
