/*
 * Public Release 3
 * 
 * $Id: dvmrp_targets.c,v 1.2 1997/07/17 22:41:29 chopps Exp $
 *
 *  Authors:
 *	Jeffrey C Honig <jch@nr-atp.cit.cornell.edu>
 *	Tom Pusateri <pusateri@netedge.com>
 */

/*
 * ------------------------------------------------------------------------
 * 
 * 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.
 */


#include "include.h"
#include "inet.h"
#include "dvmrp.h"
#include "dvmrp_targets.h"

/**/

PROTOTYPE(dvmrp_td_insert, static dvmrp_td_entry *, (dvmrp_target *, int, dvmrp_td_entry *, u_int32));
PROTOTYPE(dvmrp_td_remove, static void, (dvmrp_target *, int, dvmrp_td_entry *));
PROTOTYPE(dvmrp_td_print, static void, (FILE *, dvmrp_td_entry *));


static const bits target_flag_bits[] = {
    { TARGETF_BROADCAST,	"Broadcast" },
    { TARGETF_SOURCE,		"Source" },
    { TARGETF_SUPPLY,		"Supply" },
    { TARGETF_ALLINTF,		"AllInterfaces" },
    { TARGETF_POLICY,		"Policy" },
    { 0, NULL },
};

const bits dvmrp_target_entry_bits[] = {
    { TDF_CHANGED,	"Changed" },
    { TDF_HOLDDOWN,	"Holddown" },
    { TDF_POISON,	"Poison" },
    { 0, NULL },
} ;

static
block_t dvmrp_target_block_index = (block_t) 0;
block_t dvmrp_td_block_index = (block_t) 0;

#define	DVMRP_TD_ALLOC()    (task_block_alloc(dvmrp_td_block_index))


/* Free any routes we were announcing */
static void
dvmrp_target_release __PF1(tlp, dvmrp_target *)
{
    int maskindex, i, n, doing_right;
    dvmrp_td_entry *stack[sizeof(u_int32) * NBBY];
    register dvmrp_td_entry *dp, *dprev;

    for (maskindex=0; maskindex < sizeof(u_int32) * NBBY; maskindex++) {
	if (tlp->target_td_count[maskindex] == 0) {
	    continue;
	}
	dprev = tlp->target_td_head[maskindex];

	    /* catch the case where only one node in the tree. */
	if (dprev->dvmrp_tree_mask == DVMRP_TD_NOMASK) {
	    DVMRP_TD_CLEANUP(tlp, dprev);
	    continue;
	}

	doing_right = 0;
	i = n = 0;
	for (;;) {
	    if (doing_right) {
		dp = dprev->right;
	    } else {
		dp = dprev->left;
	    }

	    if (dp->dvmrp_tree_mask >= dprev->dvmrp_tree_mask) {

		DVMRP_TD_CLEANUP(tlp, dp);

		if (++n >= tlp->target_td_count[maskindex]) {
		    /* All found */
		    break;
		}

		if (doing_right) {
		    /*
		     * Finished right hand, back up stack for another
		     * node to be checked on the right.
		     */
		    assert(i != 0);
		    dprev = stack[--i];
		} else {
		    /*
		     * Finished left, try right on next pass
		     */
		    doing_right = 1;
		}
	    } else {
		if (doing_right) {
		    /*
		     * Node on right has children.  Step down the tree,
		     * starting on the left.  No need to stack the previous,
		     * we've already checked both children
		     */
		    doing_right = 0;
		} else {
		    /*
		     * Node on left has children, stack the previous node
		     * so we check its right child later.
		     */
		    stack[i++] = dprev;
		}
		dprev = dp;
	    }
	}
    }
}


/* Free a target list */
static void
dvmrp_target_free __PF2(tp, task *,
			tlp, dvmrp_target *)
{

    if (TRACE_TP(tp, TR_POLICY)) {
	tracef("dvmrp_target_free: FREE %A -> %A",
	       *tlp->target_src,
	       *tlp->target_dst);
	if (tlp->target_ifap) {
	    tracef(" interface %A(%s)",
		   tlp->target_ifap->ifa_addr,
		   tlp->target_ifap->ifa_link->ifl_name);
	}
	if (tlp->target_gwp) {
	    tracef(" gateway %A",
		   tlp->target_gwp->gw_addr);
	}
	trace_only_tp(tp,
		      0,
		      (" flags <%s>",
		       trace_bits(target_flag_bits, tlp->target_flags)));
    }

    dvmrp_target_release(tlp);
	
    if (tlp->target_ifap) {
	/* Free the interface */
	    
	BIT_RESET(tlp->target_ifap->ifa_rtactive, RTPROTO_BIT(tp->task_rtproto));

	IFA_FREE(tlp->target_ifap);
    }

    /* Free the target entry */
    REMQUE(tlp);
    task_block_free(dvmrp_target_block_index, (void_t) tlp);
}


void
dvmrp_target_free_list __PF2(tp, task *,
		             list, dvmrp_target *)
{
    register dvmrp_target *tlp;

    while ((tlp = list->target_forw) != list) {
	dvmrp_target_free(tp, tlp);
    }
}


dvmrp_target *
dvmrp_target_locate __PF3(list, dvmrp_target *,
		          ifap, if_addr *,
		          gwp, gw_entry *)
{
    dvmrp_target *tlp;
    
    TARGET_LIST(tlp, list) {
	if ((BIT_TEST(tlp->target_flags, TARGETF_BROADCAST)
	     && tlp->target_ifap == ifap)
	    || (BIT_TEST(tlp->target_flags, TARGETF_SOURCE)
		&& tlp->target_gwp == gwp)) {

	    return tlp;
	}
    } TARGET_LIST_END(tlp, list) ;

    return (dvmrp_target *) 0;
}


PROTOTYPE(dvmrp_target_alloc,
	  static inline dvmrp_target *,
	  (task *,
	   dvmrp_target *,
	   dvmrp_target *,
	   if_addr *,
	   gw_entry *,
	   int,
	   _PROTOTYPE(ta_dump,
		      void,
		      (FILE *,
		       rt_head *,
		       void_t,
		       const char *))));	   
static inline dvmrp_target *
dvmrp_target_alloc(ta_tp, old_list, new_list, ifap, gwp, alloc, ta_dump)
task *ta_tp;
dvmrp_target *old_list;
dvmrp_target *new_list;
if_addr *ifap;
gw_entry *gwp;
int alloc;
_PROTOTYPE(ta_dump,
	   void,
	   (FILE *,
	    rt_head *,
	    void_t,
	    const char *));
{
    dvmrp_target *ta_tlp;

    /* Locate this target on the old list */
    TARGET_LIST(ta_tlp, old_list) {
	if (ta_tlp->target_ifap == ifap
	    && ta_tlp->target_gwp == gwp) {
	    REMQUE(ta_tlp)
	    break;
	}
    } TARGET_LIST_END(ta_tlp, old_list) ;

    /* If not on the old list, allocate a new one */
    if (!ta_tlp) {
	int masklen;

	/* Allocate our block index */
	if (!dvmrp_target_block_index) {
	    dvmrp_target_block_index = task_block_init(sizeof (dvmrp_target), "dvmrp_target");
	    dvmrp_td_block_index = task_block_init(sizeof (dvmrp_td_entry), "dvmrp_td_entry");
	}

	/* Allocate this block */
    	ta_tlp = (dvmrp_target *) task_block_alloc(dvmrp_target_block_index);
	for (masklen=0; masklen < sizeof(u_int32) * NBBY; masklen++) {
	    ta_tlp->target_td_head[masklen] = (dvmrp_td_entry *) 0;
	}
    }

    if (alloc) {
	if (!BIT_TEST(ta_tlp->target_flags, TARGETF_SUPPLY)) {

	    /* Indicate we supply to this guy */
	    BIT_SET(ta_tlp->target_flags, TARGETF_SUPPLY);
	}
    } else if (BIT_TEST(ta_tlp->target_flags, TARGETF_SUPPLY)) {

	/* Release routes and free the bit */
	dvmrp_target_release(ta_tlp);

	/* Indicate that we don't want to supply any more */
	BIT_RESET(ta_tlp->target_flags, TARGETF_SUPPLY);
    }
    
    /* Append to the end of the new list */
    INSQUE(ta_tlp, new_list->target_back);

    return ta_tlp;
}


/* Allocate and build a target list for the given parameters */
int
dvmrp_target_build(tp, list, gw_list, flags, dump)
task *tp;
dvmrp_target *list;
gw_entry *gw_list;
flag_t flags;
_PROTOTYPE(dump,
	   void,
	   (FILE *,
	    rt_head *,
	    void_t,
	    const char *));
{
    int targets = 0;
    if_addr *ifap;
    dvmrp_target old, *tlp;

    /* Copy the root of the list so we can build a new one */
    old = *list;
    if (old.target_forw != list) {
	old.target_forw->target_back = &old;
	old.target_back->target_forw = &old;
    } else {
	old.target_forw = old.target_back = &old;
    }
    list->target_forw = list->target_back = list;

    /* Reset the active bits on any interfaces */
    TARGET_LIST(tlp, &old) {
	BIT_RESET(tlp->target_ifap->ifa_rtactive, RTPROTO_BIT(tp->task_rtproto));
    } TARGET_LIST_END(tlp, &old) ;

    /* First add interfaces */
    if (BIT_TEST(flags, TARGETF_ALLINTF|TARGETF_BROADCAST)) {
	IF_ADDR(ifap) {
	    int alloc;
	    sockaddr_un **dest;

	    if (socktype(ifap->ifa_addr) != AF_INET
		|| !BIT_TEST(ifap->ifa_state, IFS_UP)
		|| !BIT_TEST(ifap->ifa_state, IFS_MULTICAST)
		|| BIT_TEST(ifap->ifa_ps[tp->task_rtproto].ips_state, IFPS_NOOUT)) {
		/* Wrong protocol, interface is down, */
		/* no announcements allowed or policy has not seen this interface yet */

	    Continue:
		continue;
	    }

	    switch (BIT_TEST(ifap->ifa_state, IFS_BROADCAST|IFS_POINTOPOINT|IFS_LOOPBACK)) {
	    case IFS_LOOPBACK:
		/* The default is not to send packets to the loopback */
		/* interface.  This can be overridden by specifying the */
		/* loopback address in the sourcegateway clause */
		continue;

	    case IFS_BROADCAST:
		/* On broadcast interfaces, send to the broadcast address */
		
		dest = &ifap->ifa_addr_broadcast;

		/* Verify that we do not have another interface on this network */
		/* XXX - We should open one socket per local address and bind it. */
		TARGET_LIST(tlp, list) {
		    if (BIT_TEST(tlp->target_flags, TARGETF_BROADCAST)
			&& BIT_TEST(tlp->target_ifap->ifa_state, IFS_BROADCAST)
			&& sockaddrcmp(*dest, *tlp->target_dst)) {
			/* This is a duplicate */

			goto Continue;
		    }
		} TARGET_LIST_END(tlp, list) ;
		break;

	    case IFS_POINTOPOINT:
		/* On P2P interfaces, send to the destination address. */

		dest = &ifap->ifa_addr;
		break;

	    default:
		/* On NBMA interfaces we send packets to our self in order */
		/* to test that the interface is working.  This assumes that */
		/* packets send to myself over that interface will actually */
		/* get looped back by the hardware */

		if (BIT_TEST(ifap->ifa_state, IFS_NOAGE)) {
		    /* The test is not desired */

		    continue;
		}
		dest = &ifap->ifa_addr;
		break;
	    }

	    /* Locate old or allocate new */
	    tlp = dvmrp_target_alloc(tp,
				     &old,
				     list,
				     ifap,
				     (gw_entry *) 0,
				     BIT_TEST(flags, TARGETF_BROADCAST) ? TRUE : FALSE,
				     dump);

	    alloc = tlp->target_ifap != ifap;

	    /* Fill in the information */
	    tlp->target_dst = dest;
	    tlp->target_src = &ifap->ifa_addr_local;
	    tlp->target_gwp = (gw_entry *) 0;
	    if (tlp->target_ifap != ifap) {
		IFA_ALLOC(ifap);
		if (tlp->target_ifap) {
		    IFA_FREE(tlp->target_ifap);
		}
		tlp->target_ifap = ifap;
	    }
	    BIT_RESET(tlp->target_flags, TARGETF_SOURCE);
	    BIT_SET(tlp->target_flags, TARGETF_BROADCAST);
	    if (BIT_TEST(flags, TARGETF_BROADCAST)) {
		/* Indicate we are active on this interface */

		BIT_SET(ifap->ifa_rtactive, RTPROTO_BIT(tp->task_rtproto));

		/* And count it */
		targets++;
	    }

	    if (TRACE_TP(tp, TR_POLICY)) {
		tracef("dvmrp_target_build: %s %A -> %A",
		       alloc ? "ALLOC" : "REUSE",
		       *tlp->target_src,
		       *tlp->target_dst);
		if (tlp->target_ifap) {
		    tracef(" interface %A(%s)",
			   tlp->target_ifap->ifa_addr,
			   tlp->target_ifap->ifa_link->ifl_name);
		}
		trace_only_tp(tp,
			      0,
			      (" flags <%s>",
			       trace_bits(target_flag_bits, tlp->target_flags)));
	    }
	} IF_ADDR_END(ifap) ;
    }

    /* Then add the source gateways if any */
    if (BIT_TEST(flags, TARGETF_SOURCE)) {
	gw_entry *gwp;

	GW_LIST(gw_list, gwp) {
	    if (!BIT_TEST(gwp->gw_flags, GWF_SOURCE)) {
		continue;
	    }

	    if (!(ifap = if_withdst(gwp->gw_addr))) {
		if (BIT_TEST(task_state, TASKS_STRICTIFS)) {
		    trace_log_tp(tp,
				 0,
				 LOG_INFO,
				 ("dvmrp_target_build: Ignoring source gateway %A not on attached net",
				  gwp->gw_addr));
		}
		continue;
	    }

	    if (!BIT_TEST(ifap->ifa_state, IFS_UP)) {
		continue;
	    }

	    /* Look to see if this destination is the remote end of */
	    /* a P2P link I am already sending to */
	    TARGET_LIST(tlp, list) {
		if (sockaddrcmp(gwp->gw_addr, *tlp->target_dst)) {
		    if (!BIT_TEST(tlp->target_flags, TARGETF_SUPPLY)) {
			/* Allocate a bit for this target */

			/* Indicate we supply to this guy */
			BIT_SET(tlp->target_flags, TARGETF_SUPPLY);
		    }
		    tlp->target_gwp = gwp;
		    break;
		}
	    } TARGET_LIST_END(tlp, list) ;

	    if (!tlp) {
		/* Locate old or allocate new */
		tlp = dvmrp_target_alloc(tp,
					 &old,
					 list,
					 ifap,
					 gwp,
					 TRUE,
					 dump);

		/* Fill in the information */
		tlp->target_dst = &gwp->gw_addr;
		tlp->target_src = &ifap->ifa_addr_local;
		if (tlp->target_ifap != ifap) {
		    IFA_ALLOC(ifap);
		    if (tlp->target_ifap) {
			IFA_FREE(tlp->target_ifap);
		    }
		}
		tlp->target_ifap = ifap;
		tlp->target_gwp = gwp;
		BIT_RESET(tlp->target_flags, TARGETF_BROADCAST);
	    }

	    /* Indicate we are supplying to this gateway */
	    BIT_SET(tlp->target_flags, TARGETF_SOURCE);

	    /* Indicate we are active on this interface */
	    BIT_SET(ifap->ifa_rtactive, RTPROTO_BIT(tp->task_rtproto));

	    /* And count it */
	    targets++;
	    
	} GW_LIST_END(gw_list, gwp);
    }

    /* Finally, free any remaining targets */
    if (old.target_forw != &old) {
	dvmrp_target_free_list(tp, &old);
    }

    return targets;
}


/*
 *	Dump a target list
 */
void
dvmrp_target_dump __PF3(fp, FILE *,
		        list, dvmrp_target *,
		        bp, const bits *)
{
    dvmrp_target *tlp;

    if (list) {
	(void) fprintf(fp, "\n\tTargets:\n");
	
	TARGET_LIST(tlp, list) {
	    int maskindex, i, n, doing_right;
	    dvmrp_td_entry *stack[sizeof(u_int32) * NBBY];
	    register dvmrp_td_entry *dp, *dprev;

	    (void) fprintf(fp, "\t\t%-15A -> %-15A\tInterface: %A(%s)\n",
			   *tlp->target_src,
			   *tlp->target_dst,
			   tlp->target_ifap->ifa_addr,
			   tlp->target_ifap->ifa_link->ifl_name);
	    (void) fprintf(fp, "\t\t\tFlags: <%s>\n",
			   trace_bits2(bp, target_flag_bits, tlp->target_flags));
	    (void) fprintf(fp, "\n");

	    /* Dump the routes */
	    (void) fprintf(fp, "\t\t\tRoutes:\n");

	    for (maskindex=0; maskindex < sizeof(u_int32) * NBBY; maskindex++) {
		if (tlp->target_td_count[maskindex] == 0) {
		    continue;
		}
		dprev = tlp->target_td_head[maskindex];

		    /* catch the case where only one node in the tree. */
		if (dprev->dvmrp_tree_mask == DVMRP_TD_NOMASK) {
		    dvmrp_td_print(fp, dprev);
		    continue;
		}

		doing_right = 0;
		i = n = 0;
		for (;;) {
		    if (doing_right) {
			dp = dprev->right;
		    } else {
			dp = dprev->left;
		    }

		    if (dp->dvmrp_tree_mask >= dprev->dvmrp_tree_mask) {

			dvmrp_td_print(fp, dp);

			if (++n >= tlp->target_td_count[maskindex]) {
			    /* All found */
			    break;
			}

			if (doing_right) {
			    /*
			     * Finished right hand, back up stack for another
			     * node to be checked on the right.
			     */
			    assert(i != 0);
			    dprev = stack[--i];
			} else {
			    /*
			     * Finished left, try right on next pass
			     */
			    doing_right = 1;
			}
		    } else {
			if (doing_right) {
			    /*
			     * Node on right has children.  Step down the tree,
			     * starting on the left.  No need to stack the previous,
			     * we've already checked both children
			     */
			    doing_right = 0;
			} else {
			    /*
			     * Node on left has children, stack the previous node
			     * so we check its right child later.
			     */
			    stack[i++] = dprev;
			}
			dprev = dp;
		    }
		}
	    }
	    (void) fprintf(fp, "\n");
	} TARGET_LIST_END(tlp, list) ;
    }
}


/*
 * These routines manage the DVMRP target td entry tables.
 * There is a separate patricia tree for each possible mask value.
 * This allows sorting all sources by mask first. The heads of
 * the trees are an array indexed by the mask prefix length.
 */




/*
 * Lookup table to do an ffs() from the high order end (the ffs() routine
 * runs from the other end), on a byte.  This is useful to find the first
 * differing bit for the patricia tree.
 */
const byte dvmrp_td_bit_table[256] = {
    0x00, 0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};

/*
 * dvmrp_td_locate - locate a dvmrp td entry in the tree
 */
dvmrp_td_entry *
dvmrp_td_locate __PF2(tlp, dvmrp_target *,
		      rth, rt_head *)
{
    int masklen = inet_prefix_mask(rth->rth_dest_mask);
    register dvmrp_td_entry *dp = tlp->target_td_head[masklen];
    register u_int32 key = sock2ip(rth->rth_dest);

    /*
     * Try to find the address in the tree
     */
    if (dp) {
	register u_int32 mask;
	do {
	    mask = dp->dvmrp_tree_mask;
	    if (key & mask) {
		dp = dp->right;
	    } else {
		dp = dp->left;
	    }
	} while (mask > dp->dvmrp_tree_mask);
    }

    /*
     * Put the neighbor in the tree.
     * It may attach to an existing node, or may require a new node inserted.
     */
    if (!dp || dp->dvmrp_tree_key != key) {
	dp  = (dvmrp_td_entry *) 0;
    }

    return dp;
}

/*
 * dvmrp_td_add - add a dvmrp td entry to the target tree
 */
dvmrp_td_entry *
dvmrp_td_add __PF2(tlp, dvmrp_target *,
		   rt, rt_entry *)
{
    int masklen = inet_prefix_mask(rt->rt_head->rth_dest_mask);
    register dvmrp_td_entry *dp = tlp->target_td_head[masklen];
    register u_int32 key = sock2ip(rt->rt_head->rth_dest);

    /*
     * Try to find the address in the tree
     */
    if (dp) {
	register u_int32 mask;
	do {
	    mask = dp->dvmrp_tree_mask;
	    if (key & mask) {
		dp = dp->right;
	    } else {
		dp = dp->left;
	    }
	} while (mask > dp->dvmrp_tree_mask);
    }

    /*
     * Put the neighbor in the tree.
     * It may attach to an existing node, or may require a new node inserted.
     */
    if (!dp || dp->dvmrp_tree_key != key) {
	dp = dvmrp_td_insert(tlp, masklen, dp, key);
	dp->td_rt = rt;
    }
    return dp;
}


/*
 * dvmrp_td_delete - delete a dvmrp td entry from the tree
 */
void
dvmrp_td_delete __PF2(tlp, dvmrp_target *,
		      dp, dvmrp_td_entry *)
{
    int masklen = inet_prefix_mask(dp->td_rt->rt_head->rth_dest_mask);

    /*
     * remove the node from the tree
     */
    dvmrp_td_remove(tlp, masklen, dp);
}


/*
 * dvmrp_td_insert - add a node to the demux tree.  Note that
 *		   found_dp points to the node that was "found" when the
 *		   tree was searched for the address we are adding.
 *		   If it is NULL we do the search on our own.
 */
static dvmrp_td_entry *
dvmrp_td_insert __PF4(tlp, dvmrp_target *,
		      maskindex, int,
		      found_dp, dvmrp_td_entry *,
		      key, u_int32)
{
    register dvmrp_td_entry *dp;
    register dvmrp_td_entry *dprev, *dcurr;
    register u_int32 mask, omask;

    /*
     * Fetch a new structure and initialize it
     */
    dp = DVMRP_TD_ALLOC();
    dp->dvmrp_tree_key = key;
    tlp->target_td_count[maskindex]++;

    /*
     * If he didn't include a "found" demux node, but there are node
     * to "find", "find" it.  If there are no node to find then
     * this is the first node in the tree and just needs to get
     * initialized appropriately.
     */
    if (!found_dp) {
	if (!(dcurr = tlp->target_td_head[maskindex])) {
	    tlp->target_td_head[maskindex] = dp->right = dp->left = dp;
	    dp->dvmrp_tree_mask = DVMRP_TD_NOMASK;
	    return dp;
        }
	mask = DVMRP_TD_NOMASK;
	while (mask > dcurr->dvmrp_tree_mask) {
	    mask = dcurr->dvmrp_tree_mask;
	    if (key & mask) {
		dcurr = dcurr->right;
	    } else {
		dcurr = dcurr->left;
	    }
	}
	found_dp = dcurr;
    }

    /*
     * Here we have an insertion to do.  Figure out the first
     * (most significant) bit which differs from the "found" node.  This
     * tells us the bit we need to test in the node we insert.
     *
     * Do a binary search to find the byte the first bit is in, then use
     * the table to form the mask.
     */
    mask = key ^ found_dp->dvmrp_tree_key;
    if (mask >= 0x10000) {
	if (mask >= 0x1000000) {
	    mask = ((u_int32)(dvmrp_td_bit_table[mask >> 24])) << 24;
	} else {
	    mask = ((u_int32)(dvmrp_td_bit_table[mask >> 16])) << 16;
	}
    } else {
	if (mask >= 0x100) {
	    mask = ((u_int32)(dvmrp_td_bit_table[mask >> 8])) << 8;
	} else {
	    mask = (u_int32)(dvmrp_td_bit_table[mask]);
	}
    }

    /*
     * XXX Sanity.  This could only happen if a node with the same key
     * is in the tree already, which should never happen.
     */
    assert(mask != 0);
    dp->dvmrp_tree_mask = mask;

    /*
     * Now we locate where this guy needs to be inserted.  Search down
     * the tree until we find either an end node (i.e. an upward link)
     * or a node with a mask smaller than ours.  We insert our node
     * above this (watch for the case where the node we find is the
     * current root node), making one of the pointers an uplink pointing
     * at the node itself.
     */
    dprev = (dvmrp_td_entry *) 0;
    dcurr = tlp->target_td_head[maskindex];
    omask = DVMRP_TD_NOMASK;
    while (omask > dcurr->dvmrp_tree_mask) {
	omask = dcurr->dvmrp_tree_mask;
	if (mask >= omask) {
	    break;
	}
	dprev = dcurr;
	if (key & omask) {
	    dcurr = dcurr->right;
	} else {
	    dcurr = dcurr->left;
	}
    }
    assert(mask != dcurr->dvmrp_tree_mask);

    /*
     * Point the new node at the current node, and at itself.  The
     * pointer to the current node in the previous node is changed to
     * point at the new node.  Simple(?).
     */
    if (key & mask) {
	dp->right = dp;
	dp->left = dcurr;
    } else {
	dp->right = dcurr;
	dp->left = dp;
    }

    if (!dprev) {
	/* New root */
	tlp->target_td_head[maskindex] = dp;
    } else if (dprev->right == dcurr) {
	dprev->right = dp;
    } else {
	dprev->left = dp;
    }
    /* done */
    return dp;
}


/*
 * dvmrp_td_remove - remove a node from the demux tree.
 */
static void
dvmrp_td_remove __PF3(tlp, dvmrp_target *,
		      maskindex, int,
		      remove_dp, dvmrp_td_entry *)
{
    register dvmrp_td_entry *dp = remove_dp;
    register dvmrp_td_entry *tmp;
    register dvmrp_td_entry *dprev, *dcurr;
    register u_int32 key;
    dvmrp_td_entry *intprev;

    /*
     * Detect the trivial case, that there is only one guy in the tree,
     * and fix this up.
     */
    dcurr = tlp->target_td_head[maskindex];
    if (dcurr->dvmrp_tree_mask == DVMRP_TD_NOMASK) {
	assert(dcurr == dp);
	task_block_free(dvmrp_td_block_index, (void_t)dp);
	tlp->target_td_head[maskindex] = (dvmrp_td_entry *) 0;
	tlp->target_td_count[maskindex]--;
	return;
    }

    /*
     * We will be in the tree twice, once as an internal node and once
     * as an external (or up-)node.  Find the guy who points to our man
     * in the tree in both cases, and the guy who points to him in the
     * latter.
     */
    dprev = (dvmrp_td_entry *) 0;
    intprev = (dvmrp_td_entry *) 0;
    key = dp->dvmrp_tree_key;
    for (;;) {
	if (key & dcurr->dvmrp_tree_mask) {
	    tmp = dcurr->right;
	} else {
	    tmp = dcurr->left;
	}
	if (tmp == dp) {
	    if (dp->dvmrp_tree_mask >= dcurr->dvmrp_tree_mask) {
		break;		/* got it */
	    }
	    intprev = dcurr;	/* current is in front of internal */
	} else {
	    assert(tmp->dvmrp_tree_mask < dcurr->dvmrp_tree_mask);
	}
	dprev = dcurr;
	dcurr = tmp;
    }

    /*
     * Now we have dcurr pointing at the node which contains the up-link
     * to our deleted node, nprev pointing at the node which points to
     * ncurr and intprev pointing at the guy who precedes our node in its
     * internal incarnation.
     *
     * There are several cases here.  We know the information in dcurr
     * is no longer needed, and that the sibling pointer in dcurr will
     * be moved to dprev (replacing dcurr).  If our node is the special
     * one with no internal information, we make dcurr the special.
     * If dcurr is our node (i.e. intprev == dprev) then we simply
     * point nprev at our sibling and we're done.  Otherwise we
     * additionally copy our information to dcurr, and point intprev
     * at dcurr.
     *
     * There is probably a better way to do this, but I'm an engineer,
     * not a (mere) programmer.
     */
    if (dcurr->right == dp) {
	tmp = dcurr->left;
    } else {
	tmp = dcurr->right;
    }
    if (!dprev) {
	tlp->target_td_head[maskindex] = tmp;
    } else if (dprev->right == dcurr) {
	dprev->right = tmp;
    } else {
	dprev->left = tmp;
    }
    if (dcurr != dp) {
	if (dp->dvmrp_tree_mask == DVMRP_TD_NOMASK) {
	    dcurr->dvmrp_tree_mask = DVMRP_TD_NOMASK;
	    dcurr->left = dcurr->right = dcurr;
	} else {
	    dcurr->dvmrp_tree_mask = dp->dvmrp_tree_mask;
	    dcurr->left = dp->left;
	    dcurr->right = dp->right;
	    if (!intprev) {
		tlp->target_td_head[maskindex] = dcurr;
	    } else if (intprev->right == dp) {
		intprev->right = dcurr;
	    } else {
		intprev->left = dcurr;
	    }
	}
    }
    tlp->target_td_count[maskindex]--;
    task_block_free(dvmrp_td_block_index, (void_t)(dp));
}

static void
dvmrp_td_print __PF2(fp, FILE *,
		     tdp, dvmrp_td_entry *)
{

    if (BIT_TEST(tdp->td_flags, TDF_HOLDDOWN|TDF_POISON)) {
	(void) fprintf(fp, "\t\t\t\t%A/%A <%s> count left %u\n",
		       tdp->td_rt->rt_head->rth_dest,
		       tdp->td_rt->rt_head->rth_dest_mask,
		       trace_bits(dvmrp_target_entry_bits, tdp->td_flags),
		       tdp->td_metric);
    } else {
	(void) fprintf(fp, "\t\t\t\t%A/%A <%s> metric  %u\n",
		       tdp->td_rt->rt_head->rth_dest,
		       tdp->td_rt->rt_head->rth_dest_mask,
		       trace_bits(dvmrp_target_entry_bits, tdp->td_flags),
		       tdp->td_metric);
    }
}
