summaryrefslogblamecommitdiffstats
path: root/freebsd/sys/netatalk/aarp.c
blob: 4ce3701a27e8ec366e6a80da04982a1cc496f43d (plain) (tree)
1
                                           




















































                                                                             
                                      
 





                                
 

                      
 
                       
            
                             
 




                               
 
                                       












































































































































































































































































































































                                                                             
                                   








                                                                             
                                             



                                     
                                     











































































































































































































































































































                                                                               
#include <machine/rtems-bsd-kernel-space.h>

/*-
 * Copyright (c) 2004-2009 Robert N. M. Watson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Copyright (c) 1990,1991,1994 Regents of The University of Michigan.
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appears in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation, and that the name of The University
 * of Michigan not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission. This software is supplied as is without expressed or
 * implied warranties of any kind.
 *
 * This product includes software developed by the University of
 * California, Berkeley and its contributors.
 *
 *	Research Systems Unix Group
 *	The University of Michigan
 *	c/o Wesley Craig
 *	535 W. William Street
 *	Ann Arbor, Michigan
 *	+1-313-764-2278
 *	netatalk@umich.edu
 *
 * $FreeBSD$
 */

#include <rtems/bsd/local/opt_atalk.h>

#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/syslog.h>

#include <net/if.h>
#include <net/if_dl.h>

#include <netinet/in.h>
#undef s_net
#include <netinet/if_ether.h>

#include <netatalk/at.h>
#include <netatalk/at_var.h>
#include <netatalk/aarp.h>
#include <netatalk/phase2.h>
#include <netatalk/at_extern.h>

#include <security/mac/mac_framework.h>

static void	aarptfree(struct aarptab *aat);
static void	at_aarpinput(struct ifnet *ifp, struct mbuf *m);

#define	AARPTAB_BSIZ	9
#define	AARPTAB_NB	19
#define	AARPTAB_SIZE	(AARPTAB_BSIZ * AARPTAB_NB)
static struct aarptab	aarptab[AARPTAB_SIZE];

struct mtx	aarptab_mtx;
MTX_SYSINIT(aarptab_mtx, &aarptab_mtx, "aarptab_mtx", MTX_DEF);

#define	AARPTAB_HASH(a)	((((a).s_net << 8) + (a).s_node) % AARPTAB_NB)

#define	AARPTAB_LOOK(aat, addr)	do {					\
	int n;								\
									\
	AARPTAB_LOCK_ASSERT();						\
	aat = &aarptab[ AARPTAB_HASH(addr) * AARPTAB_BSIZ ];		\
	for (n = 0; n < AARPTAB_BSIZ; n++, aat++) {			\
		if (aat->aat_ataddr.s_net == (addr).s_net &&		\
		    aat->aat_ataddr.s_node == (addr).s_node)		\
			break;						\
	}								\
	if (n >= AARPTAB_BSIZ)						\
		aat = NULL;						\
} while (0)

#define	AARPT_AGE	(60 * 1)
#define	AARPT_KILLC	20
#define	AARPT_KILLI	3

static const u_char	atmulticastaddr[6] = {
	0x09, 0x00, 0x07, 0xff, 0xff, 0xff,
};

u_char	at_org_code[3] = {
	0x08, 0x00, 0x07,
};
const u_char	aarp_org_code[3] = {
	0x00, 0x00, 0x00,
};

static struct callout_handle	aarptimer_ch =
    CALLOUT_HANDLE_INITIALIZER(&aarptimer_ch);

static void
aarptimer(void *ignored)
{
	struct aarptab *aat;
	int i;

	aarptimer_ch = timeout(aarptimer, NULL, AARPT_AGE * hz);
	aat = aarptab;
	AARPTAB_LOCK();
	for (i = 0; i < AARPTAB_SIZE; i++, aat++) {
		if (aat->aat_flags == 0 || (aat->aat_flags & ATF_PERM))
			continue;
		if (++aat->aat_timer < ((aat->aat_flags & ATF_COM) ?
		    AARPT_KILLC : AARPT_KILLI))
			continue;
		aarptfree(aat);
	}
	AARPTAB_UNLOCK();
}

/* 
 * Search through the network addresses to find one that includes the given
 * network.  Remember to take netranges into consideration.
 *
 * The _locked variant relies on the caller holding the at_ifaddr lock; the
 * unlocked variant returns a reference that the caller must dispose of.
 */
struct at_ifaddr *
at_ifawithnet_locked(struct sockaddr_at  *sat)
{
	struct at_ifaddr *aa;
	struct sockaddr_at *sat2;

	AT_IFADDR_LOCK_ASSERT();

	TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) {
		sat2 = &(aa->aa_addr);
		if (sat2->sat_addr.s_net == sat->sat_addr.s_net)
			break;
		if ((aa->aa_flags & AFA_PHASE2) &&
		    (ntohs(aa->aa_firstnet) <= ntohs(sat->sat_addr.s_net)) &&
		    (ntohs(aa->aa_lastnet) >= ntohs(sat->sat_addr.s_net)))
			break;
	}
	return (aa);
}

struct at_ifaddr *
at_ifawithnet(struct sockaddr_at *sat)
{
	struct at_ifaddr *aa;

	AT_IFADDR_RLOCK();
	aa = at_ifawithnet_locked(sat);
	if (aa != NULL)
		ifa_ref(&aa->aa_ifa);
	AT_IFADDR_RUNLOCK();
	return (aa);
}

static void
aarpwhohas(struct ifnet *ifp, struct sockaddr_at *sat)
{
	struct mbuf *m;
	struct ether_header *eh;
	struct ether_aarp *ea;
	struct at_ifaddr *aa;
	struct llc *llc;
	struct sockaddr	sa;

	AARPTAB_UNLOCK_ASSERT();
	m = m_gethdr(M_DONTWAIT, MT_DATA);
	if (m == NULL)
		return;
#ifdef MAC
	mac_netatalk_aarp_send(ifp, m);
#endif
	m->m_len = sizeof(*ea);
	m->m_pkthdr.len = sizeof(*ea);
	MH_ALIGN(m, sizeof(*ea));

	ea = mtod(m, struct ether_aarp *);
	bzero((caddr_t)ea, sizeof(*ea));

	ea->aarp_hrd = htons(AARPHRD_ETHER);
	ea->aarp_pro = htons(ETHERTYPE_AT);
	ea->aarp_hln = sizeof(ea->aarp_sha);
	ea->aarp_pln = sizeof(ea->aarp_spu);
	ea->aarp_op = htons(AARPOP_REQUEST);
	bcopy(IF_LLADDR(ifp), (caddr_t)ea->aarp_sha, sizeof(ea->aarp_sha));

	/*
	 * We need to check whether the output ethernet type should be phase
	 * 1 or 2.  We have the interface that we'll be sending the aarp out.
	 * We need to find an AppleTalk network on that interface with the
	 * same address as we're looking for.  If the net is phase 2,
	 * generate an 802.2 and SNAP header.
	 */
	aa = at_ifawithnet(sat);
	if (aa == NULL) {
		m_freem(m);
		return;
	}

	eh = (struct ether_header *)sa.sa_data;

	if (aa->aa_flags & AFA_PHASE2) {
		bcopy(atmulticastaddr, eh->ether_dhost,
		    sizeof(eh->ether_dhost));
		eh->ether_type = htons(sizeof(struct llc) +
		    sizeof(struct ether_aarp));
		M_PREPEND(m, sizeof(struct llc), M_DONTWAIT);
		if (m == NULL) {
			ifa_free(&aa->aa_ifa);
			return;
		}
		llc = mtod(m, struct llc *);
		llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
		llc->llc_control = LLC_UI;
		bcopy(aarp_org_code, llc->llc_org_code,
		    sizeof(aarp_org_code));
		llc->llc_ether_type = htons(ETHERTYPE_AARP);
		bcopy(&AA_SAT(aa)->sat_addr.s_net, ea->aarp_spnet,
		    sizeof(ea->aarp_spnet));
		bcopy(&sat->sat_addr.s_net, ea->aarp_tpnet,
		    sizeof(ea->aarp_tpnet));
		ea->aarp_spnode = AA_SAT(aa)->sat_addr.s_node;
		ea->aarp_tpnode = sat->sat_addr.s_node;
	} else {
		bcopy(ifp->if_broadcastaddr, (caddr_t)eh->ether_dhost,
		    sizeof(eh->ether_dhost));
		eh->ether_type = htons(ETHERTYPE_AARP);
		ea->aarp_spa = AA_SAT(aa)->sat_addr.s_node;
		ea->aarp_tpa = sat->sat_addr.s_node;
	}

#ifdef NETATALKDEBUG
	printf("aarp: sending request for %u.%u\n",
	    ntohs(AA_SAT(aa)->sat_addr.s_net), AA_SAT(aa)->sat_addr.s_node);
#endif /* NETATALKDEBUG */
	ifa_free(&aa->aa_ifa);

	sa.sa_len = sizeof(struct sockaddr);
	sa.sa_family = AF_UNSPEC;
	ifp->if_output(ifp, m, &sa, NULL);
}

int
aarpresolve(struct ifnet *ifp, struct mbuf *m, struct sockaddr_at *destsat,
    u_char *desten)
{
	struct at_ifaddr *aa;
	struct aarptab *aat;

	AT_IFADDR_RLOCK();
	if (at_broadcast(destsat)) {
		m->m_flags |= M_BCAST;
		if ((aa = at_ifawithnet_locked(destsat)) == NULL)  {
			AT_IFADDR_RUNLOCK();
			m_freem(m);
			return (0);
		}
		if (aa->aa_flags & AFA_PHASE2)
			bcopy(atmulticastaddr, (caddr_t)desten,
			    sizeof(atmulticastaddr));
		else
			bcopy(ifp->if_broadcastaddr, (caddr_t)desten,
			    sizeof(ifp->if_addrlen));
		AT_IFADDR_RUNLOCK();
		return (1);
	}
	AT_IFADDR_RUNLOCK();

	AARPTAB_LOCK();
	AARPTAB_LOOK(aat, destsat->sat_addr);
	if (aat == NULL) {
		/* No entry. */
		aat = aarptnew(&destsat->sat_addr);

		/* We should fail more gracefully. */
		if (aat == NULL)
			panic("aarpresolve: no free entry");
		goto done;
	}

	/* Found an entry. */
	aat->aat_timer = 0;
	if (aat->aat_flags & ATF_COM) {
		/* Entry is COMplete. */
		bcopy((caddr_t)aat->aat_enaddr, (caddr_t)desten,
		    sizeof(aat->aat_enaddr));
		AARPTAB_UNLOCK();
		return (1);
	}

	/* Entry has not completed. */
	if (aat->aat_hold)
		m_freem(aat->aat_hold);
done:
	aat->aat_hold = m;
	AARPTAB_UNLOCK();
	aarpwhohas(ifp, destsat);
	return (0);
}

void
aarpintr(struct mbuf *m)
{
	struct arphdr *ar;
	struct ifnet *ifp;

	ifp = m->m_pkthdr.rcvif;
	if (ifp->if_flags & IFF_NOARP)
		goto out;

	if (m->m_len < sizeof(struct arphdr))
		goto out;

	ar = mtod(m, struct arphdr *);
	if (ntohs(ar->ar_hrd) != AARPHRD_ETHER)
		goto out;
	
	if (m->m_len < sizeof(struct arphdr) + 2 * ar->ar_hln +
	    2 * ar->ar_pln)
		goto out;
	
	switch(ntohs(ar->ar_pro)) {
	case ETHERTYPE_AT:
		at_aarpinput(ifp, m);
		return;
	default:
		break;
	}

out:
	m_freem(m);
}

static void
at_aarpinput(struct ifnet *ifp, struct mbuf *m)
{
	struct ether_aarp *ea;
	struct at_ifaddr *aa;
	struct aarptab *aat;
	struct ether_header *eh;
	struct llc *llc;
	struct sockaddr_at sat;
	struct sockaddr sa;
	struct at_addr spa, tpa, ma;
	int op;
	u_short net;

	ea = mtod(m, struct ether_aarp *);

	/* Check to see if from my hardware address. */
	if (!bcmp((caddr_t)ea->aarp_sha, IF_LLADDR(ifp), ETHER_ADDR_LEN)) {
		m_freem(m);
		return;
	}

	/* Don't accept requests from broadcast address. */
	if (!bcmp(ea->aarp_sha, ifp->if_broadcastaddr, ifp->if_addrlen)) {
		log(LOG_ERR, "aarp: source link address is broadcast\n");
		m_freem(m);
		return;
	}

	op = ntohs(ea->aarp_op);
	bcopy(ea->aarp_tpnet, &net, sizeof(net));

	if (net != 0) {
		/* Should be ATADDR_ANYNET? */
		sat.sat_len = sizeof(struct sockaddr_at);
		sat.sat_family = AF_APPLETALK;
		sat.sat_addr.s_net = net;
		aa = at_ifawithnet(&sat);
		if (aa == NULL) {
			m_freem(m);
			return;
		}
		bcopy(ea->aarp_spnet, &spa.s_net, sizeof(spa.s_net));
		bcopy(ea->aarp_tpnet, &tpa.s_net, sizeof(tpa.s_net));
	} else {
		/*
		 * Since we don't know the net, we just look for the first
		 * phase 1 address on the interface.
		 */
		IF_ADDR_RLOCK(ifp);
		for (aa = (struct at_ifaddr *)TAILQ_FIRST(&ifp->if_addrhead);
		    aa;
		    aa = (struct at_ifaddr *)aa->aa_ifa.ifa_link.tqe_next) {
			if (AA_SAT(aa)->sat_family == AF_APPLETALK &&
			    (aa->aa_flags & AFA_PHASE2) == 0) {
				break;
			}
		}
		if (aa == NULL) {
			IF_ADDR_RUNLOCK(ifp);
			m_freem(m);
			return;
		}
		ifa_ref(&aa->aa_ifa);
		IF_ADDR_RUNLOCK(ifp);
		tpa.s_net = spa.s_net = AA_SAT(aa)->sat_addr.s_net;
	}

	spa.s_node = ea->aarp_spnode;
	tpa.s_node = ea->aarp_tpnode;
	ma.s_net = AA_SAT(aa)->sat_addr.s_net;
	ma.s_node = AA_SAT(aa)->sat_addr.s_node;

	/*
	 * This looks like it's from us.
	 */
	if (spa.s_net == ma.s_net && spa.s_node == ma.s_node) {
		if (aa->aa_flags & AFA_PROBING) {
			/*
			 * We're probing, someone either responded to our
			 * probe, or probed for the same address we'd like to
			 * use. Change the address we're probing for.
	    		 */
			callout_stop(&aa->aa_callout);
			wakeup(aa);
			ifa_free(&aa->aa_ifa);
			m_freem(m);
			return;
		} else if (op != AARPOP_PROBE) {
			/*
			 * This is not a probe, and we're not probing.  This
			 * means that someone's saying they have the same
			 * source address as the one we're using.  Get upset.
			 */
			ifa_free(&aa->aa_ifa);
			log(LOG_ERR,
			    "aarp: duplicate AT address!! %x:%x:%x:%x:%x:%x\n",
			    ea->aarp_sha[0], ea->aarp_sha[1],
			    ea->aarp_sha[2], ea->aarp_sha[3],
			    ea->aarp_sha[4], ea->aarp_sha[5]);
			m_freem(m);
			return;
		}
	}

	AARPTAB_LOCK();
	AARPTAB_LOOK(aat, spa);
	if (aat != NULL) {
		if (op == AARPOP_PROBE) {
			/*
			 * Someone's probing for spa, dealocate the one we've
			 * got, so that if the prober keeps the address,
			 * we'll be able to arp for him.
			 */
			aarptfree(aat);
			AARPTAB_UNLOCK();
			ifa_free(&aa->aa_ifa);
			m_freem(m);
			return;
		}

		bcopy((caddr_t)ea->aarp_sha, (caddr_t)aat->aat_enaddr,
		    sizeof(ea->aarp_sha));
		aat->aat_flags |= ATF_COM;
		if (aat->aat_hold) {
			struct mbuf *mhold = aat->aat_hold;
			aat->aat_hold = NULL;
			AARPTAB_UNLOCK();
			sat.sat_len = sizeof(struct sockaddr_at);
			sat.sat_family = AF_APPLETALK;
			sat.sat_addr = spa;
			(*ifp->if_output)(ifp, mhold,
			    (struct sockaddr *)&sat, NULL); /* XXX */
		} else
			AARPTAB_UNLOCK();
	} else if ((tpa.s_net == ma.s_net) && (tpa.s_node == ma.s_node)
	    && (op != AARPOP_PROBE) && ((aat = aarptnew(&spa)) != NULL)) {
		bcopy((caddr_t)ea->aarp_sha, (caddr_t)aat->aat_enaddr,
		    sizeof(ea->aarp_sha));
		aat->aat_flags |= ATF_COM;
	        AARPTAB_UNLOCK();
	} else
		AARPTAB_UNLOCK();

	/*
	 * Don't respond to responses, and never respond if we're still
	 * probing.
	 */
	if (tpa.s_net != ma.s_net || tpa.s_node != ma.s_node ||
	    op == AARPOP_RESPONSE || (aa->aa_flags & AFA_PROBING)) {
		ifa_free(&aa->aa_ifa);
		m_freem(m);
		return;
	}

	bcopy((caddr_t)ea->aarp_sha, (caddr_t)ea->aarp_tha,
	    sizeof(ea->aarp_sha));
	bcopy(IF_LLADDR(ifp), (caddr_t)ea->aarp_sha, sizeof(ea->aarp_sha));

	/* XXX */
	eh = (struct ether_header *)sa.sa_data;
	bcopy((caddr_t)ea->aarp_tha, (caddr_t)eh->ether_dhost,
	    sizeof(eh->ether_dhost));

	if (aa->aa_flags & AFA_PHASE2) {
		eh->ether_type = htons(sizeof(struct llc) +
		    sizeof(struct ether_aarp));
		M_PREPEND(m, sizeof(struct llc), M_DONTWAIT);
		if (m == NULL) {
			ifa_free(&aa->aa_ifa);
			return;
		}
		llc = mtod(m, struct llc *);
		llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
		llc->llc_control = LLC_UI;
		bcopy(aarp_org_code, llc->llc_org_code,
		    sizeof(aarp_org_code));
		llc->llc_ether_type = htons(ETHERTYPE_AARP);

		bcopy(ea->aarp_spnet, ea->aarp_tpnet,
		    sizeof(ea->aarp_tpnet));
		bcopy(&ma.s_net, ea->aarp_spnet, sizeof(ea->aarp_spnet));
	} else
		eh->ether_type = htons(ETHERTYPE_AARP);
	ifa_free(&aa->aa_ifa);

	ea->aarp_tpnode = ea->aarp_spnode;
	ea->aarp_spnode = ma.s_node;
	ea->aarp_op = htons(AARPOP_RESPONSE);

	sa.sa_len = sizeof(struct sockaddr);
	sa.sa_family = AF_UNSPEC;
	(*ifp->if_output)(ifp, m, &sa, NULL); /* XXX */
	return;
}

static void
aarptfree(struct aarptab *aat)
{

	AARPTAB_LOCK_ASSERT();
	if (aat->aat_hold)
		m_freem(aat->aat_hold);
	aat->aat_hold = NULL;
	aat->aat_timer = aat->aat_flags = 0;
	aat->aat_ataddr.s_net = 0;
	aat->aat_ataddr.s_node = 0;
}

struct aarptab *
aarptnew(struct at_addr *addr)
{
	int n;
	int oldest = -1;
	struct aarptab *aat, *aato = NULL;
	static int first = 1;

	AARPTAB_LOCK_ASSERT();
	if (first) {
		first = 0;
		aarptimer_ch = timeout(aarptimer, (caddr_t)0, hz);
	}
	aat = &aarptab[AARPTAB_HASH(*addr) * AARPTAB_BSIZ];
	for (n = 0; n < AARPTAB_BSIZ; n++, aat++) {
		if (aat->aat_flags == 0)
			goto out;
		if (aat->aat_flags & ATF_PERM)
			continue;
		if ((int) aat->aat_timer > oldest) {
			oldest = aat->aat_timer;
			aato = aat;
		}
	}
	if (aato == NULL)
		return (NULL);
	aat = aato;
	aarptfree(aat);
out:
	aat->aat_ataddr = *addr;
	aat->aat_flags = ATF_INUSE;
	return (aat);
}


void
aarpprobe(void *arg)
{
	struct ifnet *ifp = arg;
	struct mbuf *m;
	struct ether_header *eh;
	struct ether_aarp *ea;
	struct at_ifaddr *aa;
	struct llc *llc;
	struct sockaddr sa;

	/*
	 * We need to check whether the output ethernet type should be phase
	 * 1 or 2.  We have the interface that we'll be sending the aarp out.
	 * We need to find an AppleTalk network on that interface with the
	 * same address as we're looking for.  If the net is phase 2,
	 * generate an 802.2 and SNAP header.
	 */
	AARPTAB_LOCK();
	for (aa = (struct at_ifaddr *)TAILQ_FIRST(&ifp->if_addrhead); aa;
	    aa = (struct at_ifaddr *)aa->aa_ifa.ifa_link.tqe_next) {
		if (AA_SAT(aa)->sat_family == AF_APPLETALK &&
		    (aa->aa_flags & AFA_PROBING))
			break;
	}
	if (aa == NULL) {
		/* Serious error XXX. */
		AARPTAB_UNLOCK();
		printf("aarpprobe why did this happen?!\n");
		return;
	}

	if (aa->aa_probcnt <= 0) {
		aa->aa_flags &= ~AFA_PROBING;
		wakeup(aa);
		AARPTAB_UNLOCK();
		return;
	} else
		callout_reset(&aa->aa_callout, hz / 5, aarpprobe, ifp);
	ifa_ref(&aa->aa_ifa);
	AARPTAB_UNLOCK();

	m = m_gethdr(M_DONTWAIT, MT_DATA);
	if (m == NULL) {
		ifa_free(&aa->aa_ifa);
		return;
	}
#ifdef MAC
	mac_netatalk_aarp_send(ifp, m);
#endif
	m->m_len = sizeof(*ea);
	m->m_pkthdr.len = sizeof(*ea);
	MH_ALIGN(m, sizeof(*ea));

	ea = mtod(m, struct ether_aarp *);
	bzero((caddr_t)ea, sizeof(*ea));

	ea->aarp_hrd = htons(AARPHRD_ETHER);
	ea->aarp_pro = htons(ETHERTYPE_AT);
	ea->aarp_hln = sizeof(ea->aarp_sha);
	ea->aarp_pln = sizeof(ea->aarp_spu);
	ea->aarp_op = htons(AARPOP_PROBE);
	bcopy(IF_LLADDR(ifp), (caddr_t)ea->aarp_sha,
	    sizeof(ea->aarp_sha));

	eh = (struct ether_header *)sa.sa_data;

	if (aa->aa_flags & AFA_PHASE2) {
		bcopy(atmulticastaddr, eh->ether_dhost,
		    sizeof(eh->ether_dhost));
		eh->ether_type = htons(sizeof(struct llc) +
		    sizeof(struct ether_aarp));
		M_PREPEND(m, sizeof(struct llc), M_WAIT);
		llc = mtod(m, struct llc *);
		llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
		llc->llc_control = LLC_UI;
		bcopy(aarp_org_code, llc->llc_org_code,
		    sizeof(aarp_org_code));
		llc->llc_ether_type = htons(ETHERTYPE_AARP);

		bcopy(&AA_SAT(aa)->sat_addr.s_net, ea->aarp_spnet,
		    sizeof(ea->aarp_spnet));
		bcopy(&AA_SAT(aa)->sat_addr.s_net, ea->aarp_tpnet,
		    sizeof(ea->aarp_tpnet));
		ea->aarp_spnode = ea->aarp_tpnode =
		    AA_SAT(aa)->sat_addr.s_node;
	} else {
		bcopy(ifp->if_broadcastaddr, (caddr_t)eh->ether_dhost,
		    sizeof(eh->ether_dhost));
		eh->ether_type = htons(ETHERTYPE_AARP);
		ea->aarp_spa = ea->aarp_tpa = AA_SAT(aa)->sat_addr.s_node;
	}

#ifdef NETATALKDEBUG
	printf("aarp: sending probe for %u.%u\n",
	    ntohs(AA_SAT(aa)->sat_addr.s_net), AA_SAT(aa)->sat_addr.s_node);
#endif /* NETATALKDEBUG */
	ifa_free(&aa->aa_ifa);

	sa.sa_len = sizeof(struct sockaddr);
	sa.sa_family = AF_UNSPEC;
	(*ifp->if_output)(ifp, m, &sa, NULL); /* XXX */
	aa->aa_probcnt--;
}

void
aarp_clean(void)
{
	struct aarptab *aat;
	int i;

	untimeout(aarptimer, 0, aarptimer_ch);
	AARPTAB_LOCK();
	for (i = 0, aat = aarptab; i < AARPTAB_SIZE; i++, aat++) {
		if (aat->aat_hold) {
			m_freem(aat->aat_hold);
			aat->aat_hold = NULL;
		}
	}
	AARPTAB_UNLOCK();
}