#include "include.h"
#include "inet6.h"
#include "inet6_multi.h"
#include "krt.h"
#include "krt_var.h"
#include "krt_ipv6multi.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_v6(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(&mfcp->mfc_group->group_key, &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 IPV6MULTI_PROTO_PIMV6:
	{
	    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;
			if (!IS_LINKLADDR6(up.ifap->ifa_addr->in6.gin6_addr)) {
				/* In case the host doesn't use LL address to do report */
				if_addr *ifap2;

				IF_ADDR(ifap2) {
					if ((ifap2->ifa_link == up.ifap->ifa_link)
					   && (ifap2->ifa_addr->in6.gin6_family == AF_INET6)
					   && (IS_LINKLADDR6(ifap2->ifa_addr->in6.gin6_addr))) {
							up.ifap = ifap2;
					   break;
					}
				} IF_ADDR_END(ifap2);
		  }
		}	
	}
	break;

	case IPV6MULTI_PROTO_DVMRP:
	{
	    rt_entry *rt;
	    if_addr *ifap;
	    
		/*
		 * 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(RTS_ACTIVE,
			   (flag_t) 0,
			   src_addr,
			   RTPROTO_BIT(RTPROTO_DIRECT));
	    if (rt) {
		ifap = RT_IFAP(rt);
	    }
	    if (!rt || rt->rt_preference != ifap->ifa_preference) {
		rt = rt_lookup(RTS_NOTINSTALL,
			       (flag_t) 0,
			       src_addr,
			       RTPROTO_BIT(RTPROTO_DVMRP));
	    }
	    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);
}
