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


/* This file includes CHAOS startup and input packets demultiplexing
 * as well as support for all CHAOS network support tasks; routing
 * generation and listening, etc. Packets coming in from
 * the nets have their IORB pointers passed to this process.
 */

/*
 *------------------------------------------------------------------
 *
 * $Source: /u1/jis/gw/cgw/gw/ch/RCS/chmux.c,v $
 * $Revision: 1.2 $
 * $Date: 87/07/15 19:57:50 $
 * $State: Exp $
 * $Author: jon $
 * $Locker:  $
 *
 * $Log:	chmux.c,v $
 * Revision 1.2  87/07/15  19:57:50  jon
 * Registers chflg (chaos) with log_register_flag
 * 
 * 
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_chmux_c = "$Header: chmux.c,v 1.2 87/07/15 19:57:50 jon Exp $";
#endif	lint

#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	"../opcon/opcon.h"
#include	"ch.h"
#include	"chparam.h"
#include	"chext.h"


/* Interface to command interpreter. */
void ch_prrte();

static subcommand ch_scmd[] = {
    { "drt", "Dump routing table", ch_prrte, 0 },
    { NULL, NULL, NULL, 0 },
};
static command ch_cmd = { NULL, "ch", "ChaosNet", ch_scmd };


/* Useful constants */

#define	CHSP	040	/* Space in CHAOS char set */
#define	CHNL	010	/* NL in CHAOS char set */


/* Error and info messages */

static char chnopc[] =	"No hndlr fr opcode %o frm hst %o/%o, ign\n";
static char chnbrd[] =	"No hndlr fr BRD frm hst %o/%o, ign\n";
static char chnsrv[] =	"No svr fr RFC to %s frm hst %o/%o, ign\n";
static char chcls[] =	"Snding CLS of %s to hst %o/%o\n";



/* CHAOS protocol startup. Sets up some initial stuff, clears out the
 * routing tables, rebuild the initial routing tables from device
 * device interface info and then calls the per network protocol
 * initialization for networks that we know about. Finally it starts
 * the mux, which accepts all CHAOS packets addressed to this host.
 */

CHNET()
{
	nprots++;
	clrq(&chq);
	clrq(&chlpq);

	clrchtabs();
	initchtabs();
	chprinit();

	opcon_register(&ch_cmd);
	log_register_flag ("chaos", &MSGFLG);
}


/* Clear all CHAOS routing tables. Note that the gateway tables are not
 * cleared since you can tell merely by looking at the type field
 * what kind of connection you are dealing with, and all non direct
 * and fixed routes are cleared at startup.
 */

clrchtabs()

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

	nip = chitab;
	gtp = chttab;
	gcp = chgctab;
	for (snet = 0; snet < CHMSNET; snet++) {
		*nip++ = NULL;
		*gtp++ = T_NONE;
		*gcp++ = CHMXCST;
		}
}


/* Enter entries from hardware interface address tables.
 */

initchtabs()

{	reg	unsb	snet;

	{	reg	net	*netp;
		reg	chia	*iap;

		for (netp = &nets[0]; netp < lstnet; netp++) {
			if ((iap = chiatlst[netp->n_net]) == NULL)
				continue;
			if (netp->n_prinit != NULL)
				(*netp->n_prinit)(netp, P_CH, iap);

			while (iap != NULL) {
			    if ((snet = mkcha(iap->chia_addr)->oc_addr.oc_snt)
				== 0)
			      bughalt("Illegal subnet");
			    if (snet >= CHMSNET)
			      bughalt("Table overflow");
			    if (chttab[snet] != T_NONE)
			      bughalt("Table overwrite");
			    chitab[snet] = netp;
			    chttab[snet] = T_DIR;
			    chgctab[snet] = iap->chia_cost;
			    iap = iap->chia_link;
			    }
			}
	}

	{	reg	chfre	*frep;
		reg	unsb	dsnet;
			unsb	nfre;
		
		frep = chfretbl;
		for (nfre = 0; nfre < nchfre; nfre++, frep++) {
			if ((snet = frep->chfre_snt) == 0)
				bughalt("Illegal subnet");
			if (snet >= CHMSNET)
				bughalt("Table overflow");
			if (chttab[snet] != T_NONE)
				bughalt("Table overwrite");
			dsnet = mkcha(frep->chfre_addr)->oc_addr.oc_snt;
			if (chttab[dsnet] != T_DIR)
				bughalt("Bad fixed route");
			chitab[snet] = chitab[dsnet];
			chttab[snet] = T_FIX;
			chgatab[snet].oc_word =
					mkcha(frep->chfre_addr)->oc_word;
			chgctab[snet] = frep->chfre_cost;
			}
		}
}

/* Handles packets on Chaos input queue.
 */
chxmux()
{
    while (tstneq(&chlpq)) {
	/* there should be a check in here to throw out to a higher
	 * priority task */
	chmux(rmq(&chlpq));
    }
}

/* Top level for CHAOS protocol services; basically waits to be handed
 * packets from the forwarding process.  Needs to send a LOS.
 */

chmux(iob)
reg	iorb	*iob;

{	reg	ochpkt	*pkt;

	pkt = mkoch(iob->i_addr);

	switch (mkunsb(pkt->oc_opc)) {

	  case CHRUT:	chrut(iob);
			break;

	  case CHRFC:	chrfc(iob);
			break;

	  case CHBRD:	niflog(L_INFC)
			  dolog(chnbrd, mkscha(&pkt->oc_src));
			freebuf(iob);
			break;

	  default:	niflog(L_ERRU)
			  dolog(chnopc, mkunsb(pkt->oc_opc),
				mkscha(&pkt->oc_src));

	  case CHMNT:	freebuf(iob);
			break;
	  }
}


/* Process incoming RFC's. Parses the string and converts it to a C
 * style string, and then, if there are not arguments, provides
 * a null string for the 'rest', otherwise converting the rest to a
 * C string.
 */

chrfc(iob)
reg	iorb	*iob;

{	reg	ochpkt	*pkt;
	reg	char	*srvr;
	unss	len =	0;
	unss	plen;

	pkt = mkoch(iob->i_addr);
	srvr = (char *)pkt + sizeof(ochpkt);
 	plen = pkt->oc_len;

	while ((len < plen) && (*srvr != CHSP)) {
		srvr++;
		len++;
		}
	*srvr++ = '\0';
	if (len == plen)
		*srvr = '\0';
	  else
	        ((char *)(pkt + 1))[plen] = '\0';
/*		pkt->oc_data[plen] = '\0';	*/

	if (strcmp(pkt + 1, "DUMP-ROUTING-TABLE") == 0) {
		chdrutt(iob);
		return;
		}
	if (strcmp(pkt + 1, "STATUS") == 0) {
		chstatus(iob);
		return;
		}
	if (strcmp(pkt + 1, "PULSAR") == 0) {
		chpulsar(iob, srvr);
		return;
		}

	niflog(L_ERRU)
	  dolog(chnsrv, pkt + 1, mkscha(&pkt->oc_src));
	chmkcls(iob, "No server for RFC");
}


/* Routine to turn a packet into an ANS. */

chmkans(pkt)
reg	ochpkt	*pkt;

{	swrd	src;

	pkt->oc_opc = CHANS;
	pkt->oc_fc = 0;
	src = pkt->oc_dst.oc_word;
	pkt->oc_dst.oc_word = pkt->oc_src.oc_word;
	pkt->oc_src.oc_word = src;
	pkt->oc_dind = pkt->oc_sind;
	pkt->oc_sind = 0;
	pkt->oc_ack = pkt->oc_pkt;
	pkt->oc_pkt = 0;
}


/* Routine to turn a packet into an CLS with a string error message
 * and zap it off.
 */

chmkcls(iob, errstr)
reg	iorb	*iob;
char	*errstr;

{	reg	ochpkt	*pkt;
	reg	char	*p, *q;
	swrd	src;
	unss	len =	0;

	pkt = mkoch(iob->i_addr);
	niflog(L_ERRU)
	  dolog(chcls, errstr, mkscha(&pkt->oc_src));
	pkt->oc_opc = CHCLS;
	pkt->oc_fc = 0;
	src = pkt->oc_dst.oc_word;
	pkt->oc_dst.oc_word = pkt->oc_src.oc_word;
	pkt->oc_src.oc_word = src;
	pkt->oc_dind = pkt->oc_sind;
	pkt->oc_sind = 0;
	pkt->oc_ack = pkt->oc_pkt;
	pkt->oc_pkt = 0;

	p = errstr;
	q = (char *)(pkt + 1);
	while (*p != '\0') {
		*q++ = *p++;
		len++;
		}
	pkt->oc_len = len;
	iob->i_bxfr = (len + sizeof(ochpkt));
	chaddq(iob);		/* Should skip checks in forwarder somehow */
}
