V8/usr/sys/chncp/chrcv.c
#include "../chunix/chsys.h"
#include "../chunix/chconf.h"
#include "../chaos/chaos.h"
/*
* Receive side - basically driven by an incoming packet.
*/
#define send_los(x,y) sendlos(x,y,sizeof(y) - 1)
/*
* Receive a packet - called from receiver interrupt level.
*/
rcvpkt(pkt)
register struct packet *pkt;
{
register struct connection *conn;
register unsigned index;
debug(DPKT,printf("Rcvpkt: "));
pkt->pk_next = NOPKT;
if (pkt->pk_op == RUTOP)
rcvrut(pkt);
else if (pkt->pk_op >= MAXOP && !ISDATOP(pkt))
ch_free((char *)pkt);
else if (pkt->pk_daddr != Chmyaddr)
if ((++pkt->pk_fc) == 0) {
debug(DPKT|DABNOR,printf("Overforwarded packet\n"));
ignore:
ch_free((char *)pkt);
} else if (pkt->pk_saddr == Chmyaddr) {
debug(DPKT|DABNOR,printf("Got my own packet back\n"));
ch_free((char *)pkt);
} else if (Chmyaddr == -1)
ch_free((char *)pkt);
else {
debug(DPKT,printf("Forwarding pkt daddr=%x\n", pkt->pk_daddr));
sendctl(pkt);
}
else if (pkt->pk_op == MNTOP)
goto ignore;
else if (pkt->pk_op == RFCOP)
rcvrfc(pkt);
/*
* Check for various flavors of bad indexes
*/
else if ((index = pkt->pk_dtindex) >= CHNCONNS ||
((conn = Chconntab[index]) == NOCONN) ||
conn->cn_lidx != pkt->pk_didx) {
debug(DPKT|DABNOR,printf("Packet with bad index: %x, op:%d\n",
pkt->pk_didx, pkt->pk_op));
send_los(pkt, "Connection doesn't exist");
/*
* Handle responses to our RFC
*/
} else if (conn->cn_state == CSRFCSENT)
switch(pkt->pk_op) {
case OPNOP:
debug(DCONN,printf("Conn #%x: OPEN received\n", conn->cn_lidx));
/*
* Make the connection open, taking his index
*/
conn->cn_state = CSOPEN;
conn->cn_fidx = pkt->pk_sidx;
conn->cn_active = Chclock;
/*
* Indicate we have received AND "read" the OPEN
* Read his window size, indicate that he has
* received and acked the RFC, and acknowledge the OPN
*/
conn->cn_rread = conn->cn_rlast = pkt->pk_pkn;
conn->cn_twsize = pkt->pk_rwsize;
receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
/*
* Wakeup the user waiting for the connection to open.
*/
makests(conn, pkt);
reflect(pkt);
NEWSTATE(conn);
break;
case CLSOP:
case ANSOP:
case FWDOP:
debug(DCONN,printf("Conn #%x: CLOSE/ANS received for RFC\n", conn->cn_lidx));
clsconn(conn, CSCLOSED, pkt);
break;
default:
debug(DPKT|DABNOR,
printf("bad packet type for conn #%x in CSRFCSENT: %d\n",
conn->cn_lidx, pkt->pk_op));
send_los(pkt, "Bad packet type reponse to RFC");
}
/*
* Process a packet for an open connection
*/
else if (conn->cn_state == CSOPEN) {
conn->cn_active = Chclock;
if (ISDATOP(pkt))
rcvdata(conn, pkt);
else switch (pkt->pk_op) {
case OPNOP:
/*
* Ignore duplicate opens.
*/
debug(DPKT|DABNOR,printf("Duplicate open received\n"));
ch_free((char *)pkt);
break;
case SNSOP:
debug(DPKT,prpkt(pkt, "SNS"));
receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
makests(conn, pkt);
reflect(pkt);
break;
case EOFOP:
rcvdata(conn, pkt);
break;
case LOSOP:
case CLSOP:
debug(DCONN, printf("Close rcvd on %x\n", conn->cn_lidx));
clsconn(conn, pkt->pk_op == CLSOP ? CSCLOSED : CSLOST,
pkt);
break;
/*
* Uncontrolled data - que it at the head of the rlist
*/
case UNCOP:
if ((pkt->pk_next = conn->cn_rhead) == NOPKT)
conn->cn_rtail = pkt;
conn->cn_rhead = pkt;
if (pkt == conn->cn_rtail)
INPUT(conn);
receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
break;
case STSOP:
debug(DABNOR,prpkt(pkt, "STS"));
debug(DABNOR,printf("Receipt=%d, Trans Window=%d\n",(unsigned)pkt->pk_idata[0], pkt->pk_idata[1]));
if (pkt->pk_rwsize > conn->cn_twsize)
OUTPUT(conn);
conn->cn_twsize = pkt->pk_rwsize;
receipt(conn, pkt->pk_ackn, pkt->pk_receipt);
ch_free((char *)pkt);
/* could check for very recent packets here */
if ((pkt = conn->cn_thead) != NOPKT) {
conn->cn_thead = conn->cn_ttail = NOPKT;
senddata(pkt);
}
break;
default:
debug(DPKT|DABNOR, printf("bad opcode:%d\n", pkt->pk_op));
send_los(pkt, "Bad opcode");
/* should we do clsconn here? */
}
/*
* Connection is neither waiting for an OPEN nor OPEN.
*/
} else {
debug(DPKT|DABNOR,printf("Packet for conn #%x (not open) state=%d, op:%d\n", conn->cn_lidx, conn->cn_state, pkt->pk_op));
send_los(pkt, "Connection is closed");
}
}
/*
* Send a control packet back to its source
*/
reflect(pkt)
register struct packet *pkt;
{
register short temp;
temp = pkt->pk_sidx;
pkt->pk_sidx = pkt->pk_didx;
pkt->pk_didx = temp;
temp = pkt->pk_saddr;
pkt->pk_saddr = pkt->pk_daddr;
pkt->pk_daddr = temp;
sendctl(pkt);
}
/*
* Process a received data packet - or an EOF packet which is mostly treated
* the same.
*/
rcvdata(conn, pkt)
register struct connection *conn;
register struct packet *pkt;
{
register struct packet *npkt;
debug(DPKT,(prpkt(pkt,"DATA"),printf("\n")) );
if (cmp_gt(pkt->pk_pkn, conn->cn_rread + conn->cn_rwsize)) {
debug(DPKT|DABNOR,printf("Discarding data out of window\n"));
ch_free((char *)pkt);
return;
}
receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
if (cmp_le(pkt->pk_pkn, conn->cn_rlast)) {
debug(DPKT,printf("Duplicate data packet\n"));
makests(conn, pkt);
reflect(pkt);
return;
}
/*
* Link the out of order list onto the new packet in case
* it fills the gap between in-order and out-of-order lists
* and to make it easy to grab all now-in-order packets from the
* out-of-order list.
*/
pkt->pk_next = conn->cn_routorder;
/*
* Now transfer all in-order packets to the in-order list
*/
for (npkt = pkt;
npkt != NOPKT &&
npkt->pk_pkn == (unsigned short)(conn->cn_rlast + 1);
npkt = npkt->pk_next) {
if (conn->cn_rhead == NOPKT)
conn->cn_rhead = npkt;
else
conn->cn_rtail->pk_next = npkt;
conn->cn_rtail = npkt;
conn->cn_rlast++;
}
/*
* If we queued up any in-order pkts, check if spontaneous STS is needed
*/
if (pkt != npkt) {
debug(DPKT,printf("new ordered data packet\n"));
conn->cn_rtail->pk_next = NOPKT;
conn->cn_routorder = npkt;
if (conn->cn_rhead == pkt)
INPUT(conn);
/*
* If we are buffering "many" packets, keep him up to date
* about the window. The second test can be flushed if it is
* more important to receipt than to save sending an "extra"
* STS with no new acknowledgement.
* Lets do without it for a while.
if ((short)(conn->cn_rlast - conn->cn_racked) > conn->cn_rsts &&
conn->cn_racked != conn->cn_rread)
sendsts(conn);
*/
/*
* Here we have received an out of order packet which must be
* inserted into the out-of-order queue, in packet number order.
*/
} else {
for (npkt = pkt; (npkt = npkt->pk_next) != NOPKT &&
cmp_gt(pkt->pk_pkn, npkt->pk_pkn); )
pkt->pk_next = npkt; /* save the last pkt here */
if (npkt != NOPKT && pkt->pk_pkn == npkt->pk_pkn) {
debug(DPKT|DABNOR,
printf("Duplicate out of order packet\n"));
pkt->pk_next = NOPKT;
makests(conn, pkt);
reflect(pkt);
} else {
if (npkt == conn->cn_routorder)
conn->cn_routorder = pkt;
else
pkt->pk_next->pk_next = pkt;
pkt->pk_next = npkt;
debug(DPKT|DABNOR,
printf("New out of order packet\n"));
}
}
}
/*
* Make a STS packet for a connection, reflecting its current state
*/
makests(conn, pkt)
register struct connection *conn;
register struct packet *pkt;
{
pkt->pk_op = STSOP;
pkt->pk_lenword = sizeof(struct sts_data);
pkt->pk_ackn = conn->cn_racked = conn->cn_rread;
pkt->pk_receipt = conn->cn_rlast;
pkt->pk_rwsize = conn->cn_rwsize;
}
/*
* Process receipts and acknowledgements using recnum as the receipt.
*/
receipt(conn, acknum, recnum)
register struct connection *conn;
unsigned short acknum, recnum;
{
register struct packet *pkt, *pktl;
/*
* Process a receipt, freeing packets that we now know have been
* received.
*/
if (cmp_gt(recnum, conn->cn_trecvd)) {
for (pktl = conn->cn_thead;
pktl != NOPKT && cmp_le(pktl->pk_pkn, recnum);
pktl = pkt) {
pkt = pktl->pk_next;
ch_free((char *)pktl);
}
if ((conn->cn_thead = pktl) == NOPKT)
conn->cn_ttail = NOPKT;
conn->cn_trecvd = recnum;
}
/*
* If the acknowledgement is new, update our idea of the
* latest acknowledged packet, and wakeup output that might be blocked
* on a full transmit window.
*/
if (cmp_gt(acknum, conn->cn_tacked))
if (cmp_gt(acknum, conn->cn_tlast))
debug(DABNOR, (printf("Invalid acknowledgment(%d,%d)\n",
acknum, conn->cn_tlast)));
else {
conn->cn_tacked = acknum;
OUTPUT(conn);
}
}
/*
* Send a LOS in response to the given packet.
* Don't bother if the packet is itself a LOS or a CLS since the other
* end doesn't care anyway and would only return it again.
* Append the host name to the error message.
*/
sendlos(pkt, str, len)
register struct packet *pkt;
char *str;
{
if (pkt->pk_op == LOSOP || pkt->pk_op == CLSOP)
ch_free((char *)pkt);
else {
register char *cp;
register int length;
for (cp = Chmyname, length = 0; *cp && length < CHSTATNAME;) {
length++;
cp++;
}
if (pkt = pktstr(pkt, str, len + length + sizeof("(from )") - 1)) {
chmove("(from ", &pkt->pk_cdata[len], 6);
chmove(Chmyname, &pkt->pk_cdata[len + 6], length);
chmove(")", &pkt->pk_cdata[len + 6 + length], 1);
pkt->pk_op = LOSOP;
reflect(pkt);
}
}
}
/*
* Process a received RFC
*/
rcvrfc(pkt)
register struct packet *pkt;
{
register struct connection *conn, **conptr;
struct packet **opkt, *pktl;
debug(DPKT,prpkt(pkt,"RFC"));
debug(DPKT,printf("contact = %s\n", pkt->pk_cdata));
/*
* Check if this is a duplicate RFC, and if so throw it away,
* and retransmit the OPEN.
*/
for (conptr = &Chconntab[0]; conptr < &Chconntab[CHNCONNS];)
if ((conn = *conptr++) != NOCONN &&
conn->cn_fidx == pkt->pk_sidx &&
conn->cn_faddr == pkt->pk_saddr) {
if (conn->cn_state == CSOPEN) {
debug(DPKT|DABNOR,
printf("Rcvrfc: Retransmitting open chan #%x\n", (*conptr)->cn_lidx));
sendopn(conn);
} else {
debug(DPKT|DABNOR,
printf("Rcvrfc: Duplicate RFC: %x\n",
conn->cn_lidx));
}
ch_free((char *)pkt);
return;
}
/*
* Scan the listen list for a listener and if one is found
* open the connection and remove the listen packet from the
* listen list.
*/
for (opkt = &Chlsnlist; (pktl = *opkt) != NOPKT; opkt = &pktl->pk_next)
if(concmp(pkt, pktl->pk_cdata, (int)pktl->pk_len)) {
conn = Chconntab[pktl->pk_stindex];
*opkt = pktl->pk_next;
ch_free((char *)pktl);
lsnmatch(pkt, conn);
NEWSTATE(conn);
return;
}
if (concmp(pkt, "STATUS", 6)) {
statusrfc(pkt);
return;
}
if (concmp(pkt, "TIME", 4)) {
timerfc(pkt);
return;
}
if (Chrfcrcv == 0) {
send_los(pkt, "All servers disabled");
return;
}
/*
* There was no listener, so queue the RFC on the unmatched RFC list
* again checking for duplicates.
*/
pkt->pk_ackn = 0; /* this is for ch_rskip, in chuser.c */
pkt->pk_next = NOPKT;
if ((pktl = Chrfclist) == NOPKT)
Chrfclist = Chrfctail = pkt;
else {
do {
if(pktl->pk_sidx == pkt->pk_sidx &&
pktl->pk_saddr == pkt->pk_saddr) {
debug(DPKT/*|DABNOR*/,printf("Rcvrfc: Discarding duplicate Rfc on Chrfclist\n"));
ch_free((char *)pkt);
return;
}
} while ((pktl = pktl->pk_next) != NOPKT);
Chrfctail->pk_next = pkt;
Chrfctail = pkt;
}
debug(DCONN,printf("Rcvrfc: Queued Rfc on Chrfclist: '%c%c%c%c'\n",
pkt->pk_cdata[0], pkt->pk_cdata[1], pkt->pk_cdata[2],
pkt->pk_cdata[3]));
RFCINPUT;
}
/*
* An RFC has matched a listener, either by an RFC coming and finding a match
* on the listener list, or by a listen being done and matching an RFC on the
* unmatched RFC list. So we change the state of the connection to CSRFCRCVD
*/
lsnmatch(rfcpkt, conn)
register struct packet *rfcpkt;
register struct connection *conn;
{
debug(DCONN,printf("Conn #%x: LISTEN matched \n", conn->cn_lidx));
/*
* Initialize the conection
*/
conn->cn_active = Chclock;
conn->cn_state = CSRFCRCVD;
conn->cn_rwsize = CHDRWSIZE;
conn->cn_faddr = rfcpkt->pk_saddr;
conn->cn_fidx = rfcpkt->pk_sidx;
/*
* Queue up the RFC for the user to read if he wants it.
*/
rfcpkt->pk_next = NOPKT;
conn->cn_rhead = conn->cn_rtail = rfcpkt;
/*
* Indicate to the other end that we have received and "read"
* the RFC so that the open will truly acknowledge it.
*/
conn->cn_rlast = conn->cn_rread = rfcpkt->pk_pkn;
}
/*
* Remove a listener from the listener list, due to the listener bailing out.
* Called from top level at high priority
*/
rmlisten(conn)
register struct connection *conn;
{
register struct packet *opkt, *pkt;
opkt = NOPKT;
for (pkt = Chlsnlist; pkt != NOPKT; opkt = pkt, pkt = pkt->pk_next)
if (pkt->pk_stindex == conn->cn_ltidx) {
if(opkt == NOPKT)
Chlsnlist = pkt->pk_next;
else
opkt->pk_next = pkt->pk_next;
ch_free((char *)pkt);
break;
}
}
/*
* Compare the RFC contact name with the listener name.
*/
concmp(rfcpkt, lsnstr, lsnlen)
struct packet *rfcpkt;
register char *lsnstr;
register int lsnlen;
{
register char *rfcstr = rfcpkt->pk_cdata;
register int rfclen;
debug(DPKT,printf("Rcvrfc: Comparing %s and %s\n", rfcstr, lsnstr));
for (rfclen = rfcpkt->pk_len; rfclen; rfclen--, lsnlen--)
if (lsnlen <= 0)
return ((*rfcstr == ' ') ? 1 : 0);
else if (*rfcstr++ != *lsnstr++)
return(0);
return (lsnlen == 0);
}
/*
* Process a routing packet.
*/
rcvrut(pkt)
struct packet *pkt;
{
register int i;
register struct rut_data *rd;
register struct chroute *r;
debug(DPKT, ( prpkt(pkt,"RUT"),printf("\n") ) );
rd = pkt->pk_rutdata;
if (pkt->pk_ssubnet >= CHNSUBNET)
printf("CHAOS: bad subnet %d in RUT pkt\n", pkt->pk_ssubnet);
else if (Chroutetab[pkt->pk_ssubnet].rt_type != CHDIRECT)
printf("CHAOS: RUT pkt from unconnected subnet %d\n",
pkt->pk_ssubnet);
else for (i = pkt->pk_len; i; i -= sizeof(struct rut_data), rd++) {
if (rd->pk_subnet >= CHNSUBNET) {
debug(DABNOR, printf("CHAOS: bad subnet %d in RUT pkt\n",
rd->pk_subnet));
continue;
}
r = &Chroutetab[rd->pk_subnet];
switch (r->rt_type) {
case 0:
case CHBRIDGE:
if (rd->pk_cost <= r->rt_cost) {
r->rt_cost = rd->pk_cost;
r->rt_type = CHBRIDGE;
r->rt_addr = pkt->pk_saddr;
}
break;
case CHDIRECT:
case CHFIXED:
break;
default:
printf("CHAOS: Illegal chaos routing table entry %d",
r->rt_type);
}
}
ch_free((char *)pkt);
}
/*
* process a RFC for contact name STATUS
*/
statusrfc(rpkt)
register struct packet *rpkt;
{
register struct packet *pkt;
register struct chroute *r;
register struct chxcvr *xp;
register struct statdata *sp;
register int i;
for (i = 0, r = Chroutetab; r < &Chroutetab[CHNSUBNET]; r++)
if (r->rt_type == CHDIRECT)
i++;
i *= sizeof(struct stathead) + sizeof(struct statxcvr);
i += CHSTNAME;
if ((pkt = pkalloc(i, 1)) == NOPKT) {
ch_free((char *)rpkt);
return;
}
pkt->pk_daddr = rpkt->pk_saddr;
pkt->pk_didx = rpkt->pk_sidx;
ch_free((char *)rpkt);
pkt->pk_type = 0;
pkt->pk_op = ANSOP;
pkt->pk_next = NOPKT;
pkt->pk_saddr = Chmyaddr;
pkt->pk_sidx = pkt->pk_pkn = pkt->pk_ackn = 0;
pkt->pk_lenword = i;
chmove(Chmyname, pkt->pk_status.sb_name, CHSTATNAME);
sp = &pkt->pk_status.sb_data[0];
for (r = Chroutetab; r < &Chroutetab[CHNSUBNET]; r++)
if (r->rt_type == CHDIRECT) {
xp = r->rt_xcvr;
sp->sb_ident = 0400 + xp->xc_subnet;
sp->sb_nshorts = sizeof(struct statxcvr) / sizeof(short);
sp->sb_xstat = xp->xc_xstat;
#ifdef pdp11
swaplong(&sp->sb_xstat,
sizeof(struct statxcvr) / sizeof(long));
#endif
sp = (struct statdata *)((char *)sp +
sizeof(struct stathead) +
sizeof(struct statxcvr));
}
sendctl(pkt);
}
/*
* process a RFC for contact name TIME
*/
timerfc(pkt)
register struct packet *pkt;
{
long t;
pkt->pk_op = ANSOP;
pkt->pk_next = NOPKT;
pkt->pk_pkn = pkt->pk_ackn = 0;
pkt->pk_lenword = sizeof(long);
ch_time(&t);
pkt->pk_ldata[0] = t;
#ifdef pdp11
swaplong(&pkt->pk_ldata, 1);
#endif pdp11
reflect(pkt);
}
#ifdef DEBUG
prpkt(pkt, str)
register struct packet *pkt;
char *str;
{
printf("op=%s(%o) len=%d fc=%d\ndhost=%o didx=%x\nshost=%o sidx=%x\npkn=%d ackn=%d ",
str, pkt->pk_op, pkt->pk_len, pkt->pk_fc, pkt->pk_dhost,
pkt->pk_didx, pkt->pk_shost, pkt->pk_sidx,
(unsigned)pkt->pk_pkn, (unsigned)pkt->pk_ackn);
}
#endif