diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/tdi/tcpip/ip/iprcv.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/tdi/tcpip/ip/iprcv.c')
-rw-r--r-- | private/ntos/tdi/tcpip/ip/iprcv.c | 1145 |
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); + + +} |