/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(CCBPtr); 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(CCBPtr, MATCHPtr); 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(CCBPtr, MATCHPtr); 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( __unused 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; 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_HTON(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_NTOH(f->CID); m.dstCID = UAS_VALUE_NTOH(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((CCB *)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((CCB *)AT_ADSP_STREAMS, &m, (ProcPtr)MatchListener)) == 0) return 1; 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 = UAL_VALUE_NTOH(f->pktNextRecvSeq); n->u.openParams.sendWindow = UAS_VALUE_NTOH(f->pktRecvWdw); n->u.openParams.attnSendSeq = UAL_VALUE_NTOH(op->pktAttnRecvSeq); n->ioResult = 0; completepb(sp, n); /* complete copy of request */ /* complete(n, 0); */ return 0; } /* found CLListen */ p = n; /* down the list we go... */ } /* while */ return 1; } *spPtr = sp; /* Save ptr to stream we just found */ 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 = UAL_VALUE_NTOH(f->pktNextRecvSeq); sp->sendWdwSeq = UAL_VALUE_NTOH(f->pktNextRecvSeq) + UAS_VALUE_NTOH(f->pktRecvWdw) - 1; sp->attnSendSeq = UAL_VALUE_NTOH(op->pktAttnRecvSeq); /* on even boundry */ sp->remCID = UAS_VALUE_NTOH(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 */ } 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; 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_NTOH(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); sp->sendSeq = sp->firstRtmtSeq; sp->pktSendCnt = 0; sp->waitingAck = 0; sp->callSend = 1; } 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); /* 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; }