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

/* YESSIR protocol processing routines.
 */

#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"


/* Process the RTCP message that has just taken off the wire
 *
 * Check IP, UDP, RTCP and YESSIR one at a time.
 *
 * Input:
 *	msg:	the head of the IP packet 
 *	lgt:	size of the received message
 *	in_if:	receiving interface index
 *	local:  if set, local packets
 */

void
yessir_read(u_char *msg, int lgt, int in_if, int local)
{
	register struct ip *ip;
	register struct udphdr *uhdr;
	register struct rtcphdr *rhdr;
	struct in_addr da;
	register u_int32_t hlen, curr_len;
	yessirErr err;

	ip = (struct ip*)(msg);

	da.s_addr = ntohl((ip->ip_dst).s_addr);
	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
	curr_len = lgt;

	if ((ip->ip_len > curr_len) || (hlen > curr_len)) {
		yestat->rx_bad_pkt++;
		return;
	}

	uhdr = (struct udphdr *)(msg + hlen);
	curr_len -= hlen;

	if ((ntohs(uhdr->uh_ulen)) > curr_len) {
		yestat->incomp_pkt++;
		return;
	}

	/* check rtcp: relay it anyway if bad 
	 */
	rhdr = (struct rtcphdr *)(uhdr + 1);
	curr_len -= sizeof(struct udphdr);

	if (!rtcp_map(rhdr, curr_len)) {
		yestat->bad_rtcp++;
		yes_send(msg, lgt, &da);
		return;
	}

	yessir_valid(&err);
	if (err.code) {
		YESSIR_SET_BAD(yesmap->cmd);
		yes_send_error(msg, lgt, &da, &err);
		return;
	}

	/* if the upstream resv is already screwed up, forget it. 
	 */
	if (yesmap->cmd && YESSIR_BAD(yesmap->cmd)) {
		yestat->rx_rejected_yessir++;
		yes_send(msg, lgt, &da);
		return;
	}

	yessir_input(msg, lgt, in_if, local, &da);

	return;
}


/* Go through the RTCP message, and map all payloads. This is to make sure
 * we are not processing a bad RTCP message to begin with.
 *
 * return:
 *	-1	good RTCP packet
 *	0	format is wrong
 */
int
rtcp_map(struct rtcphdr *rpkt, int lgt)
{
	register char *data, *end_data;
	register u_int16_t rh_flags;

	memset((char *)yesmap, 0, sizeof(YESSIR_MAP));
	data = (char *)rpkt;
	end_data = data + lgt;

	/* walk through the packet */
	while (data < end_data) {

		rh_flags = ntohs(rpkt->rh_flags);
		if (RTCP_VER_OF(rh_flags) != RTP_VERSION) {
			yestat->rtcp_bad_version++;
			return 0;
		}

		switch (RTCP_PT_OF(rh_flags)) {
		case RTCP_PT_SR:
			if (yesmap->sr) {
				yestat->rtcp_dup_pt++;
				return 0;
			}
			yesmap->sr = rpkt;
			break;

		case RTCP_PT_RR:
			if (yesmap->rr) {
				yestat->rtcp_dup_pt++;
				return 0;
			}
			yesmap->rr = rpkt;
			break;

		case RTCP_PT_SDES:
			if (yesmap->sdes) {
				yestat->rtcp_dup_pt++;
				return 0;
			}
			yesmap->sdes = rpkt;
			break;

		case RTCP_PT_BYE:
			if (yesmap->bye) {
				yestat->rtcp_dup_pt++;
				return 0;
			}
			yesmap->bye = rpkt;
			break;

		case RTCP_PT_APP:
			if (yesmap->app) {
				yestat->rtcp_dup_pt++;
				return 0;
			}
			yesmap->app = rpkt;
			break;

		case RTCP_PT_SIR:		/* YESSIR */
			if (yesmap->sir) {
				yestat->rtcp_dup_pt++;
				return 0;
			}
			yesmap->sir = rpkt;
			break;

		case RTCP_PT_RIR:		/* YESSIR  */
			if (yesmap->rir) {
				yestat->rtcp_dup_pt++;
				return 0;
			}
			yesmap->rir = rpkt;
			break;

		default: {
			yestat->rtcp_unknown_pt++;
			return 0;
		}
		}

		data += RTCP_LEN_OF(ntohs(rpkt->rh_len));
		rpkt = (struct rtcphdr *)data;

		if (data > end_data) {
			yestat->rtcp_bad_len++;
			return 0;
		}
	}

	return -1; 
}


/* Validation checks for the received YESSIR messages. We also map
 * as much packet-level information into yesmap[] as possible. This
 * serves two goals: (1) to reduce the main routine's complexity;
 * (2) to reduce packet access. Normally, packet buffer is in slow
 * DRAM, while control tables are in SRAM (SDRAM). Always try to
 * make the use of fast memory.
 */
void
yessir_valid(yessirErr *err)
{
	register struct rtcphdr *rpkt;
	register struct yessir_obj_hdr *yobj;
	register struct yessir_fspec *fspec;
	register struct intserv_spec *ispec;
	register char *data, *end_data;
	register int ftype;

	err->code = err->val = 0;

	/* no explict resv data, but try to make resv with SR's
	 */
	if (!yesmap->sir) {

		if (yesmap->sr) {
			yesmap->action = YESSIR_RESV;
			yesmap->ftype = YESSIR_MB_FSPEC;
			yesmap->shared_resv = 0;
			return;
		}

		yestat->null_yessir++;		/* have nothing */
		err->code = YE_MISSRTCP;
		return;
	}

	rpkt = (struct rtcphdr *)(yesmap->sir);
	end_data = ((char *)rpkt) + RTCP_LEN_OF(ntohs(rpkt->rh_len));
	data = (char *)(rpkt+1);
	yobj = (struct yessir_obj_hdr *)data;

	/* map and check all YESSIR objects
	 */
	while (data < end_data) {

		switch(YESSIR_OBJ_TYPE(yobj)) {

		case YESSIR_CMD:
			yesmap->cmd = (struct yessir_cmd *)(yobj);

			if (YESSIR_INVALID_ACT(yesmap->cmd)) {
				yestat->unknown_action++;
				err->code = YE_UNKNOWNACT;
				err->val = yesmap->cmd->action;
				return;
			}

			yesmap->action = yesmap->cmd->action;
			yesmap->shared_resv = YESSIR_STYLE_OF(yesmap->cmd);

			break;

		case YESSIR_FSPEC:
			yesmap->fspec = fspec = (struct yessir_fspec *)(yobj);
			yesmap->ftype = ftype = fspec->ftype;

			if (!(ftype & YESSIR_VALID_FSPEC)) {
				yestat->unknown_ftype++;
				err->code = YE_BADFLOWSPEC;
				err->val = ftype;
				return;
			}

			if (ftype == YESSIR_IS_FSPEC) {
				yesmap->ispec = ispec 
					= (struct intserv_spec *)(fspec+1);

				/* check the integrity of the message */
				if ((fspec->len <= INTSERV_LEN(ispec)) || 
					!(INTSERV_VALID_VER(ispec)) ||
					!(INTSERV_VALID_SERV(ispec)) || 
					!(INTSERV_VALID_SERVLEN(ispec))) {

					err->code = YE_BADINTSERV;
					return;
				}
			}
			break;

		case YESSIR_MON:
			yesmap->monitor = (struct yessir_mon *)(yobj);
			break;

		case YESSIR_ERR:
			yesmap->err = (struct yessir_IPv4_err *)(yobj);
			break;

		default: {
			yestat->yessir_unknown_obj++;
			err->code = YE_UNKNOWNOBJ;
			return;
		}
		}

		data += YESSIR_OBJ_LEN(yobj);
		yobj = (struct yessir_obj_hdr *)data;

		if (data > end_data) {
			yestat->rtcp_bad_len++;
			err->code = YE_BADOBJLEN;
			return;
		}
	}

	if (!yesmap->cmd || !yesmap->fspec) {
		yestat->missing_yessir_obj++;
		err->code = YE_MISSOBJ;
		return;
	}
	return;
}

/*
 * Yes, sir: this is all you need to make a reservation:
 * - locate reservation state;
 * - query routes to know where to send next;
 *    ( by the way, if there are route changes, reclaim resource from old
 *    interfaces.)
 * - process the reservation command: make, purge, ...
 *   (if make resv, find the reservation amount from the message)
 * - relay the message:
 *   the make-reservation routine will send the message directly,
 *   while the remove-reservation routine sends the message after
 *   all process being done. This is because, in case of multicast,
 *   the output message may look different at each egress. 
 */


void
yessir_input(char *msg, int lgt, int in_if, int local, struct in_addr *dst)
{
	register struct ip *ip;
	struct udphdr *uhdr;
	struct in_addr sa, da;
	register u_int32_t sp, dp;
	register yesEntry *ye;
	register yesOutEntry  *yoe;
	register RIF *rif;
	register int hlen, out_if, i, need_retry;
	yessirErr err;

	yestat->rtcp_rx_pkt++;

	if (local) {	/* to the host */
		yestat->rx_host++;
		/* insert the upcall function here if needed */
		return;
	}

	ip = (struct ip *)msg;
	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
	uhdr = (struct udphdr *)(msg + hlen);

	sa.s_addr = (ip->ip_src).s_addr;
	da.s_addr = (ip->ip_dst).s_addr;
	sp = ntohs(uhdr->uh_sport);
	dp = ntohs(uhdr->uh_dport);

	/* locate the reservation state 
	 */
	if (!(ye = yesget(&da, dp, &sa, sp))) {

		if (!(ye = (yesEntry *)yesadd(&da, dp, &sa, sp))) {
			err.code = YE_NOBUF;
			err.val = 0;
			yes_send_error(msg, lgt, dst, &err);
			return;
		}
	}

	ye->in_if = in_if;
	ye->curr_round = 0;		/* reset timer */

	/* Route Query: find the next-hop(s) and compare with 
	 * the existing one(s) to detect route changes.
	 */
	if (IN_MULTICAST(ntohl(da.s_addr))) {
		yestat->rx_mcast++;
		return;
	}
	else {	/* unicast */
		if ((out_if = yes_rtquery(&da)) == -1) {
			yestat->dst_unreach++;
			return;
		}

		if (in_if == out_if)  {	
			yestat->looping++;
			return;
		}

		if (ye->egress.cnt == 0)
			yoe = (yesOutEntry *)yesadd_egress(ye);
		else {
			yoe = (yesOutEntry *)(ye->egress.head);

			if (yoe->out_if != out_if)
				/* route changed, reclaim resource */
				yes_rmresv(ye, yoe);
		}

		yoe->out_if = out_if;
	}

	/* Process reservation
	 */ 
	if (yesmap->action == YESSIR_RESV) {	/* make reservation */

		/* setup the ingress classifier */
		if (!ye->fhandle) {

			ye->fhandle = TC_Add_Filter(ye->in_if, 
				ye->id.daddr, ye->id.dport, IPPROTO_UDP, 
				ye->id.saddr, ye->id.sport);

			if (!ye->fhandle)
				need_retry++;
		}

		/* at each egress, (1) read message, (2) make reservation, 
		 * and (3) forward message. If reservation has failed,
		 * wait and retry later.
		 */

		need_retry = 0;

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

			yestat->resv_attempt++;

			if (!(rif = rif_get(yoe->out_if)))
				break;

			switch (yesmap->ftype) {
			case YESSIR_MB_FSPEC:

				if (yessir_mb_input(yoe, rif))  {
					if (yessir_mb_resv(ye, yoe, rif))
						yestat->resv_success++;
					else
						need_retry++;
				}

				yes_send(msg, lgt, dst);
				break;

			case YESSIR_IS_FSPEC:

				if (!yessir_is_input(yoe, rif, &err))
					break;

				(!yessir_is_resv(ye, yoe, rif, &err)) ?  
					need_retry++ : yestat->resv_success++ ;

				if (err.code)
					yes_send_error(msg, lgt, dst, &err);
				else
					yes_send_chksum(msg, lgt, dst);

				break;

			case YESSIR_DS_FSPEC:

				if (!yessir_ds_input(yoe, rif, &err))
					break;

				(!yessir_ds_resv(ye, yoe, rif, &err)) ?  
					need_retry++ : yestat->resv_success++ ;

				if (err.code)
					yes_send_error(msg, lgt, dst, &err);
				else
					yes_send_chksum(msg, lgt, dst);

				break;

			default:
				yestat->unknown_ftype++;
				break;
			}
		}

		if (need_retry)
			yesenq_resv(ye);

	}
	else {		/* YESSIR_RESVTEAR */

		/* free the classification entry
		 */
		if (ye->fhandle)
			TC_Del_Filter(ye->in_if, ye->fhandle);

		/* free the resource at each egress
		 */
		i = 0;
		yoe = (yesOutEntry *)ye->egress.head;

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

		/* relay the message and dequeue the pending req's
		 */
		yes_send(msg, lgt, dst);
		yesfree(ye);
		yes_retry();
	}
	return;
}


/* Parse measurement-based, IntServ and DiffServ reservation data:
 * Return:
 *	0	bad message
 *	-1	not bad
 */

/* RTCP SR's do not include the byte count for LLC, IP and UDP
 * encapsulation. To compute the correct bandwidth, we need to include
 * all of them.
 */
#define YES_OVERHEAD (sizeof(struct ip) + sizeof(struct udphdr))

int
yessir_mb_input(yesOutEntry *yoe, RIF *rif)
{
	struct rtcphdr		*rtcp = (struct rtcphdr *)(yesmap->sr);
	struct rtcp_sr		*sr = (struct rtcp_sr *)(rtcp + 1);
	register MB_spec	*req=(MB_spec *)&(yoe->req_qos.mb);
	register u_int32_t	ssrc, np, nb, ntp;
	register int		overhead, delta_bytes, delta_time;

	/* locate reservation interface */

	/* read QoS info from the packet */
	ssrc = ntohl(rtcp->rh_ssrc);
	np = ntohl(sr->sr_np);
	nb = ntohl(sr->sr_nb);
/* confused ...
	ntp = ((ntohl(sr->sr_ntp.upper) << 16) 
			| (ntohl(sr->sr_ntp.lower) >> 16));
 */
	ntp = ntohl(sr->sr_ntp.upper);

	/* copy the sender's resport and skip the reservation if:
	 * (1) this is the very first SR we have received;
	 * (2) the previous flowspec is not measurement-based reservation;
	 * (3) the SR's SSRC has changed;
	 * (4) the egress interface does not support resource reservation.
	 */
	if (!(yoe->status & YES_REQ_BIT) ||
		(yoe->req_ftype != YESSIR_MB_FSPEC) || 
		(ssrc != req->ssrc) ||
		(rif->state != RESV_ENABLE)) {

		yoe->req_ftype = YESSIR_MB_FSPEC;
		YES_SET_REQ(yoe);
		req->ssrc = ssrc;
		req->np = np;
		req->nb = nb;
		req->ntp = ntp;
		req->bw = 0;

		return 0;
	}

	/* compute the bandwidth, and store the current request 
	 */
	overhead = (np - req->np) * (YES_OVERHEAD + rif->if_hdrlen);
	delta_bytes = (nb - req->nb);
	delta_time = (ntp - req->ntp);

	yoe->req_ftype = YESSIR_MB_FSPEC;
	req->ssrc = ssrc;
	req->np = np;
	req->nb = nb;
	req->ntp = ntp;
	req->bw = 0;

	/* end user screwed up */
	if (overhead < 0 || delta_bytes < 0 || delta_time <= 0)
		return 0;

	/* Check if we have enough bandwidth on egress:
	 * Note: We here check to see if there is enough bandwidth on
	 * outgoing interface. In reality, we may also need to check
	 * incoming interface to make sure enough resource (buffers) 
	 * being allocated for receive. This may be too much implementation 
	 * dependent, so just deal with egress bandwidth for now (unless people
	 * start to complain in the future).
	 */

	req->bw = ((overhead + delta_bytes) / delta_time);

	return -1;
}


int
yessir_is_input(yesOutEntry *yoe, RIF *rif, yessirErr *err)
{
	struct intserv_spec	*ispec = yesmap->ispec;
	struct intserv_clspec	*clspec;
	struct intserv_gsspec	*gsspec;
	register IntServ_spec	*req_is=(IntServ_spec *)&(yoe->req_qos.is);

	err->code = err->val = 0;

	if (rif->state != RESV_ENABLE) {
		YESSIR_SET_VERF(yesmap->cmd);
		err->code = YE_CANNOTRESV;
		return 0;
	}

	if (ispec->serv == IntServ_ControlledLoad) {

		/* read controlled load flowspec */
		clspec = (struct intserv_clspec *)ispec;

		if (clspec->tspec_id != Parameter_TSPEC) {
			YESSIR_SET_BAD(yesmap->cmd);
			err->code = YE_BADINTSERV;
			return 0;
		}
			
		yoe->req_ftype = YESSIR_IS_FSPEC;
		req_is->serv = clspec->serv;
		req_is->peak = clspec->peak;
		req_is->avg = clspec->token_rate;
		req_is->burst = clspec->token_size;
		req_is->mintu = clspec->min_size;
		req_is->maxtu = clspec->max_size;
	}
	else {		
		/* read guaranteed service flowspec */
		gsspec = (struct intserv_gsspec *)ispec;

		if ((gsspec->tspec_id != Parameter_TSPEC) ||
			(gsspec->rspec_id != Parameter_GSRSPEC)) {

			YESSIR_SET_BAD(yesmap->cmd);
			err->code = YE_BADINTSERV;
			return 0;
		}
			
		yoe->req_ftype = YESSIR_IS_FSPEC;
		req_is->serv = gsspec->serv;
		req_is->peak = gsspec->peak;
		req_is->avg = gsspec->token_rate;
		req_is->burst = gsspec->token_size;
		req_is->mintu = gsspec->min_size;
		req_is->maxtu = gsspec->max_size;
		req_is->R = gsspec->rate;
		req_is->s = gsspec->slack;
	}

	return -1;
}


int
yessir_ds_input(yesOutEntry *yoe, RIF *rif, yessirErr *err)
{
	return 0;
}


/* Try to grab the resource for the pending flows.
 */
void
yes_retry()
{
	register LIFO	*fbl = (LIFO *)&yestab->pending_fbl;
	register FIFO	*q = (FIFO *)&yestab->pending_q;
	yesPendingEntry	*pe, *old_pe;
	u_int32_t 	i;

	old_pe = NULL;
	pe = (yesPendingEntry *)q->head;

	for (i=0; i < yestab->curr_pending; i++) {

		if (yes_retry_resv(pe)) {
			/* success, so dequeue */
			if (!old_pe)
				q->head = pe->next;
			else
				old_pe->next = pe->next;

			QA_LIFO(fbl, pe);
			yestab->curr_pending--;
			return;
		}

		old_pe = pe;
		pe = pe->next;
	}

	return;
}


/* Once again, try to make reservation in a FIFO order
 *
 * Return:
 *	0	need to retry again
 * 	-1	either get resource or forget about it
 *
 * Note: if the reservation entry is really screwed up and YES_RESV_INVALID
 * is returned from each egress entry, this routine will return (-1) too.
 * This is to force the entry to be removed in yes_retry().
 */

int
yes_retry_resv(yesPendingEntry *pe)
{
	register yesEntry *ye;
	register yesOutEntry *yoe;
	register RIF *rif;
	yessirErr err;
	register int need_retry, i;

	ye = (yesEntry *)(pe->yep);

	if (!ye->fhandle) {

		ye->fhandle = TC_Add_Filter(ye->in_if, 
			ye->id.daddr, ye->id.dport, IPPROTO_UDP, 
			ye->id.saddr, ye->id.sport);

		if (!ye->fhandle)	/* cannot setup filter */
			need_retry++;
	}

	/* at each egress, make reservation.
	 * If reservation has failed, wait and retry later.
	 */

	need_retry = 0;

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

		yestat->resv_attempt++;

		if (!(rif = rif_get(yoe->out_if)))
			break;

		if (!(yoe->status & YES_PENDING_BIT))
			break;

		switch (yoe->req_ftype) {

		case YESSIR_MB_FSPEC:
			(!yessir_mb_resv(ye, yoe, rif)) ?  
				need_retry++ : yestat->resv_success++ ;
			break;

		case YESSIR_IS_FSPEC:
			(!yessir_is_resv(ye, yoe, rif, &err)) ?  
				need_retry++ : yestat->resv_success++ ;
			break;

		case YESSIR_DS_FSPEC:
			(!yessir_ds_resv(ye, yoe, rif, &err)) ?  
				need_retry++ : yestat->resv_success++ ;
			break;

		default:
			yestat->unknown_ftype++;
			break;
		}
	}

	if (need_retry) {
		pe->tries++;
		return 0;
	}

	return -1;
}


/* Make measurement-based, IntServ and DiffServ reservation 
 * at an egress interface.
 *
 * Return:
 *	0	need to retry reservation
 *	-1	all done
 */

int
yessir_mb_resv(yesEntry *ye, yesOutEntry *yoe, RIF *rif)
{
	register int bw;

	bw = yoe->req_qos.mb.bw;

	YES_SET_PENDING(yoe);

	if (!yoe->rhandle) {
		if (rif->avail_bw <= bw)
			return 0;

		if (!(yoe->rhandle = TC_Make_Resv(ye->in_if, yoe->out_if, bw)))
			return 0;

		rif->avail_bw -= bw;
	}
	else {
		if ((rif->avail_bw + yoe->eff_qos.mb.bw) <= (u_int32_t)bw)
			return 0;

		if (!TC_Modify_Resv(yoe->rhandle, bw))
			return 0;

		rif->avail_bw += yoe->eff_qos.mb.bw;
		rif->avail_bw -= bw;
	}

	YES_RESET_PENDING(yoe);
	YES_SET_RESV(yoe);
	yoe->eff_ftype = YESSIR_MB_FSPEC;
	yoe->eff_qos.mb.bw = bw;

	return -1;
}


int
yessir_is_resv(yesEntry *ye, yesOutEntry *yoe, RIF *rif, yessirErr *err)
{
	register IntServ_spec *req_is;

	req_is = (IntServ_spec *)&(yoe->req_qos.is);
	YES_SET_PENDING(yoe);

	/* Figuring out if the link has enough resource for this reservation
	 * can be very tricky. It is the combination of token size, rate,
	 * average bandwidth and slack (if guaranteed service). We leave that 
	 * for TC to decide, while providing the current link bandwidth as
	 * the input.
	 */
	if (!TC_Admit_IntServ(yoe->out_if, &rif->avail_bw, req_is))  {

		YESSIR_SET_VERF(yesmap->cmd);
		err->code = YE_REJECT;
		return 0;
	}

	/* make the reservation 
	 */
	if (yoe->rhandle) { 	/* have reservation already */

		if (!TC_Modify_IntServ_Resv(yoe->rhandle, req_is)) {

			YESSIR_SET_VERF(yesmap->cmd);
			err->code = YE_NORESOURCE;
			return 0;
		}
	}
	else {
		yoe->rhandle = TC_Make_IntServ_Resv(ye->in_if, 
							yoe->out_if, req_is);

		if (!yoe->rhandle) {
			YESSIR_SET_VERF(yesmap->cmd);
			err->code = YE_NORESOURCE;
			return 0;
		}
	}

	/* copy the flowspec over and quit 
	 */
	yoe->eff_ftype = YESSIR_IS_FSPEC;
	YES_RESET_PENDING(yoe);
	YES_SET_RESV(yoe);

	memcpy((char *)&(yoe->eff_qos.is), (char *)req_is, sizeof(*req_is));

	return -1;
}


int
yessir_ds_resv(yesEntry *ye, yesOutEntry *yoe, RIF *rif, yessirErr *err)
{
	return 0;
}


/* Release resource at an egress interface 
 */
void
yes_rmresv(yesEntry *ye, yesOutEntry *yoe)
{
	register RIF	*rif;

	if (!yoe->rhandle)
		return;

	rif = rif_get(yoe->out_if);
	if (!rif)
		return;

	YES_RESET_RESV(yoe);
	YES_RESET_PENDING(yoe);
	YES_RESET_REQ(yoe);

	if (yoe->eff_ftype == YESSIR_MB_FSPEC)
		rif->avail_bw -= yoe->eff_qos.mb.bw;

	TC_Del_Resv(ye->in_if, yoe->out_if, yoe->rhandle);

	return;
}


/* Output routines: 
*/

/* Send pkt with error coding and UDP chksum 
 */
void
yes_send_error(char *msg, int lgt, struct in_addr *da, yessirErr *err)
{
	struct yessir_IPv4_err	*err_obj;

	if (yesmap->cmd)
		YESSIR_SET_BAD(yesmap->cmd);

	if (yesmap->err) {
		err_obj = (struct yessir_IPv4_err *)yesmap->err;
		err_obj->err_code = err->code;
		err_obj->err_val = err->val;
	}

	if (yesmap->sir)
		/* need udp chksum here */

	yes_send(msg, lgt, da);
}


/* Send pkt with udp cheksum only
 */
void
yes_send_chksum(char *msg, int lgt, struct in_addr *da)
{
	/* need udp chksum here */

	yes_send(msg, lgt, da);
}


/* Soft-state routines:
 */
/* 
 * We cannot assume the number of timers that the OS can give us,
 * so we maintain our own timer queues. We use a single timer that can wake up 
 * every so-often to update/check the timer queues. 
 * 
 * Note: the routine uses gettimeofday() to get the universal time ticks.
 * If there is a settimeofday() somewhere, we may be in deep xxxx.... on the
 * other hand, soft-state only deals with approximated time... 
 * we may be OK then. The best solution is to have a wrapper to 
 * make sure the time ticks are always consistant here.
 *
 */

/* assumption:
 * the routine must be called within YESSIR_CYCLE seconds,
 * or we will miss refreshing some entries.
 */

int
yes_timerinit()
{
	struct timeval clk;

	yestime = (yessirTime *)malloc(sizeof(yessirTime));
	if (yestime == NULL) {
		msglog("timerinit: no memory");
		return 0;
	}
	memset((char *)yestime, 0, sizeof(yessirTime));

	gettimeofday(&clk, 0);
	yestime->init = clk;
	yestime->last = clk;
	yestime->now = clk;
	yestime->next.tv_sec = clk.tv_sec + YES_CHECK_INTERVAL;
	yestime->prev_scanned = YESSIR_TIMEQ(clk.tv_sec);

	return -1;
}

void
yesrefresh()
{
	struct timeval	clk;
	int	prev_slot, curr_slot, ticks, k, slot;

	timevalsub(&clk, &yestime->now, &yestime->init);
	if (clk.tv_sec < 0) {
		/* don't play with the clock for now */
		yestat->timer_outsync++;
		return;
	}

	curr_slot = YESSIR_TIMEQ(clk.tv_sec);
	prev_slot = yestime->prev_scanned;

	if (prev_slot < curr_slot)
		ticks = curr_slot - prev_slot;
	else
		ticks = (YESSIR_CYCLE - prev_slot) + curr_slot;

	for (k=1, slot=prev_slot; k <= ticks; k++, slot++) {
		if ((slot + k) == YESSIR_CYCLE)
			slot = 0;

		yesrefresh_slot(slot);
	}

	/* sechdule for the next refresh */
	yestime->next.tv_sec = clk.tv_sec + YES_CHECK_INTERVAL;
	yestime->prev_scanned = curr_slot;

	return;
}


void
yesrefresh_slot(int slot_num)
{
	yesTimeEntry	*yte, *tmp_yte;
	yesEntry	*ye;
	LIFO		*timeq;
	int		cnt, i;

	timeq = (LIFO *)&yestab->time_table[slot_num];
	if ((cnt = timeq->cnt) == 0)	/* nothing to check */
		return;

	i = 0;
	tmp_yte = yte = (yesTimeEntry *)timeq->head;
	for (; i < cnt; i++, yte = tmp_yte) {
		ye = (yesEntry *)(yte->yep);
		tmp_yte = tmp_yte->next;

		if (ye->curr_round < ye->max_round)
			ye->curr_round++;
		else {
			yesfree(ye);
			yes_retry(ye);
		}
	}
	return;
}

