mirror of https://github.com/tongzx/nt5src
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.
2974 lines
81 KiB
2974 lines
81 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tcpip\ip\mcastfwd.c
|
|
|
|
Abstract:
|
|
|
|
The actual multicast forwarding code
|
|
|
|
Author:
|
|
|
|
Amritansh Raghav
|
|
|
|
Revision History:
|
|
|
|
AmritanR Created
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
#if IPMCAST
|
|
#define __FILE_SIG__ FWD_SIG
|
|
|
|
#include "ipmcast.h"
|
|
#include "ipmcstxt.h"
|
|
#include "mcastmfe.h"
|
|
#include "tcpipbuf.h"
|
|
#include "info.h"
|
|
|
|
uchar
|
|
ParseRcvdOptions(
|
|
IPOptInfo *,
|
|
OptIndex *
|
|
);
|
|
|
|
IPHeader *
|
|
GetFWPacket(
|
|
PNDIS_PACKET *Packet
|
|
);
|
|
|
|
void
|
|
FreeFWPacket(
|
|
PNDIS_PACKET Packet
|
|
);
|
|
|
|
UINT
|
|
GrowFWBuffer(
|
|
VOID
|
|
);
|
|
|
|
IP_STATUS
|
|
IPFragment(
|
|
Interface *DestIF,
|
|
uint MTU,
|
|
IPAddr FirstHop,
|
|
PNDIS_PACKET Packet,
|
|
IPHeader *Header,
|
|
PNDIS_BUFFER Buffer,
|
|
uint DataSize,
|
|
uchar *Options,
|
|
uint OptionSize,
|
|
int *SentCount,
|
|
BOOLEAN bDontLoopback,
|
|
void *ArpCtxt
|
|
);
|
|
|
|
IPHeader *
|
|
GetIPHeader(
|
|
PNDIS_PACKET *PacketPtr
|
|
);
|
|
|
|
IP_STATUS
|
|
SendIPPacket(
|
|
Interface *IF,
|
|
IPAddr FirstHop,
|
|
PNDIS_PACKET Packet,
|
|
PNDIS_BUFFER Buffer,
|
|
IPHeader *Header,
|
|
uchar *Options,
|
|
uint OptionSize,
|
|
BOOLEAN IPSeced,
|
|
void *ArpCtxt,
|
|
BOOLEAN DontFreePacket
|
|
);
|
|
|
|
void
|
|
FWSendComplete(
|
|
void *SendContext,
|
|
PNDIS_BUFFER Buffer,
|
|
IP_STATUS SendStatus
|
|
);
|
|
|
|
|
|
uchar
|
|
UpdateOptions(
|
|
uchar *Options,
|
|
OptIndex *Index,
|
|
IPAddr Address
|
|
);
|
|
|
|
int
|
|
ReferenceBuffer(
|
|
BufferReference *BR, int Count
|
|
);
|
|
|
|
|
|
EXTERNAL_LOCK(FWBufFreeLock);
|
|
|
|
extern PNDIS_BUFFER FWBufFree;
|
|
extern NDIS_HANDLE BufferPool;
|
|
|
|
//
|
|
// A quick way of getting to the flags.
|
|
//
|
|
|
|
#define PCFLAGS pc_common.pc_flags
|
|
#define FCFLAGS fc_pc.PCFLAGS
|
|
|
|
NDIS_STATUS
|
|
AllocateCopyBuffers(
|
|
IN PNDIS_PACKET pnpPacket,
|
|
IN ULONG ulDataLength,
|
|
OUT PNDIS_BUFFER *ppnbBuffer,
|
|
OUT PULONG pulNumBufs
|
|
);
|
|
|
|
NTSTATUS
|
|
IPMForward(
|
|
PNDIS_PACKET pnpPacket,
|
|
PSOURCE pSource,
|
|
BOOLEAN bSendFromQueue
|
|
);
|
|
|
|
VOID
|
|
IPMForwardAfterTD(
|
|
NetTableEntry *pPrimarySrcNte,
|
|
PNDIS_PACKET pnpPacket,
|
|
UINT uiBytesCopied
|
|
);
|
|
|
|
BOOLEAN
|
|
IPMForwardAfterRcv(
|
|
NetTableEntry *pPrimarySrcNte,
|
|
IPHeader UNALIGNED *pHeader,
|
|
ULONG ulHeaderLength,
|
|
PVOID pvData,
|
|
ULONG ulBufferLength,
|
|
NDIS_HANDLE LContext1,
|
|
UINT LContext2,
|
|
BYTE byDestType,
|
|
LinkEntry *pLink
|
|
);
|
|
|
|
BOOLEAN
|
|
IPMForwardAfterRcvPkt(
|
|
NetTableEntry *pPrimarySrcNte,
|
|
IPHeader UNALIGNED *pHeader,
|
|
ULONG ulHeaderLength,
|
|
PVOID pvData,
|
|
ULONG ulBufferLength,
|
|
NDIS_HANDLE LContext1,
|
|
UINT LContext2,
|
|
BYTE byDestType,
|
|
UINT uiMacHeaderSize,
|
|
PNDIS_BUFFER pNdisBuffer,
|
|
uint *pClientCnt,
|
|
LinkEntry *pLink
|
|
);
|
|
|
|
NDIS_STATUS
|
|
__inline
|
|
ProcessOptions(
|
|
FWContext *pFWC,
|
|
ULONG ulHeaderLength,
|
|
IPHeader UNALIGNED *pHeader
|
|
);
|
|
|
|
//
|
|
// VOID
|
|
// LinkHeaderAndData(
|
|
// PNDIS_PACKET _pPacket,
|
|
// FWContext *_pFWC,
|
|
// PBYTE _pOptions,
|
|
// PNDIS_BUFFER _pOptBuff
|
|
// )
|
|
//
|
|
// This routine links up the header, options (if any) and the data
|
|
// portions of an IP Packet.
|
|
// It takes an NDIS_PACKET, which has the IP data portion in NDIS_BUFFERs
|
|
// linked to it, as its input. The FWContext for the packet must
|
|
// have the header, header buffer and options set up.
|
|
// It adds the options up front and then adds the header before that
|
|
//
|
|
|
|
#define UnlinkDataFromPacket(_pPacket, _pFWC) \
|
|
{ \
|
|
PNDIS_BUFFER _pDataBuff, _pHeaderBuff; \
|
|
PVOID _pvVirtualAddress; \
|
|
ULONG _ulBufLen; \
|
|
RtAssert(_pFWC == (FWContext *)_pPacket->ProtocolReserved); \
|
|
_pDataBuff = _pPacket->Private.Head; \
|
|
_pHeaderBuff = _pFWC->fc_hndisbuff; \
|
|
_pPacket->Private.Head = _pHeaderBuff; \
|
|
_pPacket->Private.Tail = _pHeaderBuff; \
|
|
NDIS_BUFFER_LINKAGE(_pHeaderBuff) = NULL; \
|
|
_pPacket->Private.TotalLength = sizeof(IPHeader); \
|
|
_pPacket->Private.Count = 1; \
|
|
_pvVirtualAddress = NdisBufferVirtualAddress(_pHeaderBuff); \
|
|
_pPacket->Private.PhysicalCount = \
|
|
ADDRESS_AND_SIZE_TO_SPAN_PAGES(_pvVirtualAddress, \
|
|
sizeof(IPHeader)); \
|
|
}
|
|
|
|
//
|
|
// Code to dump the header of a packet. For debug purposes
|
|
//
|
|
|
|
#define DumpIpHeader(s,e,p) \
|
|
Trace(s,e, \
|
|
("Src %d.%d.%d.%d Dest %d.%d.%d.%d\n", \
|
|
PRINT_IPADDR((p)->iph_src), \
|
|
PRINT_IPADDR((p)->iph_dest))); \
|
|
Trace(s,e, \
|
|
("HdrLen %d Total Len %d\n", \
|
|
((((p)->iph_verlen)&0x0f)<<2), \
|
|
net_short((p)->iph_length))); \
|
|
Trace(s,e, \
|
|
("TTL %d XSum %x\n",(p)->iph_ttl, (p)->iph_xsum))
|
|
|
|
|
|
//
|
|
// Since this is used both in IPMForwardAfterRcv and IPMForwardAfterRcvPkt,
|
|
// we put the code here so bugs can be fixed in one place
|
|
//
|
|
|
|
#if MREF_DEBUG
|
|
|
|
#define InitForwardContext() \
|
|
{ \
|
|
pFWC = (FWContext *)pnpNewPacket->ProtocolReserved; \
|
|
RtAssert(pFWC->fc_buffhead is NULL); \
|
|
RtAssert(pFWC->fc_hbuff is pNewHeader); \
|
|
RtAssert(pFWC->fc_optlength is 0); \
|
|
RtAssert(pFWC->fc_options is NULL); \
|
|
RtAssert(!(pFWC->FCFLAGS & PACKET_FLAG_OPTIONS)); \
|
|
RtAssert(pFWC->FCFLAGS & PACKET_FLAG_FW); \
|
|
RtAssert(!(pFWC->FCFLAGS & PACKET_FLAG_MFWD)); \
|
|
pFWC->FCFLAGS |= PACKET_FLAG_MFWD; \
|
|
pFWC->fc_options = NULL; \
|
|
pFWC->fc_optlength = 0; \
|
|
pFWC->fc_if = NULL; \
|
|
pFWC->fc_mtu = 0; \
|
|
pFWC->fc_srcnte = pPrimarySrcNte; \
|
|
pFWC->fc_nexthop = 0; \
|
|
pFWC->fc_sos = DisableSendOnSource; \
|
|
pFWC->fc_dtype = DEST_REM_MCAST; \
|
|
pFWC->fc_pc.pc_br = NULL; \
|
|
if(pLink) { CTEInterlockedIncrementLong(&(pLink->link_refcount)); } \
|
|
pFWC->fc_iflink = pLink; \
|
|
}
|
|
|
|
#else
|
|
|
|
#define InitForwardContext() \
|
|
{ \
|
|
pFWC = (FWContext *)pnpNewPacket->ProtocolReserved; \
|
|
RtAssert(pFWC->fc_buffhead is NULL); \
|
|
RtAssert(pFWC->fc_hbuff is pNewHeader); \
|
|
RtAssert(pFWC->fc_optlength is 0); \
|
|
RtAssert(pFWC->fc_options is NULL); \
|
|
RtAssert(!(pFWC->FCFLAGS & PACKET_FLAG_OPTIONS)); \
|
|
RtAssert(pFWC->FCFLAGS & PACKET_FLAG_FW); \
|
|
pFWC->fc_options = NULL; \
|
|
pFWC->fc_optlength = 0; \
|
|
pFWC->fc_if = NULL; \
|
|
pFWC->fc_mtu = 0; \
|
|
pFWC->fc_srcnte = pPrimarySrcNte; \
|
|
pFWC->fc_nexthop = 0; \
|
|
pFWC->fc_sos = DisableSendOnSource; \
|
|
pFWC->fc_dtype = DEST_REM_MCAST; \
|
|
pFWC->fc_pc.pc_br = NULL; \
|
|
if(pLink) { CTEInterlockedIncrementLong(&(pLink->link_refcount)); } \
|
|
pFWC->fc_iflink = pLink; \
|
|
}
|
|
|
|
#endif
|
|
|
|
#define ProcessWrongIfUpcall(pIf, pSrc, pLink, pHdr, ulHdrLen, pvOpt, ulOptLen, pvData, ulDataLen) \
|
|
{ \
|
|
if(pIf->if_mcastflags & IPMCAST_IF_WRONG_IF) \
|
|
{ \
|
|
PEXCEPT_IF pTempIf; \
|
|
BOOLEAN bWrong = TRUE; \
|
|
LONGLONG llCurrentTime, llTime; \
|
|
\
|
|
KeQueryTickCount((PLARGE_INTEGER)&llCurrentTime); \
|
|
llTime = llCurrentTime - pIf->if_lastupcall; \
|
|
\
|
|
if((llCurrentTime > pIf->if_lastupcall) && (llTime < SECS_TO_TICKS(UPCALL_PERIOD))) { \
|
|
\
|
|
bWrong = FALSE; \
|
|
\
|
|
} else { \
|
|
\
|
|
for(pTempIf = (pSrc)->pFirstExceptIf; \
|
|
pTempIf != NULL; \
|
|
pTempIf = pTempIf->pNextExceptIf) \
|
|
{ \
|
|
if(pTempIf->dwIfIndex == (pIf)->if_index) \
|
|
{ \
|
|
bWrong = FALSE; \
|
|
break; \
|
|
} \
|
|
} \
|
|
} \
|
|
\
|
|
if(bWrong) \
|
|
{ \
|
|
SendWrongIfUpcall((pIf), (pLink), (pHdr), (ulHdrLen), (pvOpt), (ulOptLen), (pvData), (ulDataLen)); \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
|
|
//
|
|
// Again common code, but this is too big to put as a #define.
|
|
// Make it inline for better speed
|
|
//
|
|
|
|
//
|
|
// MUST BE PAGED IN
|
|
//
|
|
|
|
#pragma alloc_text(PAGEIPMc, ProcessOptions)
|
|
|
|
NDIS_STATUS
|
|
__inline
|
|
ProcessOptions(
|
|
FWContext *pFWC,
|
|
ULONG ulHeaderLength,
|
|
IPHeader UNALIGNED *pHeader
|
|
)
|
|
{
|
|
IPOptInfo oiOptInfo;
|
|
BYTE byErrIndex;
|
|
|
|
pFWC->fc_index.oi_srtype = NO_SR;
|
|
pFWC->fc_index.oi_srindex = MAX_OPT_SIZE;
|
|
pFWC->fc_index.oi_rrindex = MAX_OPT_SIZE;
|
|
pFWC->fc_index.oi_tsindex = MAX_OPT_SIZE;
|
|
|
|
oiOptInfo.ioi_options = (uchar *)(pHeader + 1);
|
|
oiOptInfo.ioi_optlength = (BYTE)(ulHeaderLength - sizeof(IPHeader));
|
|
|
|
byErrIndex = ParseRcvdOptions(&oiOptInfo,
|
|
&pFWC->fc_index);
|
|
if(byErrIndex < MAX_OPT_SIZE)
|
|
{
|
|
return NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
pFWC->fc_options = CTEAllocMem(oiOptInfo.ioi_optlength);
|
|
|
|
if(pFWC->fc_options is NULL)
|
|
{
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
// copy the options across
|
|
RtlCopyMemory( pFWC->fc_options,
|
|
oiOptInfo.ioi_options,
|
|
oiOptInfo.ioi_optlength );
|
|
|
|
pFWC->fc_optlength = oiOptInfo.ioi_optlength;
|
|
|
|
pFWC->FCFLAGS |= PACKET_FLAG_OPTIONS;
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Routines //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// MUST BE PAGED IN
|
|
//
|
|
|
|
#pragma alloc_text(PAGEIPMc, IPMForwardAfterTD)
|
|
|
|
VOID
|
|
IPMForwardAfterTD(
|
|
NetTableEntry *pPrimarySrcNte,
|
|
PNDIS_PACKET pnpPacket,
|
|
UINT uiBytesCopied
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the function called by IPTDComplete when a Transfer Data completes
|
|
and it figures out that the packet was a multicast that needed to be
|
|
forwarded
|
|
Unlike the unicast code, TD is called very early on in the forward routine
|
|
(before the main forwarding workhorse is called).
|
|
We need to patch up the NDIS_PACKET so that the header, options and data are
|
|
in the right order. Then we call the main forwarding function
|
|
|
|
Locks:
|
|
|
|
|
|
Arguments:
|
|
|
|
pPrimarySrcNte
|
|
pnpPacket
|
|
uiBytesCopied
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
FWContext *pFWC;
|
|
|
|
//
|
|
// DONT CALL ENTERDRIVER() HERE BECAUSE WE DID NOT CALL EXITDRIVER
|
|
// IF TD WAS PENDING
|
|
//
|
|
|
|
TraceEnter(FWD, "IPMForwardAfterTD");
|
|
|
|
pFWC = (FWContext *)pnpPacket->ProtocolReserved;
|
|
|
|
RtAssert(uiBytesCopied is pFWC->fc_datalength);
|
|
|
|
//
|
|
// After TD we get the data portion in the packet and the options, are in
|
|
// the Just call the main multicast function with the right arguments
|
|
//
|
|
|
|
|
|
IPMForward(pnpPacket,
|
|
NULL,
|
|
FALSE);
|
|
|
|
TraceLeave(FWD, "IPMForwardAfterTD");
|
|
|
|
ExitDriver();
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// MUST BE PAGED IN
|
|
//
|
|
|
|
#pragma alloc_text(PAGEIPMc, IPMForwardAfterRcv)
|
|
|
|
BOOLEAN
|
|
IPMForwardAfterRcv(
|
|
NetTableEntry *pPrimarySrcNte,
|
|
IPHeader UNALIGNED *pHeader,
|
|
ULONG ulHeaderLength,
|
|
PVOID pvData,
|
|
ULONG ulBufferLength,
|
|
NDIS_HANDLE LContext1,
|
|
UINT LContext2,
|
|
BYTE byDestType,
|
|
LinkEntry *pLink
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the forwarding function called from IPRcv.
|
|
We look up the (S,G) entry.
|
|
If the entry is present, we do the RPF check. If it fails or if the
|
|
entry was NEGATIVE, we toss the packet out.
|
|
(The case of no entry is covered in IPMForward)
|
|
|
|
We get a new packet and header and fill that up. We set up the
|
|
forwarding context, and then check if we need to do a Transfer Data. If
|
|
so we call the lower layer's TD routine. If that returns pending, we
|
|
are done. If the TD is synchronous or was not needed at all, we set
|
|
the NDIS_PACKET so that the header, options and data are all properly
|
|
chained. Then we call the main forwarding routine
|
|
|
|
Locks:
|
|
|
|
|
|
Arguments:
|
|
|
|
SrcNTE - Pointer to NTE on which packet was received.
|
|
Packet - Packet being forwarded, used for TD.
|
|
Data - Pointer to data buffer being forwarded.
|
|
DataLength - Length in bytes of Data.
|
|
BufferLength - Length in bytes available in buffer pointer to by Data.
|
|
Offset - Offset into original data from which to transfer.
|
|
LContext1, LContext2 - Context values for the link layer.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the IP filter-driver needs to be notified, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
Interface *pInIf;
|
|
PSOURCE pSource;
|
|
IPHeader *pNewHeader;
|
|
FWContext *pFWC;
|
|
ULONG ulDataLength, ulCopyBufLen;
|
|
ULONG ulDataLeft, ulNumBufs;
|
|
PVOID pvCopyPtr;
|
|
NDIS_STATUS nsStatus;
|
|
|
|
|
|
PNDIS_PACKET pnpNewPacket;
|
|
PNDIS_BUFFER pnbNewBuffer, pnbCopyBuffer;
|
|
|
|
|
|
#if DBG
|
|
ULONG ulBufCopied;
|
|
#endif
|
|
|
|
EnterDriverWithStatus(FALSE);
|
|
|
|
TraceEnter(RCV, "IPMForwardAfterRcv");
|
|
|
|
pInIf = pPrimarySrcNte->nte_if;
|
|
|
|
RtAssert(pInIf isnot &DummyInterface);
|
|
|
|
DumpIpHeader(RCV, INFO, pHeader);
|
|
|
|
Trace(RCV, INFO,
|
|
("IPMForwardAfterRcv: Incoming interface at 0x%x is %d\n",
|
|
pInIf, pInIf->if_index));
|
|
|
|
//
|
|
// Lookup the (S,G) entry and see if this needs to be discarded, if so
|
|
// throw it away
|
|
//
|
|
|
|
pSource = FindSGEntry(pHeader->iph_src,
|
|
pHeader->iph_dest);
|
|
|
|
if(pSource isnot NULL)
|
|
{
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcv: Found Source at 0x%x. In i/f is 0x%x. State is %x\n",
|
|
pSource, pSource->pInIpIf, pSource->byState));
|
|
|
|
//
|
|
// If the source doesnt exist we will do the right thing
|
|
// in IPMForwardPkt()
|
|
//
|
|
|
|
switch(pSource->byState)
|
|
{
|
|
case MFE_UNINIT:
|
|
{
|
|
pSource->ulInPkts++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
pSource->ulUninitMfe++;
|
|
|
|
RtAssert(FALSE);
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
ExitDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case MFE_NEGATIVE:
|
|
{
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcv: Negative MFE \n"));
|
|
|
|
pSource->ulInPkts++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
pSource->ulNegativeMfe++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
ExitDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case MFE_QUEUE:
|
|
{
|
|
//
|
|
// if we are going to drop the packet, may as well do it
|
|
// now, before we allocate and resources
|
|
//
|
|
|
|
if(pSource->ulNumPending >= MAX_PENDING)
|
|
{
|
|
pSource->ulInPkts++;
|
|
pSource->ulQueueOverflow++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
Trace(RCV, INFO,
|
|
("IPMForwardAfterRcv: MFE Queue is full\n"));
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case MFE_INIT:
|
|
{
|
|
if(pInIf isnot pSource->pInIpIf)
|
|
{
|
|
UpdateActivityTime(pSource);
|
|
|
|
//
|
|
// See if we need to generate a wrong i/f upcall
|
|
//
|
|
|
|
ProcessWrongIfUpcall(pInIf,
|
|
pSource,
|
|
pLink,
|
|
pHeader,
|
|
ulHeaderLength,
|
|
NULL,
|
|
0,
|
|
pvData,
|
|
ulBufferLength);
|
|
|
|
//
|
|
// If the packet shouldnt be accepted - stop now
|
|
//
|
|
|
|
if(!(pInIf->if_mcastflags & IPMCAST_IF_ACCEPT_ALL))
|
|
{
|
|
pSource->ulInPkts++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
pSource->ulPktsDifferentIf++;
|
|
|
|
Trace(RCV, ERROR,
|
|
("IPMForwardAfterRcv: Pkt from %d.%d.%d.%d to %d.%d.%d.%d came in on 0x%x instead of 0x%x\n",
|
|
PRINT_IPADDR(pHeader->iph_src),
|
|
PRINT_IPADDR(pHeader->iph_dest),
|
|
pInIf ? pInIf->if_index : 0,
|
|
pSource->pInIpIf ? pSource->pInIpIf->if_index : 0));
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcv: RPF failed \n"));
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
ExitDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
RtAssert(FALSE);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have come in through Receive Indication, means we dont
|
|
// have ownership of the packet. So we copy out to our
|
|
// own packet
|
|
//
|
|
|
|
//
|
|
// Get a header for the packet. We use the incoming interface as
|
|
// the IF
|
|
//
|
|
|
|
|
|
if((pNewHeader = GetFWPacket(&pnpNewPacket)) is NULL)
|
|
{
|
|
if(pSource)
|
|
{
|
|
pSource->ulInDiscards++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
Trace(RCV, ERROR,
|
|
("IPMForwardAfterRcv: Unable to get new packet/header \n"));
|
|
|
|
//
|
|
// Could not get a packet. We have not allocated anything as yet
|
|
// so just bail out
|
|
//
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Should see which is more effecient - RtlCopyMemory or structure
|
|
// assignment
|
|
//
|
|
|
|
RtlCopyMemory(pNewHeader,
|
|
pHeader,
|
|
sizeof(IPHeader));
|
|
|
|
//
|
|
// Macro defined above
|
|
//
|
|
|
|
#if MCAST_COMP_DBG
|
|
|
|
Trace(FWD, INFO, ("IPMForwardAfterRcv: New Packet 0x%x New Header 0x%x\n",pnpNewPacket, pNewHeader));
|
|
|
|
((PacketContext *)pnpNewPacket->ProtocolReserved)->PCFLAGS |= PACKET_FLAG_IPMCAST;
|
|
|
|
#endif
|
|
|
|
InitForwardContext();
|
|
|
|
if(ulHeaderLength isnot sizeof(IPHeader))
|
|
{
|
|
//
|
|
// We have options, Do the Right Thing (TM)
|
|
//
|
|
|
|
nsStatus = ProcessOptions(pFWC,
|
|
ulHeaderLength,
|
|
pHeader);
|
|
|
|
if(nsStatus isnot NDIS_STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// No ICMP packet if we fail
|
|
//
|
|
|
|
if(pSource)
|
|
{
|
|
pSource->ulInHdrErrors++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
IPSInfo.ipsi_inhdrerrors++;
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
|
|
FreeFWPacket(pnpNewPacket);
|
|
|
|
ExitDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Total size of the IP Datagram sans header and options
|
|
//
|
|
|
|
ulDataLength = net_short(pHeader->iph_length) - ulHeaderLength;
|
|
|
|
pFWC->fc_datalength = ulDataLength;
|
|
|
|
//
|
|
// Get the buffers for the packet. This routine
|
|
// chains the buffers to the front of the packet
|
|
//
|
|
|
|
if (!ulDataLength)
|
|
{
|
|
pnbNewBuffer = NULL;
|
|
ulNumBufs = 0;
|
|
nsStatus = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
nsStatus = AllocateCopyBuffers(pnpNewPacket,
|
|
ulDataLength,
|
|
&pnbNewBuffer,
|
|
&ulNumBufs);
|
|
}
|
|
|
|
if(nsStatus isnot STATUS_SUCCESS)
|
|
{
|
|
if(pSource)
|
|
{
|
|
pSource->ulInDiscards++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
Trace(RCV, ERROR,
|
|
("IPMForwardAfterRcv: Unable to allocate buffers for copying\n"));
|
|
|
|
//
|
|
// At this point we have allocate the packet and possibly, space
|
|
// for options. FreeFWPacket takes care of all that provided
|
|
// the fc_options points to the options. It however does not
|
|
// clear the option flag
|
|
//
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpNewPacket);
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// See if the packet requires a transfer data
|
|
//
|
|
|
|
if(ulDataLength <= ulBufferLength)
|
|
{
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcv: All data is present, copying\n"));
|
|
|
|
//
|
|
// Everything here copy and call the main forwarding function
|
|
//
|
|
|
|
pnbCopyBuffer = pnbNewBuffer;
|
|
ulDataLeft = ulDataLength;
|
|
|
|
#if DBG
|
|
ulBufCopied = 0;
|
|
#endif
|
|
|
|
while(ulDataLeft)
|
|
{
|
|
//
|
|
// TODO: This is inefficient. Figure out a better way.
|
|
//
|
|
|
|
TcpipQueryBuffer(pnbCopyBuffer,
|
|
&pvCopyPtr,
|
|
&ulCopyBufLen,
|
|
NormalPagePriority);
|
|
|
|
if(pvCopyPtr is NULL)
|
|
{
|
|
nsStatus = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
|
|
RtlCopyMemory(pvCopyPtr,
|
|
pvData,
|
|
ulCopyBufLen);
|
|
|
|
pvData = (PVOID)((PBYTE)pvData + ulCopyBufLen);
|
|
|
|
ulDataLeft -= ulCopyBufLen;
|
|
pnbCopyBuffer = NDIS_BUFFER_LINKAGE(pnbCopyBuffer);
|
|
|
|
#if DBG
|
|
ulBufCopied++;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Cleanup on data copy failure.
|
|
//
|
|
|
|
if (nsStatus isnot STATUS_SUCCESS)
|
|
{
|
|
if(pSource)
|
|
{
|
|
pSource->ulInDiscards++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
Trace(RCV, ERROR,
|
|
("IPMForwardAfterRcv: Unable to copy data\n"));
|
|
|
|
//
|
|
// At this point we have allocate the packet and possibly, sp
|
|
// for options. FreeFWPacket takes care of all that provided
|
|
// the fc_options points to the options. It however does not
|
|
// clear the option flag
|
|
//
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpNewPacket);
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
RtAssert(ulBufCopied is ulNumBufs);
|
|
#endif
|
|
|
|
nsStatus = IPMForward(pnpNewPacket,
|
|
pSource,
|
|
FALSE);
|
|
|
|
//
|
|
// Do not need to release the lock or deref source because
|
|
// IPMForward would have done it
|
|
//
|
|
}
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Either all the data is not around, or lower layer
|
|
// wants to force us to do a TD
|
|
//
|
|
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcv: Datagram size is %d, buffer is %d. Copy flag is %s. TD needed\n",
|
|
ulDataLength,
|
|
ulBufferLength,
|
|
(pPrimarySrcNte->nte_flags & NTE_COPY)? "SET":"CLEARED"));
|
|
|
|
//
|
|
// Call the transfer data function
|
|
//
|
|
|
|
nsStatus = (pInIf->if_transfer)(pInIf->if_lcontext,
|
|
LContext1,
|
|
LContext2,
|
|
ulHeaderLength,
|
|
ulDataLength,
|
|
pnpNewPacket,
|
|
&(pFWC->fc_datalength));
|
|
|
|
if(nsStatus isnot NDIS_STATUS_PENDING)
|
|
{
|
|
if(nsStatus isnot NDIS_STATUS_SUCCESS)
|
|
{
|
|
if(pSource)
|
|
{
|
|
pSource->ulInDiscards++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
Trace(RCV, ERROR,
|
|
("IPMForwardAfterRcv: TD failed status %X\n",
|
|
nsStatus));
|
|
|
|
//
|
|
// Failed for some reason, bail out here
|
|
// Since we have allocated resources, call the send
|
|
// completion routine
|
|
//
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpNewPacket);
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// TD finished synchronously
|
|
//
|
|
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcv: TD returned synchronously\n"));
|
|
|
|
nsStatus = IPMForward(pnpNewPacket,
|
|
pSource,
|
|
FALSE);
|
|
|
|
//
|
|
// Again, dont release or deref
|
|
//
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Transfer is pending
|
|
//
|
|
|
|
//
|
|
// The source info is lost across transfers if the Xfer is not
|
|
// synchronouse
|
|
//
|
|
|
|
if(pSource)
|
|
{
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcv: TD is pending\n"));
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcv");
|
|
|
|
//
|
|
// DO NOT CALL EXITDRIVER() HERE SINCE THE XFER DATA IS PENDING
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// MUST BE PAGED IN
|
|
//
|
|
|
|
#pragma alloc_text(PAGEIPMc, IPMForwardAfterRcvPkt)
|
|
|
|
BOOLEAN
|
|
IPMForwardAfterRcvPkt(
|
|
NetTableEntry *pPrimarySrcNte,
|
|
IPHeader UNALIGNED *pHeader,
|
|
ULONG ulHeaderLength,
|
|
PVOID pvData,
|
|
ULONG ulBufferLength,
|
|
NDIS_HANDLE LContext1,
|
|
UINT LContext2,
|
|
BYTE byDestType,
|
|
UINT uiMacHeaderSize,
|
|
PNDIS_BUFFER pNdisBuffer,
|
|
uint *pClientCnt,
|
|
LinkEntry *pLink
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called from when we get a ReceivePacket indication
|
|
We look up the (S,G) entry. If the entry is not present, we copy and queue
|
|
the packet, and complete an IRP up to the Router Manager.
|
|
If the entry is present, we do the RPF check. If it fails we toss the
|
|
packet out.
|
|
If the (S,G) entry is a negative cache, we discard the packet
|
|
If the entry is queueing at present, we copy and queue the packet
|
|
|
|
Then we create a new packet since the Miniport reserved fields are being
|
|
used by the receive miniport. We set up the forwarding context and munge
|
|
the old header.
|
|
|
|
If there is only one outgoing interface (no need to copy), no
|
|
fragmentation is needed, no demand dial needs to be done, there are no
|
|
options and there is no padding put on by the lower layers, we take the
|
|
fast path and send the same packet out
|
|
|
|
On the slow path, we copy out the packet and header and call the main
|
|
forwarding function
|
|
|
|
Locks:
|
|
|
|
|
|
Arguments:
|
|
|
|
pPrimarySrcNte
|
|
pHeader
|
|
ulHeaderLength
|
|
pvData
|
|
ulBufferLength
|
|
LContext1
|
|
LContext2
|
|
byDestType
|
|
uiMacHeaderSize
|
|
pNdisBuffer
|
|
pClientCnt
|
|
pLink
|
|
|
|
Return Value:
|
|
|
|
TRUE if the IP filter-driver needs to be notified, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
Interface *pInIf;
|
|
PSOURCE pSource;
|
|
IPHeader *pNewHeader;
|
|
FWContext *pFWC;
|
|
ULONG ulBytesCopied;
|
|
ULONG ulDataLength, ulSrcOffset;
|
|
ULONG ulNumBufs, ulBuffCount;
|
|
NDIS_STATUS nsStatus;
|
|
PNDIS_BUFFER pCurrentNdisBuffer;
|
|
PVOID pvDataToCopy;
|
|
POUT_IF pOutIf;
|
|
IPOptInfo oiOptInfo;
|
|
BOOLEAN bHoldPacket;
|
|
PNDIS_PACKET pnpNewPacket;
|
|
PNDIS_BUFFER pnbNewBuffer;
|
|
FORWARD_ACTION faAction;
|
|
ULONG xsum;
|
|
|
|
#if DBG
|
|
|
|
ULONG ulPacketLength;
|
|
|
|
#endif
|
|
|
|
EnterDriverWithStatus(FALSE);
|
|
|
|
TraceEnter(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
//
|
|
// Set client count to 0 so that the lower layer doesnt
|
|
// think we are holding on to the packet, if we bail out
|
|
//
|
|
|
|
*pClientCnt = 0;
|
|
bHoldPacket = TRUE;
|
|
|
|
|
|
pInIf = pPrimarySrcNte->nte_if;
|
|
|
|
RtAssert(pInIf isnot &DummyInterface);
|
|
|
|
DumpIpHeader(RCV, INFO, pHeader);
|
|
|
|
Trace(RCV, INFO,
|
|
("IPMForwardAfterRcvPkt: Incoming interface at 0x%x is %d\n",
|
|
pInIf, pInIf->if_index));
|
|
|
|
//
|
|
// As usual, first thing is to lookup the (S,G) entry for this packet
|
|
//
|
|
|
|
pSource = FindSGEntry(pHeader->iph_src,
|
|
pHeader->iph_dest);
|
|
|
|
if(pSource isnot NULL)
|
|
{
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcvPkt: Found Source at 0x%x. In i/f is 0x%x. State is %x\n",
|
|
pSource, pSource->pInIpIf, pSource->byState));
|
|
|
|
//
|
|
// If the source doesnt exist we will do the right thing
|
|
// in IPMForwardPkt()
|
|
//
|
|
|
|
//
|
|
// We only increment statistics if we are not going to
|
|
// call IPMForward().
|
|
//
|
|
|
|
switch(pSource->byState)
|
|
{
|
|
case MFE_UNINIT:
|
|
{
|
|
pSource->ulInPkts++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
pSource->ulUninitMfe++;
|
|
|
|
RtAssert(FALSE);
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case MFE_NEGATIVE:
|
|
{
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcvPkt: Negative MFE \n"));
|
|
|
|
pSource->ulInPkts++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
pSource->ulNegativeMfe++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case MFE_QUEUE:
|
|
{
|
|
if(pSource->ulNumPending >= MAX_PENDING)
|
|
{
|
|
pSource->ulInPkts++;
|
|
pSource->ulQueueOverflow++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
Trace(RCV, INFO,
|
|
("IPMForwardAfterRcvPkt: MFE Queue is full\n"));
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
pOutIf = NULL;
|
|
|
|
bHoldPacket = FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
case MFE_INIT:
|
|
{
|
|
if(pInIf isnot pSource->pInIpIf)
|
|
{
|
|
UpdateActivityTime(pSource);
|
|
|
|
//
|
|
// See if we need to generate a wrong i/f upcall
|
|
//
|
|
|
|
ProcessWrongIfUpcall(pInIf,
|
|
pSource,
|
|
pLink,
|
|
pHeader,
|
|
ulHeaderLength,
|
|
NULL,
|
|
0,
|
|
pvData,
|
|
ulBufferLength);
|
|
|
|
//
|
|
// If the packet shouldnt be accepted - stop now
|
|
//
|
|
|
|
if(!(pInIf->if_mcastflags & IPMCAST_IF_ACCEPT_ALL))
|
|
{
|
|
pSource->ulInPkts++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
pSource->ulPktsDifferentIf++;
|
|
|
|
Trace(RCV, ERROR,
|
|
("IPMForwardAfterRcvPkt: Pkt from %d.%d.%d.%d to %d.%d.%d.%d came in on 0x%x instead of 0x%x\n",
|
|
PRINT_IPADDR(pHeader->iph_src),
|
|
PRINT_IPADDR(pHeader->iph_dest),
|
|
pInIf ? pInIf->if_index : 0,
|
|
pSource->pInIpIf ? pSource->pInIpIf->if_index : 0));
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
Trace(RCV, TRACE,
|
|
("IPMForwardAfterRcvPkt: RPF failed \n"));
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
pOutIf = pSource->pFirstOutIf;
|
|
|
|
RtAssert(pOutIf);
|
|
|
|
bHoldPacket = (pOutIf->pIpIf isnot &DummyInterface);
|
|
bHoldPacket = (pSource->ulNumOutIf is 1);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
RtAssert(FALSE);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bHoldPacket = FALSE;
|
|
}
|
|
|
|
//
|
|
// Since this function was called due to ReceivePacket, we dont have
|
|
// ownership of the Protocol reserved section, so allocate a new packet
|
|
// Unfortunately, getting a new packet, causes a new header to be allocate
|
|
// but what the heck, we will go with that scheme instead of inventing
|
|
// our own buffer management
|
|
//
|
|
|
|
//
|
|
// For the interface we use the INCOMING interface.
|
|
// And we specify DestType to be DEST_REMOTE. This way the queue looked at
|
|
// is the interface queue, as opposed to the global bcast queue.
|
|
//
|
|
|
|
|
|
if((pNewHeader = GetFWPacket(&pnpNewPacket)) is NULL)
|
|
{
|
|
if(pSource)
|
|
{
|
|
pSource->ulInDiscards++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
//
|
|
// Could not get a packet. We have not allocated anything as yet
|
|
// so just bail out
|
|
//
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
|
|
Trace(RCV, ERROR,
|
|
("IPMForwardAfterRcvPkt: Unable to get new packet/header\n"));
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// So we have a new packet. Fix up the packet
|
|
// Save the packet forwarding context info.
|
|
//
|
|
|
|
#if MCAST_COMP_DBG
|
|
|
|
Trace(FWD, INFO, ("IPMForwardAfterRcvPkt: New Packet 0x%x New Header 0x%x\n",pnpNewPacket, pNewHeader));
|
|
|
|
((PacketContext *)pnpNewPacket->ProtocolReserved)->PCFLAGS |= PACKET_FLAG_IPMCAST;
|
|
|
|
#endif
|
|
|
|
InitForwardContext();
|
|
|
|
if(ulHeaderLength isnot sizeof(IPHeader))
|
|
{
|
|
nsStatus = ProcessOptions(pFWC,
|
|
ulHeaderLength,
|
|
pHeader);
|
|
|
|
if(nsStatus isnot NDIS_STATUS_SUCCESS)
|
|
{
|
|
if(pSource)
|
|
{
|
|
pSource->ulInHdrErrors++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
IPSInfo.ipsi_inhdrerrors++;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpNewPacket);
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
bHoldPacket = FALSE;
|
|
}
|
|
|
|
ulDataLength = net_short(pHeader->iph_length) - ulHeaderLength;
|
|
|
|
pFWC->fc_datalength = ulDataLength;
|
|
|
|
//
|
|
// Now we can try for the fast forward path. For that
|
|
// (i) we should have a complete MFE
|
|
// (ii) the number of outgoing interface should be 1
|
|
// (iii) fragmentation should not be needed
|
|
// (iv) the lower layer driver should not be running out of buffers,
|
|
// (v) no demand dial should be necessary
|
|
// (vi) no options should be present
|
|
// (vii) IMPORTANT - there is no padding at the end of the buffer
|
|
//
|
|
|
|
|
|
|
|
if((bHoldPacket) and
|
|
(net_short(pHeader->iph_length) <= (USHORT)(pOutIf->pIpIf->if_mtu)) and
|
|
(NDIS_GET_PACKET_STATUS((PNDIS_PACKET)LContext1) isnot NDIS_STATUS_RESOURCES))
|
|
{
|
|
|
|
RtAssert(pOutIf->pNextOutIf is NULL);
|
|
RtAssert(pSource);
|
|
RtAssert(pOutIf->pIpIf isnot &DummyInterface);
|
|
RtAssert(!pFWC->fc_options);
|
|
|
|
#if DBG
|
|
|
|
if(pFWC->fc_options)
|
|
{
|
|
RtAssert(pFWC->fc_optlength);
|
|
RtAssert(pFWC->FCFLAGS & PACKET_FLAG_OPTIONS);
|
|
}
|
|
else
|
|
{
|
|
RtAssert(pFWC->fc_optlength is 0);
|
|
RtAssert(!(pFWC->FCFLAGS & PACKET_FLAG_OPTIONS));
|
|
}
|
|
|
|
#endif
|
|
|
|
Trace(FWD, INFO,
|
|
("IPMForwardAfterRcvPkt: Fast Forwarding packet\n"));
|
|
|
|
pFWC->fc_bufown = LContext1;
|
|
pFWC->fc_MacHdrSize = uiMacHeaderSize;
|
|
|
|
pFWC->fc_nexthop = pOutIf->dwNextHopAddr;
|
|
|
|
//
|
|
// Munge ttl and xsum fields
|
|
//
|
|
|
|
pHeader->iph_ttl--;
|
|
|
|
if(pHeader->iph_ttl < pOutIf->pIpIf->if_mcastttl)
|
|
{
|
|
//
|
|
// TTL is lower than scope
|
|
//
|
|
|
|
InterlockedIncrement(&(pOutIf->ulTtlTooLow));
|
|
|
|
Trace(FWD, WARN,
|
|
("IPMForwardAfterRcvPkt: Packet ttl is %d, I/f ttl is %d. Dropping\n",
|
|
pHeader->iph_ttl,
|
|
pOutIf->pIpIf->if_mcastttl));
|
|
|
|
//
|
|
// Here we always have a source
|
|
//
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
xsum = pHeader->iph_xsum + 1;
|
|
xsum = (ushort)(xsum + (xsum >> 16));
|
|
pHeader->iph_xsum = (USHORT)xsum;
|
|
|
|
//
|
|
// See if we need to filter
|
|
//
|
|
|
|
if(ForwardFilterEnabled)
|
|
{
|
|
//
|
|
// We have a pointer to the header and we have
|
|
// a pointer to the data - alles ok
|
|
//
|
|
|
|
CTEInterlockedIncrementLong(&ForwardFilterRefCount);
|
|
|
|
faAction = (*ForwardFilterPtr) (pHeader,
|
|
pvData,
|
|
ulBufferLength,
|
|
pInIf->if_index,
|
|
pOutIf->pIpIf->if_index,
|
|
NULL_IP_ADDR, NULL_IP_ADDR);
|
|
|
|
DerefFilterPtr();
|
|
|
|
if(faAction isnot FORWARD)
|
|
{
|
|
InterlockedIncrement(&(pOutIf->ulOutDiscards));
|
|
|
|
//
|
|
// We are assured of a source
|
|
//
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
//DerefIF(IF);
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adjust incoming mdl pointer and counts
|
|
//
|
|
|
|
NdisAdjustBuffer(
|
|
pNdisBuffer,
|
|
(PCHAR) NdisBufferVirtualAddress(pNdisBuffer) + uiMacHeaderSize,
|
|
NdisBufferLength(pNdisBuffer) - uiMacHeaderSize
|
|
);
|
|
|
|
//
|
|
// Now link this mdl to the packet
|
|
//
|
|
|
|
pnpNewPacket->Private.Head = pNdisBuffer;
|
|
pnpNewPacket->Private.Tail = pNdisBuffer;
|
|
|
|
RtAssert(pNdisBuffer->Next is NULL);
|
|
|
|
pnpNewPacket->Private.TotalLength = ulDataLength + ulHeaderLength;
|
|
pnpNewPacket->Private.Count = 1;
|
|
|
|
UpdateActivityTime(pSource);
|
|
|
|
//
|
|
// Let go of the lock
|
|
//
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
//
|
|
// Mark the packet as not being loop-backed
|
|
//
|
|
|
|
NdisSetPacketFlags(pnpNewPacket,
|
|
NDIS_FLAGS_DONT_LOOPBACK);
|
|
|
|
nsStatus = (*(pOutIf->pIpIf->if_xmit))(pOutIf->pIpIf->if_lcontext,
|
|
&pnpNewPacket,
|
|
1,
|
|
pOutIf->dwNextHopAddr,
|
|
NULL,
|
|
NULL);
|
|
|
|
if(nsStatus isnot NDIS_STATUS_PENDING)
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForwardAfterRcvPkt: Fast Forward completed with status %x\n",
|
|
nsStatus));
|
|
|
|
|
|
NdisAdjustBuffer(
|
|
pNdisBuffer,
|
|
(PCHAR) NdisBufferVirtualAddress(pNdisBuffer) - uiMacHeaderSize,
|
|
NdisBufferLength(pNdisBuffer) + uiMacHeaderSize
|
|
);
|
|
|
|
pnpNewPacket->Private.Head = NULL;
|
|
pnpNewPacket->Private.Tail = NULL;
|
|
|
|
pFWC->fc_bufown = NULL;
|
|
|
|
//
|
|
// Since client count is 0
|
|
// we dont need to call FWSendComplete
|
|
//
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
|
|
FreeFWPacket(pnpNewPacket);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Okay, the xmit is pending indicate this to ndis.
|
|
//
|
|
|
|
*pClientCnt = 1;
|
|
}
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy the header out at this point because if we get into
|
|
// the fast path, the copy would be a waste
|
|
//
|
|
|
|
RtlCopyMemory(pNewHeader,
|
|
pHeader,
|
|
sizeof(IPHeader));
|
|
|
|
//
|
|
// Good old slow path. We already have the header, allocate and copy
|
|
// out the data and pass it to the main function
|
|
//
|
|
|
|
if (!ulDataLength)
|
|
{
|
|
ulNumBufs = 0;
|
|
pnbNewBuffer = NULL;
|
|
nsStatus = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
nsStatus = AllocateCopyBuffers(pnpNewPacket,
|
|
ulDataLength,
|
|
&pnbNewBuffer,
|
|
&ulNumBufs);
|
|
}
|
|
|
|
if(nsStatus isnot STATUS_SUCCESS)
|
|
{
|
|
if(pSource)
|
|
{
|
|
pSource->ulInDiscards++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
Trace(RCV, ERROR,
|
|
("IPMForwardAfterRcvPkt: Unable to allocate buffers for copying\n"));
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpNewPacket);
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now we have a MDL chain which we need to copy to a chain of NDIS buffers
|
|
// which is nothing but another MDL chain.
|
|
// We want to copy out only the data. So we need to start after the
|
|
// header but copy to the beginning of the destination buffer
|
|
//
|
|
|
|
ulSrcOffset = ulHeaderLength + (ULONG)uiMacHeaderSize;
|
|
|
|
nsStatus = TdiCopyMdlChainToMdlChain(pNdisBuffer,
|
|
ulSrcOffset,
|
|
pnbNewBuffer,
|
|
0,
|
|
&ulBytesCopied);
|
|
|
|
if (nsStatus isnot STATUS_SUCCESS)
|
|
{
|
|
ULONG ulNdisPktSize;
|
|
|
|
NdisQueryPacket(pnpNewPacket,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ulNdisPktSize);
|
|
//
|
|
// Some problem here
|
|
//
|
|
|
|
if(pSource)
|
|
{
|
|
pSource->ulInDiscards++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
Trace(RCV,ERROR,
|
|
("IPMForwardAfterRcvPkt: Copying chain with offset %d to %d sized MDL-chain returned %x with %d bytes copied\n",
|
|
ulSrcOffset,
|
|
ulNdisPktSize,
|
|
nsStatus,
|
|
ulBytesCopied));
|
|
|
|
//
|
|
// Free options and option buffer if any
|
|
//
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpNewPacket);
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
NdisQueryPacket(pnpNewPacket,
|
|
NULL,
|
|
&ulBuffCount,
|
|
NULL,
|
|
&ulPacketLength);
|
|
|
|
RtAssert(ulBuffCount is ulNumBufs);
|
|
|
|
RtAssert(ulPacketLength is ulBytesCopied);
|
|
|
|
#endif
|
|
|
|
nsStatus = IPMForward(pnpNewPacket,
|
|
pSource,
|
|
FALSE);
|
|
|
|
//
|
|
// Dont release or deref
|
|
//
|
|
|
|
TraceLeave(RCV, "IPMForwardAfterRcvPkt");
|
|
|
|
ExitDriver();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// MUST BE PAGED IN
|
|
//
|
|
|
|
#pragma alloc_text(PAGEIPMc, IPMForward)
|
|
|
|
NTSTATUS
|
|
IPMForward(
|
|
IN PNDIS_PACKET pnpPacket,
|
|
IN PSOURCE pSource OPTIONAL,
|
|
IN BOOLEAN bSendFromQueue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the main multicast forwarding routine. It is called from
|
|
the three top level forwarding routines (IPMForwardAfterRcv,
|
|
IPMForwardAfterRcvPkt and IPMForwardAfterTD)
|
|
|
|
It is always called with a complete packet (either one buffer or chained
|
|
buffers) and we always have final ownership of the packet.
|
|
|
|
The NDIS_PACKET for the datagram must be a FWPacket and must be
|
|
chained when this function is called. The various parts of the data are:
|
|
|
|
Comp Size Allocated in Stored at
|
|
---------------------------------------------------------------
|
|
Header sizeof(IPHeader) GrowFWPacket fc_hbuff
|
|
Hdr Buffer NDIS_BUFFER GrowFWPacket fc_hndisbuff
|
|
Options fc_optlength ForwardAfterRcv fc_option
|
|
ForwardAfterRcvPkt
|
|
Opt Buffer NDIS_BUFFER SendIPPacket (later) 2nd buffer if
|
|
..FLAG_OPTIONS is set
|
|
Data fc_datalength ForwardAfterRcv fc_buffhead
|
|
ForwardAfterRcvPkt
|
|
|
|
The data is also chained to the NDIS_PACKET as the first buffer
|
|
|
|
The NDIS_PACKET must have the FWContext all setup before this routine
|
|
is called. All necessary information is retrieved using the FWC
|
|
|
|
All this chaining needs to be undone in the routine, since SendIPPacket()
|
|
requires an unchained buffer.
|
|
|
|
We first try and find an (S,G) entry if one is not already passed to us.
|
|
If we dont have an entry, then we copy and queue the packet while
|
|
sending a notification to Router Manager. As as side effect an entry with
|
|
a state of QUEUEING gets created so that other packets coming in just get
|
|
queued here.
|
|
If we do find an entry, then according to the state of the entry, we
|
|
either drop the packet, queue it or continue processing it.
|
|
We do an RPF check and if that fails, the packet is dropped.
|
|
Since we may potentially duplicate the packet (or even fragment it), we
|
|
allocate a BufferReference. The BuffRef keeps track of the ORIGINAL
|
|
BUFFERS. These are the ones that point to the data and were allocated
|
|
out of our FWBuffer pool.
|
|
We copy out the headers and options into a flat buffer to use with
|
|
Filtering callout.
|
|
Then for each IF on the outgoing list:
|
|
We get a pointer to the primary NTE. This is needed to process options
|
|
since we need the address of the outgoing interface
|
|
For all but the last interface, we allocate a new header and new
|
|
packet. (For the last interface we use the packet and header that was
|
|
passed to us in this routine. So for the last interface, the packet,
|
|
header, options and buffer descriptors come from the FWPacket/Buffer
|
|
pool, where as for all other interfaces, the header and packet are
|
|
plain IP buffers and the memory for options is allocated in this
|
|
routine.)
|
|
If there are options, we allocate memory for the options and update
|
|
them.
|
|
Then we see if the packet needs to be fragmented. To do this we use
|
|
the MTU of the outgoing interface. This is different from UNICAST
|
|
where we used the mtu in the route - because that is where the
|
|
updated mtu from PathMTU discovery is kept. Since we dont do path
|
|
MTU discovery for multicast, we just use the MTU of the outgoing i/f
|
|
So if the IP Data length + OptionSize + Header Size > if_mtu, we
|
|
call IPFragment(), otherwise we send the packet out using
|
|
SendIPPacket().
|
|
For each pending send from SendIPPacket(), we increase the refcount
|
|
on the BuffRef. IPFragment() may increase the refcount by more than
|
|
1 for each call because it breaks the packet into two or more packets.
|
|
|
|
NOTE: We pass the original data buffers to SendIPPacket() and to
|
|
IPFragment(). This way we only copy out the header and the options. This
|
|
is better than HenrySa's SendIPBCast() which copies out the whole data.
|
|
|
|
Locks:
|
|
|
|
This code is assumed to run at DPCLevel. If a PSOURCE is passed to the
|
|
function, it is assumed to be Referenced and Locked.
|
|
|
|
Arguments:
|
|
|
|
pnpPacket
|
|
pSource
|
|
bSendFromQueue
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
NetTableEntry *pPrimarySrcNte, *pOutNte;
|
|
IPHeader *pHeader, *pNewHeader;
|
|
FWContext *pFWC;
|
|
PNDIS_PACKET pnpNewPacket;
|
|
PNDIS_BUFFER pnbDataBuffer;
|
|
PBYTE pbyNewOptions;
|
|
POUT_IF pOutIf;
|
|
BufferReference *pBuffRef;
|
|
NTSTATUS nsStatus;
|
|
ULONG ulDataLength, ulSent;
|
|
OptIndex *pOptIndex;
|
|
PacketContext *pPC;
|
|
FORWARD_ACTION faAction;
|
|
PVOID pvData;
|
|
UINT uiFirstBufLen;
|
|
Interface *pInIf;
|
|
DWORD dwNewIndex;
|
|
INT iBufRefCount;
|
|
LinkEntry *pLink;
|
|
CTELockHandle Handle;
|
|
|
|
#if DBG
|
|
|
|
PNDIS_BUFFER pnpFirstBuffer;
|
|
ULONG ulTotalPacketLength, ulTotalHdrLength;
|
|
|
|
#endif
|
|
|
|
|
|
TraceEnter(FWD, "IPMForward");
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Lets make sure that this is a forwardable multicast
|
|
//
|
|
|
|
#endif
|
|
|
|
pFWC = (FWContext *)pnpPacket->ProtocolReserved;
|
|
|
|
pPrimarySrcNte = pFWC->fc_srcnte;
|
|
pInIf = pPrimarySrcNte->nte_if;
|
|
pHeader = pFWC->fc_hbuff;
|
|
ulDataLength = pFWC->fc_datalength;
|
|
pnbDataBuffer = pFWC->fc_buffhead;
|
|
pLink = pFWC->fc_iflink;
|
|
|
|
//
|
|
// Check to make sure the buffer and packets are
|
|
// as we expect
|
|
//
|
|
|
|
RtAssert(pPrimarySrcNte);
|
|
RtAssert(pHeader);
|
|
|
|
//
|
|
// Setup pvData to point to the first part of the data
|
|
// so that the filter driver can get to it in a flat
|
|
// buffer
|
|
//
|
|
|
|
if (!pnbDataBuffer)
|
|
{
|
|
pvData = NULL;
|
|
uiFirstBufLen = 0;
|
|
}
|
|
else
|
|
{
|
|
TcpipQueryBuffer(pnbDataBuffer,
|
|
&pvData,
|
|
&uiFirstBufLen,
|
|
NormalPagePriority);
|
|
|
|
if(pvData is NULL)
|
|
{
|
|
Trace(FWD, ERROR,
|
|
("IPMForward: failed to query data buffer.\n"));
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
|
|
if(pSource)
|
|
{
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
}
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpPacket);
|
|
|
|
TraceLeave(FWD, "IPMForward");
|
|
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
|
|
if(pFWC->fc_options)
|
|
{
|
|
RtAssert(pFWC->fc_optlength);
|
|
RtAssert(pFWC->FCFLAGS & PACKET_FLAG_OPTIONS);
|
|
}
|
|
else
|
|
{
|
|
RtAssert(pFWC->fc_optlength is 0);
|
|
RtAssert(!(pFWC->FCFLAGS & PACKET_FLAG_OPTIONS));
|
|
}
|
|
|
|
//
|
|
// "To make assurance doubly sure." Extra points to the person
|
|
// who gets the quote
|
|
//
|
|
|
|
NdisQueryPacket(pnpPacket,
|
|
NULL,
|
|
NULL,
|
|
&pnpFirstBuffer,
|
|
&ulTotalPacketLength);
|
|
|
|
RtAssert(pnpFirstBuffer is pFWC->fc_buffhead);
|
|
RtAssert(ulTotalPacketLength is ulDataLength);
|
|
|
|
ulTotalHdrLength = sizeof(IPHeader) + pFWC->fc_optlength;
|
|
ulTotalPacketLength = net_short(pHeader->iph_length) - ulTotalHdrLength;
|
|
|
|
RtAssert(ulTotalPacketLength is ulDataLength);
|
|
|
|
#endif
|
|
|
|
if(!ARGUMENT_PRESENT(pSource))
|
|
{
|
|
//
|
|
// This happens when we come through the TD path or
|
|
// when dont have a (S,G) entry in our MFIB
|
|
//
|
|
|
|
pSource = FindSGEntry(pHeader->iph_src,
|
|
pHeader->iph_dest);
|
|
}
|
|
|
|
if(pSource is NULL)
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: No (S,G,) entry found\n"));
|
|
|
|
//
|
|
// Invoke the IP filter driver.
|
|
//
|
|
|
|
if (ForwardFilterEnabled)
|
|
{
|
|
ASSERT(!bSendFromQueue);
|
|
CTEInterlockedIncrementLong(&ForwardFilterRefCount);
|
|
faAction = (*ForwardFilterPtr) (pHeader,
|
|
pvData,
|
|
uiFirstBufLen,
|
|
pPrimarySrcNte->nte_if->if_index,
|
|
INVALID_IF_INDEX,
|
|
NULL_IP_ADDR, NULL_IP_ADDR);
|
|
DerefFilterPtr();
|
|
|
|
if(faAction != FORWARD)
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: Filter returned %d\n",
|
|
faAction));
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
FreeFWPacket(pnpPacket);
|
|
TraceLeave(FWD, "IPMForward");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No S found, create one, copy and queue the packet
|
|
// and complete and IRP to the router manager
|
|
//
|
|
|
|
nsStatus = CreateSourceAndQueuePacket(pHeader->iph_dest,
|
|
pHeader->iph_src,
|
|
pInIf->if_index,
|
|
pLink,
|
|
pnpPacket);
|
|
|
|
//
|
|
// We are not done with the packet, because it
|
|
// is queued. So we dont free it or call complete on it
|
|
//
|
|
|
|
TraceLeave(FWD, "IPMForward");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Trace(FWD, TRACE,
|
|
("IPMForward: Source at 0x%x. In i/f is 0x%x. State is %x\n",
|
|
pSource, pSource->pInIpIf, pSource->byState));
|
|
|
|
pSource->ulInPkts++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
|
|
switch(pSource->byState)
|
|
{
|
|
case MFE_UNINIT:
|
|
{
|
|
RtAssert(FALSE);
|
|
|
|
pSource->ulUninitMfe++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
if (!bSendFromQueue) {
|
|
NotifyFilterOfDiscard(pPrimarySrcNte, pHeader, pvData,
|
|
uiFirstBufLen);
|
|
}
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpPacket);
|
|
|
|
TraceLeave(RCV, "IPMForward");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
case MFE_NEGATIVE:
|
|
{
|
|
//
|
|
// Throw the packet away
|
|
// IMPORTANT - DO NOT UPDATE THE ACTIVITY TIMESTAMP
|
|
// otherwise the upper layer protocols will never see the
|
|
// packets from this
|
|
//
|
|
|
|
pSource->ulNegativeMfe++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
if (!bSendFromQueue) {
|
|
NotifyFilterOfDiscard(pPrimarySrcNte, pHeader, pvData,
|
|
uiFirstBufLen);
|
|
}
|
|
|
|
Trace(FWD, INFO,
|
|
("IPMForward: MFE is negative, so discarding packet\n"));
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpPacket);
|
|
|
|
TraceLeave(FWD, "IPMForward");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
case MFE_QUEUE:
|
|
{
|
|
//
|
|
// Invoke the IP filter driver.
|
|
//
|
|
|
|
if (ForwardFilterEnabled)
|
|
{
|
|
ASSERT(!bSendFromQueue);
|
|
CTEInterlockedIncrementLong(&ForwardFilterRefCount);
|
|
faAction = (*ForwardFilterPtr) (pHeader,
|
|
pvData,
|
|
uiFirstBufLen,
|
|
pPrimarySrcNte->nte_if->if_index,
|
|
INVALID_IF_INDEX,
|
|
NULL_IP_ADDR, NULL_IP_ADDR);
|
|
DerefFilterPtr();
|
|
|
|
if(faAction != FORWARD)
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: Filter returned %d\n",
|
|
faAction));
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
FreeFWPacket(pnpPacket);
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
TraceLeave(FWD, "IPMForward");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Havent got a the MFE from user mode as yet, just
|
|
// queue the packet
|
|
//
|
|
|
|
Trace(RCV, INFO,
|
|
("IPMForward: MFE is queuing\n"));
|
|
|
|
UpdateActivityTime(pSource);
|
|
|
|
//
|
|
// Dont update pSource stats, this will be done the second
|
|
// time around
|
|
//
|
|
|
|
pSource->ulInPkts--;
|
|
pSource->ulInOctets -= net_short(pHeader->iph_length);
|
|
|
|
nsStatus = QueuePacketToSource(pSource,
|
|
pnpPacket);
|
|
|
|
if(nsStatus isnot STATUS_PENDING)
|
|
{
|
|
pSource->ulInPkts++;
|
|
pSource->ulInOctets += net_short(pHeader->iph_length);
|
|
pSource->ulInDiscards++;
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
|
|
Trace(FWD, ERROR,
|
|
("IPMForward: QueuePacketToSource returned %x\n",
|
|
nsStatus));
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpPacket);
|
|
}
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
TraceLeave(RCV, "IPMForward");
|
|
|
|
return nsStatus;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
case MFE_INIT:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
RtAssert(FALSE);
|
|
|
|
break;
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if(pSource->pInIpIf isnot pPrimarySrcNte->nte_if)
|
|
{
|
|
UpdateActivityTime(pSource);
|
|
|
|
//
|
|
// See if we need to generate a wrong i/f upcall
|
|
//
|
|
|
|
ProcessWrongIfUpcall(pPrimarySrcNte->nte_if,
|
|
pSource,
|
|
pLink,
|
|
pHeader,
|
|
sizeof(IPHeader),
|
|
pFWC->fc_options,
|
|
pFWC->fc_optlength,
|
|
pvData,
|
|
uiFirstBufLen);
|
|
|
|
//
|
|
// If the packet shouldnt be accepted - stop now
|
|
//
|
|
|
|
if(!(pInIf->if_mcastflags & IPMCAST_IF_ACCEPT_ALL))
|
|
{
|
|
pSource->ulPktsDifferentIf++;
|
|
|
|
Trace(RCV, ERROR,
|
|
("IPMForward: Pkt from %d.%d.%d.%d to %d.%d.%d.%d came in on 0x%x instead of 0x%x\n",
|
|
PRINT_IPADDR(pHeader->iph_src),
|
|
PRINT_IPADDR(pHeader->iph_dest),
|
|
pInIf ? pInIf->if_index : 0,
|
|
pSource->pInIpIf ? pSource->pInIpIf->if_index : 0));
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
//
|
|
// RPF check failed. Throw the packet away
|
|
//
|
|
|
|
Trace(FWD, INFO,
|
|
("IPMForward: RPF Failed. In i/f %x (%d). RPF i/f %x (%d)\n",
|
|
pPrimarySrcNte->nte_if, pPrimarySrcNte->nte_if->if_index,
|
|
pSource->pInIpIf, pSource->pInIpIf->if_index));
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpPacket);
|
|
|
|
TraceLeave(FWD, "IPMForward");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to unlink the packet so that the code below works properly.
|
|
// This is kind of painful, but SendIPPacket wants a packet which has
|
|
// only the header buffer chained to it
|
|
// We do th unlinking at this point because if we do it before this and
|
|
// queue the packet, then we will hit a ton of asserts we come here
|
|
// when the queue is being drained (since then we will be unlinking twice)
|
|
//
|
|
|
|
UnlinkDataFromPacket(pnpPacket, pFWC);
|
|
|
|
//
|
|
// Zero out the XSUM
|
|
//
|
|
|
|
pHeader->iph_xsum = 0x0000;
|
|
|
|
//
|
|
// Decrement the TTL
|
|
//
|
|
|
|
pHeader->iph_ttl--;
|
|
|
|
Trace(FWD, INFO,
|
|
("IPMForward: New TTL is %d\n",
|
|
pHeader->iph_ttl));
|
|
|
|
//
|
|
// The number of pending sends. Used later
|
|
//
|
|
|
|
ulSent = 0;
|
|
|
|
//
|
|
// Get a buffer reference. We need this if we are sending to
|
|
// more than one interface, or if we need to fragment.
|
|
// However, we always use a reference. This only increases
|
|
// memory and has no effect on the correctness
|
|
//
|
|
|
|
|
|
pBuffRef = CTEAllocMem(sizeof(BufferReference));
|
|
|
|
if(pBuffRef is NULL)
|
|
{
|
|
pSource->ulInDiscards++;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
pFWC->FCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(pnpPacket);
|
|
|
|
IPSInfo.ipsi_indiscards++;
|
|
|
|
Trace(FWD, ERROR,
|
|
("IPMForward: Could not allocate memory for BuffRef\n"));
|
|
|
|
TraceLeave(FWD, "IPMForward");
|
|
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
UpdateActivityTime(pSource);
|
|
|
|
//
|
|
// Everything after this is InterlockedInc'ed
|
|
// We release the spinlock but still have the pSource refcounted.
|
|
//
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock));
|
|
|
|
//
|
|
// Initialize the BufferReference.It is init to 0. Even though
|
|
// some send completes may occur before we get a chance to bump
|
|
// the ref-count, that is not a problem because the send complete
|
|
// will cause it to go to a negative number which will not have any bad
|
|
// effect
|
|
//
|
|
|
|
pBuffRef->br_buffer = pFWC->fc_buffhead;
|
|
pBuffRef->br_refcount = 0;
|
|
|
|
CTEInitLock(&(pBuffRef->br_lock));
|
|
|
|
pPC = (PacketContext *)pnpPacket->ProtocolReserved;
|
|
|
|
pPC->pc_br = pBuffRef;
|
|
|
|
//
|
|
// Start looping through the OIFs
|
|
// We allocate a packet and a header for each interface except the last
|
|
// one. (For the last one we use the one given to us - since we own it).
|
|
// Instead of using a new buffer chain for each packet, we point to the
|
|
// old chain.
|
|
// The last packet is a FWPacket. All the others are simply IP Packets
|
|
//
|
|
|
|
for(pOutIf = pSource->pFirstOutIf;
|
|
pOutIf isnot NULL;
|
|
pOutIf = pOutIf->pNextOutIf)
|
|
{
|
|
//
|
|
// Skip it if the OIF matches the IIF
|
|
// The address check is for RAS clients.
|
|
//
|
|
|
|
if((pOutIf->pIpIf is pInIf) and
|
|
(pHeader->iph_src is pOutIf->dwNextHopAddr))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Trace(FWD, INFO,
|
|
("IPMForward: Sending over i/f @ 0x%x\n",
|
|
pOutIf));
|
|
|
|
if(pOutIf->pIpIf is &DummyInterface)
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: Need to dial out\n"));
|
|
|
|
//
|
|
// Need to dial out
|
|
//
|
|
|
|
if (DODCallout isnot NULL)
|
|
{
|
|
//
|
|
// Dial out pointer has been plumbed
|
|
//
|
|
|
|
dwNewIndex = (*DODCallout)(pOutIf->dwDialContext,
|
|
pHeader->iph_dest,
|
|
pHeader->iph_src,
|
|
pHeader->iph_protocol,
|
|
pvData,
|
|
uiFirstBufLen,
|
|
pHeader->iph_src);
|
|
|
|
if(dwNewIndex isnot INVALID_IF_INDEX)
|
|
{
|
|
//
|
|
// This puts a reference on the interface
|
|
//
|
|
|
|
pOutIf->pIpIf = GetInterfaceGivenIndex(dwNewIndex);
|
|
|
|
RtAssert(pOutIf->pIpIf isnot &DummyInterface);
|
|
RtAssert(pOutIf->pIpIf isnot &LoopInterface);
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No call out!
|
|
//
|
|
|
|
RtAssert(FALSE);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(pHeader->iph_ttl < pOutIf->pIpIf->if_mcastttl)
|
|
{
|
|
//
|
|
// TTL would be too low, what do we send back?
|
|
//
|
|
|
|
InterlockedIncrement(&(pOutIf->ulTtlTooLow));
|
|
|
|
Trace(FWD, WARN,
|
|
("IPMForward: Packet ttl is %d, I/f ttl is %d. Dropping\n",
|
|
pHeader->iph_ttl, pOutIf->pIpIf->if_mcastttl));
|
|
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// See if we need to filter this
|
|
//
|
|
|
|
if (ForwardFilterEnabled)
|
|
{
|
|
uint InIFIndex = bSendFromQueue ? INVALID_IF_INDEX
|
|
: pPrimarySrcNte->nte_if->if_index;
|
|
//
|
|
// NOTE: We use the same header and data all the time.
|
|
//
|
|
|
|
CTEInterlockedIncrementLong(&ForwardFilterRefCount);
|
|
faAction = (*ForwardFilterPtr) (pHeader,
|
|
pvData,
|
|
uiFirstBufLen,
|
|
InIFIndex,
|
|
pOutIf->pIpIf->if_index,
|
|
NULL_IP_ADDR, NULL_IP_ADDR);
|
|
DerefFilterPtr();
|
|
|
|
if(faAction != FORWARD)
|
|
{
|
|
InterlockedIncrement(&(pOutIf->ulOutDiscards));
|
|
|
|
Trace(FWD, INFO,
|
|
("IPMForward: Filter returned %d\n",
|
|
faAction));
|
|
|
|
InterlockedIncrement(&(pOutIf->ulOutDiscards));
|
|
|
|
//DerefIF(IF);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// TODO Get the primary NTE for this IF.
|
|
// right now we are picking up the first NTE
|
|
//
|
|
|
|
pOutNte = pOutIf->pIpIf->if_nte;
|
|
|
|
if(pOutNte is NULL)
|
|
{
|
|
Trace(FWD, WARN,
|
|
("IPMForward: No NTE found for interface %x (%d)\n",
|
|
pOutIf->pIpIf, pOutIf->pIpIf->if_nte));
|
|
|
|
continue;
|
|
}
|
|
|
|
if(pOutIf->pNextOutIf)
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: Not the last i/f - need to allocate packets\n"));
|
|
|
|
//
|
|
// Get a plain old header and packet.
|
|
//
|
|
|
|
pNewHeader = GetIPHeader(&pnpNewPacket);
|
|
|
|
if(pNewHeader is NULL)
|
|
{
|
|
Trace(FWD, ERROR,
|
|
("IPMForward: Could not get packet/header\n"));
|
|
|
|
//
|
|
// Could not get a header and packet
|
|
//
|
|
|
|
InterlockedIncrement(&(pOutIf->ulOutDiscards));
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
#if MCAST_COMP_DBG
|
|
|
|
Trace(FWD, INFO,
|
|
("IPMForward: New Packet 0x%x New Header 0x%x\n",pnpNewPacket, pNewHeader));
|
|
|
|
#endif
|
|
|
|
//
|
|
// Set the packet context for all packets that are created
|
|
// here to be Non FW packets
|
|
// Note: Earlier we would also set the packet to be IPBUF, but
|
|
// now since we dont allocate buffers and instead just use the
|
|
// original buffers, we MUST not set the IPBUF flag
|
|
//
|
|
|
|
pPC = (PacketContext *)pnpNewPacket->ProtocolReserved;
|
|
|
|
//
|
|
// Copy out the context. STRUCTURE COPY
|
|
//
|
|
|
|
*pPC = pFWC->fc_pc;
|
|
|
|
pPC->PCFLAGS &= ~PACKET_FLAG_FW;
|
|
|
|
//
|
|
// Copy out the header. STRUCTURE COPY
|
|
//
|
|
|
|
*pNewHeader = *pHeader;
|
|
|
|
if(pFWC->fc_options)
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: FWC has options at %x. Length %d\n",
|
|
pFWC->fc_options, pFWC->fc_optlength));
|
|
|
|
RtAssert(pFWC->fc_optlength);
|
|
RtAssert(pPC->PCFLAGS & PACKET_FLAG_OPTIONS);
|
|
|
|
//
|
|
// We have options, make a copy.
|
|
//
|
|
|
|
pbyNewOptions = CTEAllocMem(pFWC->fc_optlength);
|
|
|
|
if(pbyNewOptions is NULL)
|
|
{
|
|
Trace(FWD, ERROR,
|
|
("IPMForward: Unable to allocate memory for options\n"));
|
|
|
|
//
|
|
// This gets set during the context copy
|
|
//
|
|
|
|
pPC->PCFLAGS &= ~PACKET_FLAG_OPTIONS;
|
|
|
|
FreeIPPacket(pnpNewPacket, TRUE, IP_NO_RESOURCES);
|
|
|
|
InterlockedIncrement(&(pOutIf->ulOutDiscards));
|
|
|
|
continue;
|
|
}
|
|
|
|
RtlCopyMemory(pbyNewOptions,
|
|
pFWC->fc_options,
|
|
pFWC->fc_optlength);
|
|
}
|
|
else
|
|
{
|
|
pbyNewOptions = NULL;
|
|
|
|
RtAssert(!(pPC->PCFLAGS & PACKET_FLAG_OPTIONS));
|
|
}
|
|
|
|
// NOT NEEDED - see below
|
|
// CTEGetLockAtDPC(&RouteTableLock, &Handle);
|
|
//
|
|
// pOutIf->pIpIf->if_refcount++;
|
|
// InterlockedIncrement(&(pOutIf->pIpIf->if_mfwdpktcount));
|
|
//
|
|
// CTEFreeLockFromDPC(&RouteTableLock, Handle);
|
|
//
|
|
// pPC->pc_if = pOutIf->pIpIf;
|
|
//
|
|
}
|
|
else
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: Last i/f. Using packet 0x%x. Flags 0x%X. Opt 0x%x OptLen %d\n",
|
|
pnpPacket,
|
|
pFWC->FCFLAGS,
|
|
pFWC->fc_options,
|
|
pFWC->fc_optlength));
|
|
|
|
//
|
|
// Use the original packet, header and options
|
|
//
|
|
|
|
pnpNewPacket = pnpPacket;
|
|
pNewHeader = pHeader;
|
|
pbyNewOptions = pFWC->fc_options;
|
|
|
|
// NOT NEEDED - see below
|
|
// CTEGetLockAtDPC(&RouteTableLock, &Handle);
|
|
//
|
|
// pOutIf->pIpIf->if_refcount++;
|
|
// InterlockedIncrement(&(pOutIf->pIpIf->if_mfwdpktcount));
|
|
//
|
|
// CTEFreeLockFromDPC(&RouteTableLock, Handle);
|
|
//
|
|
// pFWC->fc_if = pOutIf->pIpIf;
|
|
//
|
|
}
|
|
|
|
|
|
#if 0
|
|
UpdateOptions(pbyNewOptions,
|
|
pOptIndex,
|
|
pOutNte->nte_addr);
|
|
#endif
|
|
|
|
//
|
|
// Just need to ref across the send, not the send-sendcomplete
|
|
//
|
|
|
|
CTEGetLockAtDPC(&RouteTableLock.Lock, &Handle);
|
|
|
|
LOCKED_REFERENCE_IF(pOutIf->pIpIf);
|
|
|
|
#ifdef MREF_DEBUG
|
|
InterlockedIncrement(&(pOutIf->pIpIf->if_mfwdpktcount));
|
|
#endif
|
|
CTEFreeLockFromDPC(&RouteTableLock.Lock, Handle);
|
|
|
|
if((ulDataLength + pFWC->fc_optlength) > pOutIf->pIpIf->if_mtu)
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: Data %d Opt %d Hdr %d. MTU %d. Requires frag\n",
|
|
ulDataLength,
|
|
pFWC->fc_optlength,
|
|
sizeof(IPHeader),
|
|
pOutIf->pIpIf->if_mtu));
|
|
|
|
|
|
//
|
|
// This is too big
|
|
// If the numSent variable is null, IPFragment will
|
|
// automatically increment the buffer ref by the sent count
|
|
// We however pass ulSent (THIS MUST BE INITIALIZED TO 0).
|
|
// At the end, we increment it by the
|
|
//
|
|
|
|
InterlockedIncrement(&(pOutIf->ulFragNeeded));
|
|
|
|
|
|
nsStatus = IPFragment(pOutIf->pIpIf,
|
|
pOutIf->pIpIf->if_mtu - sizeof(IPHeader),
|
|
pOutIf->dwNextHopAddr,
|
|
pnpNewPacket,
|
|
pNewHeader,
|
|
pnbDataBuffer,
|
|
ulDataLength,
|
|
pbyNewOptions,
|
|
pFWC->fc_optlength,
|
|
&ulSent,
|
|
TRUE,
|
|
NULL);
|
|
|
|
if((nsStatus isnot STATUS_SUCCESS) and
|
|
(nsStatus isnot IP_PENDING))
|
|
{
|
|
InterlockedIncrement(&(pOutIf->ulOutDiscards));
|
|
}
|
|
else
|
|
{
|
|
InterlockedExchangeAdd(&(pOutIf->ulOutPackets),
|
|
ulSent);
|
|
|
|
InterlockedExchangeAdd(&(pSource->ulTotalOutPackets),
|
|
ulSent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: No fragmentation needed, sending packet with flags 0x%X\n",
|
|
((PacketContext *)pnpNewPacket->ProtocolReserved)->PCFLAGS));
|
|
//
|
|
// Mark as no loopback
|
|
//
|
|
|
|
NdisSetPacketFlags(pnpNewPacket,
|
|
NDIS_FLAGS_DONT_LOOPBACK);
|
|
|
|
nsStatus = SendIPPacket(pOutIf->pIpIf,
|
|
pOutIf->dwNextHopAddr,
|
|
pnpNewPacket,
|
|
pnbDataBuffer,
|
|
pNewHeader,
|
|
pbyNewOptions,
|
|
pFWC->fc_optlength,
|
|
FALSE,
|
|
NULL,
|
|
FALSE);
|
|
|
|
if((nsStatus isnot STATUS_SUCCESS) and
|
|
(nsStatus isnot IP_PENDING))
|
|
{
|
|
|
|
|
|
Trace(FWD, ERROR,
|
|
("IPMForward: Error 0x%x from SendIPPacket\n",
|
|
nsStatus));
|
|
|
|
InterlockedIncrement(&(pOutIf->ulOutDiscards));
|
|
}
|
|
else
|
|
{
|
|
InterlockedIncrement(&(pOutIf->ulOutPackets));
|
|
|
|
InterlockedIncrement(&(pSource->ulTotalOutPackets));
|
|
|
|
if(nsStatus is IP_PENDING)
|
|
{
|
|
//
|
|
// The resources allocated in this routine are
|
|
// freed because SendIPPacket calls FreeIPPacket
|
|
// We just need to track if we are done with the
|
|
// original buffer
|
|
//
|
|
|
|
ulSent++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MREF_DEBUG
|
|
InterlockedDecrement(&(pOutIf->pIpIf->if_mfwdpktcount));
|
|
#endif
|
|
DerefIF(pOutIf->pIpIf);
|
|
}
|
|
|
|
DereferenceSource(pSource);
|
|
|
|
//
|
|
// so how many do we have pending?
|
|
//
|
|
|
|
if(ulSent isnot 0)
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: Pending sends %d\n",
|
|
ulSent));
|
|
|
|
//
|
|
// So there were some pending sends (or some
|
|
// fragments)
|
|
//
|
|
|
|
iBufRefCount = ReferenceBuffer(pBuffRef, ulSent);
|
|
|
|
Trace(FWD, INFO,
|
|
("IPMForward: ReferenceBuffer returned %d\n",iBufRefCount));
|
|
|
|
if(iBufRefCount is 0)
|
|
{
|
|
//
|
|
// The sends completed before we got here. But since the
|
|
// refcount would have been negative, the buffer would
|
|
// not have been freed
|
|
//
|
|
|
|
CTEFreeMem(pBuffRef);
|
|
|
|
//
|
|
// Call FWSendComplete on the packet to free up
|
|
// resources
|
|
//
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
|
|
FWSendComplete(pnpPacket,
|
|
pFWC->fc_buffhead, IP_SUCCESS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Trace(FWD, INFO,
|
|
("IPMForward: There are no pending sends\n"));
|
|
|
|
//
|
|
// NULL out the pc_br so the completion routine does not
|
|
// try to deref it. Also generally clean stuff up
|
|
//
|
|
|
|
((PacketContext *)pnpPacket->ProtocolReserved)->pc_br = NULL;
|
|
|
|
CTEFreeMem(pBuffRef);
|
|
|
|
//
|
|
// No pending sends. There was however a buffref in the
|
|
// FWC, so the packets would not have been freed
|
|
//
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
pFWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FWSendComplete(pnpPacket,
|
|
pFWC->fc_buffhead, IP_SUCCESS);
|
|
}
|
|
|
|
TraceLeave(FWD, "IPMForward");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif // IPMCAST
|
|
|