/*
 *------------------------------------------------------------------
 *
 * $Source: /mit/cgw/src/gw/ch/RCS/chrut.c,v $
 * $Revision: 1.2 $
 * $Date: 89/08/23 16:08:37 $
 * $State: Exp $
 * $Author: jon $
 * $Locker: jon $
 *
 * $Log:	chrut.c,v $
 * Revision 1.2  89/08/23  16:08:37  jon
 * change bughalt's to returns, since we're seeing bogon ch routing packets
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_foo_c = "$Header: /mit/cgw/src/gw/ch/RCS/chrut.c,v 1.2 89/08/23 16:08:37 jon Exp Locker: jon $";
#endif	lint


/* Copyright 1984 by the Massachusetts Institute of Technology */
/* See permission and disclaimer notice in file notice.h */
#include	<notice.h>


/* This file includes support for all CHAOS network routing
 * related tasks; table generation and listening, etc.
 */


#include	<types.h>
#include	<sys.h>
#include	"../src/defs.h"
#include	"../src/macs.h"
#include	"../src/const.h"
#include	"../src/param.h"
#include	"../src/net.h"
#include	"../src/ext.h"
#include	"ch.h"
#include	"chparam.h"
#include	"chext.h"


/* RUT packets consist of a list of these entries */

struct	chrutb	{	unss	cr_snet;
			unss	cr_cost;
		};


/* The ANS to a DUMP-ROUTING-TABLE RFC contains lists of these
 * entries.
 */

struct	chdrtb	{	chaddr	cd_addr;
			unss	cd_cost;
		};


/* Error and info messages */

static char chbds[] =	"Bd sbnt 0 rte frm hst %o/%o ign\n";
static char chovfl[] =	"Routing tbl ovfl, sbnt %o frm hst %o/%o, ign\n";
static char chcost[] =	"Low cost of %d fr sbnt %o frm hst %o/%o ign\n";
static char chnre[] =	"Old rte of %d to %o/%o fr %o rplcd, nw %d to %o/%o\n";
static char chrutd[] =	"Rte of %d fr sbnt %o to hst %o/%o decayed\n";
static char chdrt[] =	"Rspnding to DUMP-ROUTING-TABLE frm hst %o/%o\n";



/* Process incoming routing packets, an entry at a time, seeing
 * if any useful information is contained.
 */

chrut(iob)
iorb	*iob;

{	reg	ochpkt	*pkt;
	reg	struct	chrutb	*rdat;
		chaddr	*hst;
		unss	*cst;
	reg	unsb	snet;
		unsb	dsnet;
		unss	ent;

	pkt = mkoch(iob->i_addr);
	rdat = ((struct chrutb *)(pkt + 1));

	for (ent = (pkt->oc_len / sizeof(struct chrutb));
					 ent > 0; ent--, rdat++) {

		if ((snet = rdat->cr_snet) == 0) {
			niflog(L_ERRU)
			  dolog(chbds, mkscha(&pkt->oc_src));
			continue;
			}
		if (snet >= CHMSNET) {
			niflog(L_ERRU)
			  dolog(chovfl, snet, mkscha(&pkt->oc_src));
			continue;
			}
		cst = &chgctab[snet];
		if (*cst <= rdat->cr_cost)
			continue;
		switch(chttab[snet]) {

		  case T_DIR:
		  case T_FIX:	niflog(L_ERRU)
		    		  dolog(chcost, rdat->cr_cost, snet,
					mkscha(&pkt->oc_src));
				break;

		  case T_NONE:	chttab[snet] = T_FLT;
				chgatab[snet].oc_word = ~0;

		  case T_FLT:	hst = &chgatab[snet];
				*cst = rdat->cr_cost;

				if (hst->oc_word == pkt->oc_src.oc_word)
					break;

				niflog(L_INFC)
				  dolog(chnre, *cst, mkscha(hst), snet,
					rdat->cr_cost, mkscha(&pkt->oc_src));

				if ((dsnet = pkt->oc_src.oc_addr.oc_snt) == 0) {
				  dolog ("ch: Illegal subnet 0 route");
				  freebuf(iob);
				  return;
				}
/*					bughalt("Illegal subnet 0 route"); */
				if (dsnet >= CHMSNET) {
				  dolog ("ch: Bad source subnet route");
				  freebuf(iob);
				  return;
				}
/*					bughalt("Bad source subnet route"); */
				if (chttab[dsnet] != T_DIR) {
				  dolog ("ch: bad routing data %o\n", dsnet);
				  freebuf(iob);
				  return;
/*					bughalt("bad routing data"); */
				}
				chitab[snet] = chitab[dsnet];
				hst->oc_word = pkt->oc_src.oc_word;
				break;

		  default:	bughalt("bad network type code");
		  }
  	}

	freebuf(iob);
}


/* Age routing info. Bogus type stuff is C lossage.
 */

swrd	charut()

{	reg	unsb	*gtp;
	reg	unss	*gcp;
	reg	unsb	snet;

	gtp = chttab;
	gcp = chgctab;
	for (snet = 0; snet < CHMSNET; snet++, gtp++, gcp++) {
		if (*gtp != T_FLT)
			continue;
		if (++(*gcp) >= CHMXCST) {
			niflog(L_ERRU)
			  dolog(chrutd, *gcp, snet, mkscha(&chgatab[snet]));
			chitab[snet] = NULL;
			*gtp = T_NONE;
			}
		}
}


/* Routine that actually sends out the routing packets. For CHAOS/IN
 * conversion, the routing packets are only broadcast for virtual
 * subnets that are old style. Loops over the network table and
 * creates a routing packet for each network, which it then queues.
 */

swrd	chxrut()
{
    reg	net	*netp;
    reg	chia	*iap;
    reg	iorb	*iob;
    unss	snet;
    word	code;
    iorb	*chmkrut();
    iorb	*netsend();
    
    for (netp = &nets[0]; netp < lstnet; netp++) {
	for (iap = chiatlst[netp->n_net]; iap != NULL; iap = iap->chia_link) {
	    if ((netp->n_cap & C_BRD) == 0)
	      bughalt("not broadcast net");
	    
	    snet = mkcha(iap->chia_addr)->oc_addr.oc_snt;
	    if ((iob = chmkrut(chgctab[snet])) == NULL)
	      return;
	    
	    mkoch(iob->i_addr)->oc_src.oc_word =
	      mkcha(iap->chia_addr)->oc_word;
	    if (iob->i_breq > netp->n_max)
	      bughalt("CHAOS pkt too big");

	    if (code = (*netp->n_send)(iob, netp, P_CH, ANYHOST) != D_OK) {
		niflog(L_ERRU)
		  dolog("CH err snding rte pkt\n");
		freebuf(iob);
	    }
	}
    }
}


/* Routine that creates the routing packets. Loops over the routing
 * table and generates a RUT packet for this net. Note code to deliberately
 * skip subnet zero.
 */

iorb	*chmkrut(cst)
unss	cst;

{	reg	iorb	*iob;
	reg	ochpkt	*pkt;
	reg	struct	chrutb	*rdat;
		struct	chrutb	*ordat;
		unsb	*gtp;
		unsb	snet;
		unss	nents =	0;

	if ((iob = mkiorb(getbuf(bufsiz))) == NULL) {
	    niflog(L_ERRU)
	      dolog("Can't allocate packet for CHAOS routing broadcast");
	    return (NULL);
	}
	pkt = mkoch(((byte *) iob) + pktoff);

	pkt->oc_pad = 0;
	pkt->oc_opc = CHRUT;
	pkt->oc_fc = 0;
	pkt->oc_sind = 0;
	pkt->oc_dst.oc_word = 0;
	pkt->oc_dind = 0;
	pkt->oc_pkt = 0;
	pkt->oc_ack = 0;

	rdat = ((struct chrutb *)(pkt + 1));
/*	ordat = rdat - 1; */
	gtp = &chttab[1];
	for (snet = 1; snet < CHMSNET; snet++, gtp++) {
		if (*gtp == T_NONE)
			continue;
/*		if (rdat != ordat + 1)
		  asm("bugw $0"); */
		rdat->cr_snet = snet;
		rdat->cr_cost = (chgctab[snet] + cst);
/*		ordat = rdat; */
		rdat++;
		nents++;
		}

	pkt->oc_len = (sizeof(struct chrutb) * nents);
	iob->i_breq = (pkt->oc_len + sizeof(ochpkt));
	iob->i_addr = ((byte *) pkt);
	return(iob);
}


/* Process DRT RFC's. Simply dump out the outing table.
 */

chdrutt(iob)
iorb	*iob;

{	reg	ochpkt	*pkt;
	reg	struct	chdrtb	*rtdbp;
	reg	unss	snet;

	pkt = mkoch(iob->i_addr);
	niflog(L_INFU)
	  dolog(chdrt, mkscha(&pkt->oc_src));

	chmkans(pkt);
	rtdbp = ((struct chdrtb *)(pkt + 1));

	for (snet = 0; snet < CHMSNET; snet++, rtdbp++) {
		switch(chttab[snet]) {

		  case T_NONE:	rtdbp->cd_addr.oc_word = 0;
				rtdbp->cd_cost = CHMXCST;
				break;

		  case T_DIR:	rtdbp->cd_addr.oc_word = (1 + (2 *
					chitab[snet]->n_net));
				rtdbp->cd_cost = chgctab[snet];
				break;

		  case T_FIX:
		  case T_FLT:	rtdbp->cd_addr.oc_word = chgatab[snet].oc_word;
				rtdbp->cd_cost = chgctab[snet];
				break;

		  default:	bughalt("bad network type code");
		  }
		}

	pkt->oc_len = (CHMSNET * sizeof(struct chdrtb));
	if (pkt->oc_len > CHMAX)
		bughalt("too many entries to fit in DRT ANS");
	iob->i_bxfr = (pkt->oc_len + sizeof(ochpkt));
	chaddq(iob);
}

/* Print the chaos routing table. */
ch_prrte(devp, arg, jcl)
dct *devp;
int arg;
char *jcl;
{
    reg	unss	snet;

    for (snet = 0; snet < CHMSNET; snet++) {
	switch(chttab[snet]) {
	case T_NONE:
	    continue;
	case T_DIR:
	    fprintf(devp, "%3o:  --direct--     %4d\n", snet, chgctab[snet]);
	    break;
	case T_FIX:
	case T_FLT:
	    fprintf(devp, "%3o: %3o/%-3o (%05o) %4d\n", snet,
		    chgatab[snet].oc_addr.oc_snt, chgatab[snet].oc_addr.oc_hst,
		    chgatab[snet].oc_word, chgctab[snet]);
	    break;
	default:
	    bughalt("bad network type code");
	}
    }
}    
