/*
 *  targets.c,v 1.26 1994/02/06 19:22:50 jch Exp
 */

/* Gated Release 3.5 */
/* Copyright (c) 1990,1991,1992,1993,1994 by Cornell University. All rights */
/* reserved. Refer to Particulars and other Copyright notices at the end of this */
/* file. */


#include "include.h"
#include "targets.h"

/**/

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

static block_t target_block_index;
block_t target_td_block;


#define	TD_CLEANUP(tlp, tdp) \
	{ \
		rttsi_reset((tdp)->td_rt->rt_head, (tlp)->target_rtbit); \
		(void) rtbit_reset((tdp)->td_rt, (tlp)->target_rtbit); \
		TD_DEQUE(tdp); \
		TD_FREE(tdp); \
	}

/* Free any routes we were announcing */
static void
target_release __PF1(tlp, target *)
{
    register td_entry *tdp;
    /* Release any routes we have announced */

    rt_open(tlp->target_task);

    TD_LIST(tdp, &tlp->target_td) {
	/* Reset the bit and tsi then dequeue and free the entry */
	TD_CLEANUP(tlp, tdp);
    } TD_LIST_END(tdp, &tlp->target_td) ;

    rt_close(tlp->target_task, (gw_entry *) 0, 0, NULL);

    if (tlp->target_free_job) {
	task_job_run(tlp->target_free_job);
    }

    /* And finally free the bit */
    rtbit_free(tlp->target_task, tlp->target_rtbit);

    tlp->target_rtbit = 0;
}


/* Free a target list */
target *
target_free __PF2(tp, task *,
		  old, target *)
{
    target *tlp;
    target *next = old;
    
    while ((tlp = next)) {
	next = tlp->target_next;

	/* Invoke protocol specific free routine */
	if (tlp->target_reset) {
	    tlp->target_reset(tp, tlp);
	}
	
	/* Free the bit if allocated */
	if (tlp->target_rtbit) {
	    target_release(tlp);
	}
	
	/* Free the interface */
	if (tlp->target_ifap) {
	    IFA_FREE(tlp->target_ifap);
	}

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

    return (target *) 0;
}


target *
target_locate __PF3(list, target *,
		    ifap, if_addr *,
		    gwp, gw_entry *)
{
    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 (target *) 0;
}


PROTOTYPE(target_alloc,
	  static inline target *,
	  (task *,
	   target **,
	   target **,
	   if_addr *,
	   gw_entry *,
	   int,
	   _PROTOTYPE(ta_dump,
		      void,
		      (rt_head *,
		       void_t,
		       char *))));	   
static inline target *
target_alloc(ta_tp, old_list, new_list, ifap, gwp, alloc, ta_dump)
task *ta_tp;
target **old_list;
target **new_list;
if_addr *ifap;
gw_entry *gwp;
int alloc;
_PROTOTYPE(ta_dump,
	   void,
	   (rt_head *,
	    void_t,
	    char *));
{
    target *prev;
    target *ta_tlp;

    /* Locate this target on the old list */
    for (ta_tlp = *old_list, prev = (target *) 0;
	 ta_tlp;
	 prev = ta_tlp, ta_tlp = ta_tlp->target_next) {
	if (ta_tlp->target_ifap == ifap
	    && ta_tlp->target_gwp == gwp) {
	    if (prev) {
		prev->target_next = ta_tlp->target_next;
	    } else {
		*old_list = ta_tlp->target_next;
	    }
	    ta_tlp->target_next = (target *) 0;
	    break;
	}
    }

    /* If not on the old list, allocate a new one */
    if (!ta_tlp) {
	/* Allocate our block index */
	if (!target_block_index) {
	    target_block_index = task_block_init(sizeof (target), "target");
	    target_td_block = task_block_init(sizeof (td_entry), "target_dest");
	}

	/* Allocate this block */
    	ta_tlp = (target *) task_block_alloc(target_block_index);
	ta_tlp->target_task = ta_tp;
	ta_tlp->target_td.td_forw = ta_tlp->target_td.td_back = &ta_tlp->target_td;
	ta_tlp->target_td_free.td_forw = ta_tlp->target_td_free.td_back = &ta_tlp->target_td_free;
    }

    if (alloc) {
	if (!BIT_TEST(ta_tlp->target_flags, TARGETF_SUPPLY)) {
	    /* Allocate a bit for this target */

	    ta_tlp->target_rtbit = rtbit_alloc(ta_tp,
					       TRUE,
					       sizeof (td_entry *),
					       (void_t) ta_tlp,
					       ta_dump);

	    /* 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 */
	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 */
    if (*new_list) {
	for (prev = *new_list; prev->target_next; prev = prev->target_next);
	prev->target_next = ta_tlp;
    } else {
	*new_list = ta_tlp;
    }

    return ta_tlp;
}


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

    *list = (target *) 0;

    /* 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) {
	    sockaddr_un **dest;

	    if (socktype(ifap->ifa_addr) != AF_INET
		|| !BIT_TEST(ifap->ifa_state, IFS_UP)
		|| BIT_TEST(ifap->ifa_ps[tp->task_rtproto].ips_state, IFPS_NOOUT)) {
		/* Interface is down or prohibited from announcing */
		/* this protocol */

		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;
		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 = target_alloc(tp,
			       &old,
			       list,
			       ifap,
			       (gw_entry *) 0,
			       BIT_TEST(flags, TARGETF_BROADCAST) ? TRUE : FALSE,
			       dump);

	    /* 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_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,
				 ("target_build: Ignoring source gateway %A not on attached net",
				  gwp->gw_addr));
		}
		continue;
	    }

	    if (!BIT_TEST(ifap->ifa_state, IFS_UP) ||
		BIT_TEST(ifap->ifa_ps[tp->task_rtproto].ips_state, IFPS_NOOUT)) {
		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 */

			tlp->target_rtbit = rtbit_alloc(tp,
							TRUE,
							sizeof (td_entry *),
							(void_t) tlp,
							dump);

			/* 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 = 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_free(tp, old);
    }

    return targets;
}


/*
 *	Free routes for a target
 */
void
target_cleanup __PF1(jp, task_job *)
{
    target *tlp = (target *) jp->task_job_data;
    td_entry *tdp;

    rt_open(tlp->target_task);

    TD_LIST(tdp, &tlp->target_td_free) {
	/* Reset the bit and tsi then dequeue and free the entry */
	TD_CLEANUP(tlp, tdp);
    } TD_LIST_END(tdp, &tlp->target_td_free) ;

    if (jp) {
	task_job_delete(jp);
	tlp->target_free_job = (task_job *) 0;
    }

    rt_close(tlp->target_task, (gw_entry *) 0, 0, NULL);
}


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

    if (list) {
	(void) fprintf(fp, "\n\tTargets:\n");
	
	TARGET_LIST(tlp, list) {
	    (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));
	    if (BIT_TEST(tlp->target_flags, TARGETF_SUPPLY)) {
		(void) fprintf(fp, "\t\t\tBit: %d\n",
			       tlp->target_rtbit);
	    }
	    (void) fprintf(fp, "\n");

	    /* Dump the routes */
	    if (tlp->target_td.td_forw != &tlp->target_td) {
		td_entry *tdp;

		(void) fprintf(fp, "\t\t\tRoutes:\n");
		TD_LIST(tdp, &tlp->target_td) {
		    if (TD_HOLDDOWN(tdp)) {
			(void) fprintf(fp, "\t\t\t\t%A/%A %sholddown count left %u\n",
				       tdp->td_rt->rt_dest,
				       tdp->td_rt->rt_dest_mask,
				       TD_CHANGED(tdp) ? "changed " : "",
				       tdp->td_metric);
		    } else {
			(void) fprintf(fp, "\t\t\t\t%A/%A %smetric  %u\n",
				       tdp->td_rt->rt_dest,
				       tdp->td_rt->rt_dest_mask,
				       TD_CHANGED(tdp) ? "changed " : "",
				       tdp->td_metric);
		    }
		} TD_LIST_END(tdp, &tlp->target_td) ;
	    }

	    /* Dump the free list */
	    if (tlp->target_td_free.td_forw != &tlp->target_td_free) {
		td_entry *tdp;

		(void) fprintf(fp, "\t\t\tFree list:\n");
		TD_LIST(tdp, &tlp->target_td_free) {
		    (void) fprintf(fp, "\t\t\t\t%A/%A\n",
				   tdp->td_rt->rt_dest,
				   tdp->td_rt->rt_dest_mask);
		} TD_LIST_END(tdp, &tlp->target_td_free) ;
	    }
	} TARGET_LIST_END(tlp, list) ;

	(void) fprintf(fp, "\n");
    }
}


/*
 * ------------------------------------------------------------------------
 * 
 * 	GateD, Release 3.5
 * 
 * 	Copyright (c) 1990,1991,1992,1993,1994 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.
 * 
 * 	Royalty-free licenses to redistribute GateD Release
 * 	3 in whole or in part may be obtained by writing to:
 * 
 * 	    GateDaemon Project
 * 	    Information Technologies/Network Resources
 * 	    200 CCC
 * 	    Cornell University
 * 	    Ithaca, NY  14853-2601  USA
 * 
 * 	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.
 * 
 * 	Please forward bug fixes, enhancements and questions to the
 * 	gated mailing list: gated-people@gated.cornell.edu.
 * 
 * ------------------------------------------------------------------------
 * 
 *       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.
 */
