/*
 * Public Release 3
 * 
 * $Id: krt_ipmulti.c,v 1.2 1997/07/17 22:42:53 chopps Exp $
 *
 *  Author: 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 "inet_multi.h"
#include "krt.h"
#include "krt_var.h"
#include "krt_ipmulti.h"

extern task *krt_task;

static block_t krt_mfc_recv_block_index;
	/*
	 * intialize double linked list of callback routines
	 */
struct krt_mfc_recv krt_mfc_recv_head =
	{ &krt_mfc_recv_head, &krt_mfc_recv_head };

/*
 * krt_init_mfc()
 */

void
krt_init_mfc __PF0(void)
{
    krt_mfc_recv_block_index = task_block_init(sizeof (struct krt_mfc_recv),
					       "krt_mfc_recv");
}


void
krt_register_mfc(error, callback)
int error;
_PROTOTYPE(callback,
	   void,
	   (int,
	    if_addr *,
	    mfc *));
{
    struct krt_mfc_recv *recv_list;

    MFC_RECV_SCAN(recv_list, &krt_mfc_recv_head) {
	if (recv_list->errno == error && recv_list->recv_routine == callback) {
		return;		/* ignore duplicates */
	}
    } MFC_RECV_SCAN_END(recv_list, &krt_mfc_recv_head);

    recv_list = (struct krt_mfc_recv *) task_block_alloc(krt_mfc_recv_block_index);
    recv_list->errno = error;
    recv_list->recv_routine = callback;

		/* just insert at head of list */
    INSQUE(recv_list, &krt_mfc_recv_head);
}


void
krt_unregister_mfc(error, callback)
int error;
_PROTOTYPE(callback,
	   void,
	   (int,
	    if_addr *,
	    mfc *));
{
    struct krt_mfc_recv *recv_list;

    MFC_RECV_SCAN(recv_list, &krt_mfc_recv_head) {
	if (recv_list->errno == error && recv_list->recv_routine == callback) {
		REMQUE(recv_list);
		task_block_free(krt_mfc_recv_block_index, (void_t) recv_list);
		return;
	}
    } MFC_RECV_SCAN_END(recv_list, &krt_mfc_recv_head);
}

void
krt_check_mfc  __PF3(use, int,
		     dst, sockaddr_un *,
		     src, sockaddr_un *)
{
    mfc *mfcp = mfc_locate_mfc(dst, src);
    if (mfcp) {
	    mfcp->mfc_lastuse = mfcp->mfc_use;
	    mfcp->mfc_use = use;
	    (*mfcp->mfc_callback)(mfcp);
    } else {
	trace_log_tp(krt_task,
		     0,
		     LOG_ERR,
		     ("krt_check_mfc: can't locate group %A source %A",
		      dst,
		      src));
    }
}

void
krt_update_mfc  __PF1(mfcp, mfc *)
{

    krt_resolve_cache(sockbuild_in(0, mfcp->mfc_group->group_key),
		      sockbuild_in(0, mfcp->mfc_src),
		      mfcp);
}


/*
 * Find the upstream interface for this source
 * Look up source on behalf of protocol specified in request_proto.
 */

upstream *
krt_locate_upstream  __PF2(src_addr, sockaddr_un *,
			   request_proto, int)
{
    static upstream up;

    up.ifap = (if_addr *) 0;
    up.nbr = (sockaddr_un *) 0;
    up.protocol = 0;
    up.metric = (metric_t) 0;

    switch(request_proto) {

	case IPMULTI_PROTO_PIM:
	{
	    rt_list *rtl;
	    rt_head *rth = (rt_head *) 0;
	    krt_parms *krtp = (krt_parms *) 0;

	    rtl = rthlist_match(src_addr);

	    RT_LIST(rth, rtl, rt_head) {

		/*
		 * Use the one thats installed in the kernel
		 */

		if ((krtp = krt_kernel_rt(rth))) {
		    break;
		}
	    } RT_LIST_END(rth, rtl, rt_head) ;

	    RTLIST_RESET(rtl);

	    if (krtp) {
		up.ifap = krtp->krtp_ifap;
		up.nbr = krtp->krtp_router;
		up.protocol = krtp->krtp_protocol;
		up.metric = krtp->krtp_metric;
	    }
	}
	break;

	case IPMULTI_PROTO_DVMRP:
	{
	    rt_entry *rt;
	    
		/*
		 * First look up remote DVMRP routes. These are not
		 * currently used in unicast routing so they are
		 * not installed and not advertised.
		 */
	    rt = rt_lookup((flag_t) 0,
			   (flag_t) 0,
			   src_addr,
			   RTPROTO_BIT(RTPROTO_DVMRP)|RTPROTO_BIT(RTPROTO_DIRECT));

	    if (rt) {
		up.ifap = RT_IFAP(rt);
		up.nbr = RT_ROUTER(rt);
		up.protocol = RTPROTO_DVMRP;
		up.metric = rt->rt_metric;
	    }
	}
	break;

	default:
	    return (upstream *) 0;
    }

    return(&up);
}
