Source to bsd/netat/adsp_Packet.c
/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Packet.c
*
* v01.23 All incoming packets come here first 06/21/90 mbs
* Modified for MP, 1996 by Tuyen Nguyen
* Modified, April 9, 1997 by Tuyen Nguyen for MacOSX.
*/
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <machine/spl.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/filedesc.h>
#include <sys/fcntl.h>
#include <sys/mbuf.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/time.h>
#include <net/if.h>
#include <netat/sysglue.h>
#include <netat/appletalk.h>
#include <netat/at_pcb.h>
#include <netat/ddp.h>
#include <netat/at_var.h>
#include <netat/adsp.h>
#include <netat/adsp_internal.h>
extern at_ifaddr_t *ifID_home;
/*
* GleanSession
*
* We just got a packet for this session, glean its address &
* reset probe timer
*
* INPUTS:
* Session
* OUTPUTS:
* none
*/
static void GleanSession(sp) /* (CCBPtr sp) */
CCBPtr sp;
{
if (sp->openState == O_STATE_OPEN) {
/* This is true for both state = sOpen & sClosing */
RemoveTimerElem(&adspGlobal.slowTimers, &sp->ProbeTimer);
InsertTimerElem(&adspGlobal.slowTimers, &sp->ProbeTimer,
sp->probeInterval);
sp->probeCntr = 4;
}
}
/*
* The same code handles incoming Open Connection Request,
* Open Request + Ack, Open Connection Ack, Open Connection Denial
*
* We could be in four different states, LISTEN, OPENWAIT, ESTABLISHED,
* OPEN.
*/
/*
*
* Ok, there are 16 combinations. 8 are do-nothings, 2 have to be
* special cased (Open Deny and Req+Ack on Open session)
*
* Build a table of actions:
* Ignore?
* What to match on (local socket, whole address, DestCID, SrcCID)
* What to send (Ack or Req+Ack)
* Next State (both the ccb state and the open state)
*/
/*
*
*/
typedef struct {
u_char match; /* Characteristics that have to match
* (Bit-Mapped, see below) */
char action; /* What to do if CCB matches */
char send; /* What to send in response
* (Bit mapped, same as sendCtl field of
* CCB) */
char openState; /* Next Open state */
char state; /* Next ccb state. */
char pad; /* Too bad we need this to make structure
* even size */
} TBL, *TBLPtr;
#define M_LSOC 0x01 /* bit 0 - Match on local socket */
#define M_ADDR 0x02 /* bit 1 - Match on whole address */
#define M_DCID 0x04 /* bit 2 - Match on DestCID */
#define M_SCID 0x08 /* bit 3 - Match SrcCID */
#define M_DCIDZERO 0x10 /* bit 4 - Dest CID must be 0 */
#define M_SCIDZERO 0x20 /* bit 5 - Src CID must be 0 */
#define M_FILTER 0x40 /* bit 6 - Match address filter */
#define M_IGNORE 0x80 /* bit 7 - Ignore */
#define A_COMPLETE 0x01 /* Complete open parameter block */
#define A_SAVEPARMS 0x02 /* Save connection parameters */
#define A_OREQACKOPEN 0x04 /* special case for open Req+Ack on
* OPEN session */
#define A_GLEAN 0x08 /* We'll be talking back to this guy */
#define A_DENY 0x10 /* We've been denied! */
/*
* So here's our table
*/
static TBL tbl[16] = {
/*
* For Open Request ($81)
*
* LISTENING
* Match on destination socket
* Match on address filter
* Dest CID must be 0
* Glean connection
* Save Open Connection parameters
* Send OREQACK
* Change state to ESTABLISHED
*/
{ M_LSOC + M_DCIDZERO + M_FILTER,
A_SAVEPARMS + A_GLEAN,
B_CTL_OREQACK,
O_STATE_ESTABLISHED,
sOpening,
0
},
/*
*
* OPENWAIT
* Match on Remote Address & destination socket
* Dest CID must be 0
* Save Open Connection parameters
* Send Ack
* Change state to ESTABLISHED
*/
{ M_LSOC + M_ADDR + M_DCIDZERO,
A_SAVEPARMS + A_GLEAN,
B_CTL_OACK,
O_STATE_ESTABLISHED,
sOpening,
0
},
/*
*
* ESTABLISHED
* Match on Remote Address & SrcCID
* Dest CID must be 0
* Send Req + Ack
*/
{ M_ADDR + M_SCID + M_DCIDZERO,
A_GLEAN,
B_CTL_OACK,
O_STATE_ESTABLISHED,
sOpening,
0
},
/*
* OPEN
* Ignore
*/
{ M_IGNORE,
0,
0,
0,
0,
0
},
/*
*
* For Open Ack ($82)
*
* LISTENING
* Ignore
*/
{ M_IGNORE,
0,
0,
0,
0,
0
},
/*
*
* OPENWAIT
* Ignore
*/
{ M_IGNORE,
0,
0,
0,
0,
0
},
/*
*
* ESTABLISHED
* Match on SrcCID & DestCID & Address & Local Socket
* Complete Listen or Connect PB
* OPEN
*/
{ M_ADDR + M_DCID + M_SCID + M_LSOC,
A_COMPLETE + A_GLEAN,
0,
O_STATE_OPEN,
sOpen,
0
},
/*
*
* OPEN
* Ignore
*/
{ M_IGNORE,
0,
0,
0,
0,
0
},
/*
*
* For Open Request + Ack ($83)
*
* LISTENING
* Ignore
*/
{ M_IGNORE,
0,
0,
0,
0,
0
},
/*
*
* OPENWAIT
* Match on DestCID & socket
* Do not test remote address -- our open req could have
* been passed to another address by a connection server
* Save Open Connection parameters
* Complete Connect parameter block
* Send Ack
* OPEN
*/
{ M_DCID + M_LSOC,
A_COMPLETE + A_SAVEPARMS + A_GLEAN,
B_CTL_OACK,
O_STATE_OPEN,
sOpen,
0
},
/*
*
* ESTABLISHED
* Ignore
*/
{ M_IGNORE,
0,
0,
0,
0,
0
},
/*
*
* OPEN
* Match on Remote Address & SrcCID & DestCID & Local Socket
* If we've never gotten any data
* Send Ack & Retransmit
*/
{ M_ADDR + M_DCID + M_SCID + M_LSOC,
A_OREQACKOPEN + A_GLEAN,
B_CTL_OACK,
O_STATE_OPEN,
sOpen,
0
},
/*
*
*
* For Open Deny ($84)
*
* LISTENING
* Ignore
*/
{ M_IGNORE,
0,
0,
0,
0,
0
},
/*
*
* OPENWAIT
* Match on DestCID & Address
* Source CID must be 0
* Complete with error
*/
{ M_SCIDZERO + M_DCID + M_ADDR,
A_DENY,
0,
O_STATE_NOTHING,
sClosed,
0
},
/*
*
* ESTABLISHED
* Ignore
*/
{ M_IGNORE,
0,
0,
0,
0,
0
}, /* %%% No we probably don't want to ignore in this case */
/*
*
* OPEN
* Ignore
*/
{ M_IGNORE,
0,
0,
0,
0,
0
}
};
extern at_ifaddr_t *ifID_table[];
/*
* Used to search down queue of sessions for a session waiting for an
* open request.
*/
typedef struct {
AddrUnion addr;
word dstCID;
word srcCID;
byte socket;
byte descriptor;
byte idx; /* Index into state tables */
TBLPtr t; /* Ptr to entry in table above */
} MATCH, *MATCHPtr;
/*
* MatchStream
*
* Called by Rx connection to find which stream (if any) should get this open
* request/ack/req+ack/deny packet.
*
*/
static boolean
MatchStream(sp, m) /* (CCBPtr sp, MATCHPtr m) */
CCBPtr sp;
MATCHPtr m;
{
unsigned char match;
struct adspcmd *opb;
if (sp->openState < O_STATE_LISTEN ||
sp->openState > O_STATE_OPEN)
return 0;
m->t = &tbl[sp->openState - O_STATE_LISTEN + m->idx];
match = m->t->match; /* Get match criteria */
if (match & M_IGNORE) /* Ignore this combination */
return 0;
if (match & M_LSOC) { /* Match on Local socket */
if (sp->localSocket != m->socket)
return 0;
}
if (match & M_ADDR) { /* Match on Address */
AddrUnion addr;
addr = m->addr; /* Make local copy for efficiency */
if (sp->remoteAddress.a.node != addr.a.node)
return 0;
if (sp->remoteAddress.a.socket != addr.a.socket)
return 0;
if (sp->remoteAddress.a.net && addr.a.net &&
(sp->remoteAddress.a.net != addr.a.net))
return 0;
/*
* Handle special case to reject self-sent open request
*/
if ((m->srcCID == sp->locCID) &&
(addr.a.node == ifID_home->ifThisNode.s_node) &&
/* *** was (addr.a.node == ddpcfg.node_addr.node) && *** */
((addr.a.net == 0) ||
(ifID_home->ifThisNode.s_net == 0) ||
(ifID_home->ifThisNode.s_net == addr.a.net)) )
/* *** was
(NET_VALUE(ddpcfg.node_addr.net) == 0) ||
(NET_VALUE(ddpcfg.node_addr.net) == NET_VALUE(addr.a.net))) )
*** */
/* CID's match, and */
/* If nodeID matches, and */
/* network matches, */
return 0; /* then came from us! */
}
if (match & M_DCID) { /* Match on DestCID */
if (sp->locCID != m->dstCID)
return 0;
}
if (match & M_SCID) { /* Match on SourceCID */
if (sp->remCID != m->srcCID)
return 0;
}
if (match & M_DCIDZERO) { /* Destination CID must be 0 */
if (m->dstCID != 0)
return 0;
}
if (match & M_SCIDZERO) /* Source CID must be 0 */
{
if (m->srcCID != 0)
return 0;
}
if (match & M_FILTER) { /* Check address filter? */
if ((opb = sp->opb)) /* There should be a param block... */
{
AddrUnion addr;
addr = m->addr; /* Make local copy for efficiency */
if ((opb->u.openParams.filterAddress.net &&
addr.a.net &&
opb->u.openParams.filterAddress.net != addr.a.net) ||
(opb->u.openParams.filterAddress.node != 0 &&
opb->u.openParams.filterAddress.node != addr.a.node)||
(opb->u.openParams.filterAddress.socket != 0 &&
opb->u.openParams.filterAddress.socket != addr.a.socket))
return 0;
}
}
return 1;
}
/*
* MatchListener
*
* Called by rx connection to see which connection listener (if any) should
* get this incoming open connection request.
*
*/
static boolean MatchListener(sp, m) /* (CCBPtr sp, MATCHPtr m) */
CCBPtr sp;
MATCHPtr m;
{
if ((sp->state == (word)sListening) && /* This CCB is a listener */
(sp->localSocket == m->socket)) /* on the right socket */
return 1;
return 0;
}
/*
* RXConnection
*
* We just received one of the 4 Open Connection packets
* Interrupts are masked OFF at this point
*
* INPUTS:
* spPtr Place to put ptr to stream (if we found one -- not
* for listeners)
* f Pointer to ADSP header for packet, data follows behind it
* len # of byte in ADSP header + data
* addr Who sent the packet
* dsoc Where they sent it to
*
* OUTPUTS:
* Returns 1 if packet was ignored
*/
static int RXConnection(gref, spPtr, f, len, addr, dsoc)
/* (CCBPtr *spPtr, ADSP_FRAMEPtr f, word len, AddrUnion addr, byte dsoc) */
gref_t *gref; /* READ queue */
CCBPtr *spPtr;
ADSP_FRAMEPtr f;
int len;
AddrUnion addr;
unsigned char dsoc;
{
CCBPtr sp;
ADSP_OPEN_DATAPtr op;
struct adspcmd *pb;
MATCH m;
gbuf_t *mp;
ADSP_FRAMEPtr adspp;
ADSP_OPEN_DATAPtr adspop;
int s;
op = (ADSP_OPEN_DATAPtr)&f->data[0]; /* Point to Open-Connection parms */
len -= ADSP_FRAME_LEN;
if (len < (sizeof(ADSP_OPEN_DATA))) /* Packet too small */
return 1;
if (UAS_VALUE(op->version) != netw(0x0100)) { /* Check version num (on even-byte) */
/*
* The open request has been denied. Try to send him a denial.
*/
mp = gbuf_alloc(AT_WR_OFFSET + DDPL_FRAME_LEN + ADSP_FRAME_LEN + ADSP_OPEN_FRAME_LEN,
PRI_LO);
gbuf_rinc(mp,AT_WR_OFFSET);
gbuf_wset(mp,DDPL_FRAME_LEN);
adspp = (ADSP_FRAMEPtr)gbuf_wptr(mp);
gbuf_winc(mp,ADSP_FRAME_LEN);
bzero((caddr_t) gbuf_rptr(mp),DDPL_FRAME_LEN + ADSP_FRAME_LEN +
ADSP_OPEN_FRAME_LEN);
adspp->descriptor = ADSP_CONTROL_BIT | ADSP_CTL_ODENY;
adspop = (ADSP_OPEN_DATAPtr)gbuf_wptr(mp);
gbuf_winc(mp,ADSP_OPEN_FRAME_LEN);
UAS_UAS(adspop->dstCID, f->CID);
UAS_ASSIGN(adspop->version, 0x100);
adsp_sendddp(0, mp, DDPL_FRAME_LEN + ADSP_FRAME_LEN +
ADSP_OPEN_FRAME_LEN, &addr, DDP_ADSP);
return 0;
}
m.addr = addr;
m.socket = dsoc;
m.descriptor = f->descriptor;
m.srcCID = UAS_VALUE(f->CID);
m.dstCID = UAS_VALUE(op->dstCID); /* On even-byte boundry */
m.idx = ((f->descriptor & ADSP_CONTROL_MASK) - 1) * 4;
/*
* See if we can find a stream that knows what to do with this packet
*/
if ((sp = (CCBPtr)qfind_m(AT_ADSP_STREAMS, &m, (ProcPtr)MatchStream)) == 0)
{
struct adspcmd *p;
struct adspcmd *n;
/*
* No match, so look for connection listeners if this is an
* open request
*/
if ((f->descriptor & ADSP_CONTROL_MASK) != (byte)ADSP_CTL_OREQ)
return 1;
if ((sp = (CCBPtr)qfind_m(AT_ADSP_STREAMS, &m,
(ProcPtr)MatchListener)) == 0)
return 1;
ATDISABLE(s, sp->lock);
p = (struct adspcmd *)&sp->opb;
while (n = (struct adspcmd *)p->qLink) /* Hunt down list of listens */
{
/* Check address filter */
if (((n->u.openParams.filterAddress.net == 0) ||
(addr.a.net == 0) ||
(n->u.openParams.filterAddress.net == addr.a.net)) &&
((n->u.openParams.filterAddress.node == 0) ||
(n->u.openParams.filterAddress.node == addr.a.node)) &&
((n->u.openParams.filterAddress.socket == 0) ||
(n->u.openParams.filterAddress.socket == addr.a.socket))) {
p->qLink = n->qLink; /* Unlink this param block */
n->u.openParams.remoteCID = m.srcCID;
*((AddrUnionPtr)&n->u.openParams.remoteAddress) = addr;
n->u.openParams.sendSeq = netdw(UAL_VALUE(f->pktNextRecvSeq));
n->u.openParams.sendWindow = netw(UAS_VALUE(f->pktRecvWdw));
n->u.openParams.attnSendSeq = netdw(UAL_VALUE(op->pktAttnRecvSeq));
n->ioResult = 0;
ATENABLE(s, sp->lock);
completepb(sp, n); /* complete copy of request */
/* complete(n, 0); */
return 0;
} /* found CLListen */
p = n; /* down the list we go... */
} /* while */
ATENABLE(s, sp->lock);
return 1;
}
*spPtr = sp; /* Save ptr to stream we just found */
ATDISABLE(s, sp->lock);
sp->openState = m.t->openState; /* Move to next state (may be same) */
sp->state = m.t->state; /* Move to next state (may be same) */
if (m.t->action & A_SAVEPARMS) { /* Need to Save open-conn parms */
sp->firstRtmtSeq = sp->sendSeq = netdw(UAL_VALUE(f->pktNextRecvSeq));
sp->sendWdwSeq = netdw(UAL_VALUE(f->pktNextRecvSeq)) + netw(UAS_VALUE(f->pktRecvWdw)) - 1;
sp->attnSendSeq = netdw(UAL_VALUE(op->pktAttnRecvSeq)); /* on even boundry */
sp->remCID = UAS_VALUE(f->CID); /* Save Source CID as RemCID */
UAS_UAS(sp->of.dstCID, f->CID); /* Save CID in open ctl packet */
sp->remoteAddress = addr; /* Save his address */
}
ATENABLE(s, sp->lock);
if (m.t->action & A_DENY) { /* We've been denied ! */
DoClose(sp, errOpenDenied, -1);
}
if (m.t->action & A_OREQACKOPEN) {
/* Special case for OREQACK */
/* on an open session */
RemoveTimerElem(&adspGlobal.fastTimers, &sp->RetryTimer);
sp->sendSeq = sp->firstRtmtSeq;
sp->pktSendCnt = 0;
sp->waitingAck = 0;
sp->callSend = 1;
}
if (m.t->send) { /* Need to send a response */
sp->sendCtl |= m.t->send;
sp->callSend = 1;
}
if (m.t->action & A_COMPLETE) { /* Need to complete open param blk */
RemoveTimerElem(&adspGlobal.slowTimers, &sp->ProbeTimer);
if (pb = sp->opb) {
sp->opb = 0;
pb->u.openParams.localCID = sp->locCID;
pb->u.openParams.remoteCID = sp->remCID;
pb->u.openParams.remoteAddress =
*((at_inet_t *)&sp->remoteAddress);
pb->u.openParams.sendSeq = sp->sendSeq;
pb->u.openParams.sendWindow = sp->sendWdwSeq - sp->sendSeq;
pb->u.openParams.attnSendSeq = sp->attnSendSeq;
pb->ioResult = 0;
completepb(sp, pb); /* complete(pb, 0); */
return 0;
}
/* Start probe timer */
InsertTimerElem(&adspGlobal.slowTimers, &sp->ProbeTimer,
sp->probeInterval);
}
return 0;
}
/*
* ADSPPacket
*
* When a packet is received by the protocol stack with DDP type equal
* to ADSP, then execution comes here
*
* DS is set to ATALK's DGROUP
*
* This routine, or one of its children MUST call glean packet
*
* INPUTS:
* Pointer to DDP header
* OUTPUTS:
* none
*
* Note that the incoming message block (mp) is usually discarded, either
* by the "ignored" path, or via the "checksend" path. The only case
* where the message is NOT freed is via the RxData case in the
* non control packet switch. I zero mp after the RxData case succeeds
* so that mp will not be freed.
*/
int adspPacket(gref, mp)
/* (bytePtr data, word len, AddrUnion a, byte dsoc) */
gref_t *gref;
gbuf_t *mp;
{
unsigned char *bp;
int len;
AddrUnion a;
int dsoc;
int s;
register DDPX_FRAME *ddp; /* DDP frame pointer */
register ADSP_FRAMEPtr f; /* Frame */
CCBPtr sp;
sp = 0; /* No stream */
bp = (unsigned char *)gbuf_rptr(mp);
ddp = (DDPX_FRAME *)bp;
if (ddp->ddpx_type != DDP_ADSP)
return -1;
f = (ADSP_FRAMEPtr)(bp + DDPL_FRAME_LEN);
len = UAS_VALUE(ddp->ddpx_length) & 0x3ff; /* (ten bits of length) */
len -= DDPL_FRAME_LEN;
if (len < (sizeof(ADSP_FRAME) - 1)) /* Packet too small */
return -1; /* mark the failure */
a.a.net = NET_VALUE(ddp->ddpx_snet);
a.a.node = ddp->ddpx_snode;
a.a.socket = ddp->ddpx_source;
dsoc = ddp->ddpx_dest;
if (sp = (CCBPtr)FindSender(f, a))
GleanSession(sp);
if (f->descriptor & ADSP_ATTENTION_BIT) { /* ATTN packet */
if (sp && RXAttention(sp, mp, f, len))
goto ignore;
else
mp = 0; /* attention data is being held */
} /* ATTENTION BIT */
else if (f->descriptor & ADSP_CONTROL_BIT) { /* Control packet */
switch (f->descriptor & ADSP_CONTROL_MASK) {
case ADSP_CTL_PROBE: /* Probe or acknowledgement */
if (sp)
CheckRecvSeq(sp, f);
break;
case ADSP_CTL_OREQ: /* Open Connection Request */
case ADSP_CTL_OREQACK: /* Open Request and acknowledgement */
case ADSP_CTL_OACK: /* Open Request acknowledgment */
case ADSP_CTL_ODENY: /* Open Request denial */
if (RXConnection(gref, &sp, f, len, a, dsoc))
goto ignore;
break;
case ADSP_CTL_CLOSE: /* Close connection advice */
if (sp) {
/* This pkt may also ack some data we sent */
CheckRecvSeq(sp, f);
RxClose(sp);
sp = 0;
} else
goto ignore;
break;
case ADSP_CTL_FRESET: /* Forward Reset */
/* May I rot in hell for the code below... */
if (sp && (CheckRecvSeq(sp, f), RXFReset(sp, f)))
goto ignore;
break;
case ADSP_CTL_FRESET_ACK: /* Forward Reset Acknowledgement */
if (sp && (CheckRecvSeq(sp, f), RXFResetAck(sp, f)))
goto ignore;
break;
case ADSP_CTL_RETRANSMIT: /* Retransmit advice */
if (sp) {
/* This pkt may also ack some data we sent */
CheckRecvSeq(sp, f);
RemoveTimerElem(&adspGlobal.fastTimers, &sp->RetryTimer);
ATDISABLE(s, sp->lock);
sp->sendSeq = sp->firstRtmtSeq;
sp->pktSendCnt = 0;
sp->waitingAck = 0;
sp->callSend = 1;
ATENABLE(s, sp->lock);
} else
goto ignore;
break;
default:
goto ignore;
} /* switch */
} /* Control packet */
else { /* Data Packet */
if ((sp == 0) || RXData(sp, mp, f, len))
goto ignore;
else
mp = 0; /* RXData used up the data, DONT free it! */
} /* Data Packet */
if (mp)
gbuf_freem(mp);
checksend: /* incoming data was not ignored */
if (sp && sp->callSend) /* If we have a stream & we need to send */
CheckSend(sp);
return 0;
ignore:
gbuf_freem(mp);
return 0;
}