#

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


/* This file contains the CHAOS packet forwarder.
 */


#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"


/* Macros */

#define chfrme()	{ if (gethq(&chlpq) == NULL) \
			    addtsk(syshnd, CHPPRI, chxmux, 0); \
			  addq(&chlpq, iob); \
			  return; }

#define cherr(s)	{ error_msg = s; goto error; }


/* Error messages */
static char chpdsc[] = "CHAOS Pkt %o-%o frm %o/%o-%o to %o/%o-%o dsc rsn %d\n";
static char chpdscm[] = "CHAOS Pkt %o-%o frm %o/%o-%o to %o/%o-%o dsc %s\n";

/* Add buffer to Chaos packet queue.
 */
chaddq(iob)
reg iorb *iob;
{
    void chxfwd();

    if ((numq(&chq) >= bufshr) && (nbfree <= buflow)) {
	reg ochpkt *pkt;

	pkt = mkoch(iob->i_addr);
	niflog(L_INFC)
	  dolog(chpdscm, pkt->oc_pkt, pkt->oc_ack,
		mkscha(&pkt->oc_src), pkt->oc_sind,
		mkscha(&pkt->oc_dst), pkt->oc_dind, "OVFL");
	freebuf(iob);
    }
    if (gethq(&chq) == NULL)
      addtsk(syshnd, CHPPRI, chxfwd, 0);
    addq(&chq, iob);
}

/* Chaos packet handler; loops over waiting packet list while nothing
 * more important is happening.
 */
chxfwd()
{
    while (tstneq(&chq))
      chfwd(rmq(&chq));
}

/* Old style chaos forwarder.
 */
chfwd(iob)
iorb	*iob;
{
    reg	ochpkt	*pkt;
    reg	net	*netp;
    reg	unsb	snet;
    chaddr	hst;
    chia	*iap;
    swrd	code;
    char	*error_msg;
    void	netsend();
    void	chxmux();
    
    pkt = mkoch(iob->i_addr);
    
    if (pkt->oc_pad != 0)
      cherr("not chaos pkt");
    if (++pkt->oc_fc == 0)
      cherr("fc ran out");
    if ((pkt->oc_len + sizeof(ochpkt)) > iob->i_bxfr)
      cherr("truncated CHAOS packet");
    
    if (pkt->oc_dst.oc_word == 0)
      chfrme();
    
    if ((snet = pkt->oc_dst.oc_addr.oc_snt) == 0)
      cherr("CHAOS pkt for subnet 0");
    if (snet >= CHMSNET)
      cherr("table ovflo");
    
    if ((netp = chitab[snet]) == NULL)
      cherr("cgtfh");
    if (chttab[snet] != T_DIR)
      hst.oc_word = chgatab[snet].oc_word;
    else {
	hst.oc_word = pkt->oc_dst.oc_word;
	
	iap = chiatlst[netp->n_net];
	while (iap != NULL) {
	    if (pkt->oc_dst.oc_word == mkcha(iap->chia_addr)->oc_word) {
		chfrme();
	    }
	    iap = iap->chia_link;
	}
    }
    
    iob->i_breq = (pkt->oc_len + sizeof(ochpkt));
    if (iob->i_breq > netp->n_max)
      cherr("CHAOS pkt too big");
    
    if (code = (*netp->n_send)(iob, netp, P_CH, &hst) != D_OK) {
	niflog(L_ERRC)
	  dolog(chpdsc, pkt->oc_pkt, pkt->oc_ack,
		mkscha(&pkt->oc_src), pkt->oc_sind,
		mkscha(&pkt->oc_dst), pkt->oc_dind, code);
	freebuf(iob);
    }
    return;

error:
    niflog(L_ERRU)
      dolog(chpdscm, pkt->oc_pkt, pkt->oc_ack,
	    mkscha(&pkt->oc_src), pkt->oc_sind,
	    mkscha(&pkt->oc_dst), pkt->oc_dind, error_msg);
    freebuf(iob);
    return;
}
