summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/ip/iprcv.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/tdi/tcpip/ip/iprcv.c')
-rw-r--r--private/ntos/tdi/tcpip/ip/iprcv.c1145
1 files changed, 1145 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/ip/iprcv.c b/private/ntos/tdi/tcpip/ip/iprcv.c
new file mode 100644
index 000000000..791bc58f6
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/iprcv.c
@@ -0,0 +1,1145 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** iprcv.c - IP receive routines.
+//
+// This module contains all receive related IP routines.
+//
+
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "info.h"
+#include "iproute.h"
+#include "ipfilter.h"
+
+extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong);
+
+extern uchar RATimeout;
+extern NDIS_HANDLE BufferPool;
+#if 0
+EXTERNAL_LOCK(PILock)
+#endif
+extern ProtInfo IPProtInfo[]; // Protocol information table.
+extern ProtInfo *LastPI; // Last protinfo structure looked at.
+extern int NextPI; // Next PI field to be used.
+extern ProtInfo *RawPI; // Raw IP protinfo
+extern NetTableEntry *NetTableList; // Pointer to the net table.
+
+DEBUGSTRING(RcvFile, "iprcv.c");
+
+#ifdef CHICAGO
+extern void RefillReasmMem(void);
+#endif
+
+//* FindUserRcv - Find the receive handler to be called for a particular protocol.
+//
+// This functions takes as input a protocol value, and returns a pointer to
+// the receive routine for that protocol.
+//
+// Input: NTE - Pointer to NetTableEntry to be searched
+// Protocol - Protocol to be searched for.
+// UContext - Place to returns UL Context value.
+//
+// Returns: Pointer to the receive routine.
+//
+ULRcvProc
+FindUserRcv(uchar Protocol)
+{
+ ULRcvProc RcvProc;
+ int i;
+#if 0
+ CTELockHandle Handle;
+
+
+ CTEGetLock(&PILock, &Handle);
+#endif
+
+ if (LastPI->pi_protocol == Protocol) {
+ RcvProc = LastPI->pi_rcv;
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ return RcvProc;
+ }
+
+ RcvProc = (ULRcvProc)NULL;
+ for ( i = 0; i < NextPI; i++) {
+ if (IPProtInfo[i].pi_protocol == Protocol) {
+ LastPI = &IPProtInfo[i];
+ RcvProc = IPProtInfo[i].pi_rcv;
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ return RcvProc;
+ }
+ }
+
+ //
+ // Didn't find a match. Use the raw protocol if it is registered.
+ //
+ if (RawPI != NULL) {
+ RcvProc = RawPI->pi_rcv;
+ }
+
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ return RcvProc;
+
+}
+
+//* IPRcvComplete - Handle a receive complete.
+//
+// Called by the lower layer when receives are temporarily done.
+//
+// Entry: Nothing.
+//
+// Returns: Nothing.
+//
+void
+IPRcvComplete(void)
+{
+ void (*ULRcvCmpltProc)(void);
+ int i;
+#if 0
+ CTELockHandle Handle;
+
+
+ CTEGetLock(&PILock, &Handle);
+#endif
+ for (i = 0; i < NextPI; i++) {
+ if ((ULRcvCmpltProc = IPProtInfo[i].pi_rcvcmplt) != NULL) {
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ (*ULRcvCmpltProc)();
+#if 0
+ CTEGetLock(&PILock, &Handle);
+#endif
+ }
+ }
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+
+}
+//* FindRH - Look up a reassembly header on an NTE.
+//
+// A utility function to look up a reassembly header. We assume the lock on the NTE
+// is taken when we are called. If we find a matching RH we'll take the lock on it.
+// We also return the predeccessor of the RH, for use in insertion or deletion.
+//
+// Input: PrevRH - Place to return pointer to previous RH
+// NTE - NTE to be searched.
+// Dest - Destination IP address
+// Src - Src IP address
+// ID - ID of RH
+// Protocol - Protocol of RH
+//
+// Returns: Pointer to RH, or NULL if none.
+//
+ReassemblyHeader *
+FindRH(ReassemblyHeader **PrevRH, NetTableEntry *NTE, IPAddr Dest, IPAddr Src, ushort Id,
+ uchar Protocol)
+{
+ ReassemblyHeader *TempPrev, *Current;
+
+ TempPrev = STRUCT_OF(ReassemblyHeader, &NTE->nte_ralist, rh_next);
+ Current = NTE->nte_ralist;
+ while (Current != (ReassemblyHeader *)NULL) {
+ if (Current->rh_dest == Dest && Current->rh_src == Src && Current->rh_id == Id &&
+ Current->rh_protocol == Protocol)
+ break;
+ TempPrev = Current;
+ Current = Current->rh_next;
+ }
+
+ *PrevRH = TempPrev;
+ return Current;
+
+}
+
+//* ParseRcvdOptions - Validate incoming options.
+//
+// Called during reception handling to validate incoming options. We make sure that everything
+// is OK as best we can, and find indices for any source route option.
+//
+// Input: OptInfo - Pointer to option info. structure.
+// Index - Pointer to optindex struct to be filled in.
+//
+//
+// Returns: Index of error if any, MAX_OPT_SIZE if no errors.
+//
+uchar
+ParseRcvdOptions(IPOptInfo *OptInfo, OptIndex *Index)
+{
+ uint i= 0; // Index variable.
+ uchar *Options = OptInfo->ioi_options;
+ uint OptLength = (uint)OptInfo->ioi_optlength;
+ uchar Length; // Length of option.
+ uchar Pointer; // Pointer field, for options that use it.
+
+ while(i < OptLength && *Options != IP_OPT_EOL) {
+ if (*Options == IP_OPT_NOP) {
+ i++;
+ Options++;
+ continue;
+ }
+ if (((Length = Options[IP_OPT_LENGTH]) + i) > OptLength) {
+ return (uchar)i + (uchar)IP_OPT_LENGTH; // Length exceeds options length.
+ }
+
+ Pointer = Options[IP_OPT_DATA] - 1;
+
+ if (*Options == IP_OPT_TS) {
+ if (Length < (MIN_TS_PTR - 1))
+ return (uchar)i + (uchar)IP_OPT_LENGTH;
+ Index->oi_tsindex = (uchar)i;
+ } else {
+ if (Length < (MIN_RT_PTR - 1))
+ return (uchar)i + (uchar)IP_OPT_LENGTH;
+
+ if (*Options == IP_OPT_LSRR || *Options == IP_OPT_SSRR) {
+ // A source route option
+ if (Pointer < Length) { // Route not complete
+
+ if ((Length - Pointer) < sizeof(IPAddr))
+ return (uchar)i + (uchar)IP_OPT_LENGTH;
+
+ Index->oi_srtype = *Options;
+ Index->oi_srindex = (uchar)i;
+ }
+ } else {
+ if (*Options == IP_OPT_RR) {
+ if (Pointer < Length)
+ Index->oi_rrindex = (uchar)i;
+ }
+ }
+ }
+
+ i += Length;
+ Options += Length;
+ }
+
+ return MAX_OPT_SIZE;
+}
+
+//* BCastRcv - Receive a broadcast or multicast packet.
+//
+// Called when we have to receive a broadcast packet. We loop through the NTE table,
+// calling the upper layer receive protocol for each net which matches the receive I/F
+// and for which the destination address is a broadcast.
+//
+// Input: RcvProc - The receive procedure to be called.
+// SrcNTE - NTE on which the packet was originally received.
+// DestAddr - Destination address.
+// SrcAddr - Source address of packet.
+// Data - Pointer to received data.
+// DataLength - Size in bytes of data
+// Protocol - Upper layer protocol being called.
+// OptInfo - Pointer to received IP option info.
+//
+// Returns: Nothing.
+//
+void
+BCastRcv(ULRcvProc RcvProc, NetTableEntry *SrcNTE, IPAddr DestAddr,
+ IPAddr SrcAddr, IPHeader UNALIGNED *Header, uint HeaderLength,
+ IPRcvBuf *Data, uint DataLength, uchar Protocol, IPOptInfo *OptInfo)
+{
+ NetTableEntry *CurrentNTE;
+ const Interface *SrcIF = SrcNTE->nte_if;
+ ulong Delivered = 0;
+
+
+ for (CurrentNTE = NetTableList;
+ CurrentNTE != NULL;
+ CurrentNTE = CurrentNTE->nte_next)
+ {
+ if ((CurrentNTE->nte_flags & NTE_ACTIVE) &&
+ (CurrentNTE->nte_if == SrcIF) &&
+ IS_BCAST_DEST(IsBCastOnNTE(DestAddr, CurrentNTE)))
+ {
+ Delivered = 1;
+
+ (*RcvProc)(CurrentNTE, DestAddr, SrcAddr, CurrentNTE->nte_addr,
+ SrcNTE->nte_addr, Header, HeaderLength, Data, DataLength,
+ TRUE, Protocol, OptInfo);
+ }
+ }
+
+ if (Delivered) {
+ IPSInfo.ipsi_indelivers++;
+ }
+}
+
+//* DeliverToUser - Deliver data to a user protocol.
+//
+// This procedure is called when we have determined that an incoming packet belongs
+// here, and any options have been processed. We accept it for upper layer processing,
+// which means looking up the receive procedure and calling it, or passing it to BCastRcv
+// if neccessary.
+//
+// Input: SrcNTE - Pointer to NTE on which packet arrived.
+// DestNTE - Pointer to NTE that is accepting packet.
+// Header - Pointer to IP header of packet.
+// HeaderLength - Length of Header in bytes.
+// Data - Pointer to IPRcvBuf chain.
+// DataLength - Length in bytes of upper layer data.
+// OptInfo - Pointer to Option information for this receive.
+// DestType - Type of destination - LOCAL, BCAST.
+//
+// Returns: Nothing.
+void
+DeliverToUser(NetTableEntry *SrcNTE, NetTableEntry *DestNTE,
+ IPHeader UNALIGNED *Header, uint HeaderLength, IPRcvBuf *Data,
+ uint DataLength, IPOptInfo *OptInfo, uchar DestType)
+{
+ ULRcvProc rcv;
+
+#ifdef DEBUG
+ if (DestType >= DEST_REMOTE)
+ DEBUGCHK;
+#endif
+
+ // Process this request right now. Look up the protocol. If we
+ // find it, copy the data if we need to, and call the protocol's
+ // receive handler. If we don't find it, send an ICMP
+ // 'protocol unreachable' message.
+ rcv = FindUserRcv(Header->iph_protocol);
+ if (rcv != NULL) {
+ IP_STATUS Status;
+
+ if (DestType == DEST_LOCAL) {
+ Status = (*rcv)(SrcNTE,Header->iph_dest, Header->iph_src,
+ DestNTE->nte_addr, SrcNTE->nte_addr, Header,
+ HeaderLength, Data, DataLength, FALSE,
+ Header->iph_protocol, OptInfo);
+
+ if (Status == IP_SUCCESS) {
+ IPSInfo.ipsi_indelivers++;
+ return;
+ }
+
+ if (Status == IP_DEST_PROT_UNREACHABLE) {
+ IPSInfo.ipsi_inunknownprotos++;
+ SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH,
+ PROT_UNREACH, 0);
+ }
+ else {
+ IPSInfo.ipsi_indelivers++;
+ SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH,
+ PORT_UNREACH, 0);
+ }
+
+ return; // Just return out of here now.
+ }
+ else {
+ BCastRcv(rcv, SrcNTE, Header->iph_dest, Header->iph_src,
+ Header, HeaderLength, Data, DataLength,
+ Header->iph_protocol, OptInfo);
+ }
+
+ } else {
+ IPSInfo.ipsi_inunknownprotos++;
+ // If we get here, we didn't find a matching protocol. Send an ICMP message.
+ SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH, PROT_UNREACH, 0);
+ }
+
+}
+
+//* FreeRH - Free a reassembly header.
+//
+// Called when we need to free a reassembly header, either because of a timeout or because
+// we're done with it.
+//
+// Input: RH - RH to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeRH(ReassemblyHeader *RH)
+{
+ RABufDesc *RBD, *TempRBD;
+
+ RBD = RH->rh_rbd;
+ while (RBD != NULL) {
+ TempRBD = RBD;
+ RBD = (RABufDesc *)RBD->rbd_buf.ipr_next;
+ CTEFreeMem(TempRBD);
+ }
+ CTEFreeMem(RH);
+
+}
+
+//* ReassembleFragment - Put a fragment into the reassembly list.
+//
+// This routine is called once we've put a fragment into the proper buffer. We look for
+// a reassembly header for the fragment. If we don't find one, we create one. Otherwise
+// we search the reassembly list, and insert the datagram in it's proper place.
+//
+// Input: NTE - NTE to reassemble on.
+// SrcNTE - NTE datagram arrived on.
+// NewRBD - New RBD to be inserted.
+// IPH - Pointer to header of datagram.
+// HeaderSize - Size in bytes of header.
+// DestType - Type of destination address.
+//
+// Returns: Nothing.
+//
+void
+ReassembleFragment(NetTableEntry *NTE, NetTableEntry *SrcNTE, RABufDesc *NewRBD,
+ IPHeader UNALIGNED *IPH, uint HeaderSize, uchar DestType)
+{
+ CTELockHandle NTEHandle; // Lock handle used for NTE
+ ReassemblyHeader *RH, *PrevRH; // Current and previous reassembly headers.
+ RABufDesc *PrevRBD; // Previous RBD in reassembly header list.
+ RABufDesc *CurrentRBD;
+ ushort DataLength = (ushort)NewRBD->rbd_buf.ipr_size, DataOffset;
+ ushort Offset; // Offset of this fragment.
+ ushort NewOffset; // Offset we'll copy from after checking RBD list.
+ ushort NewEnd; // End offset of fragment, after trimming (if any).
+
+ // If this is a broadcast, go ahead and forward it now.
+ if (IS_BCAST_DEST(DestType))
+ IPForward(SrcNTE, IPH, HeaderSize, NewRBD->rbd_buf.ipr_buffer,
+ NewRBD->rbd_buf.ipr_size, NULL, 0, DestType);
+
+
+ // We've got the buffer we need. Now get the reassembly header, if there is one. If
+ // there isn't, create one.
+ CTEGetLockAtDPC(&NTE->nte_lock, &NTEHandle);
+ RH = FindRH(&PrevRH, NTE, IPH->iph_dest, IPH->iph_src, IPH->iph_id, IPH->iph_protocol);
+ if (RH == (ReassemblyHeader *)NULL) { // Didn't find one, so create one.
+ ReassemblyHeader *NewRH;
+
+ CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle);
+ RH = CTEAllocMem(sizeof(ReassemblyHeader));
+ if (RH == (ReassemblyHeader *)NULL) { // Couldn't get a buffer.
+#ifdef CHICAGO
+ RefillReasmMem();
+#endif
+ IPSInfo.ipsi_reasmfails++;
+ CTEFreeMem(NewRBD);
+ return;
+ }
+
+ CTEGetLockAtDPC(&NTE->nte_lock, &NTEHandle);
+ // Need to look it up again - it could have changed during above call.
+ NewRH = FindRH(&PrevRH, NTE, IPH->iph_dest, IPH->iph_src, IPH->iph_id, IPH->iph_protocol);
+ if (NewRH != (ReassemblyHeader *)NULL) {
+ CTEFreeMem(RH);
+ RH = NewRH;
+ } else {
+
+ RH->rh_next = PrevRH->rh_next;
+ PrevRH->rh_next = RH;
+
+
+ // Initialize our new reassembly header.
+ RH->rh_dest = IPH->iph_dest;
+ RH->rh_src = IPH->iph_src;
+ RH->rh_id = IPH->iph_id;
+ RH->rh_protocol = IPH->iph_protocol;
+ RH->rh_ttl = RATimeout;
+ RH->rh_datasize = 0xffff; // Default datasize to maximum.
+ RH->rh_rbd = (RABufDesc *)NULL; // And nothing on chain.
+ RH->rh_datarcvd = 0; // Haven't received any data yet.
+ RH->rh_headersize = 0;
+
+ }
+ }
+
+ // When we reach here RH points to the reassembly header we want to use.
+ // and we hold locks on the NTE and the RH. If this is the first fragment we'll save
+ // the options and header information here.
+
+ Offset = IPH->iph_offset & IP_OFFSET_MASK;
+ Offset = net_short(Offset) * 8;
+
+ if (Offset == 0) { // First fragment.
+ RH->rh_headersize = HeaderSize;
+ CTEMemCopy(RH->rh_header, IPH, HeaderSize + 8);
+ }
+
+ // If this is the last fragment, update the amount of data we expect to received.
+ if (!(IPH->iph_offset & IP_MF_FLAG))
+ RH->rh_datasize = Offset + DataLength;
+
+ // Update the TTL value with the maximum of the current TTL and the incoming
+ // TTL (+1, to deal with rounding errors).
+ RH->rh_ttl = MAX(RH->rh_ttl, MIN(254, IPH->iph_ttl) + 1);
+
+ // Now we need to see where in the RBD list to put this.
+ //
+ // The idea is to go through the list of RBDs one at a time. The RBD currently
+ // being examined is CurrentRBD. If the start offset of the new fragment is less
+ // than (i.e. in front of) the offset of CurrentRBD, we need to insert the NewRBD
+ // in front of the CurrentRBD. If this is the case we need to check and see if the
+ // end of the new fragment overlaps some or all of the fragment described by
+ // CurrentRBD, and possibly subsequent fragment. If it overlaps part of a fragment
+ // we'll adjust our end down to be in front of the existing fragment. If it overlaps
+ // all of the fragment we'll free the old fragment.
+ //
+ // If the new fragment does not start in front of the current fragment we'll check
+ // to see if it starts somewhere in the middle of the current fragment. If this
+ // isn't the case, we move on the the next fragment. If this is the case, we check
+ // to see if the current fragment completely covers the new fragment. If not we
+ // move our start up and continue with the next fragment.
+
+ NewOffset = Offset;
+ NewEnd = Offset + DataLength - 1;
+ PrevRBD = STRUCT_OF(RABufDesc, STRUCT_OF(IPRcvBuf, &RH->rh_rbd, ipr_next), rbd_buf);
+ CurrentRBD = RH->rh_rbd;
+ for (; CurrentRBD != NULL; PrevRBD = CurrentRBD, CurrentRBD = (RABufDesc *)CurrentRBD->rbd_buf.ipr_next) {
+
+ // See if it starts in front of this fragment.
+ if (NewOffset < CurrentRBD->rbd_start) {
+ // It does start in front. Check to see if there's any overlap.
+
+ if (NewEnd < CurrentRBD->rbd_start)
+ break; // No overlap, so get out.
+ else {
+ // It does overlap. While we have overlap, walk down the list
+ // looking for RBDs we overlap completely. If we find one, put it
+ // on our deletion list. If we have overlap but not complete overlap,
+ // move our end down if front of the fragment we overlap.
+ do {
+ if (NewEnd > CurrentRBD->rbd_end) { // This overlaps completely.
+ RABufDesc *TempRBD;
+
+ RH->rh_datarcvd -= CurrentRBD->rbd_buf.ipr_size;
+ TempRBD = CurrentRBD;
+ CurrentRBD = (RABufDesc *)CurrentRBD->rbd_buf.ipr_next;
+ CTEFreeMem(TempRBD);
+ } else // Only partial ovelap.
+ NewEnd = CurrentRBD->rbd_start - 1;
+ // Update of NewEnd will force us out of loop.
+
+ } while (CurrentRBD != NULL && NewEnd >= CurrentRBD->rbd_start);
+ break;
+ }
+ } else {
+ // This fragment doesn't go in front of the current RBD. See if it is
+ // entirely beyond the end of the current fragment. If it is, just
+ // continue. Otherwise see if the current fragment completely subsumes
+ // us. If it does, get out, otherwise update our start offset and
+ // continue.
+
+ if (NewOffset > CurrentRBD->rbd_end)
+ continue; // No overlap at all.
+ else {
+ if (NewEnd <= CurrentRBD->rbd_end) {
+ // The current fragment overlaps the new fragment totally. Set
+ // our offsets so that we'll skip the copy below.
+ NewEnd = NewOffset - 1;
+ break;
+ } else // Only partial overlap.
+ NewOffset = CurrentRBD->rbd_end + 1;
+ }
+ }
+ } // End of for loop.
+
+ // Adjust the length and offset fields in the new RBD.
+ DataLength = NewEnd - NewOffset + 1;
+ DataOffset = NewOffset - Offset;
+ // Link him in chain.
+ NewRBD->rbd_buf.ipr_size = (uint)DataLength;
+ NewRBD->rbd_end = NewEnd;
+ NewRBD->rbd_start = NewOffset;
+ RH->rh_datarcvd += DataLength;
+ NewRBD->rbd_buf.ipr_buffer += DataOffset;
+ NewRBD->rbd_buf.ipr_next = (IPRcvBuf *)CurrentRBD;
+ PrevRBD->rbd_buf.ipr_next = &NewRBD->rbd_buf;
+
+ // If we've received all the data, deliver it to the user.
+ if (RH->rh_datarcvd == RH->rh_datasize) { // We have it all.
+ IPOptInfo OptInfo;
+ IPHeader *Header;
+ IPRcvBuf *FirstBuf;
+
+ PrevRH->rh_next = RH->rh_next;
+ CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle);
+ Header = (IPHeader *)RH->rh_header;
+ OptInfo.ioi_ttl = Header->iph_ttl;
+ OptInfo.ioi_tos = Header->iph_tos;
+ OptInfo.ioi_flags = 0; // Flags must be 0 - DF can't be set, this was reassembled.
+
+ if (RH->rh_headersize != sizeof(IPHeader)) { // We had options.
+ OptInfo.ioi_options = (uchar *)(Header + 1);
+ OptInfo.ioi_optlength = RH->rh_headersize - sizeof(IPHeader);
+ } else {
+ OptInfo.ioi_options = (uchar *)NULL;
+ OptInfo.ioi_optlength = 0;
+ }
+
+ // Make sure that the first buffer contains enough data.
+ FirstBuf = (IPRcvBuf *)RH->rh_rbd;
+ while (FirstBuf->ipr_size < MIN_FIRST_SIZE) {
+ IPRcvBuf *NextBuf = FirstBuf->ipr_next;
+ uint CopyLength;
+
+ if (NextBuf == NULL)
+ break;
+
+ CopyLength = MIN(MIN_FIRST_SIZE - FirstBuf->ipr_size,
+ NextBuf->ipr_size);
+ CTEMemCopy(FirstBuf->ipr_buffer + FirstBuf->ipr_size,
+ NextBuf->ipr_buffer, CopyLength);
+ FirstBuf->ipr_size += CopyLength;
+ NextBuf->ipr_buffer += CopyLength;
+ NextBuf->ipr_size -= CopyLength;
+ if (NextBuf->ipr_size == 0) {
+ FirstBuf->ipr_next = NextBuf->ipr_next;
+ CTEFreeMem(NextBuf);
+ }
+ }
+
+ IPSInfo.ipsi_reasmoks++;
+ DeliverToUser(SrcNTE, NTE, Header, RH->rh_headersize, FirstBuf,
+ RH->rh_datasize, &OptInfo, DestType);
+ FreeRH(RH);
+ } else
+ CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle);
+
+
+}
+
+//* RATDComplete - Completion routing for a reassembly transfer data.
+//
+// This is the completion handle for TDs invoked because we are reassembling a fragment.
+//
+// Input: NetContext - Pointer to the net table entry on which we received this.
+// Packet - Packet we received into.
+// Status - Final status of copy.
+// DataSize - Size in bytes of data transferred.
+//
+// Returns: Nothing
+//
+void
+RATDComplete(void *NetContext, PNDIS_PACKET Packet, NDIS_STATUS Status, uint DataSize)
+{
+ NetTableEntry *NTE = (NetTableEntry *)NetContext;
+ Interface *SrcIF;
+ TDContext *Context = (TDContext *)Packet->ProtocolReserved;
+ CTELockHandle Handle;
+ PNDIS_BUFFER Buffer;
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ Context->tdc_rbd->rbd_buf.ipr_size = DataSize;
+ ReassembleFragment(Context->tdc_nte, NTE, Context->tdc_rbd,
+ (IPHeader *)Context->tdc_header, Context->tdc_hlength, Context->tdc_dtype);
+ }
+
+ NdisUnchainBufferAtFront(Packet, &Buffer);
+ NdisFreeBuffer(Buffer);
+ Context->tdc_common.pc_flags &= ~PACKET_FLAG_RA;
+ SrcIF = NTE->nte_if;
+ CTEGetLockAtDPC(&SrcIF->if_lock, &Handle);
+
+ Context->tdc_common.pc_link = SrcIF->if_tdpacket;
+ SrcIF->if_tdpacket = Packet;
+ CTEFreeLockFromDPC(&SrcIF->if_lock, Handle);
+
+ return;
+
+}
+
+//* IPReassemble - Reassemble an incoming datagram.
+//
+// Called when we receive an incoming fragment. The first thing we do is get a buffer
+// to put the fragment in. If we can't we'll exit. Then we copy the data, either via
+// transfer data or directly if it all fits.
+//
+// Input: SrcNTE - Pointer to NTE that received the datagram.
+// NTE - Pointer to NTE on which to reassemble.
+// IPH - Pointer to header of packet.
+// HeaderSize - Size in bytes of header.
+// Data - Pointer to data part of fragment.
+// BufferLength - Length in bytes of user data available in the buffer.
+// DataLength - Length in bytes of the (upper-layer) data.
+// DestType - Type of destination
+// LContext1, LContext2 - Link layer context values.
+//
+// Returns: Nothing.
+//
+void
+IPReassemble(NetTableEntry *SrcNTE, NetTableEntry *NTE, IPHeader UNALIGNED *IPH,
+ uint HeaderSize,
+ uchar *Data, uint BufferLength, uint DataLength, uchar DestType, NDIS_HANDLE LContext1,
+ uint LContext2)
+{
+ Interface *RcvIF;
+ PNDIS_PACKET TDPacket; // NDIS packet used for TD.
+ TDContext *TDC = (TDContext *)NULL; // Transfer data context.
+ NDIS_STATUS Status;
+ PNDIS_BUFFER Buffer;
+ RABufDesc *NewRBD; // Pointer to new RBD to hold arriving fragment.
+ CTELockHandle Handle;
+ uint AllocSize;
+
+ IPSInfo.ipsi_reasmreqds++;
+
+ // First, get a new RBD to hold the arriving fragment. If we can't, then just skip
+ // the rest. The RBD has the buffer implicitly at the end of it. The buffer for the
+ // first fragment must be at least MIN_FIRST_SIZE bytes.
+ if ((IPH->iph_offset & IP_OFFSET_MASK) == 0)
+ AllocSize = MAX(MIN_FIRST_SIZE, DataLength);
+ else
+ AllocSize = DataLength;
+
+ NewRBD = CTEAllocMem(sizeof(RABufDesc) + AllocSize);
+
+ if (NewRBD != (RABufDesc *)NULL) {
+
+ NewRBD->rbd_buf.ipr_buffer = (uchar *)(NewRBD + 1);
+ NewRBD->rbd_buf.ipr_size = DataLength;
+ NewRBD->rbd_buf.ipr_owner = IPR_OWNER_IP;
+
+ // Copy the data into the buffer. If we need to call transfer data do so now.
+ if (DataLength > BufferLength) { // Need to call transfer data.
+ NdisAllocateBuffer(&Status, &Buffer, BufferPool, NewRBD + 1, DataLength);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ IPSInfo.ipsi_reasmfails++;
+ CTEFreeMem(NewRBD);
+ return;
+ }
+
+ // Now get a packet for transferring the frame.
+ RcvIF = SrcNTE->nte_if;
+ CTEGetLockAtDPC(&RcvIF->if_lock, &Handle);
+ TDPacket = RcvIF->if_tdpacket;
+
+ if (TDPacket != (PNDIS_PACKET)NULL) {
+
+ TDC = (TDContext *)TDPacket->ProtocolReserved;
+ RcvIF->if_tdpacket = TDC->tdc_common.pc_link;
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+
+ TDC->tdc_common.pc_flags |= PACKET_FLAG_RA;
+ TDC->tdc_nte = NTE;
+ TDC->tdc_dtype = DestType;
+ TDC->tdc_hlength = (uchar)HeaderSize;
+ TDC->tdc_rbd = NewRBD;
+ CTEMemCopy(TDC->tdc_header, IPH, HeaderSize + 8);
+ NdisChainBufferAtFront(TDPacket, Buffer);
+ Status = (*(RcvIF->if_transfer))(RcvIF->if_lcontext, LContext1, LContext2,
+ HeaderSize, DataLength, TDPacket, &DataLength);
+ if (Status != NDIS_STATUS_PENDING)
+ RATDComplete(SrcNTE, TDPacket, Status, DataLength);
+ else
+ return;
+ } else { // Couldn't get a TD packet.
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+ CTEFreeMem(NewRBD);
+ IPSInfo.ipsi_reasmfails++;
+ return;
+ }
+ } else { // It all fits, copy it.
+ CTEMemCopy(NewRBD + 1, Data, DataLength);
+ ReassembleFragment(NTE, SrcNTE, NewRBD, IPH, HeaderSize, DestType);
+ }
+ } else {
+#ifdef CHICAGO
+ RefillReasmMem();
+#endif
+
+ IPSInfo.ipsi_reasmfails++;
+ }
+
+ return;
+}
+
+
+
+//* CheckLocalOptions - Check the options received with a packet.
+//
+// A routine called when we've received a packet for this host and want to examine
+// it for options. We process the options, and return TRUE or FALSE depending on whether
+// or not it's for us.
+//
+// Input: SrcNTE - Pointer to NTE this came in on.
+// Header - Pointer to incoming header.
+// OptInfo - Place to put opt info.
+// DestType - Type of incoming packet.
+//
+// Returns: DestType - Local or remote.
+//
+uchar
+CheckLocalOptions(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header,
+ IPOptInfo *OptInfo, uchar DestType)
+{
+ uint HeaderLength; // Length in bytes of header.
+ OptIndex Index;
+ uchar ErrIndex;
+
+#ifdef DEBUG
+ if (DestType >= DEST_REMOTE)
+ DEBUGCHK;
+#endif
+
+
+ HeaderLength = (Header->iph_verlen & (uchar)~IP_VER_FLAG) << 2;
+
+#ifdef DEBUG
+ if (HeaderLength <= sizeof(IPHeader))
+ DEBUGCHK;
+#endif
+
+ OptInfo->ioi_options = (uchar *)(Header + 1);
+ OptInfo->ioi_optlength = HeaderLength - sizeof(IPHeader);
+
+
+ // We have options of some sort. The packet may or may not be bound for us.
+ Index.oi_srindex = MAX_OPT_SIZE;
+ if ((ErrIndex = ParseRcvdOptions(OptInfo, &Index)) < MAX_OPT_SIZE) {
+ SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM, PTR_VALID,
+ ((ulong)ErrIndex + sizeof(IPHeader)));
+ return DEST_INVALID; // Parameter error.
+ }
+
+ // If there's no source route, or if the destination is a broadcast, we'll take
+ // it. If it is a broadcast DeliverToUser will forward it when it's done, and
+ // the forwarding code will reprocess the options.
+ if (Index.oi_srindex == MAX_OPT_SIZE || IS_BCAST_DEST(DestType))
+ return DEST_LOCAL;
+ else
+ return DEST_REMOTE;
+
+}
+
+//* TDUserRcv - Completion routing for a user transfer data.
+//
+// This is the completion handle for TDs invoked because we need to give data to a
+// upper layer client. All we really do is call the upper layer handler with
+// the data.
+//
+// Input: NetContext - Pointer to the net table entry on which we received this.
+// Packet - Packet we received into.
+// Status - Final status of copy.
+// DataSize - Size in bytes of data transferred.
+//
+// Returns: Nothing
+//
+void
+TDUserRcv(void *NetContext, PNDIS_PACKET Packet, NDIS_STATUS Status,
+ uint DataSize)
+{
+ NetTableEntry *NTE = (NetTableEntry *)NetContext;
+ Interface *SrcIF;
+ TDContext *Context = (TDContext *)Packet->ProtocolReserved;
+ CTELockHandle Handle;
+ uchar DestType;
+ IPRcvBuf RcvBuf;
+ IPOptInfo OptInfo;
+ IPHeader *Header;
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ Header = (IPHeader *)Context->tdc_header;
+ OptInfo.ioi_ttl = Header->iph_ttl;
+ OptInfo.ioi_tos = Header->iph_tos;
+ OptInfo.ioi_flags = (net_short(Header->iph_offset) >> 13) & IP_FLAG_DF;
+ if (Context->tdc_hlength != sizeof(IPHeader)) {
+ OptInfo.ioi_options = (uchar *)(Header + 1);
+ OptInfo.ioi_optlength = Context->tdc_hlength - sizeof(IPHeader);
+ } else {
+ OptInfo.ioi_options = (uchar *)NULL;
+ OptInfo.ioi_optlength = 0;
+ }
+
+ DestType = Context->tdc_dtype;
+ RcvBuf.ipr_next = NULL;
+ RcvBuf.ipr_owner = IPR_OWNER_IP;
+ RcvBuf.ipr_buffer = (uchar *)Context->tdc_buffer;
+ RcvBuf.ipr_size = DataSize;
+
+ DeliverToUser(NTE, Context->tdc_nte, Header, Context->tdc_hlength,
+ &RcvBuf, DataSize, &OptInfo, DestType);
+ // If it's a broadcast packet forward it on.
+ if (IS_BCAST_DEST(DestType))
+ IPForward(NTE, Header, Context->tdc_hlength, RcvBuf.ipr_buffer, DataSize,
+ NULL, 0, DestType);
+ }
+
+ SrcIF = NTE->nte_if;
+ CTEGetLockAtDPC(&SrcIF->if_lock, &Handle);
+
+ Context->tdc_common.pc_link = SrcIF->if_tdpacket;
+ SrcIF->if_tdpacket = Packet;
+ CTEFreeLockFromDPC(&SrcIF->if_lock, Handle);
+
+ return;
+
+}
+
+
+//* IPRcv - Receive an incoming IP datagram.
+//
+// This is the routine called by the link layer module when an incoming IP
+// datagram is to be processed. We validate the datagram (including doing
+// the xsum), copy and process incoming options, and decide what to do with it.
+//
+// Entry: MyContext - The context valued we gave to the link layer.
+// Data - Pointer to the data buffer.
+// DataSize - Size in bytes of the data buffer.
+// TotalSize - Total size in bytes available.
+// LContext1 - 1st link context.
+// LContext2 - 2nd link context.
+// BCast - Indicates whether or not packet was received on bcast address.
+//
+// Returns: Nothing.
+//
+void
+IPRcv(void *MyContext, void *Data, uint DataSize, uint TotalSize, NDIS_HANDLE LContext1,
+ uint LContext2, uint BCast)
+{
+ IPHeader UNALIGNED *IPH = (IPHeader UNALIGNED *)Data;
+ NetTableEntry *NTE = (NetTableEntry *)MyContext; // Local NTE received on
+ NetTableEntry *DestNTE; // NTE to receive on.
+ Interface *RcvIF; // Interface corresponding to NTE.
+ CTELockHandle Handle;
+ PNDIS_PACKET TDPacket; // NDIS packet used for TD.
+ TDContext *TDC = (TDContext *)NULL; // Transfer data context.
+ NDIS_STATUS Status;
+ IPAddr DAddr; // Dest. IP addr. of received packet.
+ uint HeaderLength; // Size in bytes of received header.
+ uint IPDataLength; // Length in bytes of IP (including UL) data in packet.
+ IPOptInfo OptInfo; // Incoming header information.
+ uchar DestType; // Type (LOCAL, REMOTE, SR) of Daddr.
+ IPRcvBuf RcvBuf;
+
+#if 0
+ CTECheckMem(RcvFile); // Check heap status.
+#endif
+
+ IPSInfo.ipsi_inreceives++;
+
+ // Make sure we actually have data.
+ if (DataSize) {
+
+ // Check the header length, the xsum and the version. If any of these
+ // checks fail silently discard the packet.
+ HeaderLength = ((IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2);
+ if (HeaderLength >= sizeof(IPHeader) && HeaderLength <= DataSize &&
+ xsum(Data, HeaderLength) == (ushort)0xffff) {
+
+ // Check the version, and sanity check the total length.
+ IPDataLength = (uint)net_short(IPH->iph_length);
+ if ((IPH->iph_verlen & IP_VER_FLAG) == IP_VERSION &&
+ IPDataLength > sizeof(IPHeader) && IPDataLength <= TotalSize) {
+
+ IPDataLength -= HeaderLength;
+ Data = (uchar *)Data + HeaderLength;
+ DataSize -= HeaderLength;
+
+ DAddr = IPH->iph_dest;
+ DestNTE = NTE;
+
+ // Find local NTE, if any.
+ DestType = GetLocalNTE(DAddr, &DestNTE);
+
+ // Check to see if this is a non-broadcast IP address that
+ // came in as a link layer broadcast. If it is, throw it out.
+ // This is an important check for DHCP, since if we're
+ // DHCPing an interface all otherwise unknown addresses will
+ // come in as DEST_LOCAL. This check here will throw them out
+ // if they didn't come in as unicast.
+
+ if (BCast && !IS_BCAST_DEST(DestType)) {
+ IPSInfo.ipsi_inhdrerrors++;
+ return; // Non bcast packet on bcast address.
+ }
+
+ OptInfo.ioi_ttl = IPH->iph_ttl;
+ OptInfo.ioi_tos = IPH->iph_tos;
+ OptInfo.ioi_flags = (net_short(IPH->iph_offset) >> 13) &
+ IP_FLAG_DF;
+ OptInfo.ioi_options = (uchar *)NULL;
+ OptInfo.ioi_optlength = 0;
+
+ if (DestType < DEST_REMOTE) {
+ // It's either local or some sort of broadcast.
+
+ // The data probably belongs at this station. If there
+ // aren't any options, it definetly belongs here, and we'll
+ // dispatch it either to our reasssmbly code or to the
+ // deliver to user code. If there are options, we'll check
+ // them and then either handle the packet locally or pass it
+ // to our forwarding code.
+
+ if (HeaderLength != sizeof(IPHeader)) {
+ // We have options.
+
+ uchar NewDType;
+ NewDType = CheckLocalOptions(NTE, IPH, &OptInfo,
+ DestType);
+ if (NewDType != DEST_LOCAL) {
+ if (NewDType == DEST_REMOTE)
+ goto forward;
+ else {
+ IPSInfo.ipsi_inhdrerrors++;
+ return; // Bad Options.
+ }
+ }
+ }
+
+ // Before we go further, if we have a filter installed
+ // call it to see if we should take this.
+
+ if (ForwardFilterPtr != NULL) {
+ FORWARD_ACTION Action;
+
+ Action = (*ForwardFilterPtr)(IPH,
+ Data,
+ DataSize,
+ NTE->nte_if->if_filtercontext,
+ NULL);
+
+ if (Action != FORWARD) {
+ IPSInfo.ipsi_indiscards++;
+ return;
+ }
+ }
+
+ // No options. See if it's a fragment. If it is, call our
+ // reassembly handler.
+ if ((IPH->iph_offset & ~(IP_DF_FLAG | IP_RSVD_FLAG)) == 0) {
+
+ // We don't have a fragment. If the data all fits,
+ // handle it here. Otherwise transfer data it.
+
+#ifdef VXD
+ if (IPDataLength > DataSize) {
+ // Data isn't all in the buffer.
+#else
+ // Make sure data is all in buffer, and directly
+ // accesible.
+ if ((IPDataLength > DataSize) ||
+ !(NTE->nte_flags & NTE_COPY)) {
+#endif
+ // The data isn't all here. Transfer data it.
+ RcvIF = NTE->nte_if;
+ CTEGetLockAtDPC(&RcvIF->if_lock, &Handle);
+ TDPacket = RcvIF->if_tdpacket;
+
+ if (TDPacket != (PNDIS_PACKET)NULL) {
+
+ TDC = (TDContext *)TDPacket->ProtocolReserved;
+ RcvIF->if_tdpacket = TDC->tdc_common.pc_link;
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+
+ TDC->tdc_nte = DestNTE;
+ TDC->tdc_dtype = DestType;
+ TDC->tdc_hlength = (uchar)HeaderLength;
+ CTEMemCopy(TDC->tdc_header, IPH,
+ HeaderLength + 8);
+ Status = (*(RcvIF->if_transfer))(
+ RcvIF->if_lcontext, LContext1,
+ LContext2, HeaderLength, IPDataLength,
+ TDPacket, &IPDataLength);
+
+ // Check the status. If it's success, call the
+ // receive procedure. Otherwise, if it's pending
+ // wait for the callback.
+ Data = TDC->tdc_buffer;
+ if (Status != NDIS_STATUS_PENDING) {
+ if (Status != NDIS_STATUS_SUCCESS) {
+ IPSInfo.ipsi_indiscards++;
+ CTEGetLockAtDPC(&RcvIF->if_lock, &Handle);
+ TDC->tdc_common.pc_link =
+ RcvIF->if_tdpacket;
+ RcvIF->if_tdpacket = TDPacket;
+ CTEFreeLockFromDPC(&RcvIF->if_lock,
+ Handle);
+ return;
+ }
+ } else
+ return; // Status is pending.
+ } else { // Couldn't get a packet.
+ IPSInfo.ipsi_indiscards++;
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+ return;
+ }
+ }
+
+ RcvBuf.ipr_next = NULL;
+ RcvBuf.ipr_owner = IPR_OWNER_IP;
+ RcvBuf.ipr_buffer = (uchar *)Data;
+ RcvBuf.ipr_size = IPDataLength;
+ // When we get here, we have the whole packet. Deliver
+ // it.
+ DeliverToUser(NTE, DestNTE, IPH, HeaderLength, &RcvBuf,
+ IPDataLength, &OptInfo, DestType);
+ // When we're here, we're through with the packet
+ // locally. If it's a broadcast packet forward it on.
+ if (IS_BCAST_DEST(DestType)) {
+ IPForward(NTE, IPH, HeaderLength, Data, IPDataLength,
+ NULL, 0, DestType);
+ }
+ if (TDC != NULL) {
+ CTEGetLockAtDPC(&RcvIF->if_lock, &Handle);
+ TDC->tdc_common.pc_link = RcvIF->if_tdpacket;
+ RcvIF->if_tdpacket = TDPacket;
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+ }
+ return;
+ } else {
+ // This is a fragment. Reassemble it.
+ IPReassemble(NTE, DestNTE, IPH, HeaderLength, Data,
+ DataSize, IPDataLength, DestType, LContext1,
+ LContext2);
+ return;
+ }
+
+ }
+
+ // Not for us, may need to be forwarded. It might be an outgoing
+ // broadcast that came in through a source route, so we need to
+ // check that.
+forward:
+ if (DestType != DEST_INVALID)
+ IPForward(NTE, IPH, HeaderLength, Data, DataSize,
+ LContext1, LContext2, DestType);
+ else
+ IPSInfo.ipsi_inaddrerrors++;
+ return;
+ } // Bad version
+ } // Bad checksum
+
+ } // No data
+
+ IPSInfo.ipsi_inhdrerrors++;
+}
+
+//* IPTDComplete - IP Transfer data complete handler.
+//
+// This is the routine called by the link layer when a transfer data completes.
+//
+// Entry: MyContext - Context value we gave to the link layer.
+// Packet - Packet we originally gave to transfer data.
+// Status - Final status of command.
+// BytesCopied - Number of bytes copied.
+//
+// Exit: Nothing
+//
+void
+IPTDComplete(void *MyContext, PNDIS_PACKET Packet, NDIS_STATUS Status, uint BytesCopied)
+{
+ TDContext *TDC = (TDContext *)Packet->ProtocolReserved;
+
+ if (!(TDC->tdc_common.pc_flags & PACKET_FLAG_FW))
+ if (!(TDC->tdc_common.pc_flags & PACKET_FLAG_RA))
+ TDUserRcv(MyContext, Packet, Status, BytesCopied);
+ else
+ RATDComplete(MyContext, Packet, Status, BytesCopied);
+ else
+ SendFWPacket(Packet, Status, BytesCopied);
+
+
+}