Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1145 lines
36 KiB

/********************************************************************/
/** 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);
}