/*
 * 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/Bell Labs, 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.
 */

/* yessird: a daeom to support YESSIR, a light-weight reservation
 * protocol for real-time streams.
 *
 * For more information on YESSIR, check 
 * http://www.cs.columbia.edu/~pingpan/projects/yessir.html
 *
 * Quick intro: YESSIR is not designed as a generic reservation 
 * protocol for all traffic (though may sound more sexy that way).
 * It makes a best-effort attempt to reserve network resource 
 * for real-time traffic that uses RTP to transport data. 
 * Reservation is carried inside the RTCP messages.
 *
 * Sorry, I don't believe email and WEB downloads need user-level
 * resource reservation from the network. If users have trouble
 * with their WEB traffic, contact the ISP's to configurate the network
 * better. Only real-time traffic really deserves special treatment 
 * as far as users are concerned.
 *
 * Notes
 * -----
 * This program must be run by root or be setuid. Technically,
 * raw sockets require superuser priveleges. As a common sense,
 * only superusers should have the right to run this program
 * to grab or to release network resources. 
 *
 * This program requires a kernel mod that does not appear in any
 * availeable UNIX system: A raw socket with a new protocol family,
 * IPOPT (ip option), is used to intercept the data on all interfaces.
 * The data packets can be any IP protocol. For more detail, check
 * http://www.cs.columbia.edu/~pingpan/software/netipopt
 *
 * YESSIR uses the RTCP messages to make reservation decision, but during
 * parsing, it may drop the messages with real bad format. Other than that,
 * NO dropping and re-ordering to the orginal packets.
 *
 * There is a limitation on my clock management routines (for soft-state
 * refresh): while yessird() is running, don't change time od the day... for now.
 *
 * The physical interfaces are maganed in a hashed table (index via if-index) 
 * for performance reason. In some low-end routers, we should expect hundreads 
 * of interfaces (channelized T1, Frame Relay DLCI virtual interface....), 
 * so this is not an over-kill.
 *
 * I have assumed that the routers support "hot-plug" feature. Also when a new
 * interface is inserted, the system would assign a new if-index to it. 
 * Given I am writing and testing the code on a PC, the interface up/down
 * routines (rif_up() and rif_down()) were not tested in all cases.
 *
 * To port this code into a platform other than UNIX, replace all the routines in
 * this file.
 *  
 * - Ping Pan, March 18, 2000, 
 */

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>

#include "queue.h"
#include "table.h"
#include "yestat.h"
#include "yes_var.h"


int	lockfd;		/* use for daemon check */
fd_set	fdset;		/* the fd used in select() */
int 	sock_max;	/* max number of sockets */
int 	yes_sock;	/* YESSIR receive socket */
int	send_sock;	/* YESSIR sending socket */
u_char	*yes_ctrl;	/* yessir socket control data pointer */
u_char	*yes_buf;	/* buffer for yessir message */
int	rt_sock;	/* route socket */
int	rt_seqno;	/* route query sequence number */
u_char 	*rt_buf;	/* route query buffer */
int	stat_sock;	/* yestst() socket */
u_char	*stat_buf;	/* buffer for yestat message */

char	*program_name;

/* dummy used in sysctl() */
static struct sockaddr sa_zero = {sizeof(struct sockaddr), AF_INET};

extern	char *optarg;


int
main(int argc, char *argv[])
{
	register int n, uid, pid;
	register int debug = 0;
	register int errors = 0;
	struct timeval clk;
	char *tracename = 0, *cp;
	fd_set curr_fdset;

	setlinebuf(stdout);

	setuid(getuid());
	if ((uid = getuid())) {
		perror("need to be a superuser");
		exit(1);
	}

	if ((cp = strrchr(argv[0], '/')) != NULL)
		program_name = cp + 1;
	else
		program_name = argv[0];

	if (!yes_timerinit()) {
		perror("cannot init the timers");
		exit(1);
	}

	while ((n = getopt(argc, argv, "dT:")) != -1) {
		switch (n) {
		case 'd':
			debug++;
			break;

		case 'T':
			tracename = optarg;
			break;

		case '?':
		default:
			errors++;
			break;
		}
	}

	argc -= optind;
	argv += optind;

	if (tracename == 0 && argc >= 1) {
		tracename = *argv++;
		argc--;
	}

	if (tracename != 0 && tracename[0] == '\0')
		yes_usage();

	if (argc != 0 || errors)
		yes_usage();

	if ((n = yes_alive())) 	/* daemon exists ? */
		logbad(0, "%s: already running with PID %d", program_name, n);

	if ((!debug) && (daemon(1,0) == -1))
		logbad(1, "%s: cannot exec the daemon.", program_name);

	signal(SIGPIPE, sigpipe);
	signal(SIGINT, sigint);
	signal(SIGTERM, sigterm);

	if (tracename != 0) {
		strncpy(inittracename, tracename, sizeof(inittracename)-1);
		set_tracefile(inittracename, "%s\n", -1);
	}

	pid = getpid();
	if (!yes_savepid(pid))
		logbad(1, "%s: cannot save the pid %d.", program_name, pid);

	if (!yes_tabinit())
		logbad(1, "%s: failed on table init.", program_name);

	if (!rif_check())
		logbad(1, "%s: failed on interface init.", program_name);

	if (!sockinit())
		logbad(1, "%s: failed on socket init.", program_name);

	if (!yes_tcinit())
		logbad(1, "%s: failed on TC init.", program_name);

	for (;;) {
		yestime->last = yestime->now;
		gettimeofday(&yestime->now, 0);

		/* check flow states */
		timevalsub(&clk, &yestime->now, &yestime->next);
		if (clk.tv_sec < 0)
			yesrefresh();

		/* check interfaces */
		timevalsub(&clk, &yestime->now, &rifm->chk_timer);
		if (clk.tv_sec < 0)
			rif_check();

		clk.tv_sec = 1;
		clk.tv_usec = 0;
		curr_fdset = fdset;
		n = select(sock_max, &curr_fdset, 0, 0, &clk);
		if (n <= 0) {
			if (n < 0 && errno != EINTR && errno != EAGAIN)
				/* could happen in old PSOS */
				logbad(0, "bad select");

			continue;
		}

		if (FD_ISSET(yes_sock, &curr_fdset)) {
			yes_sockread(yes_sock);
			n--;
		}

		if (FD_ISSET(stat_sock, &curr_fdset)) {
			stat_sockread(stat_sock);
			n--;
		}
	}
}


/* signaling handlers... 
 */
void
nosir()
{
	sockreset();
	unlink(YES_PID);
	return;
}


void
sigpipe(int sig)
{
	msglog("yessid: got a SIGPIPE, %d", sig);
	signal(SIGPIPE, sigpipe);
}


void
sigint(int sig)
{
	msglog("yessid: exit on a SIGINT");
	nosir();
	_exit(sig);
}

void
sigterm(int sig)
{
	msglog("yessid: exit on a SIGTERM");
	nosir();
	_exit(sig);
}


/* Check to see if daemon is already running.... 
 * Return:
 * 	0	OK
 *      others	not OK
 */
int
yes_alive()
{
	struct sockaddr_in addr;
	FILE *fd;
	int pid;

	memset((char *)(&addr), 0, sizeof(struct sockaddr_in));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = YES_LOCK_PORT;
	addr.sin_len = sizeof(struct sockaddr_in);

	lockfd = socket(PF_INET, SOCK_DGRAM, PF_UNSPEC);
	if (lockfd == -1)
		return -1;

	if(!bind(lockfd, (struct sockaddr *)&addr, sizeof(addr)))
		return 0;

	close(lockfd);

	if ((fd = fopen(YES_PID, "r")) == NULL)
		return -1;

	if (fscanf(fd, "%u", &pid))
		return -1;

	fclose(fd);
	return pid;
}


int
yes_savepid(int pid)
{
	FILE *fp;

	if (unlink(YES_PID) == -1) {
		if (errno != ENOENT) {
			return 0;
		}
	}
	if ((fp = fopen(YES_PID, "w")) == NULL)
		return 0;

	fprintf(fp, "%u\n", pid);
	fclose(fp);
	return -1;
}


void
yes_usage()
{
	fprintf(stderr, "%s [-d] [-T tracefile]\n", program_name);
	exit(1);
}


/* Create and modify sockets to receive RTCP messages with
 * IP alert option and received interface index. Also create the sockets
 * for route query, sending packets and yestat().
 */

/* yessir socket config for rx and tx. */
int
yes_sockinit()
{
	int on=1, rc;

	/* setup rx. socket */
	if ((yes_sock = socket(PF_IPOPTION, SOCK_RAW, IPOPT_RA)) < 0) {
		logbad(0, "yes_sock: socket() < 0");
		return 0;
	}

	if (fcntl(yes_sock, F_SETFL, O_NONBLOCK) == -1) {
                logbad(0, "fcntl failed to O_NONBLOCK: %s", errno);
		return 0;
	}

	/* to receive RTCP messages */
	rc = setsockopt(yes_sock, IPPROTO_IP, IPOPT_RECVRTCP, &on, sizeof(on));
	if (rc < 0) {
		logbad(0, "setsockopt() for RTCP < 0");
		return 0;
	}

	/* always get ingress interface data */
	rc = setsockopt(yes_sock, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
	if (rc < 0) {
		logbad(0, "setsockopt() for RECVIF < 0");
		return 0;
	}

	/* get control data and receiving buffer space */
	if (!(yes_ctrl = (u_char *)malloc(YES_MAXCTRLSIZE)))
		logbad(1, "cannot get yessir control data space");

	if (!(yes_buf = (u_char *)malloc(YES_MAXPKTSIZE)))
		logbad(1, "cannot get yessir input buffer");

	FD_SET(yes_sock, &fdset);


	/* setup tx. socket */
	if ((send_sock = socket(PF_INET, SOCK_RAW, 0)) < 0) {
		logbad(0, "send_sock: socket() < 0");
		return 0;
	}

	rc = setsockopt(send_sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));
	if (rc < 0) {
		logbad(0, "setsockopt() for IP_HDRINCL < 0");
		return 0;
	}

	return -1;
}


/* route socket */
int
rt_sockinit()
{
	if ((rt_sock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
		logbad(0, "rt_sock: socket() < 0");
		return 0;
	}

	rt_buf = (u_char *)malloc(YES_RTBUFLEN);
	if (!rt_buf) {
		logbad(0, "cannot get route query space");
		return 0;
	}
	rt_seqno = 1;

	return -1;
}


/* yestat socket */
int
stat_sockinit()
{
	struct sockaddr_in dest;
	int on=1, bufsz, cc, rc;

	if ((stat_sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
		logbad(0, "stat_sock: socket() < 0");
		return 0;
	}

	if (fcntl(stat_sock, F_SETFL, O_NONBLOCK) == -1)
                logbad(1, "fcntl failed to O_NONBLOCK: %s", errno);

	rc = setsockopt(stat_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	if (rc < 0) {
		logbad(0, "setsockopt() for SO_REUSEADDR < 0");
		return 0;
	}

	rc = setsockopt(stat_sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
	if (rc < 0) {
		logbad(0, "setsockopt() for SO_REUSEPORT < 0");
		return 0;
	}

	cc = sizeof(bufsz);

	if (getsockopt(stat_sock, SOL_SOCKET, SO_SNDBUF, &bufsz, &cc) < 0) {
		logbad(0, "yestat: fail to getsockopt on SO_SNDBUF");
		return 0;
	}

	if (bufsz < YESTAT_MAXBUF) {

		bufsz = YESTAT_MAXBUF;

		rc = setsockopt(stat_sock, SOL_SOCKET, SO_SNDBUF, &bufsz, cc);
		if (rc < 0) {
			logbad(0, "setsockopt() for SO_SNDBUF < 0");
			return 0;
		}
	}

	memset((char *)(&dest), 0, sizeof(struct sockaddr_in));
	dest.sin_family = PF_INET;
	dest.sin_addr.s_addr = INADDR_ANY;
	dest.sin_port = htons(YESSIR_STAT_PORT);
	dest.sin_len = sizeof(struct sockaddr_in);

	if (bind(stat_sock, (struct sockaddr *)&dest, sizeof(dest))) { 
		logbad(0, "bind() for stat_sock failed");
		return 0;
	}

	if (!(stat_buf = (u_char *)malloc(YESTAT_MAXBUF)))
		logbad(1, "cannot get buffer for yestat");

	FD_SET(stat_sock, &fdset);

	return -1;
}

int
sockinit()
{
	sock_max = getdtablesize();

	FD_ZERO(&fdset);

	if (!yes_sockinit())
		return 0;

	if (!rt_sockinit())
		return 0;

	if (!stat_sockinit())
		return 0;

	return -1;
}


/* Returning all the space... actually makes no sense in UNIX
 */
void
sockreset()
{
	FD_CLR(yes_sock, &fdset);
	FD_CLR(stat_sock, &fdset);

	/* close all sockets */
	if (close(yes_sock) == -1)
		logbad(0, "cannot close yes_sock");

	if (close(send_sock) == -1)
		logbad(0, "cannot close send_sock");

	if (close(rt_sock) == -1)
		logbad(0, "cannot close rt_sock");

	if (close(stat_sock) == -1)
		logbad(0, "cannot close stat_sock");

	/* free buffers */
	free(yes_ctrl);
	free(yes_buf);
	free(rt_buf);
	free(stat_buf);

	return;
}


/* yessir socket I/O
 */
void
yes_sockread(int sock)
{
	struct msghdr msg;
	struct sockaddr_in from;
	struct iovec iov;
	struct cmsghdr *cmsg;
	int len, if_index, local;

	if_index = -1;
	local = -1;

	for (;;) {
		bzero(&msg, sizeof(msg));
		msg.msg_name = (caddr_t)&from;
		msg.msg_namelen = sizeof(from);
		msg.msg_control = (char *)yes_ctrl;
		msg.msg_controllen = YES_MAXCTRLSIZE;
		msg.msg_iov = &iov;
		msg.msg_iovlen = 1;

		iov.iov_base = (char *)yes_buf;
		iov.iov_len = YES_MAXPKTSIZE;
		
		if ((len = recvmsg(sock, &msg, 0)) < 0)
			break;

		if (len > YES_MAXPKTSIZE) {
			logbad(0, "rcv'd too large %d", len);
			break;
		}

		cmsg = CMSG_FIRSTHDR(&msg);
		for (; cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
			if (cmsg->cmsg_type == IP_RECVIF)
				if_index = CMSG_IFINDEX(cmsg);

			if (cmsg->cmsg_type == IPOPT_RECVLOCAL)
				local = CMSG_LOCAL(cmsg);
		}

		/* something is wrong, don't take chances 'cause looping */
		if (local == -1)
			continue;

		yessir_read(yes_buf, len, if_index, local);
	}
	return;
}


void
yes_send(char *msg, int lgt, struct in_addr *dst)
{
	struct sockaddr whereto;
	struct sockaddr_in *to = (struct sockaddr_in *)&whereto;

	yestat->rtcp_tx_pkt++;

	to->sin_len = sizeof(sin);
	to->sin_family = PF_INET;
	to->sin_addr.s_addr = dst->s_addr;

	if (sendto(send_sock, msg, lgt, 0, &whereto, sizeof(whereto)) < 0)
		msglog("fail to send YESSIR packets");

	return;
}


/* yestat socket I/O
 */

void
stat_sockread(int sock)
{
	struct sockaddr from;
	int cc, len;

	len = sizeof(from);

	for (;;) {
		cc = recvfrom(sock, stat_buf, YESTAT_MAXBUF, 0, &from, &len);

		if (cc < 0)
			break;

		if (cc > YESTAT_MAXBUF)
			logbad(1, "stat req rcv'd too large %d", cc);

		yestat_read(stat_buf, &from);
	}

	return;
}

void
stat_send(char *msg, int lgt, struct sockaddr *to)
{
	if (sendto(stat_sock, msg, lgt, 0, to, sizeof(*to)) < 0)
		msglog("fail to send a yestat packet");

	return;
}


/* Unicast routing query from kernel
 *
 * Note: check returned RTAX_DST entry to find out dest unreachable info.
 * The kernel should return ENETUNREACH in rtm->rtm_errno instead....
 * FreeBSD 3.3 does not do that.
 * 
 * return:
 *	positive number,    egress if index
 *	-1, failed
 */

int
yes_rtquery(struct in_addr *dst)
{
	struct rt_msghdr *rtm;
	struct sockaddr_in *sin;
	pid_t pid;
	ssize_t n;

	memset((char *)(rt_buf), 0, YES_RTBUFLEN);

	/* write common header: */
	rtm = (struct rt_msghdr *)rt_buf;
	rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
	rtm->rtm_version = RTM_VERSION;
	rtm->rtm_type = RTM_GET;
	rtm->rtm_addrs = RTA_DST | RTA_IFP | RTA_IFA;
	rtm->rtm_pid = pid = getpid();
	rtm->rtm_seq = rt_seqno;

	/* write to index RTAX_DST (0): */
	sin = (struct sockaddr_in *)(rtm + 1);
	sin->sin_len = sizeof(struct sockaddr_in);
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = dst->s_addr;

	write(rt_sock, rtm, rtm->rtm_msglen);
	do {
		n = read(rt_sock, rtm, YES_RTBUFLEN);
	} while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != rt_seqno ||
		rtm->rtm_pid != pid);

	rt_seqno++;
	rtm = (struct rt_msghdr *)rt_buf;

	/* read from index RTAX_DST */
	sin = (struct sockaddr_in *) (rtm + 1);
	if (sin->sin_addr.s_addr == 0)		/* no route! */
		return -1;

	return rtm->rtm_index;	
}



/* Interface management:
 *
 * The following routines are used to monitor interfaces and to manage 
 * link bandwidth. 'rif' stands for Resource-related InterFace. 
 * Actually, this is somewhat independent from the YESSIR protocol itself. 
 *
 */

/* query the system to detect the interfaces
 */
int
rif_check()
{
	struct timeval clk;
	size_t sysctl_buf_size, needed;
	struct if_msghdr *ifm, *ifm_last, *ifm_next;
	char *sysctl_buf;
	int i, j, if_index, mib[6];
	RIF *rif;

	if (!rifm) {

		/* init the rif table:
		 * - get the memory and clean it up
		 */
		rifm = (RIFM *)malloc(sizeof(RIFM));
		if (rifm == NULL) {
			logbad(1, "rif_check: no memory");
			return 0;
		}
		memset((char *)rifm, 0, sizeof(RIFM));
	}
	else {
		for (i = 0; i < RIF_HASH_SIZE; i++) {
			j = 0;
			rif = (RIF *)rif_at(i);
			for (; j < rif_num(i); j++, rif = rif->next)
				rif->test = RIF_CHECK;
		}
	}

	/* reset for the next check-up */
	gettimeofday(&clk, 0);
	rifm->chk_timer.tv_sec = clk.tv_sec + RIF_CHECK_INTERVAL;

	/* fetch the interface list */
	sysctl_buf_size = 0;
	mib[0] = CTL_NET;
	mib[1] = PF_ROUTE;
	mib[2] = 0;
	mib[3] = AF_INET;
	mib[4] = NET_RT_IFLIST;
	mib[5] = 0;

	for (;;) {

		if ((needed = sysctl_buf_size) != 0) {
			if (sysctl(mib, 6, sysctl_buf, &needed, 0, 0) >= 0)
				break;

			if (errno != ENOMEM && errno != EFAULT)
				logbad(1, "rif_check: get interface table");

			free(sysctl_buf);
			needed = 0;
		}

		if (sysctl(mib, 6, 0, &needed, 0, 0) < 0)
			logbad(1,"ifinit: route-sysctl-estimate");

		sysctl_buf_size = needed;
		sysctl_buf = (char *)malloc(needed);
		if (sysctl_buf == NULL)
			logbad(1,"ifinit: cannot get memory");
	}                

	/* add/modify to our own table 
	 */
	ifm_last = (struct if_msghdr *)(sysctl_buf + needed);
	ifm = (struct if_msghdr *)sysctl_buf;

	for(; ifm < ifm_last; ifm = ifm_next)  {
		ifm_next = (struct if_msghdr*)((char*)ifm + ifm->ifm_msglen);

		if_index = ifm->ifm_index;
		rif = rif_get(if_index);
		if (!rif) {
			if (!(rif = rif_add(if_index)))
				return 0;
		}

		switch(ifm->ifm_type) {
		case RTM_IFINFO:
			rif_up(rif, ifm);
			rif->test = RIF_OK;
			continue;

		case RTM_NEWADDR:
			rif->test = RIF_OK;
			rif_change(rif, (struct ifa_msghdr *)ifm);
			continue;

		default:
			logbad(1, "rif_check: out of sync");
			continue;
		}
	}

	/* remove the dead interfaces */
	for (i = 0; i < RIF_HASH_SIZE; i++) {

		j = 0;
		rif = (RIF *)rif_at(i);

		for (; j < rif_num(i); j++, rif = rif->next) {
			if (rif->test == RIF_CHECK)
				rif_down(rif);
		}
	}

	return -1;
}


/* called when recived RTM_IFINFO
 */
void
rif_up(RIF *rif, struct if_msghdr *ifm)
{
	struct sockaddr_dl *sdl;

	rif->if_flags = ifm->ifm_flags;
	rif->if_type = ifm->ifm_data.ifi_type;
	rif->if_hdrlen = ifm->ifm_data.ifi_hdrlen;
	rif->if_addrlen = ifm->ifm_data.ifi_addrlen;
	rif->if_physical = ifm->ifm_data.ifi_physical;
	rif->if_mtu = ifm->ifm_data.ifi_mtu;
	rif->link_bw = ifm->ifm_data.ifi_baudrate;
	rif->rx_pkt = ifm->ifm_data.ifi_ipackets;
	rif->rx_byte = ifm->ifm_data.ifi_ibytes;
	rif->rx_err = ifm->ifm_data.ifi_ierrors;
	rif->tx_pkt = ifm->ifm_data.ifi_opackets;
	rif->tx_byte = ifm->ifm_data.ifi_obytes;
	rif->tx_err = ifm->ifm_data.ifi_oerrors;

	sdl = (struct sockaddr_dl *)(ifm + 1);
	sdl->sdl_data[sdl->sdl_nlen] = 0;
	strncpy(rif->if_name, sdl->sdl_data, 
		MIN(sizeof(rif->if_name), sdl->sdl_nlen));

	if (rif->status == RIF_NEW) {
		rif->status = RIF_EXIST;

		/* the interfaces that we think we can apply reservation
		 * Ethernet is here assuming we use Ethernet switches
		 */
		if (rif->if_type == IFT_ETHER ||	/* huh? */
			rif->if_type == IFT_T1 ||
			rif->if_type == IFT_CEPT ||
			rif->if_type == IFT_PPP ||
			rif->if_type == IFT_DS3 ||
			rif->if_type == IFT_FRELAY ||
			rif->if_type == IFT_ATM ||
			rif->if_type == IFT_SONET ||
			rif->if_type == IFT_FRELAYDCE ||
			rif->if_type == IFT_V35 ||
			rif->if_type == IFT_HSSI ||
			rif->if_type == IFT_HIPPI ||
			rif->if_type == IFT_AAL5)
	
			rif->state = RESV_ENABLE;
		else
			rif->state = RESV_DISABLE;

#ifndef DEBUG
		if (rif->if_type == IFT_LOOP) {
			rif->state = RESV_ENABLE;
			rif->link_bw = 1000000;		/* 1Mbps */
		}
#endif

		/* default settings: 
		 * convert from bit/sec to byte/sec
		 */
		rif->max_resv_bw = rif->link_bw/8;
		rif->avail_bw = rif->link_bw/8;
	}
	
	return;
}


void
rif_down(RIF *rif)
{
	/* to speed up the local repair, search for the reservation
	 * flow table here to redirect reservations to new interfaces
	 */

	rif_del(rif);

	return;
}


/* map routing information to <data>
 */
void
rif_xaddrs(struct rt_addrinfo *data, struct ifa_msghdr *ifam)
{
	struct sockaddr *sa;
	char *lim, *sa_lim;
	int i, addrs, len;

	bzero(data, sizeof(*data));
	addrs = data->rti_addrs = ifam->ifam_addrs; 

	lim = (char *)((char *)ifam + ifam->ifam_msglen);
	sa = (struct sockaddr *)(ifam + 1);
	i = 0;
	for (; i < RTAX_MAX && (char *)sa < lim; i++) {
		if ((addrs & (1 << i)) == 0)
			continue;

		len = sa->sa_len;

		/* Check the sockaddr doesn't go past the end of the buffer. */
		if (len) {
			sa_lim = ((char*)sa) + len;
			if (sa_lim > (char *)lim)	/* equal is ok */
				return;
		} else {
			/*
			 * We allow the last broken sockaddr
			 * to be replaced by a good null one
			 */
			data->rti_info[i] = &sa_zero;
			return;		/* had unknown length */
		}
		data->rti_info[i] = sa;
		sa = (struct sockaddr *)((char*)(sa) + ROUNDUP(len));  
	}
	return;
}


/* Called when received RTM_NEWADDR
 */
void
rif_change(RIF *rif, struct ifa_msghdr *ifam)
{
	struct rt_addrinfo info;
	struct sockaddr *ifa;

	rif_xaddrs(&info, ifam);	/* map all addr's */
	ifa = (struct sockaddr *)(info.rti_info[RTAX_IFA]);

	if (!ifa && iff_alive(ifam->ifam_flags)) {
		msglog("rif_change: no if addr");
		rif->state = SICK_RIF;
		return;
	}
	if (ifa->sa_family != AF_INET && iff_alive(ifam->ifam_flags)) {
		msglog("rif_change: not inet addr");
		rif->state = SICK_RIF;
		return;
	}

	rif->int_addr = S_ADDR(ifa);	/* interface addr */

	if (bad_addr(rif->int_addr) && iff_alive(ifam->ifam_flags)) {
		msglog("rif_change: bad addr");
		rif->state = SICK_RIF;
		return;
	}

	if(ifam->ifam_flags & IFF_LOOPBACK) {
		rif->int_dstaddr = rif->int_addr;
		rif->int_mask = HOSTMASK;
	}
	else if (ifam->ifam_flags & IFF_POINTOPOINT) {

		ifa =  (struct sockaddr *)(info.rti_info[RTAX_BRD]);

		if ((!ifa || ifa->sa_family != AF_INET)
		    && iff_alive(ifam->ifam_flags)) {
			msglog("rif_change: bad dst addr");
			rif->state = SICK_RIF;
			return;
		}

		rif->int_dstaddr = S_ADDR(ifa);
		if (bad_addr(rif->int_dstaddr) && iff_alive(ifam->ifam_flags)) {
			msglog("rif_change: bad addr");
			rif->state = SICK_RIF;
			return;
		}
		rif->int_mask = HOSTMASK;
	}
	else {		/* such as shared media */
		ifa =  (struct sockaddr *)(info.rti_info[RTAX_NETMASK]);

		if (!ifa && iff_alive(ifam->ifam_flags)) {
			msglog("rif_change: bad mask");
			rif->state = SICK_RIF;
			return;
		}
	 	rif->int_dstaddr = rif->int_addr;
		rif->int_mask = S_ADDR(ifa);
	}

	return;
}
