#define	INCLUDE_UDP

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

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

sockaddr_un *ripng_addr;

/*  */

static void
ripng_exit __PF1(tp, task *)
{

    /* Release the target list, bit assignment, and buffers */
    target_free_list(tp, &ripng_targets);
}
    
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, 0);
	task_set_reinit(ripng_task, 0);
	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, 0);

	/* 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;
    }
}
