/*
 * Copyright (c) 2000 Columbia University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by Columbia University
 * and its contributors.'' Neither the name of the University nor the names 
 * of its contributors may 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.
 *
 * Written by Ping Pan, Columbia University, 2000
 *
 * The author would like to thank Bell Labs for providing time and freedom
 * to complete this work. Please forward bug fixes, enhancements and questions
 * to pingpan@cs.columbia.edu.
 */

/* reply the requests from yestat.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#define _IP_VHL
#include <netinet/ip.h>
#include <netinet/udp.h>

#include "queue.h"
#include "table.h"
#include "rtp.h"
#include "rtp_yessir.h"
#include "intserv.h"
#include "yes_var.h"


char version[] = "YESSIR-1.07,  soft-state tests, Ping Pan, 4/21/2000";

void
yestat_get_ver(u_char *data, struct sockaddr *from)
{
	register struct yestat_version_string *msg;
	register int i=0, len;

	msg = (struct yestat_version_string *)data;
	msg->req.ack = YESTAT_ACK;
	msg->req.more = YESTAT_SINGLE;

	while (version[i]) 
		i++;

	len = sizeof(*msg) + i;

	msg->req.len = htonl(len);
	msg->string_len = i;
	memcpy((void *)msg->ver_string, &version[0], i);
	msg->ver_string[i] = 0;

	stat_send((char *)msg, len, from);
	return;
}


void
yestat_get_sum(u_char *data, struct sockaddr *from)
{
	struct yestat_summary *msg = (struct yestat_summary *)data;

	msg->req.ack = YESTAT_ACK;
	msg->req.more = YESTAT_SINGLE;
	msg->req.len = htonl(sizeof(*msg));

	msg->max_flow = htonl(yestab->max_flow);
	msg->curr_flow = htonl(yestab->curr_flow);
	msg->curr_pend_flow = htonl(yestab->curr_pending);
	msg->total_if = htonl(rifm->curr_if);

	stat_send((char *)msg, sizeof *msg, from);
	return;
}


/* Given there may be a lot of entries here, we cannot rely on the received
 * buffer space. Instead, we allocate as much memory as we need.
 * If there are way too many entries to deal with, we may need to 
 * build several fragments. This is the same for yestat_get_flow() below.
 */

void
yestat_get_if(u_char *data, struct sockaddr *from)
{
	struct yestat_if *msg;
	struct yestat_if_info *ifmsg;
	RIF *rif;
	int len, i, j;

	/* the total packet length */
	len = sizeof (struct yestat_if) +
				rifm->curr_if * sizeof(struct yestat_if_info);

	if (!(msg = (struct yestat_if *)malloc(len))) {
		struct yestat_msg *rep;

		yestat->no_memory++;

		rep = (struct yestat_msg *)data;
		rep->ack = YESTAT_NACK;
		stat_send((char *)rep, sizeof(*rep), from);
		return;
	}

	bzero(msg, len);
	msg->req.ack = YESTAT_ACK;
	msg->req.len = htonl(len);
	msg->req.more = YESTAT_SINGLE;
	msg->req.type = YESTAT_GET_IF;

	ifmsg = (struct yestat_if_info *)(msg + 1);

	for (i = 0; i < RIF_HASH_SIZE; i++) {
		j = 0;
		rif = (RIF *)rif_at(i);
		for (; j < rif_num(i); j++, rif = rif->next) {

			strncpy(ifmsg->name, rif->if_name, 
						strlen(rif->if_name));

			ifmsg->index = htonl(rif->if_index);
			ifmsg->state = rif->state;
			ifmsg->type = rif->if_type;
			ifmsg->l2_hdrlen = rif->if_hdrlen;
			ifmsg->link_bw = htonl(rif->link_bw);
			ifmsg->max_resv_bw = htonl(rif->max_resv_bw);
			ifmsg->avail_bw = htonl(rif->avail_bw);

			ifmsg->int_addr = htonl(rif->int_addr);
			ifmsg->int_mask = htonl(rif->int_mask);
			ifmsg->int_dstaddr = htonl(rif->int_dstaddr);

			ifmsg->rx_pkt = htonl(rif->rx_pkt);
			ifmsg->rx_byte = htonl(rif->rx_byte);
			ifmsg->rx_err = htonl(rif->rx_err);
			ifmsg->tx_pkt = htonl(rif->tx_pkt);
			ifmsg->tx_byte = htonl(rif->tx_byte);
			ifmsg->tx_err = htonl(rif->tx_err);

			ifmsg += 1;
		}
	}

	stat_send((char *)msg, len, from);
	free(msg);
	return;
}


int
yestat_get_out(yesEntry *ye, char *data)
{
	struct yestat_out_info *outmsg;
	yesOutEntry *yoe;
	int i, len=0;

	outmsg = (struct yestat_out_info *)data;

	i = 0;
	yoe = (yesOutEntry *)ye->egress.head;
	for (i=0; i < ye->egress.cnt; i++, yoe = yoe->next) {

		outmsg->out_if = htons(yoe->out_if);
		outmsg->rhandle = htonl(yoe->rhandle); 
		outmsg->eff_ftype = htons(yoe->eff_ftype);
		outmsg->req_ftype = htons(yoe->req_ftype);

		if (yoe->status & YES_RESV_BIT)
			outmsg->state = htons(YESTAT_RESV);
		else if (yoe->status & YES_PENDING_BIT)
			outmsg->state = htons(YESTAT_PENDING);

		if (yoe->eff_ftype == YESSIR_MB_FSPEC) {
			if (yoe->status & YES_RESV_BIT)
				outmsg->eff_rate = htonl(yoe->eff_qos.mb.bw);
			else 
				outmsg->eff_rate = 0;
		}
		else if (yoe->eff_ftype == YESSIR_IS_FSPEC) {

			if (yoe->status & YES_RESV_BIT) {
				outmsg->eff_rate = htonl(yoe->eff_qos.is.avg);
				outmsg->is_service = 
					htons(yoe->eff_qos.is.serv);
			}
			else {
				outmsg->eff_rate = 0;
				outmsg->is_service = 0;
			}
		}
		else if (yoe->eff_ftype == YESSIR_DS_FSPEC) { /* LATER */
			outmsg->state = 0;
			outmsg->eff_rate = 0;
			outmsg->ds_phb = 0;
		}

		if (yoe->req_ftype == YESSIR_MB_FSPEC)
			outmsg->req_rate = htonl(yoe->req_qos.mb.bw);
		else if (yoe->req_ftype == YESSIR_IS_FSPEC)
			outmsg->req_rate = htonl(yoe->req_qos.is.avg);
		else if (yoe->req_ftype == YESSIR_DS_FSPEC)
			outmsg->req_rate = 0;

		len += sizeof(struct yestat_out_info);
		outmsg += 1;
	}
	return len;
}


/* Get a fragment of flows from index <start> to <end>. The total
 * required memory space is <len>
 */

void
yestat_get_flowfrag(u_char *data, struct sockaddr *from, 
					int len, int start, int end)
{
	register struct yestat_flow *msg;
	register yesEntry *ye;
	register struct yestat_flow_info *flowmsg;
	register int i, j;

	if (!(msg = (struct yestat_flow *)malloc(len))) {
		struct yestat_msg *rep;

		yestat->no_memory++;

		rep = (struct yestat_msg *)data;
		rep->ack = YESTAT_NACK;
		stat_send((char *)rep, sizeof(*rep), from);
		return;
	}

	bzero(msg, len);
	msg->req.ack = YESTAT_ACK;
	msg->req.len = htonl(len);
	msg->req.type = YESTAT_GET_FLOWDUMP;

	if (start ==0 && end == YESSIR_HASH_SIZE)
		msg->req.more = YESTAT_SINGLE;
	else if (end == YESSIR_HASH_SIZE)
		msg->req.more = YESTAT_LAST;
	else
		msg->req.more = YESTAT_MORE;

	flowmsg = (struct yestat_flow_info *)(msg + 1);
	data = (char *)flowmsg;

	for (i = start; i < end; i++) {

		j=0;
		ye = (yesEntry *)ye_at(i);
		for (; j < ye_num(i); j++, ye = ye->next) {

			flowmsg->da = htonl(ye->id.daddr);
			flowmsg->sa = htonl(ye->id.saddr);
			flowmsg->dp = htons(ye->id.dport);
			flowmsg->sp = htons(ye->id.sport);

			flowmsg->fid = htonl((u_int32_t)ye);

			flowmsg->max_lifetime = 
				htonl(ye->max_round * YESSIR_CYCLE);
			flowmsg->curr_lifetime = 
				htonl(ye->curr_round * YESSIR_CYCLE);

			flowmsg->fhandle = htonl(ye->fhandle);
			flowmsg->in_if = htons(ye->in_if);
			flowmsg->num_out_if = htons(ye->egress.cnt);

			data = (char *)(flowmsg + 1);
			data += yestat_get_out(ye, data);
			flowmsg = (struct yestat_flow_info *)data;
		}
	}

	stat_send((char *)msg, len, from);
	free(msg);

	return;
}


void
yestat_get_flow(u_char *data, struct sockaddr *from)
{
	register struct yestat_flow *msg;
	register yesEntry *ye;
	register int len, n, i, j, first;

	len = sizeof(*msg);
	first = 0;

	for (i=0; i < YESSIR_HASH_SIZE; i++) {

		n = 0;
		ye = (yesEntry *)ye_at(i);

		for (j=0; j < ye_num(i); j++) {
			n += sizeof(struct yestat_flow_info);
			n += ye->egress.cnt * sizeof(struct yestat_out_info);
 			ye = ye->next;
		}

		if ((len + n) > YESTAT_MAXBUF) {
			yestat_get_flowfrag(data, from, len, first, i);

			first = i;
			len = sizeof(*msg);
		}

		len += n;
	}

	yestat_get_flowfrag(data, from, len, first, YESSIR_HASH_SIZE);
	return;
}


void
yestat_get_pending(u_char *data, struct sockaddr *from)
{
	return;
}


void
yestat_get_counter(u_char *data, struct sockaddr *from)
{
	register struct yestat_msg *msg;
	register struct yestat_cnt *cnt;
	register int i=0, len;

	len = sizeof(*msg) + sizeof(*cnt);

	msg = (struct yestat_msg *)data;
	msg->ack = YESTAT_ACK;
	msg->more = YESTAT_SINGLE;
	msg->len = htonl(len);

	cnt = (struct yestat_cnt *)(msg + 1);
	bzero(cnt, sizeof(*cnt));
	for (i=0; i < len; i++)
		((u_int32_t *)cnt)[i] = htonl(((u_int32_t *)yestat)[i]);

	stat_send((char *)msg, len, from);
	return;
}


struct yestat_cmd {
	int	type;
	void	(*action)(u_char *, struct sockaddr *from);
} yestat_cmd[] = {
	{ YESTAT_GET_VER, yestat_get_ver },
	{ YESTAT_GET_SUM, yestat_get_sum },
	{ YESTAT_GET_IF, yestat_get_if },
	{ YESTAT_GET_FLOWDUMP, yestat_get_flow },
	{ YESTAT_GET_PENDING, yestat_get_pending },
	{ YESTAT_GET_COUNTER, yestat_get_counter },
};

void
yestat_read(u_char *data, struct sockaddr *from)
{
	struct yestat_msg *msg = (struct yestat_msg *)data;
	u_int32_t i=0;

	while (i < sizeof yestat_cmd / sizeof yestat_cmd[0]) {

		if (yestat_cmd[i].type == msg->type) {
			yestat_cmd[i].action(data, from);
			return;
		}

		i++;
	}

	msg->ack = YESTAT_NACK;
	stat_send((char *)msg, sizeof *msg, from);

	return;
}
