/*
 * Public Release 3
 * 
 * $Id: ripng_init.c,v 1.1.2.3 1998/10/20 19:31:38 swright Exp $
 */

/*
 * ------------------------------------------------------------------------
 * 
 * Copyright (c) 1996, 1997, 1998 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 and UC Berkeley's routing
 * daemon	 (routed).
 * 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.
 */

#define	INCLUDE_UDP

#include "include.h"
#include "targets.h"
#include "inet6.h"
#include "ripng_internal.h"

flag_t	ripng_flags = 0;		/* Options */
trace *ripng_trace_options = { 0 };	/* Trace flags */
metric_t ripng_preference = 0;		/* Preference for RIPng routes */

sockaddr_un *ripng_addr;

const flag_t ripng_trace_masks[RIPNG_COMMAND_MAX] = {
    TR_ALL,			/* 0 - Invalid */
    TR_RIPNG_DETAIL_REQUEST,	/* 1 - REQUEST */
    TR_RIPNG_DETAIL_RESPONSE,	/* 2 - RESPONSE */
} ;

const bits ripng_trace_types[] = {
    { TR_DETAIL,	"detail packets" },
    { TR_DETAIL_SEND,	"detail send packets" },
    { TR_DETAIL_RECV,	"detail recv packets" },
    { TR_PACKET,	"packets" },
    { TR_PACKET_SEND,	"send packets" },
    { TR_PACKET_RECV,	"recv packets" },
    { TR_DETAIL_1,	"detail request" },
    { TR_DETAIL_SEND_1,	"detail send request" },
    { TR_DETAIL_RECV_1,	"detail recv request" },
    { TR_PACKET_1,	"request" },
    { TR_PACKET_SEND_1,	"send request" },
    { TR_PACKET_RECV_1,	"recv request" },
    { TR_DETAIL_2,	"detail response" },
    { TR_DETAIL_SEND_2,	"detail send response" },
    { TR_DETAIL_RECV_2,	"detail recv response" },
    { TR_PACKET_2,	"response" },
    { TR_PACKET_SEND_2,	"send response" },
    { TR_PACKET_RECV_2,	"recv response" },
    { TR_DETAIL_3,	"detail other" },
    { TR_DETAIL_SEND_3,	"detail send other" },
    { TR_DETAIL_RECV_3,	"detail recv other" },
    { TR_PACKET_3,	"other" },
    { TR_PACKET_SEND_3,	"send other" },
    { TR_PACKET_RECV_3,	"recv other" },
    { 0, NULL }
};

/*  */

/*
 *	Cleanup before re-init
 */
static void
ripng_cleanup __PF1(tp, task *)
{
    adv_cleanup(RTPROTO_RIPNG,
		0,
		0,
		ripng_gw_list,
		&ripng_int_policy,
		&ripng_import_list,
		&ripng_export_list);

    if (tp) {
	trace_freeup(tp->task_trace);
    }
    trace_freeup(ripng_trace_options);
}

/*
 *	Reinit after parse
 */
static void
ripng_reinit __PF1(tp, task *)
{
    int entries = 0;
    gw_entry *gwp;

    trace_set(tp->task_trace, ripng_trace_options);

    /* Open the routing table */
    rt_open(tp);

    GW_LIST(ripng_gw_list, gwp) {
	rt_entry *rt;

	RTQ_LIST(&gwp->gw_rtq, rt) {
	    pref_t preference = ripng_preference;

	    /* Calculate preference of this route */
	    if (import(rt->rt_dest,
		       rt->rt_dest_mask,
		       ripng_import_list,
		       RT_IFAP(rt)->ifa_ps[tp->task_rtproto].ips_import,
		       rt->rt_gwp->gw_import,
		       &preference,
		       (metric_t *) 0,
		       RT_IFAP(rt),
		       (void_t) 0)) {
		if (rt->rt_preference != preference) {
		    /* The preference has changed, change the route */
		    (void) rt_change(rt,
				     rt->rt_metric,
				     rt->rt_metric2,
				     rt->rt_tag,
				     preference,
				     rt->rt_preference2,
				     rt->rt_n_gw, rt->rt_routers);
		}
		entries++;
	    } else {
		/* This route is now restricted */
		rt_delete(rt);
	    }
	} RTQ_LIST_END(&gwp->gw_rtq, rt) ;
    } GW_LIST_END(ripng_gw_list, gwp) ;

    /* Close the routing table */
    rt_close(tp, (gw_entry *) 0, entries, NULL);

    /* Indicate a reconfig in process */
    BIT_SET(ripng_flags, RIPNGF_RECONFIG);
}

static void
ripng_exit __PF1(tp, task *)
{
    if_addr *ifap;
    gw_entry *gwp;

    /* Release the target list, bit assignment, and buffers */
    target_free_list(tp, &ripng_targets);

    /* Reset the policy */
    IF_ADDR(ifap) {
	if (socktype(ifap->ifa_addr) == AF_INET6) {
	    ripng_control_reset(tp, ifap);
	}
    } IF_ADDR_END(ifap) ;

    rt_open(tp);

    GW_LIST(ripng_gw_list, gwp) {
	rt_entry *rt;

	RTQ_LIST(&gwp->gw_rtq, rt) {
	    rt_delete(rt);
	} RTQ_LIST_END(&gwp->gw_rtq, rt) ;
    } GW_LIST(ripng_gw_list, gwp) ;

    rt_close(tp, (gw_entry *) 0, 0, NULL);

    ripng_cleanup(tp);


}

/*
 *	Re-evaluate routing table
 */
static void
ripng_newpolicy __PF2(tp, task *,
		    change_list, rt_list *)
{
    /* Indicate reconfig done */
    BIT_RESET(ripng_flags, RIPNGF_RECONFIG);

    /* And evaluate policy */
    ripng_flash(tp, change_list);
}
    
static void
ripng_terminate __PF1(tp, task *)
{
    BIT_SET(ripng_flags, RIPNGF_TERMINATE);
    if (BIT_TEST(ripng_flags, RIPNGF_ON)) {
        /* Disable receive */
        task_set_recv(tp, 0);
        task_set_socket(tp, tp->task_socket);
        if (ripng_timer_update) 
          task_timer_reset(ripng_timer_update);
        if (ripng_timer_expire) 
          task_timer_reset(ripng_timer_expire);
        if (ripng_timer_garbage)
          task_timer_reset(ripng_timer_garbage);
    }
    ripng_exit(tp);
    task_delete(tp);
    ripng_timer_update = (task_timer *) 0;
    ripng_timer_expire = (task_timer *) 0;
    ripng_timer_garbage = (task_timer *) 0;
}

void
ripng_var_init()
{
    ripng_flags = RIPNGF_ON;
    ripng_preference = RTPREF_RIPNG;
}

void
ripng_init()
{
    static task *ripng_task;

    if (BIT_TEST(ripng_flags, RIPNGF_ON)) {
	/* set the Trace flag */
	trace_inherit_global(ripng_trace_options, 0, (flag_t) 0);

	/* make the task */
	if (!ripng_task) {
	    ripng_task = task_alloc("RIPng",
				    TASKPRI_PROTO,
				    ripng_trace_options);
	}

	/* addr & port for RIPng */
	{
	    u_int addr[] = RIPNG_ADDR;
	    u_short port = task_get_port(ripng_trace_options,
					 "route6",
					 "udp",
					 htons(RIPNG_PORT));

#ifdef notdef
	    ripng_task->task_addr = sockdup(sockbuild_in6(0, (byte *)&addr));
	    ripng_task->task_addr->in6.gin6_port = port;
#else
	    ripng_task->task_addr = sockdup(inet6_addr_any);
	    ripng_task->task_addr->in6.gin6_port = port;
	    ripng_addr = sockdup(sockbuild_in6(0, (byte *)&addr));
	    ripng_addr->in6.gin6_port = port;
#endif
	}

	ripng_task->task_rtproto = RTPROTO_RIPNG;
	ripng_task->task_rtfamily = AF_INET6;
	task_set_recv(ripng_task, ripng_recv);
	task_set_cleanup(ripng_task, ripng_cleanup);
	task_set_reinit(ripng_task, ripng_reinit);
	task_set_dump(ripng_task, ripng_dump);
	task_set_terminate(ripng_task, ripng_terminate);
	task_set_ifachange(ripng_task, ripng_ifachange);
	task_set_flash(ripng_task, ripng_flash);
	task_set_newpolicy(ripng_task, ripng_newpolicy);

	/* socket initialization */
	if ((ripng_task->task_socket =
	     task_get_socket(ripng_task, AF_INET6, SOCK_DGRAM, 0)) < 0) {
	    task_quit(errno);
	}
	if (!task_create(ripng_task)) {
	    task_quit(EINVAL);
	}

	if (task_set_option(ripng_task,
			    TASKOPTION_RECVBUF,
			    task_maxpacket) < 0) {
	    task_quit(errno);
	}
	(void) task_set_option(ripng_task,
			       TASKOPTION_RCVDSTADDR,
			       TRUE);
	if (task_set_option(ripng_task,
			    TASKOPTION_NONBLOCKING,
			    TRUE) < 0) {
	    task_quit(errno);
	}
	(void) task_set_option(ripng_task,
			       TASKOPTION_RCVIF,
			       TRUE);
	(void) task_set_option(ripng_task,
			       TASKOPTION_RCVHOPLIM,
			       TRUE);
        (void) task_set_option(ripng_task,
                               TASKOPTION_MULTI_TTL,
                               255);

	/* bind() */
	if (task_addr_local(ripng_task, ripng_task->task_addr)) {
	    trace_log_tp(ripng_task,
			 0,
			 LOG_ERR,
			 ("ripng_init: is routed6 or an old copy of gated running?"));
	    task_quit(errno);
	}

	/* Allocate the buffers */
#ifdef notdef
	全インタフェースの MTU が分かってから書く。
	task_alloc_send(ripng_task, RIPNG_PKTSIZE);
	task_alloc_recv(ripng_task, RIPNG_PKTSIZE);
#endif /* notdef */

    } else {
	ripng_task = (task *) 0;
    }
}
