/* Copyright 1984 by the Massachusetts Institute of Technology */

/*	C Gateway  -  Noel Chiappa  - MIT/LCS/CSR
 *
 *	To Johann, for his 'abstract architecture', and to 'oi 'Ellenoi,
 *	for everything.
 *
 *
 * All the comments on the general code structure haven't been updated
 * since the introduction of tasking. Caveat Haquur.
 *
 * This code implements a sophisticated multi-protocol multi-network
 * packet forwarder, with provisions for running protocol implementations
 * and server tasks on top if space and processing power permit. It runs in
 * the MOS operating system, designed by Jim Mathis of SRI.
 *
 * This module contains the top level loop of the gateway process. All packet
 * forwarding and network handling (input and output) is done in this process.
 * (Checking network/interface status will also be done in this process.)
 * Most of the code is actually in other modules, mostly per protocol
 * and per network. Protocol stuff (such as, for IN, GGP packets, routing
 * table hacking, etc) is done in per protocol handler processes. Note
 * that not all configurations will want to include all the possible
 * protocols, just as most will not provide all possible interfaces.
 *
 * The way this process works is all the networks run as little tasks; this
 * is very easy to do in MOS, by implementing event call channels; a table
 * is kept that relates MOS event opcodes to handlers; when any signal
 * is encountered by the top-level loop it simply dispatches through
 * the table. On startup each network's init routine is called; it is
 * responsible for registering MOS opcodes and call handlers for input
 * and output completion (and possible timers) in the table, as well as
 * initiating the first transfers/timers to start the ball rolling.
 * Presumably the individual drivers know best how to handle bringing
 * the net up, down, figuring out how to queue up output packets, etc,
 * so all of that is done in the per-network drivers.
 *
 * Higher level protocol functions generally run in per protocol
 * processes which run at lower priority than the simple packet
 * forwarding.
 *
 * Note that for vanilla local networks, code (and a stylized lower
 * level interface) is available in net.c to do most of the work.
 * The average local net driver with the scheme is only a page or so.
 *
 * The data in the packet more or less must start at an offset of hdrsiz
 * into the packet, so that the data is never shuffled as headers
 * are frobbed; the headers merely grow and shrink above the data.
 * (See the comments in ext.h.)
 *
 * This code is intended to be easily made portable but isn't so
 * now. The main areas of concern have to do with the lack of
 * unsigned bytes in the PDP-11, as well as the problems of word
 * and byte order in words and longwords. There are some macros
 * that are alleged to help with this, but things are a long way
 * from done. Each protocol header file should contain a writeup
 * on the way the packets should look in memory, etc.
 */

/*
 *------------------------------------------------------------------
 *
 * $Source: /u1/jis/gw/cgw/gw/src/RCS/gate.c,v $
 * $Revision: 1.2 $
 * $Date: 87/07/15 01:04:56 $
 * $State: Exp $
 * $Author: jon $
 * $Locker:  $
 *
 * $Log:	gate.c,v $
 * Revision 1.2  87/07/15  01:04:56  jon
 * Registers msgflg with log_register_flag
 * 
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_gate_c = "$Header: gate.c,v 1.2 87/07/15 01:04:56 jon Exp $";
#endif	lint

#include	<types.h>
#include	<sys.h>
#include	"const.h"
#include	"param.h"
#include	"defs.h"
#include	"macs.h"
#include	"net.h"
#include	"ext.h"



/* Externals; amount of free memory left to operating system.
 */
ext	unsw	FRMEM;


/* Error and information messages */

char gwstmsg[] = "\nC Gateway %s started\n\n";
char sysqae[] =	"System %s queue advisory allocation of %d exceeded %d\n";
char ntuplen[] = "Unusable pkt lngth %d on net %d intfc %s/%d\n";
char gwbafl[] =	"Buffers: %d avail %d fairsh %d low %d\n";
char gwbadhd[] = "Bad conf info for net hdr lengths\n";

/* Initialization - do various bits of house-cleaning, clear all
 * state information. Note the delay to make sure that all other
 * per protocol startups have time to run, etc before we turn on
 * tasking. Notice slight layering violation where it knows the
 * structure of a hndlre.
 */

initgw()
{
	reg	unsw	bufpsiz;
		void	netin();
		void	netirstrt();
		void	netout();
		void	netorstrt();
		iorb	*netget();


	msgflg |= (L_PANIC | L_FATAL | L_CHECK);
	log_register_flag ("msg", &msgflg);

	netino = netin;
	netipro = netirstrt;
	netouto = netout;
	netdlyo = netorstrt;

	nprots = 0;

	initmem();
	initnets();

	dolog(gwstmsg, name);

	bufpsiz = ((nbufs * MEMBPN) / MEMBPD);
	bufshr = (bufpsiz / (nnets + nprots));
	iflog(L_INFU)
		dolog(gwbafl, nbufs, nbfree, bufshr, buflow);
}


/* Queue and buffer initialization. This is where most of the buffer
 * allocation decisions are made; the amount of free memory to use for
 * buffers and the individual queue allocations. The way the latter work
 * is that up to a certain fraction of the pool is allowed to be used for
 * packets on system queues, and another fraction is allowed to be used
 * for unquestioned allocation to individual reclaimable queues. Finally,
 * windfall (unused) buffers may be used by any queue as long as a low
 * water mark is respected.
 */

initmem()

{	reg	iorb	*bufp;
	reg	net	*netp;
	reg	unsb	i;
		unsw	memlv;
		unsw	nsysbp;
		unsw	nsysbt;
		unsw	sysbufa;

	printf("\tGW: initmem\n");
	nbfree = 0;
	bfrlst = NULL;
	memlv = ((FRMEM >> MEMUSE) + bufsiz);
	while (FRMEM > memlv) {
		if ((bufp = (iorb *)getmem(bufsiz)) == NULL)
			bughalt("Can't get buff mem\n");
		freebuf(bufp);
		}
	nbufs = nbfree;
	buflow = ((nbufs * MEMLWN) / MEMLWD);

	nsysbp = 0;
	nsysbt = 0;
	for (netp = &nets[0]; netp < &nets[nnets]; netp++) {
		nsysbp += netp->n_maxip;
		nsysbt += netp->n_maxop;
		}
	sysbufa = ((nbufs * MEMPSN) / MEMPSD);
	if (nsysbp > sysbufa)
		iflog(L_INFU)
			dolog(sysqae, "permanent", sysbufa, nsysbp);
	sysbufa = ((nbufs * MEMTSN) / MEMTSD);
	if (nsysbt > sysbufa)
		iflog(L_INFU)
			dolog(sysqae, "transient", sysbufa, nsysbt);
}


/* Network initialization; make entries in the network table,
 * and call network initialization routines.
 */

initnets()

{	reg	net	*netp;
	reg	unsb	i;

	printf("\tGW: initnets\n");
	netp = &nets[0];
	for (i = 0; i < nnets; i++) {
	 	if (netp->n_get == NULL)
			netp->n_get = netget;

		if ((netp->n_hsiz > hdrsiz) || (netp->n_tsiz > tlrsiz))
			bughalt(gwbadhd);
		netp->n_siz = (netp->n_hsiz + netp->n_tsiz);
		netp->n_max = (netp->n_msiz - netp->n_siz);
		if (netp->n_max > pktsiz) {
			iflog(L_INFU)
				dolog(ntuplen, (netp->n_max - pktsiz),
						mksnet(netp));
			netp->n_max = pktsiz;
			}

		netp->n_int = ((netp->n_idevp - netp->n_bdevp) >> 1);
		netp->n_net = i;

		netp->n_flg = 0;
		netp->n_ipc = 0;
		netp->n_opc = 0;
		clrq(&netp->n_ipq);
		clrq(&netp->n_opq);

		if (netp->n_init != NULL)
			(*netp->n_init)(netp);
		  else
			netinit(netp);
		netp++;
		}
}
