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

static const bits ripng_flag_bits[] = {
    { RIPNGF_ON,	"ON" },
    { RIPNGF_TERMINATE, "Terminate" },
    { 0 }
};

static const bits ripng_target_bits[] = {
    { RIPNGTF_MC,	"Multicast" },
    { RIPNGTF_POLL,	"Poll" },
    { 0 }
};

const bits ripng_cmd_bits[] = {
    { 0,			"Invalid" },
    { RIPNG_COMMAND_REQUEST,	"Request" },
    { RIPNG_COMMAND_RESPONSE,	"Response" },
    { 0 }
};


/*
 *	Dump info about RIPng
 */
static void
ripng_int_dump __PF2(fd, FILE *,
		     list, config_entry *)
{
    register config_entry *cp;

    CONFIG_LIST(cp, list) {
	switch (cp->config_type) {
	case RIPNG_CONFIG_IN:
	    (void) fprintf(fd, " %sripin",
			   GA2S(cp->config_data) ? "" : "no");
	    break;

	case RIPNG_CONFIG_OUT:
	    (void) fprintf(fd, " %sripout",
			   GA2S(cp->config_data) ? "" : "no");
	    break;

	case RIPNG_CONFIG_METRICIN:
	    (void) fprintf(fd, " metricin %u",
			   (metric_t) GA2S(cp->config_data));
	    break;

	case RIPNG_CONFIG_METRICOUT:
	    (void) fprintf(fd, " metricout %u",
			   (metric_t) GA2S(cp->config_data));
	    break;

	default:
	    assert(FALSE);
	    break;
	}
    } CONFIG_LIST_END(cp, list) ;
}

void
ripng_dump __PF2(tp, task *,
		 fd, FILE *)
{
    (void) fprintf(fd, "\tFlags: %s\tDefault metric: %d\t\tDefault preference: %d\n",
		   trace_bits(ripng_flag_bits, ripng_flags),
		   ripng_default_metric,
		   ripng_preference);
    target_dump(fd, &ripng_targets, ripng_target_bits);
    if (ripng_gw_list) {
	(void) fprintf(fd, "\tActive gateways:\n");
	gw_dump(fd,
		"\t\t",
		ripng_gw_list,
		tp->task_rtproto);
	(void) fprintf(fd, "\n");
    }
    if (ripng_int_policy) {
	(void) fprintf(fd, "\tInterface policy:\n");
	control_interface_dump(fd, 2, ripng_int_policy, ripng_int_dump);
    }
    control_import_dump(fd,1, RTPROTO_RIPNG, ripng_import_list, ripng_gw_list);
    control_export_dump(fd,1, RTPROTO_RIPNG, ripng_export_list, ripng_gw_list);
    (void) fprintf(fd, "\n");
}

void
ripng_trace __PF7(trp, trace *,
		  dir, int,
		  ifap, if_addr *,
		  who, sockaddr_un *,
		  msg, register struct ripng *,
		  size, register size_t,
		  detail, int)
{
    register const char *cmd = trace_state(ripng_cmd_bits, msg->ripng_command < RIPNG_COMMAND_MAX ? msg->ripng_command : 0);
    if (dir) {
	/* Trace packet transmission */

	tracef("RIPng %sSENT %A(%s) -> %#A",
	       dir > 0 ? "" : "*NOT* ",
	       ifap->ifa_addr_local,
               ifap->ifa_link->ifl_name,
	       who);
    } else {
	/* Trace packet reception */
	tracef("RIPng RECV %#A",
	       who);
	if (task_recv_dstaddr) {
	    tracef(" -> %A",
		   task_recv_dstaddr);
	}
	if (task_recv_interface) {
	    tracef(" interface %s(%A)",
		   task_recv_interface->ifa_link->ifl_name,
		   task_recv_interface->ifa_addr_local);
	}
    }
    tracef(" version %d,  command %s, length %d",
	   msg->ripng_version,
	   cmd,
	   size);

    switch(msg->ripng_version) {
      case RIPNG_VERSION_1:
	{
	    if (msg->ripng_null16) {
		tracef(" ?");
	    }

	    trace_only_tf(trp,
			  0,
			  (NULL));	/* flush the trace */

	    if (detail) {
		struct ripng_netinfo *rte = (struct ripng_netinfo *) ((void_t) (msg + 1));

		for (size -= sizeof(struct ripng);
		     size >= sizeof(struct ripng_netinfo);
		     rte++, size -= sizeof(struct ripng_netinfo)) {

		    char zero = ' ';

		    if (rte->ripng_metric == RIPNG_METRIC_NEXTHOP) {
#define rtn ((struct ripng_nexthop *)rte)
			/* Verify that all reserved fields are zero */
			if (rtn->ripng_null16
			    || rtn->ripng_null8) {
			    zero = '?';
			}

			tracef("\tNext Hop of the belows are %A",
			       sockbuild_in6(0, (byte *)&rtn->ripng_nexthop));
#undef rtn
		    } else {
			/* Verify that all reserved fields are zero */
			if (rte->ripng_tag) {
			    zero = '?';
			}
			tracef("\t%A/%d metric %d%s",
			       sockbuild_in6(0, (byte *)&rte->ripng_prefix),
			       rte->ripng_prefixlen,
			       rte->ripng_metric,
			       (rte->ripng_metric > 16) ? "(???)" : "");
		    }
		}
	    }
	    trace_only_tf(trp,
			  TRC_NOSTAMP,
			  (NULL));
	}
	break;

      default:
	{
	    trace_only_tf(trp,
			  0,
			  (NULL));
	    break;
	}
    }
}

void
ripng_tsi_dump __PF4(fp, FILE *,
		     rth, rt_head *,
		     data, void_t,
		     pfx, const char *)
{
    target *tlp = (target *) data;
    td_entry *tdp;

    TD_TSI_GET(tlp, rth, tdp);

    if (tdp) {
	(void) fprintf(fp,
		       "%sRIPng %A%s <%s> metric %u\n",
			pfx,
			*tlp->target_dst,
			BIT_TEST(tlp->target_flags, RIPNGTF_MC) ? "(mc)" : "",
			trace_bits(target_entry_bits, tdp->td_flags),
			tdp->td_metric);
    }
}
