V10/ipc/internet/routed.c

Compare this file to the similar file:
Show the results in this format:

#include <sys/param.h>
#include <fio.h>
#include <signal.h>
#include <errno.h>
#include <sys/filio.h>
#include <sys/inet/in.h>
#include <sys/inet/ip_var.h>
#include <sys/inet/udp_user.h>
#include "config.h"

#define IPDEVICE "/dev/ip16"	/* device for getting ip info from */
#define RPORT 520

int udpfd, ipfd, verbose, trace, quiet, traceinput, hopoffset;
extern errno;
extern	int buf_ld;
extern char *in_ntoa();
extern u_short cksum();

struct udppacket {
	struct udpaddr addr;
	char buf[4096];
};

main(argc, argv)
char *argv[];
{
	int n, i;
	struct udppacket packet;
	char *cp;

	for(i = 1; i < argc; i++){
		if(argv[i][0] == '-'){
			for(cp = argv[i]+1; *cp; cp++){
				switch(*cp){
				case 'v':
					verbose++;
					break;
				case 'q':
					quiet++;
					break;
				case 't':
					trace++;
					break;
				case 'T':
					traceinput++;
					break;
				default:
					if((*cp >= '0' && *cp <= '9') || *cp == '-') {
						hopoffset = atoi(cp);
						while(*cp >= '0' && *cp <= '9')
							cp++;
						cp--;
					} else {
						fprint(2, "usage: routed [-qtvT#]\n");								exit(1);
					}
					break;
				}
			}
		} else
			recsafe(argv[i]);
	}

	if(verbose || traceinput || trace) {
		if(hopoffset)
			fprint(2, "%s: adding %d to each metric\n", argv[0], hopoffset);
	}

	if((udpfd = udp_datagram(RPORT)) < 0) {
		perror("udp route daemon: no connection");
		exit(1);
	}

	/*
	 *  try pushing bufld, complain but don't exit if unable
	 */
/*
	if(ioctl(udpfd, FIOPUSHLD, &buf_ld)<0){
		perror("pushing buf_ld");
	}
	if(ioctl(udpfd, FIOPUSHLD, &buf_ld)<0){
		perror("pushing buf_ld");
	}
	if(ioctl(udpfd, FIOPUSHLD, &buf_ld)<0){
		perror("pushing buf_ld");
	}
*/

	if((ipfd = open(IPDEVICE, 2)) < 0) {
		perror(IPDEVICE);
		exit(1);
	}

	readgateways();
	if(!(verbose || traceinput || trace)) {
		detach("routed");
		nice(-20);
	}
	timer();

again:
	while((n = read(udpfd, &packet, sizeof(struct udppacket))) > 0) {
		routed(&packet, n - sizeof(struct udpaddr));
	}
	if(n < 0 && errno == EINTR)
		goto again;
}


/*	protocol.h	4.10	83/08/11	*/
/*
 * Routing Information Protocol
 *
 * Derived from Xerox NS Routing Information Protocol
 * by changing 32-bit net numbers to sockaddr's and
 * padding stuff to 32-bit boundaries.
 */
#define	RIPVERSION	1
#define AF_INET		2
#define AF_UNSPEC	0

struct sockaddr{
	short sin_family;
	u_short sin_port;
	u_long sin_addr;
	char sin_zero[8];
};
struct netinfo {
	struct	sockaddr rip_dst;	/* destination net/host */
	int	rip_metric;		/* cost of route */
};

struct rip {
	u_char	rip_cmd;		/* request/response */
	u_char	rip_vers;		/* protocol version # */
	u_char	rip_res1[2];		/* pad to 32-bit boundary */
	union {
		struct	netinfo ru_nets[1];	/* variable length... */
		char	ru_tracefile[1];	/* ditto ... */
	} ripun;
#define	rip_nets	ripun.ru_nets
#define	rip_tracefile	ripun.ru_tracefile
};
 
/*
 * Packet types.
 */
#define	RIPCMD_REQUEST		1	/* want info */
#define	RIPCMD_RESPONSE		2	/* responding to request */
#define	RIPCMD_TRACEON		3	/* turn tracing on */
#define	RIPCMD_TRACEOFF		4	/* turn it off */

#define	RIPCMD_MAX		5
#ifdef RIPCMDS
char *ripcmds[RIPCMD_MAX] =
  { "#0", "REQUEST", "RESPONSE", "TRACEON", "TRACEOFF" };
#endif

#define	HOPCNT_INFINITY		16	/* per Xerox NS */
#define	MAXPACKETSIZE		488	/* max broadcast size */
#define MAXROUTESPERPACKET	(MAXPACKETSIZE-4)/sizeof(struct netinfo)

/*
 * Timer values used in managing the routing table.
 * Every update forces an entry's timer to be reset.  After
 * EXPIRE_TIME without updates, the entry is marked invalid,
 * but held onto until GARBAGE_TIME so that others may
 * see it "be deleted".
 */
#define	TIMER_RATE		60	/* interval in seconds over which we bcast to all
					 * directly connected interfaces */

#define	EXPIRE_TIME		180	/* time to mark entry invalid */
#define	GARBAGE_TIME		240	/* time to garbage collect */

#define NIFS TIMER_RATE
struct ipif ifs[NIFS];
int nextifs;			/* next interface to broadcast to */
int nifs;			/* number of functioning interfaces */

routed(up, len)
	struct udppacket *up;
	u_int len;
{
	struct rip *rip;

	if(traceinput)
		logevent("from %s\n", in_ntoa(up->addr.host));
	rip = (struct rip *)(up->buf);
	if(rip->rip_vers != RIPVERSION)
		return;
	switch(rip->rip_cmd){
	case RIPCMD_RESPONSE:
		if(up->addr.port != RPORT)
			logevent("routed: %s %d masquerading as routed\n",
				in_host(up->addr.host), up->addr.port);
		else
			response(up, len);
		break;
	case RIPCMD_REQUEST:
		request(up, len);
		break;
	default:
		logevent("routed: %s %d sent strange command %d\n",
			in_host(up->addr.host), up->addr.port, rip->rip_cmd);
		return;
	}
}

/*
 *  don't believe responses from this host.  no use infinitely
 *  recursing.
 */
response(up, len)
	struct udppacket *up;
	u_int len;
{
	struct rip *rip;
	struct sockaddr *sin;
	struct netinfo *np;

	rip = (struct rip *)(up->buf);
	if(myaddr(up->addr.host))
		return;
	np = rip->rip_nets;
	while(np < (struct netinfo *)(&(up->buf[len]))){
		sin = &(np->rip_dst);
		np->rip_metric = ntohl(np->rip_metric);
		sin->sin_port = ntohs(sin->sin_port);
		sin->sin_addr = ntohl(sin->sin_addr);
		if(traceinput) {
			logevent(" %s %d\n", in_ntoa(sin->sin_addr), np->rip_metric);
		}
		if (np->rip_metric > 0 && saferoute(sin->sin_addr) == 0)
			rtinstall(sin->sin_addr, up->addr.host, np->rip_metric, 0, -1);
		np++;
	}
}

/*
 *  Only accept requests for all routes or for internet routes.
 *  In all cases send back everything.
 */
request(up, len)
	struct udppacket *up;
	u_int len;
{
	struct rip *rip;
	struct sockaddr *sin;
	struct netinfo *np;

	rip = (struct rip *)(up->buf);
	np = rip->rip_nets;
	if(traceinput)
		logevent("request from %s\n", in_ntoa(up->addr.host));

	sin = &(np->rip_dst);
	np->rip_metric = ntohl(np->rip_metric);
	sin->sin_family = ntohs(sin->sin_family);
	if((sin->sin_family == AF_UNSPEC && np->rip_metric == HOPCNT_INFINITY)
	|| sin->sin_family == AF_INET)
		send(up);
}

struct route{
	u_long dst;
	u_long gate;
	int metric;
	int age;
	int nif;
};
#define NROUTES 360
struct route routes[NROUTES];

rtinstall(dst, gate, metric, age, nif)
u_long dst, gate;
{
	struct route *rp, *save = 0;
	char hname[128];

	/*
	 *  don't let a default route get set unless we are quiet
	 */
	if(dst==0 && !quiet)
		return;

	/*
	 *  ignore any route with inifinite hop counts
	 */
	if(metric >= HOPCNT_INFINITY)
		return;

	/*
	 *  look up the route in our local tables (not the kernel's)
	 */
	for(rp = routes; rp < &routes[NROUTES]; rp++){
		if(rp->dst == dst)
			break;
		if(rp->dst == 0 && save == 0)
			save = rp;
	}
	if(rp >= &routes[NROUTES] && (rp = save) == 0){
		logevent("routed: out of routes?\n");
		return;
	}

	/*
	 *  if this is a new route, make sure the new count is smaller
	 */
	if(rp->dst == 0)
		rp->metric = HOPCNT_INFINITY + 1;

	/*
	 *  if new route is closer than the old route
	 *  or the gate is the same but the distance has changed
	 *  or the gate is the same but the age has changed,
	 *  then use the new gate
	 */
	if(metric < rp->metric
	   || (gate == rp->gate && metric != rp->metric)
	   || (gate == rp->gate && age != rp->age)){
		rp->metric = metric;
		rp->age = age;
		if(rp->dst != dst || rp->gate != gate){
			if(verbose){
				strcpy(hname, in_host(gate));
				logevent("%s installing as route to %s, metric %d\n",
					hname, in_host(dst), metric);
			}
			rp->dst = dst;
			rp->gate = gate;
			rp->nif = nif;
			if(ioctl(ipfd, IPIOROUTE, rp) < 0)
				logevent("can't install routed %s\n", in_ntoa(rp->gate));
		} else {
			if(verbose > 1){
				strcpy(hname, in_host(gate));
				logevent("%s confirmed as route to %s, metric %d\n",
					hname, in_host(dst), metric);
			}
			rp->dst = dst;
			rp->gate = gate;
		}
	}
}

/*
 * keep a list of routes
 * that belong to us;
 * don't let anyone claim them
 */

#define	NSAFE	20

u_long safer[NSAFE];
int nsafe = 0;

recsafe(s)
char *s;
{
	if (nsafe < NSAFE)
		safer[nsafe++] = in_address(s);
}

saferoute(dst)
u_long dst;
{
	register int i;

	for (i = 0; i < nsafe; i++)
		if (dst == safer[i])
			return (1);
	return (0);
}

readgateways()
{
	int fd;
	char *cp;
	char net[32], gateway[32], which[32];
	u_long dst, gate;
	int metric;

	if((fd = open(GATEWAYS, 0)) == 0)
		return;
	Finit(fd, 0);
	while(cp=Frdline(fd)){
		if(sscanf(cp, "%s %s gateway %s metric %d",
			  which, net, gateway, &metric) != 4)
			continue;
		dst = in_address(net);
		gate = in_address(gateway);
		rtinstall(dst, gate, metric, -1, -1);
	}
}

timer()
{
	int i;
	struct route *rp;
	struct ipif ipif;
	u_long net;
	char haddr1[32], haddr2[322];

	if(nextifs == 0){
		/*
		 *  get interfaces from kernel.  ignore interfaces that are down,
		 *  interfaces that are loop place holders (this==that), and the
		 *  loop-back network
		 */
		for(i = nifs = 0; nifs < NIFS; i++){
			*(int *)&ipif = i;
			if(ioctl(ipfd, IPIOGETIFS, &ipif) < 0)
				break;
			if((ipif.flags&IFF_UP) && ipif.thishost != ipif.that &&
			   ipif.that != 0x7f000000) {
				if(ipif.bcast[0] == 0)
					ipif.bcast[0] = ipif.that;
				ifs[nifs++] = ipif;
				if(trace){
					strcpy(haddr1, in_ntoa(ipif.that));
					strcpy(haddr2, in_ntoa(ipif.thishost));
					logevent("ifs[%d] %s %s %s\n", nifs, haddr1,
						haddr2, in_ntoa(ipif.bcast[0]));
				}
				rtinstall(ipif.that, ipif.that, 0, 0, nifs - 1);
			}
		}
	}
	
	if(!quiet)
		broadcast();

	/*
	 *  age routes && get rid of too old routes
	 */
	if(nextifs == 0)
		for(rp = routes; rp < &routes[NROUTES]; rp++){
			if(rp->dst == 0)
				continue;
			if(rp->age > 10){
				rtdelete(rp);
			} else if(rp->age >= 0){
				rp->age++;
			}
		}

	/*
	 *  post alarm for broadcast to next interface
	 */
	signal(SIGALRM, timer);
	i = nifs>0 ? TIMER_RATE/nifs : TIMER_RATE;
	alarm(i+1);
}

/*
 *  Send a broadcast to the next interface.  This happens once every TIMER_RATE/nifs
 *  seconds so that n TIMER_RATE seconds we shoulc hit every interface.
 */
broadcast()
{
	static long last;
	long now;
	int reps;
	struct udppacket p;

	/*
	 *  broadcast to interfaces.  approximate one broadcast every
	 *  TIMER_RATE/nifs seconds.
	 */
	now = time((long *)0);
	reps = nifs ? (now-last)/(TIMER_RATE/nifs) : 0;
	if(reps > nifs)
		reps = nifs;
	for(; reps>0; reps--){
		if(++nextifs >= nifs)
			nextifs = 0;

		p.addr.host = ifs[nextifs].bcast[0];
		p.addr.port = RPORT;
		send(&p);
	}
	last = now;
}

/*
 *  put an entry into the bradcast message
 */
struct netinfo *
insert(rp, up, np, rip)
	struct udppacket *up;
	struct route *rp;
	struct netinfo *np;
	struct rip *rip;
{
	struct sockaddr *sin;
	char haddr[32];

 	/*
	 *  don't send routes back onto the network from which they
	 *  came
	 */
	if((rp->gate & ifs[nextifs].mask) == ifs[nextifs].that)
		return np;

	/*
	 *  put the routing entry into the message.
	 */
	bzero(np, sizeof(struct netinfo));
	sin = &(np->rip_dst);
	np->rip_metric = htonl(rp->metric + 1 + hopoffset);
	sin->sin_port = 0;

	/*
	 *  don't send information about one network's subnets to other networks
	 */
	if(in_netof(rp->dst) == in_netof(ifs[nextifs].that))
		sin->sin_addr = htonl(rp->dst);
	else
		sin->sin_addr = htonl(in_netof(rp->dst));

	sin->sin_family = htons((u_short)AF_INET);
	if(trace){
		strcpy(haddr, in_ntoa(ntohl(sin->sin_addr)));
		logevent(" %s %s %d\n",
			haddr,
			in_ntoa(rp->dst),
			ntohl(np->rip_metric));
	}
	np++;

	/*
	 *  if the message is at maximum size, send it and start a new one
	 */
	if (np-rip->rip_nets == MAXROUTESPERPACKET) {
		psend(up, (char *)np);
		np = rip->rip_nets;
	}
	return np;
}

/*
 *  send info about all routes
 */
send(up)
	struct udppacket *up;
{
	struct route *rp;
	struct rip *rip;
	struct netinfo *np;
	long net;

	net = in_netof(ifs[nextifs].bcast[0]);
	rip = (struct rip *)up->buf;
	rip->rip_cmd = RIPCMD_RESPONSE;
	rip->rip_vers = RIPVERSION;
	np = rip->rip_nets;
	if(trace)
		logevent("BROADCAST:\n");

	/*
	 *  first send info about directly connected networks
	 */
	for(rp = routes; rp < &routes[NROUTES]; rp++){
		if(rp->dst == 0 || rp->metric > 0)
			continue;
		if(rp->metric + hopoffset + 1 >= HOPCNT_INFINITY)
			continue;
		np = insert(rp, up, np, rip);
	}

	/* 
	 *  now send info about the rest
	 */
	for(rp = routes; rp < &routes[NROUTES]; rp++){
		if(rp->dst==0 || rp->metric<=0)
			continue;
		if(rp->metric + hopoffset + 1 >= HOPCNT_INFINITY)
			continue;
		np = insert(rp, up, np, rip);
	}
	if (np > rip->rip_nets)
		psend(up, (char *)np);
}

/* send one routing packet to one network */
psend(pp, cp)
	struct udppacket *pp;
	char *cp;
{
	int len=cp-(char *)pp;
	int i;

	if(trace)
		logevent("to %s %d\n", in_ntoa(pp->addr.host), len);
	if (write(udpfd, pp, len) !=len)
		logevent("udp write error\n");
}

myaddr(x)
u_long x;
{
	int i;

	for(i = 0; i < nifs; i ++)
		if(ifs[i].thishost == x)
			return(1);
	return(0);
}

rtdelete(rp)
struct route *rp;
{
	if(verbose) {
		logevent("deleting %s %d\n", in_ntoa(rp->dst), rp->metric);
	}
	if(rp->gate != rp->dst){
		rp->gate = 0;
		if(ioctl(ipfd, IPIOROUTE, rp) < 0)
			logevent("can't remove route %s\n", in_ntoa(rp->gate));
	}
	rp->gate = rp->dst = 0;
	rp->age = rp->metric = 0;
}