/*
 * 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.
 */

/* This contains the YESSIR Traffic Conditioning routines. I have defined 
 * a bunch of API's (modeled after RSVP and Altq). Users should be able to 
 * choose from a range of TC functions at init time by putting own
 * desired functions in yes_tcinit().
 *
 */
 
#include <stdio.h>
#include <string.h>
#include <sys/types.h>

/* IntServ flowspec descriptor:
 *
 * this is the same as one in table.h
 */

typedef struct {
	u_int32_t	serv;  		/* service class */
	float		peak;		/* peak rate */
	float		avg;		/* token rate */
	float		burst;		/* token size */
	u_int32_t    	mintu;       	/* for Policing algorithm */
	u_int32_t    	maxtu;       	/* for Admission ctl */
	float		R;		/* R (rate) from GS */
	u_int32_t    	s;	       	/* s (slack) from GS */
} IntServ_spec;


/* Traffic Control API's
 *
 * Note:
 *   You'd better return from an API asap. If your router has resource 
 *   table and classifier on the line cards, queue up the commands and return. 
 *   You gotta be an idiot to call and wait for your command completation here. 
 *   (I've recently seen and unfortunately worked on a "great" router project 
 *   where the system takes more than 5 seconds to setup a reservation.)
 */

/* Add a packet filter:
 * Input:
 *	in_if:		ingress interface index,... the place to put the filter
 *	daddr:		IPv4 destination address
 *	dport:		IPv4 destination port
 *	ptype:		IPv4 protocol type
 *	saddr:		IPv4 source address
 *	sport:		IPv4 source port
 *
 * Return:
 *	0		failed
 *	non-zero 	a number that identify the filter
 */

u_int32_t	(*TC_Add_Filter)(int in_if, u_int32_t daddr, u_int32_t dport, 
			u_int32_t ptype, u_int32_t saddr, u_int32_t sport);


/* Delete a filter:
 *
 * Input:
 *	in_if:		ingress interface index
 *	fhandle		the number that identifies the filter
 *			(returned from TC_Add_Filter())
 * Note:
 *  Depending on the implementation, there may not be a need to give <in_if>.
 */

void	(*TC_Del_Filter)(int in_if, u_int32_t fhandle);


/* Make a reservation:
 * Input:
 *	in_if:		ingress interface index
 *	out_if:		egress interface index
 *	req_bw:		required bandwidth in byte/sec
 *
 * Return:
 *	non-zero	an unique number to identify this reservation
 *	0		fail to reserve
 *
 * Note:
 *   Only have <out_if> won't be enough. For DiffServ-like traffic conditioning,
 *   we have to deal with both incoming and outgoing resource (buffer) 
 *   allocation.
 */

u_int32_t	(*TC_Make_Resv)(int inf_if, int out_if, u_int32_t req_bw);


/* Modify a reservation:
 * Input:
 *	rhandle:	the unique number that identifies the flow
 *			(returned from TC_Make_Resv()
 *	req_bw:		required new bandwidth in byte/sec
 *
 * Return:
 *	0		failed
 *	-1		OK
 *
 * Note:
 *   In case of the router cannot give the resource to the new reservation,
 *   the previous one must remain.
 */

int	(*TC_Modify_Resv)(u_int32_t rhandle, u_int32_t req_bw);


/* Delete a reservation:
 * Input:
 *	in_if:		ingress interface index
 *	out_if:		egress interface index
 *	rhandle:	the unique number that identifies the flow
 *			(returned from TC_Make_Resv()
 * Note:
 *   Actually, there is no need to provide the interface information here,
 *   I'm just trying to be nice to the driver guys.
 */

void	(*TC_Del_Resv)(int in_if, int out_if, u_int32_t rhandle);


/* Make an IntServ reservation:
 * Input:
 *	in_if:		ingress interface index
 *	out_if:		egress interface index
 *	flowspec:	IntServ flowspec
 *
 * Return:
 *	non-zero	an unique number to identify this reservation
 *	0		fail to reserve
 *
 * Note:
 *   Only have <out_if> won't be enough. For DiffServ-like, we have to 
 *   deal with both incoming and outgoing resource(buffer) allocation.
 */

u_int32_t (*TC_Make_IntServ_Resv)(int in_if, int out_if, IntServ_spec *flowspec);


/* Modify an IntServ reservation:
 * Input:
 *	rhandle:	the unique number that identifies the flow
 *			(returned from TC_Make_IntServ_Resv()
 *	flowspec:	new flowspec
 *
 * Return:
 *	0		failed
 *	-1		OK
 *
 * Note:
 */

int	(*TC_Modify_IntServ_Resv)(u_int32_t rhandle, IntServ_spec *flowspec);


/* Query for IntServ resource availability
 * Input:
 *	out_if:		egress interface where the reservation is required
 *	curr_bw:	the current available bandwidth on the egress
 *	flowspec:	the required IntServ flowspec
 *
 * Return:
 *	0		failed
 *	-1		OK
 */

int	(*TC_Admit_IntServ)(int out_if, int *curr_bw, IntServ_spec *flowspec);



/*
 * Stub functions for testing
 */

u_int32_t
tc_addfilter_stub(int in_if, u_int32_t daddr, u_int32_t dport, 
		u_int32_t ptype, u_int32_t saddr, u_int32_t sport)
{
	int 	n;

	n = in_if + daddr + dport + ptype + saddr + sport;
	/* add testing stuff */

	return 0x617373;
}

void
tc_delfilter_stub(int in_if, u_int32_t fhandle)
{
	int	n;

	n = in_if + fhandle;
	/* add testing stuff */

	return;
}


u_int32_t
tc_makeresv_stub(int in_if, int out_if, u_int32_t req_bw)
{
	int	n;

	n = in_if + out_if + req_bw;
	/* add random stuff here for testing later */

	return 0x63726170;
}

int
tc_modifyresv_stub(u_int32_t rhandle, u_int32_t req_bw)
{
	int	n;

	n = rhandle + req_bw;
	/* add testing stuff */

	return -1;
}

void
tc_delresv_stub(int in_if, int out_if, u_int32_t rhandle)
{
	int	n;

	n = in_if + out_if + rhandle;
	/* add testing stuff */

	return;
}

int
tc_admitis_stub(int out_if, int *bw, IntServ_spec *fspec)
{
	int	avail_bw, req_bw;

	/* get rid of a warning */
	out_if = 1;

	/* this is not correct in theory */
	req_bw = (fspec->peak + fspec->avg)/2;
	avail_bw = *bw;

	if (avail_bw < req_bw)
		return 0;

	*bw = avail_bw - req_bw;
	return -1;
}

u_int32_t
tc_makeisresv_stub(int in_if, int out_if, IntServ_spec *flowspec)
{
	int	n;

	n = in_if + out_if + flowspec->peak + flowspec->avg;
	/* add random stuff here for testing later */

	return 0x777466;
}

int
tc_modifyisresv_stub(u_int32_t rhandle, IntServ_spec *flowspec)
{
	int	n;

	n = rhandle + flowspec->peak + flowspec->avg;
	/* add testing stuff */

	return -1;
}

	
/* function registration 
 */

/* traffic controller setup */
int
yes_tcinit()
{
	TC_Add_Filter = tc_addfilter_stub;
	TC_Del_Filter = tc_delfilter_stub;

	TC_Make_Resv = tc_makeresv_stub;
	TC_Modify_Resv = tc_modifyresv_stub;

	TC_Admit_IntServ = tc_admitis_stub;
	TC_Make_IntServ_Resv = tc_makeisresv_stub;
	TC_Modify_IntServ_Resv = tc_modifyisresv_stub;

	TC_Del_Resv = tc_delresv_stub;
	return -1;
}
