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.
5592 lines
199 KiB
5592 lines
199 KiB
/*++
|
|
|
|
Copyright (c) 1990-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ipxmit.c - IP transmit routines.
|
|
|
|
Abstract:
|
|
|
|
This module contains all transmit related IP routines.
|
|
|
|
Author:
|
|
|
|
|
|
[Environment:]
|
|
|
|
kernel mode only
|
|
|
|
[Notes:]
|
|
|
|
optional-notes
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "info.h"
|
|
#include "iproute.h"
|
|
#include "iprtdef.h"
|
|
#include "arpdef.h"
|
|
#include "tcpipbuf.h"
|
|
#include "mdlpool.h"
|
|
#include "tcp.h"
|
|
#include "tcpsend.h"
|
|
|
|
|
|
|
|
#if DBG
|
|
ulong DbgIPSendHwChkSum = 0;
|
|
uint dbg_hdrincl = 0;
|
|
#endif
|
|
|
|
extern uint IPSecStatus;
|
|
extern IPSecQStatusRtn IPSecQueryStatusPtr;
|
|
extern Interface *IFList;
|
|
extern NetTableEntry **NewNetTableList; // hash table for NTEs
|
|
extern uint NET_TABLE_SIZE;
|
|
extern NetTableEntry *LoopNTE; // Pointer to loopback NTE.
|
|
extern RefPtr DHCPRefPtr; // Referenced pointer to NTE
|
|
// currently being DHCP'd.
|
|
extern ulong TimeStamp; // Starting timestamp.
|
|
extern ulong TSFlag; // Mask to use on this.
|
|
extern uint NumNTE;
|
|
|
|
IPID_CACHE_LINE IPIDCacheLine;
|
|
|
|
// Global variables for buffers and packets.
|
|
HANDLE IpHeaderPool;
|
|
|
|
//
|
|
// the global address for unnumbered interfaces
|
|
//
|
|
|
|
extern IPAddr g_ValidAddr;
|
|
|
|
BufferReference *GetBufferReference(void);
|
|
|
|
IP_STATUS ARPResolve(IPAddr DestAddress, IPAddr SourceAddress,
|
|
ARPControlBlock *ControlBlock, ArpRtn Callback);
|
|
|
|
NDIS_STATUS ARPResolveIP(void *Context, IPAddr Destination,
|
|
ARPControlBlock *ArpContB);
|
|
|
|
IP_STATUS SendICMPIPSecErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong);
|
|
|
|
int ReferenceBuffer(BufferReference * BR, int Count);
|
|
|
|
|
|
|
|
extern Interface LoopInterface;
|
|
extern uint NumIF;
|
|
|
|
NDIS_HANDLE NdisPacketPool = NULL;
|
|
NDIS_HANDLE BufferPool;
|
|
|
|
#define BCAST_IF_CTXT (Interface *)-1
|
|
|
|
uint PacketPoolSizeMin = PACKET_GROW_COUNT;
|
|
uint PacketPoolSizeMax = SMALL_POOL;
|
|
|
|
|
|
//** GetIPID - Routine to get IP identification
|
|
//
|
|
// Input: None
|
|
//
|
|
// Returns: IPID+1
|
|
//
|
|
ushort
|
|
GetIPID()
|
|
{
|
|
return((ushort)InterlockedExchangeAdd((PLONG) &IPIDCacheLine.Value, 1));
|
|
}
|
|
|
|
|
|
|
|
//** FreeIPHdrBuffer - Free a buffer back to the pool.
|
|
//
|
|
// Input: Buffer - Hdr buffer to be freed.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
__inline
|
|
VOID
|
|
FreeIPHdrBuffer(PNDIS_BUFFER Buffer)
|
|
{
|
|
MdpFree(Buffer);
|
|
}
|
|
|
|
//** FreeIPBufferChain - Free a chain of IP buffers.
|
|
//
|
|
// This routine takes a chain of NDIS_BUFFERs, and frees them all.
|
|
//
|
|
// Entry: Buffer - Pointer to buffer chain to be freed.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
FreeIPBufferChain(PNDIS_BUFFER Buffer)
|
|
{
|
|
PNDIS_BUFFER NextBuffer;
|
|
|
|
while (Buffer != (PNDIS_BUFFER) NULL) {
|
|
NdisGetNextBuffer(Buffer, &NextBuffer);
|
|
NdisFreeBuffer(Buffer);
|
|
Buffer = NextBuffer;
|
|
}
|
|
}
|
|
|
|
//** Free payload mdl
|
|
//
|
|
// Input: Buffer - Bufferchain which has ip allocated ndis_buffer
|
|
// OriginalBuffer - Original buffer which needs to be restored
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
__inline
|
|
VOID
|
|
FreeIPPayloadBuffer(PNDIS_BUFFER Buffer, PNDIS_BUFFER OrgBuffer)
|
|
{
|
|
PNDIS_BUFFER PayloadBuffer;
|
|
|
|
PayloadBuffer = NDIS_BUFFER_LINKAGE(Buffer);
|
|
NDIS_BUFFER_LINKAGE(Buffer) = OrgBuffer;
|
|
ASSERT(NDIS_BUFFER_LINKAGE(OrgBuffer) == NDIS_BUFFER_LINKAGE(PayloadBuffer));
|
|
//KdPrint(("sendbcast restoring hdrincl %x %x\n",OrgBuffer,PayloadBuffer));
|
|
NDIS_BUFFER_LINKAGE(PayloadBuffer) = NULL;
|
|
NdisFreeBuffer(PayloadBuffer);
|
|
}
|
|
|
|
//** RestoreUserBuffer - Restores original user supplied buffer
|
|
//
|
|
// Takes orginal buffer and chains it back in the packet,
|
|
// freeing the one allocated by the stack.
|
|
//
|
|
// Entry: Packet
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
RestoreUserBuffer(PNDIS_PACKET Packet)
|
|
{
|
|
PNDIS_BUFFER NextBuffer;
|
|
PacketContext *pc = (PacketContext *) Packet->ProtocolReserved;
|
|
PNDIS_BUFFER OrgBuffer, FirewallBuffer, Buffer;
|
|
BufferReference *BufRef;
|
|
|
|
BufRef = pc->pc_br;
|
|
FirewallBuffer = pc->pc_firewall;
|
|
|
|
OrgBuffer = pc->pc_hdrincl;
|
|
ASSERT(OrgBuffer != NULL);
|
|
pc->pc_hdrincl = NULL;
|
|
|
|
NdisQueryPacket(Packet, NULL, NULL, &NextBuffer, NULL);
|
|
|
|
if (!FirewallBuffer) {
|
|
// Firewall didn't munge the buffer: apply the normal stuff
|
|
// if bufref is true, IPFrag was called.
|
|
// buffer chain will be at ->br_buffer.
|
|
|
|
if (BufRef == (BufferReference *) NULL) {
|
|
Buffer = NDIS_BUFFER_LINKAGE(NextBuffer);
|
|
if (pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS) {
|
|
Buffer = NDIS_BUFFER_LINKAGE(Buffer);
|
|
}
|
|
} else {
|
|
Buffer = BufRef->br_buffer;
|
|
}
|
|
|
|
FreeIPPayloadBuffer(Buffer, OrgBuffer);
|
|
} else {
|
|
if (BufRef == NULL) {
|
|
Buffer = FirewallBuffer;
|
|
if (pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS) {
|
|
Buffer = NDIS_BUFFER_LINKAGE(Buffer);
|
|
}
|
|
|
|
FreeIPPayloadBuffer(Buffer, OrgBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
//* FreeIPPacket - Free an IP packet when we're done with it.
|
|
//
|
|
// Called when a send completes and a packet needs to be freed. We look at the
|
|
// packet, decide what to do with it, and free the appropriate components.
|
|
//
|
|
// Entry: Packet - Packet to be freed.
|
|
// FixHdrs - If true, restores headers changed by IPSec/firewall and
|
|
// header-include processing before freeing the packet.
|
|
// Status - final status from packet-processing.
|
|
//
|
|
// Returns: Pointer to next unfreed buffer on packet, or NULL if all buffers
|
|
// freed (i.e. this was a fragmented packet).
|
|
//
|
|
PNDIS_BUFFER
|
|
FreeIPPacket(PNDIS_PACKET Packet, BOOLEAN FixHdrs, IP_STATUS Status)
|
|
{
|
|
PNDIS_BUFFER NextBuffer, OldBuffer;
|
|
PacketContext *pc = (PacketContext *) Packet->ProtocolReserved;
|
|
|
|
PNDIS_BUFFER FirewallBuffer = NULL;
|
|
|
|
FWContext *FWC = (FWContext *) Packet->ProtocolReserved;
|
|
BufferReference *BufRef; // Buffer reference, if any.
|
|
BOOLEAN InitFirewallContext = FALSE;
|
|
|
|
NDIS_PER_PACKET_INFO_FROM_PACKET(Packet,
|
|
ClassificationHandlePacketInfo) = NULL;
|
|
NdisClearPacketFlags(Packet,
|
|
(NDIS_FLAGS_DONT_LOOPBACK | NDIS_FLAGS_LOOPBACK_ONLY));
|
|
#if !MILLEN
|
|
// ndis 5.1 feature
|
|
NDIS_SET_PACKET_CANCEL_ID(Packet, NULL);
|
|
#endif
|
|
|
|
NdisQueryPacket(Packet, NULL, NULL, &NextBuffer, NULL);
|
|
|
|
if ((pc->pc_common.pc_flags & PACKET_FLAG_FW) && FWC->fc_bufown) {
|
|
//Pkt forwarded thru buffer owner ship
|
|
ASSERT(pc->pc_firewall == NULL);
|
|
|
|
|
|
return NextBuffer;
|
|
}
|
|
BufRef = pc->pc_br;
|
|
|
|
// Restore the original buffer and MDL chain back.
|
|
// We should restore the reverse order in which the input Buffer was
|
|
// modified.
|
|
// Order of modification: hdr_incl -> firewall -> ipsec
|
|
// Order of restoration: ipsec -> firewall -> hdr_incl
|
|
|
|
//
|
|
// See if IPSEC has to fix up anything
|
|
//
|
|
if (FixHdrs && pc->pc_common.pc_IpsecCtx) {
|
|
PNDIS_BUFFER NewBuffer;
|
|
|
|
if (!BufRef || (pc->pc_ipsec_flags & IPSEC_FLAG_FRAG_DONE)) {
|
|
|
|
ASSERT(IPSecSendCmpltPtr);
|
|
(*IPSecSendCmpltPtr) (Packet,
|
|
NextBuffer,
|
|
pc->pc_common.pc_IpsecCtx,
|
|
Status,
|
|
&NewBuffer);
|
|
|
|
pc->pc_common.pc_IpsecCtx = NULL;
|
|
|
|
if (NewBuffer) {
|
|
NextBuffer = NewBuffer;
|
|
} else {
|
|
|
|
//
|
|
// Reinjected packet, no IP resources to free
|
|
//
|
|
pc->pc_firewall = NULL;
|
|
pc->pc_firewall2 = NULL;
|
|
|
|
NdisFreePacket(Packet);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
pc->pc_common.pc_IpsecCtx = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// FirewallBuffer will point to the input buffer which was passed to the
|
|
// firewall hook it will be non-NULL only if hook touched the packet
|
|
//
|
|
FirewallBuffer = pc->pc_firewall;
|
|
|
|
//
|
|
// Check if the buffers were munged by the firewall: FirewallBuffer != NULL
|
|
// If yes, restore original buffer
|
|
//
|
|
if (FixHdrs && FirewallBuffer) {
|
|
PNDIS_BUFFER NewBuffer;
|
|
PNDIS_BUFFER TmpBuffer;
|
|
|
|
|
|
if (BufRef == NULL) {
|
|
|
|
// Non fragmentation path
|
|
// if bufref is true means
|
|
// IPFrag was called buffer chain will.
|
|
// be at ->br_buffer.
|
|
// restoration will be done in ipsendcomplete when last fragment
|
|
// send completes
|
|
|
|
NewBuffer = NextBuffer;
|
|
|
|
if (!((pc->pc_common.pc_flags & PACKET_FLAG_IPHDR) ||
|
|
(pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS))) {
|
|
// neither header nor option buffer
|
|
NdisReinitializePacket(Packet);
|
|
NdisChainBufferAtBack(Packet, FirewallBuffer);
|
|
NextBuffer = FirewallBuffer;
|
|
} else if ((pc->pc_common.pc_flags & PACKET_FLAG_IPHDR) &&
|
|
(pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS)) {
|
|
|
|
// both header and option buffer
|
|
ASSERT(NewBuffer != NULL);
|
|
NewBuffer = NDIS_BUFFER_LINKAGE(NewBuffer); // skip hdr buffer
|
|
|
|
ASSERT(NewBuffer != NULL);
|
|
TmpBuffer = NewBuffer;
|
|
NewBuffer = NDIS_BUFFER_LINKAGE(NewBuffer); // skip options buffer
|
|
|
|
NDIS_BUFFER_LINKAGE(TmpBuffer) = FirewallBuffer;
|
|
} else {
|
|
|
|
// just header buffer
|
|
ASSERT(pc->pc_common.pc_flags & PACKET_FLAG_IPHDR);
|
|
ASSERT(!(pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS));
|
|
ASSERT(NewBuffer != NULL);
|
|
TmpBuffer = NewBuffer;
|
|
NewBuffer = NDIS_BUFFER_LINKAGE(NewBuffer); // skip the header buffer
|
|
|
|
NDIS_BUFFER_LINKAGE(TmpBuffer) = FirewallBuffer;
|
|
}
|
|
|
|
//
|
|
// At this point NewBuffer points to the MDL chain allocated by
|
|
// the firewall. WE have already restored the original chain back
|
|
//
|
|
FreeIPBufferChain(NewBuffer);
|
|
pc->pc_firewall = NULL;
|
|
|
|
//
|
|
// We have to free OutRcvBuf chain we allocated and passed to
|
|
// firewall. This is the completion point, so we should free this
|
|
// chain here
|
|
//
|
|
ASSERT(pc->pc_firewall2);
|
|
IPFreeBuff(pc->pc_firewall2);
|
|
pc->pc_firewall2 = NULL;
|
|
} else { // bufref != NULL
|
|
|
|
// Firewall Headers are restored in IPSendComplete
|
|
// or in completion path that is executed when
|
|
// bufrefcnt is zero.
|
|
// These paths have already captured pc_firewall pointer.
|
|
// Initialize the packetcontext after calling RestoreUserBuffer
|
|
// below.
|
|
|
|
InitFirewallContext = TRUE;
|
|
|
|
}
|
|
}
|
|
// If users header is used as IP header, restore it.
|
|
|
|
if (FixHdrs && pc->pc_hdrincl) {
|
|
RestoreUserBuffer(Packet);
|
|
}
|
|
|
|
if (InitFirewallContext) {
|
|
pc->pc_firewall = NULL;
|
|
pc->pc_firewall2 = NULL;
|
|
}
|
|
|
|
|
|
// If there's no IP header on this packet, we have nothing else to do.
|
|
if (!(pc->pc_common.pc_flags & (PACKET_FLAG_IPHDR | PACKET_FLAG_FW))) {
|
|
pc->pc_firewall = NULL;
|
|
pc->pc_firewall2 = NULL;
|
|
|
|
NdisFreePacket(Packet);
|
|
return NextBuffer;
|
|
}
|
|
|
|
pc->pc_common.pc_flags &= ~PACKET_FLAG_IPHDR;
|
|
|
|
OldBuffer = NextBuffer;
|
|
ASSERT(OldBuffer != NULL);
|
|
|
|
NextBuffer = NDIS_BUFFER_LINKAGE(NextBuffer);
|
|
|
|
if (pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS) {
|
|
|
|
// Have options with this packet.
|
|
|
|
PNDIS_BUFFER OptBuffer;
|
|
void *Options;
|
|
uint OptSize;
|
|
|
|
OptBuffer = NextBuffer;
|
|
ASSERT(OptBuffer != NULL);
|
|
|
|
NdisGetNextBuffer(OptBuffer, &NextBuffer);
|
|
|
|
ASSERT(NextBuffer != NULL);
|
|
|
|
TcpipQueryBuffer(OptBuffer, &Options, &OptSize, HighPagePriority);
|
|
// If this is a FW packet, the options don't really belong to us, so
|
|
// don't free them.
|
|
if (!(pc->pc_common.pc_flags & PACKET_FLAG_FW)) {
|
|
if (Options != NULL) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
// Else leak Options b/c we can't get virtual address.
|
|
}
|
|
NdisFreeBuffer(OptBuffer);
|
|
pc->pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS;
|
|
}
|
|
if (pc->pc_common.pc_flags & PACKET_FLAG_IPBUF) { // This packet is all
|
|
// IP buffers.
|
|
|
|
(void)FreeIPBufferChain(NextBuffer);
|
|
NextBuffer = (PNDIS_BUFFER) NULL;
|
|
pc->pc_common.pc_flags &= ~PACKET_FLAG_IPBUF;
|
|
}
|
|
|
|
if (!(pc->pc_common.pc_flags & PACKET_FLAG_FW)) {
|
|
FreeIPHdrBuffer(OldBuffer);
|
|
pc->pc_firewall = NULL;
|
|
pc->pc_firewall2 = NULL;
|
|
|
|
NdisFreePacket(Packet);
|
|
}
|
|
return NextBuffer;
|
|
}
|
|
|
|
//** AllocIPPacketList - Allocate the packet pool
|
|
//
|
|
// Called during initialization to allocate the packet pool
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: TRUE if it succeeds, FALSE otherwise
|
|
//
|
|
BOOLEAN
|
|
AllocIPPacketList(void)
|
|
{
|
|
NDIS_STATUS Status;
|
|
|
|
//
|
|
// Determine the size of the machine and allocate the packet pool accordingly
|
|
//
|
|
|
|
#if MILLEN
|
|
PacketPoolSizeMax = SMALL_POOL;
|
|
#else // MILLEN
|
|
switch (MmQuerySystemSize()) {
|
|
case MmSmallSystem:
|
|
PacketPoolSizeMax = SMALL_POOL;
|
|
break;
|
|
case MmMediumSystem:
|
|
PacketPoolSizeMax = MEDIUM_POOL;
|
|
break;
|
|
case MmLargeSystem:
|
|
PacketPoolSizeMax = LARGE_POOL;
|
|
break;
|
|
}
|
|
#endif // !MILLEN
|
|
|
|
NdisAllocatePacketPoolEx(&Status,
|
|
&NdisPacketPool,
|
|
PacketPoolSizeMin,
|
|
PacketPoolSizeMax-PacketPoolSizeMin,
|
|
sizeof(PacketContext));
|
|
if (Status == NDIS_STATUS_SUCCESS) {
|
|
NdisSetPacketPoolProtocolId(NdisPacketPool, NDIS_PROTOCOL_ID_TCP_IP);
|
|
}
|
|
|
|
return ((BOOLEAN) (NdisPacketPool != NULL));
|
|
|
|
}
|
|
|
|
//** GetIPPacket - Get an NDIS packet to use.
|
|
//
|
|
// A routine to allocate an NDIS packet.
|
|
//
|
|
// Entry: Nothing.
|
|
//
|
|
// Returns: Pointer to NDIS_PACKET if allocated, or NULL.
|
|
//
|
|
PNDIS_PACKET
|
|
GetIPPacket(void)
|
|
{
|
|
PNDIS_PACKET Packet;
|
|
NDIS_STATUS Status;
|
|
|
|
NdisAllocatePacket(&Status, &Packet, NdisPacketPool);
|
|
|
|
if (Packet != NULL) {
|
|
PNDIS_PACKET_EXTENSION PktExt;
|
|
PacketContext * pc;
|
|
|
|
|
|
PktExt = NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
PktExt->NdisPacketInfo[TcpIpChecksumPacketInfo] = NULL;
|
|
PktExt->NdisPacketInfo[IpSecPacketInfo] = NULL;
|
|
PktExt->NdisPacketInfo[TcpLargeSendPacketInfo] = NULL;
|
|
PktExt->NdisPacketInfo[ClassificationHandlePacketInfo] = NULL;
|
|
|
|
|
|
NdisClearPacketFlags(Packet, (NDIS_FLAGS_DONT_LOOPBACK | NDIS_FLAGS_LOOPBACK_ONLY));
|
|
|
|
pc = (PacketContext *)Packet->ProtocolReserved;
|
|
pc->pc_if = NULL;
|
|
pc->pc_iflink = NULL;
|
|
pc->pc_common.pc_flags = 0;
|
|
pc->pc_common.pc_owner = PACKET_OWNER_IP;
|
|
pc->pc_hdrincl = 0;
|
|
pc->pc_common.pc_IpsecCtx = NULL;
|
|
}
|
|
|
|
return Packet;
|
|
}
|
|
|
|
//** GetIPHdrBuffer - Get an IP header buffer.
|
|
//
|
|
// A routine to allocate an IP header buffer, with an NDIS buffer.
|
|
//
|
|
// Entry: Nothing.
|
|
//
|
|
// Returns: Pointer to NDIS_BUFFER if allocated, or NULL.
|
|
//
|
|
__inline
|
|
PNDIS_BUFFER
|
|
GetIPHdrBuffer(IPHeader **Header)
|
|
{
|
|
return MdpAllocate(IpHeaderPool, Header);
|
|
}
|
|
|
|
//** GetIPHeader - Get a header buffer and packet.
|
|
//
|
|
// Called when we need to get a header buffer and packet. We allocate both,
|
|
// and chain them together.
|
|
//
|
|
// Input: Pointer to where to store packet.
|
|
//
|
|
// Returns: Pointer to IP header.
|
|
//
|
|
IPHeader *
|
|
GetIPHeader(PNDIS_PACKET *PacketPtr)
|
|
{
|
|
PNDIS_BUFFER Buffer;
|
|
PNDIS_PACKET Packet;
|
|
IPHeader *pIph;
|
|
|
|
Packet = GetIPPacket();
|
|
if (Packet != NULL) {
|
|
Buffer = GetIPHdrBuffer(&pIph);
|
|
if (Buffer != NULL) {
|
|
PacketContext *PC = (PacketContext *) Packet->ProtocolReserved;
|
|
PC->pc_common.pc_flags |= PACKET_FLAG_IPHDR;
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
*PacketPtr = Packet;
|
|
return pIph;
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//** ReferenceBuffer - Reference a buffer.
|
|
//
|
|
// Called when we need to update the count of a BufferReference strucutre, either
|
|
// by a positive or negative value. If the count goes to 0, we'll free the buffer
|
|
// reference and return success. Otherwise we'll return pending.
|
|
//
|
|
// Entry: BR - Pointer to buffer reference.
|
|
// Count - Amount to adjust refcount by.
|
|
//
|
|
// Returns: Success, or pending.
|
|
//
|
|
int
|
|
ReferenceBuffer(BufferReference * BR, int Count)
|
|
{
|
|
CTELockHandle handle;
|
|
int NewCount;
|
|
|
|
if (BR == NULL) {
|
|
return 0;
|
|
}
|
|
CTEGetLock(&BR->br_lock, &handle);
|
|
BR->br_refcount += Count;
|
|
NewCount = BR->br_refcount;
|
|
CTEFreeLock(&BR->br_lock, handle);
|
|
return NewCount;
|
|
}
|
|
|
|
//* IPSendComplete - IP send complete handler.
|
|
//
|
|
// Called by the link layer when a send completes. We're given a pointer to a
|
|
// net structure, as well as the completing send packet and the final status of
|
|
// the send.
|
|
//
|
|
// Entry: Context - Context we gave to the link layer.
|
|
// Packet - Completing send packet.
|
|
// Status - Final status of send.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
__stdcall
|
|
IPSendComplete(void *Context, PNDIS_PACKET Packet, NDIS_STATUS Status)
|
|
{
|
|
PacketContext *PContext = (PacketContext *) Packet->ProtocolReserved;
|
|
void (*xmitdone) (void *, PNDIS_BUFFER, IP_STATUS);
|
|
void *UContext; // Upper layer context.
|
|
BufferReference *BufRef; // Buffer reference, if any.
|
|
PNDIS_BUFFER Buffer;
|
|
PNDIS_PACKET_EXTENSION PktExt;
|
|
Interface *IF; // The interface on which this completed.
|
|
BOOLEAN fIpsec = (BOOLEAN) (PContext->pc_common.pc_IpsecCtx != NULL);
|
|
PNDIS_BUFFER PC_firewall;
|
|
struct IPRcvBuf *PC_firewall2;
|
|
PNDIS_BUFFER PC_hdrincl;
|
|
LinkEntry *Link;
|
|
IP_STATUS SendStatus;
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
// Copy useful information from packet.
|
|
xmitdone = PContext->pc_pi->pi_xmitdone;
|
|
UContext = PContext->pc_context;
|
|
BufRef = PContext->pc_br;
|
|
|
|
PC_firewall = PContext->pc_firewall;
|
|
PC_firewall2 = PContext->pc_firewall2;
|
|
PC_hdrincl = PContext->pc_hdrincl;
|
|
|
|
IF = PContext->pc_if;
|
|
Link = PContext->pc_iflink;
|
|
|
|
SendStatus = (Status == NDIS_STATUS_FAILURE) ? IP_GENERAL_FAILURE
|
|
: IP_SUCCESS;
|
|
|
|
PktExt = NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
|
|
if (PtrToUlong(PktExt->NdisPacketInfo[TcpLargeSendPacketInfo])) {
|
|
|
|
//We are sure that this is tcp.
|
|
// get its context and pass on this info.
|
|
|
|
((SendCmpltContext *) UContext)->scc_ByteSent =
|
|
PtrToUlong(PktExt->NdisPacketInfo[TcpLargeSendPacketInfo]);
|
|
}
|
|
|
|
if (BufRef == (BufferReference *) NULL) {
|
|
|
|
// If this is a header include packet
|
|
// make sure that duped data part is
|
|
// freed here.
|
|
|
|
|
|
Buffer = FreeIPPacket(Packet, TRUE, SendStatus);
|
|
if (!Buffer) {
|
|
//
|
|
// if NULL was returned by IPSEC, it is ok since IPSEC
|
|
// might have released all the MDLs.
|
|
//
|
|
if (fIpsec) {
|
|
// We're done with the packet now, we may need to dereference
|
|
// the interface.
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (IF) {
|
|
DerefIF(IF);
|
|
}
|
|
return;
|
|
} else {
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
ASSERT(Buffer);
|
|
|
|
(*xmitdone) (UContext, Buffer, SendStatus);
|
|
} else {
|
|
|
|
// Check if this is the last refcnt on this buffer.
|
|
// Decrement this reference only after all the operations are
|
|
// done on this packet.
|
|
|
|
if (ReferenceBuffer(BufRef, -1) == 0) {
|
|
|
|
PContext->pc_ipsec_flags |= IPSEC_FLAG_FRAG_DONE;
|
|
|
|
// Check for header include option on the packet.
|
|
// If true, then original buffer needs to be hooked
|
|
// back in to the chain freeing the one allocated by us.
|
|
// Note that this pc_hdrincl will be true only if the packet
|
|
// traversed thru slow path in ipxmit.
|
|
|
|
|
|
FreeIPPacket(Packet, TRUE, SendStatus);
|
|
|
|
Buffer = BufRef->br_buffer;
|
|
|
|
if (!Buffer) {
|
|
//
|
|
// if NULL was returned by IPSEC, it is ok since IPSEC
|
|
// might have released all the MDLs.
|
|
//
|
|
if (fIpsec) {
|
|
|
|
// We're done with the packet now, we may need to dereference
|
|
// the interface.
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (IF) {
|
|
DerefIF(IF);
|
|
}
|
|
|
|
CTEFreeMem(BufRef);
|
|
|
|
return;
|
|
} else {
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
ASSERT(Buffer);
|
|
|
|
if (PC_firewall) {
|
|
PNDIS_BUFFER FirewallBuffer;
|
|
|
|
FirewallBuffer = PC_firewall;
|
|
FreeIPBufferChain(Buffer);
|
|
Buffer = FirewallBuffer;
|
|
|
|
ASSERT(PC_firewall2);
|
|
IPFreeBuff(PC_firewall2);
|
|
|
|
if (PC_hdrincl) {
|
|
FreeIPPayloadBuffer(Buffer,PC_hdrincl);
|
|
}
|
|
}
|
|
|
|
CTEFreeMem(BufRef);
|
|
|
|
(*xmitdone) (UContext, Buffer, SendStatus);
|
|
|
|
} else {
|
|
|
|
// Since there are more outstanding packets using the headers
|
|
// in attached to this packet, do not restore them now.
|
|
|
|
Buffer = FreeIPPacket(Packet, FALSE, SendStatus);
|
|
|
|
// We're not done with the send yet, so NULL the IF to
|
|
// prevent dereferencing it.
|
|
IF = NULL;
|
|
Link = NULL;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// We're done with the packet now, we may need to dereference
|
|
// the interface.
|
|
if (Link != NULL) {
|
|
DerefLink(Link);
|
|
}
|
|
if (IF == NULL) {
|
|
return;
|
|
} else {
|
|
DerefIF(IF);
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
ULONG DebugLockdown = 0;
|
|
#endif
|
|
|
|
//** SendIPPacket - Send an IP packet.
|
|
//
|
|
// Called when we have a filled in IP packet we need to send. Basically, we
|
|
// compute the xsum and send the thing.
|
|
//
|
|
// Entry: IF - Interface to send it on.
|
|
// FirstHop - First hop address to send it to.
|
|
// Packet - Packet to be sent.
|
|
// Buffer - Buffer to be sent.
|
|
// Header - Pointer to IP Header of packet.
|
|
// Options - Pointer to option buffer.
|
|
// OptionLength - Length of options.
|
|
//
|
|
// Returns: IP_STATUS of attempt to send.
|
|
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)
|
|
{
|
|
ulong csum;
|
|
NDIS_STATUS Status;
|
|
IP_STATUS SendStatus;
|
|
|
|
#if DBG
|
|
//
|
|
// If DebugLockdown is set to 1, this means no unicast packets with
|
|
// protocol other than AH or ESP can be sent out; and we assert if so.
|
|
//
|
|
if (DebugLockdown) {
|
|
USHORT *pPort = NULL;
|
|
ULONG Length = 0;
|
|
USHORT IsakmpPort = net_short(500);
|
|
USHORT KerberosPort = net_short(88);
|
|
|
|
NdisQueryBuffer(Buffer, &pPort, &Length);
|
|
if (pPort &&
|
|
Header->iph_protocol != PROTOCOL_AH &&
|
|
Header->iph_protocol != PROTOCOL_ESP &&
|
|
IPGetAddrType(Header->iph_dest) == DEST_REMOTE) {
|
|
//
|
|
// We assert here unless this is exempt traffic.
|
|
//
|
|
ASSERT(Header->iph_protocol == PROTOCOL_RSVP ||
|
|
(Header->iph_protocol == PROTOCOL_UDP &&
|
|
(pPort[1] == IsakmpPort ||
|
|
pPort[0] == KerberosPort ||
|
|
pPort[1] == KerberosPort)) ||
|
|
(Header->iph_protocol == PROTOCOL_TCP &&
|
|
(pPort[0] == KerberosPort ||
|
|
pPort[1] == KerberosPort)));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ASSERT(IF->if_refcount != 0);
|
|
|
|
DEBUGMSG(DBG_TRACE && DBG_IP && DBG_TX,
|
|
(DTEXT("+SendIPPacket(%x, %x, %x, %x, %x, %x, %x, %X, %X, %x)\n"),
|
|
IF, FirstHop, Packet, Buffer, Header, Options, OptionSize, IPSeced,
|
|
ArpCtxt, DontFreePacket));
|
|
|
|
//
|
|
// If we IPSECed this buffer, then the packet is ready to go courtesy IPSEC
|
|
//
|
|
if (!IPSeced) {
|
|
|
|
csum = xsum(Header, sizeof(IPHeader));
|
|
if (Options) { // We have options, oh boy.
|
|
|
|
PNDIS_BUFFER OptBuffer;
|
|
PacketContext *pc = (PacketContext *) Packet->ProtocolReserved;
|
|
|
|
NdisAllocateBuffer(&Status, &OptBuffer, BufferPool,
|
|
Options, OptionSize);
|
|
if (Status != NDIS_STATUS_SUCCESS) { // Couldn't get the needed
|
|
// option buffer.
|
|
|
|
CTEFreeMem(Options);
|
|
if (!DontFreePacket) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
}
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
pc->pc_common.pc_flags |= PACKET_FLAG_OPTIONS;
|
|
NdisChainBufferAtBack(Packet, OptBuffer);
|
|
csum += xsum(Options, OptionSize);
|
|
csum = (csum >> 16) + (csum & 0xffff);
|
|
csum += (csum >> 16);
|
|
}
|
|
Header->iph_xsum = ~(ushort) csum;
|
|
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
} else {
|
|
// Make sure that packet tail is pointing to the
|
|
// last MDL.
|
|
PNDIS_BUFFER tmp = Buffer;
|
|
|
|
if (tmp) {
|
|
while(NDIS_BUFFER_LINKAGE(tmp)) {
|
|
tmp = NDIS_BUFFER_LINKAGE(tmp);
|
|
}
|
|
Packet->Private.Tail = tmp;
|
|
}
|
|
}
|
|
|
|
if (CLASSD_ADDR(Header->iph_dest)) {
|
|
|
|
IF->if_OutMcastPkts++;
|
|
IF->if_OutMcastOctets += net_short(Header->iph_length) - sizeof(IPHeader);
|
|
}
|
|
|
|
Status = (*(IF->if_xmit)) (IF->if_lcontext, &Packet, 1, FirstHop,
|
|
NULL, ArpCtxt);
|
|
|
|
if (Status == NDIS_STATUS_PENDING) {
|
|
return IP_PENDING;
|
|
}
|
|
// Status wasn't pending. Map the status, and free the packet.
|
|
if (Status == NDIS_STATUS_SUCCESS)
|
|
SendStatus = IP_SUCCESS;
|
|
else {
|
|
if (Status == NDIS_STATUS_FAILURE)
|
|
SendStatus = IP_GENERAL_FAILURE;
|
|
else
|
|
SendStatus = IP_HW_ERROR;
|
|
}
|
|
if (!DontFreePacket)
|
|
FreeIPPacket(Packet, TRUE, SendStatus);
|
|
return SendStatus;
|
|
}
|
|
|
|
//* SendDHCPPacket - Send a broadcast for DHCP.
|
|
//
|
|
// Called when somebody is sending a broadcast packet with a NULL source
|
|
// address. We assume this means they're sending a DHCP packet. We loop
|
|
// through the NTE table, and when we find an entry that's not valid we
|
|
// send out the interface associated with that entry.
|
|
//
|
|
// Input: Dest - Destination of packet.
|
|
// Packet - Packet to be send.
|
|
// Buffer - Buffer chain to be sent.
|
|
// Header - Pointer to header buffer being sent.
|
|
//
|
|
// Return: Status of send attempt.
|
|
//
|
|
IP_STATUS
|
|
SendDHCPPacket(IPAddr Dest, PNDIS_PACKET Packet, PNDIS_BUFFER Buffer,
|
|
IPHeader * IPH, void *ArpCtxt)
|
|
{
|
|
if (RefPtrValid(&DHCPRefPtr)) {
|
|
NetTableEntry* DHCPNTE = AcquireRefPtr(&DHCPRefPtr);
|
|
if (DHCPNTE->nte_flags & NTE_ACTIVE) {
|
|
IP_STATUS Status;
|
|
// The DHCP NTE is currently invalid, and active. Send on that
|
|
// interface.
|
|
Status = SendIPPacket(DHCPNTE->nte_if, Dest, Packet, Buffer, IPH,
|
|
NULL, 0, (BOOLEAN) (IPSecHandlerPtr != NULL),
|
|
ArpCtxt, FALSE);
|
|
ReleaseRefPtr(&DHCPRefPtr);
|
|
return Status;
|
|
}
|
|
ReleaseRefPtr(&DHCPRefPtr);
|
|
}
|
|
// Didn't find an invalid NTE! Free the resources, and return the failure.
|
|
FreeIPPacket(Packet, TRUE, IP_DEST_HOST_UNREACHABLE);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
|
|
//* IPCopyBuffer - Copy an NDIS buffer chain at a specific offset.
|
|
//
|
|
// This is the IP version of the function NdisCopyBuffer, which didn't
|
|
// get done properly in NDIS3. We take in an NDIS buffer chain, an offset,
|
|
// and a length, and produce a buffer chain describing that subset of the
|
|
// input buffer chain.
|
|
//
|
|
// This routine is not particularly efficient. Since only IPFragment uses
|
|
// it currently, it might be better to just incorporate this functionality
|
|
// directly into IPFragment.
|
|
//
|
|
// Input: OriginalBuffer - Original buffer chain to copy from.
|
|
// Offset - Offset from start to dup.
|
|
// Length - Length in bytes to dup.
|
|
//
|
|
// Returns: Pointer to new chain if we can make one, NULL if we can't.
|
|
//
|
|
PNDIS_BUFFER
|
|
IPCopyBuffer(PNDIS_BUFFER OriginalBuffer, uint Offset, uint Length)
|
|
{
|
|
|
|
PNDIS_BUFFER CurrentBuffer; // Pointer to current buffer.
|
|
PNDIS_BUFFER *NewBuffer = NULL; // Pointer to pointer to current new buffer.
|
|
PNDIS_BUFFER FirstBuffer; // First buffer in new chain.
|
|
UINT CopyLength; // Length of current copy.
|
|
NDIS_STATUS NewStatus; // Status of NdisAllocateBuffer operation.
|
|
|
|
PVOID pvBuffer;
|
|
|
|
// First skip over the number of buffers we need to to reach Offset.
|
|
CurrentBuffer = OriginalBuffer;
|
|
|
|
while (Offset >= NdisBufferLength(CurrentBuffer)) {
|
|
Offset -= NdisBufferLength(CurrentBuffer);
|
|
CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
|
|
|
|
if (CurrentBuffer == (PNDIS_BUFFER) NULL)
|
|
return NULL;
|
|
}
|
|
|
|
// Now CurrentBuffer is the buffer from which we start building the new chain, and
|
|
// Offset is the offset into CurrentBuffer from which to start.
|
|
FirstBuffer = NULL;
|
|
NewBuffer = &FirstBuffer;
|
|
|
|
do {
|
|
CopyLength = MIN(Length, NdisBufferLength(CurrentBuffer) - Offset);
|
|
|
|
pvBuffer = TcpipBufferVirtualAddress(CurrentBuffer, NormalPagePriority);
|
|
if (pvBuffer == NULL) {
|
|
break;
|
|
}
|
|
|
|
NdisAllocateBuffer(&NewStatus, NewBuffer, BufferPool,
|
|
((uchar *) pvBuffer) + Offset,
|
|
CopyLength);
|
|
if (NewStatus != NDIS_STATUS_SUCCESS)
|
|
break;
|
|
|
|
Offset = 0; // No offset from next buffer.
|
|
NewBuffer = &(NDIS_BUFFER_LINKAGE(*NewBuffer));
|
|
CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
|
|
Length -= CopyLength;
|
|
} while (Length != 0 && CurrentBuffer != (PNDIS_BUFFER) NULL);
|
|
|
|
if (Length == 0) { // We succeeded
|
|
return FirstBuffer;
|
|
} else { // We exited the loop because of an error.
|
|
|
|
// We need to free any allocated buffers, and return.
|
|
CurrentBuffer = FirstBuffer;
|
|
while (CurrentBuffer != (PNDIS_BUFFER) NULL) {
|
|
PNDIS_BUFFER Temp = CurrentBuffer;
|
|
CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
|
|
NdisFreeBuffer(Temp);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//** IPFragment - Fragment and send an IP datagram.
|
|
//
|
|
// Called when an outgoing datagram is larger than the local MTU, and needs
|
|
// to be fragmented. This is a somewhat complicated operation. The caller
|
|
// gives us a prebuilt IP header, packet, and options. We use the header and
|
|
// packet on the last fragment of the send, as the passed in header already
|
|
// has the more fragments bit set correctly for the last fragment.
|
|
//
|
|
// The basic idea is to figure out the maximum size which we can send as a
|
|
// multiple of 8. Then, while we can send a maximum size fragment we'll
|
|
// allocate a header, packet, etc. and send it. At the end we'll send the
|
|
// final fragment using the provided header and packet.
|
|
//
|
|
// Entry: DestIF - Outbound interface of datagram.
|
|
// MTU - MTU to use in transmitting.
|
|
// FirstHop - First (or next) hop for this datagram.
|
|
// Packet - Packet to be sent.
|
|
// Header - Prebuilt IP header.
|
|
// Buffer - Buffer chain for data to be sent.
|
|
// DataSize - Size in bytes of data.
|
|
// Options - Pointer to option buffer, if any.
|
|
// OptionSize - Size in bytes of option buffer.
|
|
// SentCount - Pointer to where to return pending send count (may be NULL).
|
|
// bDontLoopback - Determines whether NDIS_FLAGS_DONT_LOOPBACK needs
|
|
// to be set
|
|
//
|
|
// Returns: IP_STATUS of send.
|
|
//
|
|
|
|
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)
|
|
{
|
|
BufferReference *BR; // Buffer reference we'll use.
|
|
PacketContext *PContext = (PacketContext *) Packet->ProtocolReserved;
|
|
FWContext *FWC = (FWContext *) Packet->ProtocolReserved;
|
|
PacketContext *CurrentContext; // Current Context in use.
|
|
uint MaxSend; // Maximum size (in bytes) we can send here.
|
|
uint PendingSends = 0; // Counter of how many pending sends we have.
|
|
PNDIS_BUFFER CurrentBuffer; // Current buffer to be sent.
|
|
PNDIS_PACKET CurrentPacket; // Current packet we're using.
|
|
IP_STATUS SendStatus; // Status of send command.
|
|
IPHeader *CurrentHeader; // Current header buffer we're using.
|
|
ushort Offset = 0; // Current offset into fragmented packet.
|
|
ushort StartOffset; // Starting offset of packet.
|
|
ushort RealOffset; // Offset of new fragment.
|
|
uint FragOptSize = 0; // Size (in bytes) of fragment options.
|
|
uchar FragmentOptions[MAX_OPT_SIZE]; // Master copy of options sent for fragments.
|
|
uchar Error = FALSE; // Set if we get an error in our main loop.
|
|
BOOLEAN NukeFwPktOptions = FALSE;
|
|
PNDIS_BUFFER HdrIncl = NULL;
|
|
uint FirewallMode = 0;
|
|
PNDIS_BUFFER TempBuffer, PC_Firewall;
|
|
struct IPRcvBuf *PC_Firewall2;
|
|
PNDIS_PACKET LastPacket = NULL;
|
|
PIPSEC_SEND_COMPLETE_CONTEXT pIpsecCtx;
|
|
BOOLEAN PC_reinject = FALSE;
|
|
PVOID PC_context = NULL;
|
|
void (*xmitdone) (void *, PNDIS_BUFFER, IP_STATUS);
|
|
|
|
xmitdone = NULL;
|
|
|
|
MaxSend = (MTU - OptionSize) & ~7; // Determine max send size.
|
|
ASSERT(MaxSend < DataSize);
|
|
|
|
BR = PContext->pc_br; // Get the buffer reference we'll need.
|
|
ASSERT(BR);
|
|
|
|
FirewallMode = ProcessFirewallQ();
|
|
TempBuffer = BR->br_buffer;
|
|
PC_Firewall = PContext->pc_firewall;
|
|
PC_Firewall2 = PContext->pc_firewall2;
|
|
|
|
pIpsecCtx = PContext->pc_common.pc_IpsecCtx;
|
|
if (pIpsecCtx && (pIpsecCtx->Flags & SCF_FLUSH)) {
|
|
PC_reinject = TRUE;
|
|
PC_context = PContext->pc_context;
|
|
}
|
|
|
|
HdrIncl = PContext->pc_hdrincl;
|
|
|
|
xmitdone = PContext->pc_pi->pi_xmitdone;
|
|
|
|
if (Header->iph_offset & IP_DF_FLAG) { // Don't fragment flag set.
|
|
// Error out.
|
|
//
|
|
// If options are already linked in, dont free them. FreeIPPacket will.
|
|
//
|
|
|
|
if (Options &&
|
|
!(PContext->pc_common.pc_flags & PACKET_FLAG_OPTIONS)) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
PContext->pc_ipsec_flags |= (IPSEC_FLAG_FRAG_DONE | IPSEC_FLAG_FLUSH);
|
|
FreeIPPacket(Packet, FALSE, IP_PACKET_TOO_BIG);
|
|
|
|
if (SentCount == (int *)NULL) {
|
|
|
|
//
|
|
// Only non-bcast call instance of IPFragment
|
|
// calls with SentCount == NULL and we need
|
|
// to do the cleanup in this case.
|
|
// Also BR count will be zero in this case.
|
|
//
|
|
|
|
if (ReferenceBuffer(BR, PendingSends) == 0) {
|
|
if (!PC_reinject) {
|
|
|
|
//
|
|
// Need to undo ipsec, firewall and then
|
|
// header include changes to the buffer list.
|
|
//
|
|
|
|
if (pIpsecCtx) {
|
|
(*IPSecSendCmpltPtr)(NULL, TempBuffer, pIpsecCtx,
|
|
IP_PACKET_TOO_BIG, &TempBuffer);
|
|
}
|
|
|
|
//
|
|
// If this is user header include packet,
|
|
// relink the original user buffer if necessary
|
|
//
|
|
|
|
if (PC_Firewall) {
|
|
BR->br_buffer = PC_Firewall;
|
|
}
|
|
|
|
if (BR->br_userbuffer) {
|
|
FreeIPPayloadBuffer(BR->br_buffer, BR->br_userbuffer);
|
|
}
|
|
}
|
|
|
|
if (FirewallMode && PC_Firewall) {
|
|
|
|
//
|
|
// Free the mdl chain
|
|
// allocated in firewall path.
|
|
//
|
|
|
|
FreeIPBufferChain(TempBuffer);
|
|
IPFreeBuff(PC_Firewall2);
|
|
|
|
}
|
|
|
|
CTEFreeMem(BR);
|
|
|
|
} else {
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
IPSInfo.ipsi_fragfails++;
|
|
return IP_PACKET_TOO_BIG;
|
|
}
|
|
|
|
#if DBG && GPC
|
|
if (PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(Packet,
|
|
ClassificationHandlePacketInfo))) {
|
|
IF_IPDBG(IP_DEBUG_GPC)
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPFrag: Packet %p with CH\n", Packet));
|
|
}
|
|
#endif
|
|
|
|
StartOffset = Header->iph_offset & IP_OFFSET_MASK;
|
|
StartOffset = net_short(StartOffset) * 8;
|
|
|
|
// If we have any options, copy the ones that need to be copied, and figure
|
|
// out the size of these new copied options.
|
|
|
|
if (Options != (uchar *) NULL) { // We have options.
|
|
|
|
uchar *TempOptions = Options;
|
|
const uchar *EndOptions = (const uchar *)(Options + OptionSize);
|
|
|
|
// Copy the options into the fragment options buffer.
|
|
NdisFillMemory(FragmentOptions, MAX_OPT_SIZE, IP_OPT_EOL);
|
|
while ((TempOptions < EndOptions) &&
|
|
(TempOptions[IP_OPT_TYPE] != IP_OPT_EOL)) {
|
|
|
|
if (TempOptions[IP_OPT_TYPE] & IP_OPT_COPIED) {
|
|
// This option needs to be copied.
|
|
|
|
uint TempOptSize;
|
|
|
|
TempOptSize = TempOptions[IP_OPT_LENGTH];
|
|
RtlCopyMemory(&FragmentOptions[FragOptSize], TempOptions,
|
|
TempOptSize);
|
|
FragOptSize += TempOptSize;
|
|
TempOptions += TempOptSize;
|
|
} else {
|
|
// A non-copied option, just skip over it.
|
|
|
|
if (TempOptions[IP_OPT_TYPE] == IP_OPT_NOP)
|
|
TempOptions++;
|
|
else
|
|
TempOptions += TempOptions[IP_OPT_LENGTH];
|
|
}
|
|
}
|
|
// Round the copied size up to a multiple of 4.
|
|
FragOptSize = ((FragOptSize & 3) ? ((FragOptSize & ~3) + 4) : FragOptSize);
|
|
//Is this from FW path?
|
|
if (PContext->pc_common.pc_flags & PACKET_FLAG_FW) {
|
|
//Nuke PContext->fc_options after first IpsendPacket
|
|
//To prevent double freeing of option buffer
|
|
NukeFwPktOptions = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
PContext->pc_common.pc_flags |= PACKET_FLAG_IPBUF;
|
|
|
|
// Now, while we can build maximum size fragments, do so.
|
|
do {
|
|
PVOID CancelId;
|
|
uchar Owner;
|
|
|
|
if ((CurrentHeader = GetIPHeader(&CurrentPacket)) == (IPHeader *) NULL) {
|
|
// Couldn't get a buffer. Break out, since no point in sending others.
|
|
SendStatus = IP_NO_RESOURCES;
|
|
Error = TRUE;
|
|
break;
|
|
}
|
|
NDIS_PER_PACKET_INFO_FROM_PACKET(CurrentPacket, ClassificationHandlePacketInfo) =
|
|
NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ClassificationHandlePacketInfo);
|
|
|
|
#if !MILLEN
|
|
// Set the cancel requestID from parent packet.
|
|
CancelId = NDIS_GET_PACKET_CANCEL_ID(Packet);
|
|
NDIS_SET_PACKET_CANCEL_ID(CurrentPacket, CancelId);
|
|
#endif
|
|
|
|
// Copy the buffer into a new one, if we can.
|
|
CurrentBuffer = IPCopyBuffer(Buffer, Offset, MaxSend);
|
|
if (CurrentBuffer == NULL) { // No buffer, free resources and
|
|
// break.
|
|
|
|
// header cleanup will be done in error handling
|
|
// routine
|
|
|
|
SendStatus = IP_NO_RESOURCES;
|
|
FreeIPPacket(CurrentPacket, FALSE, SendStatus);
|
|
Error = TRUE;
|
|
break;
|
|
}
|
|
//
|
|
// Options for this send are set up when we get here, either from the
|
|
// entry from the loop, or from the allocation below.
|
|
|
|
// We have all the pieces we need. Put the packet together and send it.
|
|
//
|
|
CurrentContext = (PacketContext *) CurrentPacket->ProtocolReserved;
|
|
Owner = CurrentContext->pc_common.pc_owner;
|
|
*CurrentContext = *PContext;
|
|
CurrentContext->pc_common.pc_owner = Owner;
|
|
|
|
|
|
*CurrentHeader = *Header;
|
|
CurrentContext->pc_common.pc_flags &= ~PACKET_FLAG_FW;
|
|
CurrentHeader->iph_verlen = (UCHAR) (IP_VERSION +
|
|
((OptionSize + (uint) sizeof(IPHeader)) >> 2));
|
|
CurrentHeader->iph_length = net_short(MaxSend + OptionSize + sizeof(IPHeader));
|
|
RealOffset = (StartOffset + Offset) >> 3;
|
|
CurrentHeader->iph_offset = net_short(RealOffset) | IP_MF_FLAG;
|
|
|
|
if (bDontLoopback) {
|
|
NdisSetPacketFlags(CurrentPacket,
|
|
NDIS_FLAGS_DONT_LOOPBACK);
|
|
} else {
|
|
if (CurrentHeader->iph_ttl == 0) {
|
|
NdisSetPacketFlags(CurrentPacket, NDIS_FLAGS_LOOPBACK_ONLY);
|
|
}
|
|
}
|
|
|
|
// Clear Options flag if we are not sending any options
|
|
|
|
if (Options == NULL) {
|
|
CurrentContext->pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS;
|
|
}
|
|
|
|
|
|
// Do not free the packet in SendIPPacket, as we may need
|
|
// to chain the buffer in case of IP_NO_RESOURCES
|
|
|
|
SendStatus = SendIPPacket(DestIF, FirstHop, CurrentPacket,
|
|
CurrentBuffer, CurrentHeader, Options,
|
|
OptionSize, FALSE, ArpCtxt, TRUE);
|
|
|
|
|
|
if (SendStatus == IP_PENDING) {
|
|
PendingSends++;
|
|
} else {
|
|
if(SendStatus == IP_NO_RESOURCES) {
|
|
// SendIPPacket has not chained the buffer..
|
|
NdisChainBufferAtBack(CurrentPacket, CurrentBuffer);
|
|
}
|
|
FreeIPPacket(CurrentPacket, FALSE, SendStatus);
|
|
}
|
|
|
|
IPSInfo.ipsi_fragcreates++;
|
|
Offset = Offset + (USHORT) MaxSend;
|
|
DataSize -= MaxSend;
|
|
|
|
if (NukeFwPktOptions) {
|
|
//This is to avoid double frees of option
|
|
// in IpFreepacket and Freefwpacket.
|
|
|
|
FWC->fc_options = (uchar *) NULL;
|
|
FWC->fc_optlength = 0;
|
|
NukeFwPktOptions = FALSE;
|
|
|
|
}
|
|
// If we have any fragmented options, set up to use them next time.
|
|
|
|
if (FragOptSize) {
|
|
|
|
Options = CTEAllocMemN(OptionSize = FragOptSize, 'qiCT');
|
|
if (Options == (uchar *) NULL) { // Can't get an option buffer.
|
|
|
|
SendStatus = IP_NO_RESOURCES;
|
|
Error = TRUE;
|
|
break;
|
|
}
|
|
RtlCopyMemory(Options, FragmentOptions, OptionSize);
|
|
} else {
|
|
Options = (uchar *) NULL;
|
|
OptionSize = 0;
|
|
}
|
|
} while (DataSize > MaxSend);
|
|
|
|
|
|
// Clear Options flag if we are not sending any options
|
|
|
|
if (Options == NULL) {
|
|
PContext->pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS;
|
|
}
|
|
|
|
|
|
//
|
|
// We've sent all of the previous fragments, now send the last one. We
|
|
// already have the packet and header buffer, as well as options if there
|
|
// are any - we need to copy the appropriate data.
|
|
//
|
|
|
|
|
|
|
|
|
|
if (!Error) { // Everything went OK above.
|
|
|
|
CurrentBuffer = IPCopyBuffer(Buffer, Offset, DataSize);
|
|
if (CurrentBuffer == NULL) { // No buffer, free resources
|
|
//
|
|
// If options are already linked in, dont free them. FreeIPPacket will.
|
|
//
|
|
|
|
if (Options &&
|
|
!(PContext->pc_common.pc_flags & PACKET_FLAG_OPTIONS)) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
|
|
|
|
if (PC_reinject)
|
|
LastPacket = Packet;
|
|
else
|
|
FreeIPPacket(Packet, FALSE, IP_NO_RESOURCES);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
} else { // Everything's OK, send it.
|
|
|
|
Header->iph_verlen = (UCHAR) (IP_VERSION +
|
|
((OptionSize + (uint) sizeof(IPHeader)) >> 2));
|
|
Header->iph_length = net_short(DataSize + OptionSize + sizeof(IPHeader));
|
|
RealOffset = (StartOffset + Offset) >> 3;
|
|
Header->iph_offset = net_short(RealOffset) | (Header->iph_offset & IP_MF_FLAG);
|
|
|
|
if (bDontLoopback) {
|
|
NdisSetPacketFlags(Packet,
|
|
NDIS_FLAGS_DONT_LOOPBACK);
|
|
} else {
|
|
if (Header->iph_ttl == 0) {
|
|
NdisSetPacketFlags(Packet, NDIS_FLAGS_LOOPBACK_ONLY);
|
|
}
|
|
}
|
|
|
|
|
|
// Do not free the packet in SendIPPacket, as we may need
|
|
// to chain the buffer in case of IP_NO_RESOURCES
|
|
|
|
SendStatus = SendIPPacket(DestIF, FirstHop, Packet,
|
|
CurrentBuffer, Header, Options,
|
|
OptionSize, FALSE, ArpCtxt, TRUE);
|
|
|
|
if (SendStatus == IP_PENDING) {
|
|
PendingSends++;
|
|
} else if (PC_reinject) {
|
|
LastPacket = Packet;
|
|
} else {
|
|
if (SendStatus == IP_NO_RESOURCES) {
|
|
// SendIPPacket has not chained the buffer..
|
|
NdisChainBufferAtBack(Packet, CurrentBuffer);
|
|
}
|
|
FreeIPPacket(Packet, FALSE, SendStatus);
|
|
}
|
|
|
|
IPSInfo.ipsi_fragcreates++;
|
|
IPSInfo.ipsi_fragoks++;
|
|
}
|
|
} else { // We had some sort of error.
|
|
// Free resources.
|
|
//
|
|
// If options are already linked in, dont free them. FreeIPPacket will.
|
|
//
|
|
|
|
if (Options &&
|
|
!(PContext->pc_common.pc_flags & PACKET_FLAG_OPTIONS)) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
if (PC_reinject)
|
|
LastPacket = Packet;
|
|
else
|
|
FreeIPPacket(Packet, FALSE, SendStatus);
|
|
|
|
IPSInfo.ipsi_outdiscards++;
|
|
}
|
|
|
|
// Now, figure out what error code to return and whether or not we need to
|
|
// free the BufferReference.
|
|
|
|
if (SentCount == (int *)NULL) { // No sent count is to be
|
|
// returned.
|
|
|
|
if (ReferenceBuffer(BR, PendingSends) == 0) {
|
|
|
|
|
|
if (PC_reinject) {
|
|
|
|
if (LastPacket) {
|
|
PacketContext *pc = (PacketContext *) LastPacket->ProtocolReserved;
|
|
|
|
pc->pc_ipsec_flags |= (IPSEC_FLAG_FRAG_DONE | IPSEC_FLAG_FLUSH);
|
|
// This is the last packet that is being freed
|
|
// Fixup ipsec/firewall/hdrincl headers, if any
|
|
|
|
FreeIPPacket(LastPacket, TRUE, IP_SUCCESS);
|
|
} else if (PendingSends) {
|
|
//
|
|
// IPSEC reinject and last packet is NULL, but we still
|
|
// return success !!!!
|
|
// Also, pendingsends is +ve =>ipsendcomplete already
|
|
// called in same thread somebody has to free IPSEC's buffer
|
|
// freeippacket has been called by ipsendcomplete
|
|
// the only remaining way is calling xmitdone
|
|
// since ipsendcomplete won't have called xmit done as
|
|
// refcount would be -ve
|
|
//
|
|
|
|
(*IPSecSendCmpltPtr)(NULL, TempBuffer, pIpsecCtx,
|
|
IP_SUCCESS, &TempBuffer);
|
|
(*xmitdone)(PC_context, TempBuffer, IP_SUCCESS);
|
|
}
|
|
} else {
|
|
|
|
// Need to undo ipsec, firewall and then
|
|
// header include changes to teh buffer list.
|
|
|
|
if (pIpsecCtx) {
|
|
(*IPSecSendCmpltPtr)(NULL, TempBuffer, pIpsecCtx,
|
|
IP_SUCCESS, &TempBuffer);
|
|
}
|
|
|
|
// If this is user header include packet,
|
|
// relink the original user buffer if necessary
|
|
if (PC_Firewall) {
|
|
BR->br_buffer = PC_Firewall;
|
|
}
|
|
|
|
if (BR->br_userbuffer) {
|
|
FreeIPPayloadBuffer(BR->br_buffer, BR->br_userbuffer);
|
|
}
|
|
|
|
|
|
}
|
|
CTEFreeMem(BR);
|
|
|
|
if (FirewallMode && PC_Firewall) {
|
|
FreeIPBufferChain(TempBuffer); // free the mdl chain
|
|
// allocated in firewall path
|
|
|
|
IPFreeBuff(PC_Firewall2); // free the rcvbuf chain
|
|
|
|
}
|
|
return IP_SUCCESS;
|
|
}
|
|
//
|
|
// This send is still pending. Call freepacket without setting
|
|
// pc_ipsec flag
|
|
//
|
|
if (LastPacket)
|
|
FreeIPPacket(LastPacket, FALSE, IP_PENDING);
|
|
|
|
return IP_PENDING;
|
|
} else
|
|
*SentCount += PendingSends;
|
|
|
|
// Just free the packet. Headers will be restored when the last packet completes.
|
|
|
|
if (LastPacket)
|
|
FreeIPPacket(LastPacket, FALSE, IP_PENDING);
|
|
return IP_PENDING;
|
|
|
|
}
|
|
|
|
|
|
//* UpdateRouteOption - Update a SR or RR options.
|
|
//
|
|
// Called by UpdateOptions when it needs to update a route option.
|
|
//
|
|
// Input: RTOption - Pointer to route option to be updated.
|
|
// Address - Address to update with.
|
|
//
|
|
// Returns: TRUE if we updated, FALSE if we didn't.
|
|
//
|
|
uchar
|
|
UpdateRouteOption(uchar * RTOption, IPAddr Address)
|
|
{
|
|
uchar Pointer; // Pointer value of option.
|
|
|
|
Pointer = RTOption[IP_OPT_PTR] - 1;
|
|
if (Pointer < RTOption[IP_OPT_LENGTH]) {
|
|
if ((RTOption[IP_OPT_LENGTH] - Pointer) < sizeof(IPAddr)) {
|
|
return FALSE;
|
|
}
|
|
*(IPAddr UNALIGNED *) & RTOption[Pointer] = Address;
|
|
RTOption[IP_OPT_PTR] += sizeof(IPAddr);
|
|
}
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
//* UpdateOptions - Update an options buffer.
|
|
//
|
|
// Called when we need to update an options buffer outgoing. We stamp the indicated
|
|
// options with our local address.
|
|
//
|
|
// Input: Options - Pointer to options buffer to be updated.
|
|
// Index - Pointer to information about which ones to update.
|
|
// Address - Local address with which to update the options.
|
|
//
|
|
// Returns: Index of option causing the error, or MAX_OPT_SIZE if all goes well.
|
|
//
|
|
uchar
|
|
UpdateOptions(uchar * Options, OptIndex * Index, IPAddr Address)
|
|
{
|
|
uchar *LocalOption;
|
|
uchar LocalIndex;
|
|
|
|
// If we have both options and an index, update the options.
|
|
if (Options != (uchar *) NULL && Index != (OptIndex *) NULL) {
|
|
|
|
//
|
|
// If we have a source route to update, update it. If this
|
|
// fails return the index of the source route.
|
|
//
|
|
LocalIndex = Index->oi_srindex;
|
|
if (LocalIndex != MAX_OPT_SIZE)
|
|
if (!UpdateRouteOption(Options + LocalIndex, Address))
|
|
return LocalIndex;
|
|
|
|
// Do the same thing for any record route option.
|
|
LocalIndex = Index->oi_rrindex;
|
|
if (LocalIndex != MAX_OPT_SIZE)
|
|
if (!UpdateRouteOption(Options + LocalIndex, Address))
|
|
return LocalIndex;
|
|
|
|
// Now handle timestamp.
|
|
if ((LocalIndex = Index->oi_tsindex) != MAX_OPT_SIZE) {
|
|
uchar Flags, Length, Pointer;
|
|
|
|
LocalOption = Options + LocalIndex;
|
|
Pointer = LocalOption[IP_OPT_PTR] - 1;
|
|
Flags = LocalOption[IP_TS_OVFLAGS] & IP_TS_FLMASK;
|
|
|
|
// If we have room in the option, update it.
|
|
if (Pointer < (Length = LocalOption[IP_OPT_LENGTH])) {
|
|
ulong Now;
|
|
ulong UNALIGNED *TSPtr;
|
|
|
|
//
|
|
// Get the current time as milliseconds from midnight GMT,
|
|
// mod the number of milliseconds in 24 hours.
|
|
//
|
|
Now = ((TimeStamp + CTESystemUpTime()) | TSFlag) % (24 * 3600 * 1000);
|
|
Now = net_long(Now);
|
|
TSPtr = (ulong UNALIGNED *) & LocalOption[Pointer];
|
|
|
|
switch (Flags) {
|
|
|
|
//
|
|
// Just record the TS. If there is some room but not
|
|
// enough for an IP
|
|
// address we have an error.
|
|
//
|
|
case TS_REC_TS:
|
|
if ((Length - Pointer) < sizeof(IPAddr))
|
|
return LocalIndex; // Error - not enough room.
|
|
|
|
*TSPtr = Now;
|
|
LocalOption[IP_OPT_PTR] += sizeof(ulong);
|
|
break;
|
|
|
|
// Record only matching addresses.
|
|
case TS_REC_SPEC:
|
|
//
|
|
// If we're not the specified address, break out, else
|
|
// fall through to the record address case.
|
|
//
|
|
if (*(IPAddr UNALIGNED *) TSPtr != Address)
|
|
break;
|
|
|
|
//
|
|
// Record an address and timestamp pair. If there is some
|
|
// room but not enough for the address/timestamp pait, we
|
|
// have an error, so bail out.
|
|
//
|
|
case TS_REC_ADDR:
|
|
if ((Length - Pointer) < (sizeof(IPAddr) + sizeof(ulong)))
|
|
return LocalIndex; // Not enough room.
|
|
|
|
*(IPAddr UNALIGNED *) TSPtr = Address; // Store the address.
|
|
|
|
TSPtr++; // Update to where to put TS.
|
|
|
|
*TSPtr = Now; // Store TS
|
|
|
|
LocalOption[IP_OPT_PTR] += (sizeof(ulong) + sizeof(IPAddr));
|
|
break;
|
|
default: // Unknown flag type. Just ignore it.
|
|
|
|
break;
|
|
}
|
|
} else { // Have overflow.
|
|
|
|
//
|
|
// We have an overflow. If the overflow field isn't maxed,
|
|
// increment it. If it is maxed we have an error.
|
|
//
|
|
|
|
if ((LocalOption[IP_TS_OVFLAGS] & IP_TS_OVMASK) != IP_TS_MAXOV)
|
|
|
|
// This is not maxed, so increment it.
|
|
|
|
LocalOption[IP_TS_OVFLAGS] += IP_TS_INC;
|
|
|
|
else
|
|
return LocalIndex; // Would have overflowed.
|
|
|
|
}
|
|
}
|
|
}
|
|
return MAX_OPT_SIZE;
|
|
}
|
|
|
|
typedef struct {
|
|
IPAddr bsl_addr;
|
|
Interface *bsl_if;
|
|
uint bsl_mtu;
|
|
ushort bsl_flags;
|
|
ushort bsl_if_refs;
|
|
} BCastSendList;
|
|
|
|
VOID
|
|
FreeBCastSendList(BCastSendList * SendList, uint SendListSize)
|
|
{
|
|
uint i;
|
|
|
|
CTELockHandle LockHandle;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &LockHandle);
|
|
|
|
for (i = 0; i < SendListSize / sizeof(BCastSendList); i++) {
|
|
|
|
if (SendList[i].bsl_if) {
|
|
LockedDerefIF(SendList[i].bsl_if);
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, LockHandle);
|
|
|
|
CTEFreeMem(SendList);
|
|
}
|
|
|
|
//** SendIPBcast - Send a local BCast IP packet.
|
|
|
|
//
|
|
// This routine is called when we need to send a bcast packet. This may
|
|
// involve sending on multiple interfaces. We figure out which interfaces
|
|
// to send on, then loop through sending on them.
|
|
//
|
|
// Some care is needed to avoid sending the packet onto the same physical media
|
|
// multiple times. What we do is loop through the NTE table, deciding in we
|
|
// should send on the interface. As we go through we build up a list of
|
|
// interfaces to send on. Then we loop through this list, sending on each
|
|
// interface. This is a little cumbersome, but allows us to localize the
|
|
// decision on where to send datagrams into one spot. If SendOnSource is FALSE
|
|
// coming in we assume we've already sent on the specified source NTE and
|
|
// initialize data structures accordingly. This feature is used in routing
|
|
// datagrams.
|
|
//
|
|
// Entry: SrcNTE - NTE for source of send (unused if SendOnSource == TRUE).
|
|
// Destination - Destination address
|
|
// Packet - Prebuilt packet to broadcast.
|
|
// IPH - Pointer to header buffer
|
|
// Buffer - Buffer of data to be sent.
|
|
// DataSize - Size of data to be sent.
|
|
// Options - Pointer to options buffer.
|
|
// OptionSize - Size in bytes of options.
|
|
// SendOnSource - Indicator of whether or not this should be sent on the source net.
|
|
// Index - Pointer to opt index array; may be NULL;
|
|
//
|
|
// Returns: Status of attempt to send.
|
|
//
|
|
|
|
IP_STATUS
|
|
SendIPBCast(NetTableEntry * SrcNTE, IPAddr Destination, PNDIS_PACKET Packet,
|
|
IPHeader * IPH, PNDIS_BUFFER Buffer, uint DataSize, uchar * Options,
|
|
uint OptionSize, uchar SendOnSource, OptIndex * Index)
|
|
{
|
|
BufferReference *BR; // Buffer reference to use for this
|
|
// buffer.
|
|
PacketContext *PContext = (PacketContext *) Packet->ProtocolReserved;
|
|
NetTableEntry *TempNTE;
|
|
uint i, j;
|
|
uint NeedFragment; // TRUE if we think we'll need to
|
|
// fragment.
|
|
int Sent = 0; // Count of how many we've sent.
|
|
IP_STATUS Status = IP_SUCCESS;
|
|
uchar *NewOptions; // Options we'll use on each send.
|
|
IPHeader *NewHeader;
|
|
PNDIS_BUFFER NewUserBuffer;
|
|
PNDIS_PACKET NewPacket;
|
|
BCastSendList *SendList;
|
|
uint NetsToSend;
|
|
IPAddr SrcAddr;
|
|
Interface *SrcIF;
|
|
IPHeader *Temp = NULL;
|
|
FORWARD_ACTION Action = FORWARD;
|
|
IPPacketFilterPtr FilterPtr;
|
|
PNDIS_BUFFER TempBuffer, PC_Firewall = NULL;
|
|
struct IPRcvBuf *PC_Firewall2;
|
|
PIPSEC_SEND_COMPLETE_CONTEXT pIpsecCtx = NULL;
|
|
BOOLEAN PC_reinject = FALSE;
|
|
PVOID PC_context = NULL;
|
|
void (*xmitdone) (void *, PNDIS_BUFFER, IP_STATUS);
|
|
uint mtu;
|
|
uchar *NewOptions2; // Options we'll use on each send.
|
|
IPHeader *NewHeader2;
|
|
PNDIS_BUFFER NewUserBuffer2;
|
|
PNDIS_PACKET NewPacket2;
|
|
CTELockHandle LockHandle;
|
|
uint SendListSize = sizeof(BCastSendList) * NumNTE;
|
|
uint k;
|
|
uchar PacketOwner;
|
|
|
|
PVOID pvBuffer;
|
|
|
|
xmitdone = NULL;
|
|
|
|
SendList = CTEAllocMemN(SendListSize, 'riCT');
|
|
|
|
if (SendList == NULL) {
|
|
if (PContext->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet,Buffer);
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
}
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
RtlZeroMemory(SendList, SendListSize);
|
|
|
|
// If SendOnSource, initalize SrcAddr and SrcIF to be non-matching.
|
|
// Otherwise initialize them to the masked source address and source
|
|
// interface.
|
|
if (SendOnSource != DisableSendOnSource) {
|
|
SrcAddr = NULL_IP_ADDR;
|
|
SrcIF = NULL;
|
|
} else {
|
|
ASSERT(SrcNTE != NULL);
|
|
SrcAddr = (SrcNTE->nte_addr & SrcNTE->nte_mask);
|
|
SrcIF = SrcNTE->nte_if;
|
|
}
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &LockHandle);
|
|
|
|
NeedFragment = FALSE;
|
|
// Loop through the NTE table, making a list of interfaces and
|
|
// corresponding addresses to send on.
|
|
NetsToSend = 0;
|
|
|
|
|
|
for (k = 0; k < NET_TABLE_SIZE; k++) {
|
|
for (TempNTE = NewNetTableList[k]; TempNTE != NULL; TempNTE = TempNTE->nte_next) {
|
|
IPAddr TempAddr;
|
|
|
|
// Don't send through invalid or the loopback NTE.
|
|
if (!(TempNTE->nte_flags & NTE_VALID) || TempNTE == LoopNTE)
|
|
continue;
|
|
|
|
// If the broadcast-mode is source-only, skip all NTEs
|
|
// other than the source-NTE.
|
|
if (SendOnSource == OnlySendOnSource &&
|
|
!IP_ADDR_EQUAL(TempNTE->nte_addr, IPH->iph_src))
|
|
continue;
|
|
|
|
TempAddr = TempNTE->nte_addr & TempNTE->nte_mask;
|
|
|
|
// If he matches the source address or SrcIF, skip him.
|
|
if (IP_ADDR_EQUAL(TempAddr, SrcAddr) || TempNTE->nte_if == SrcIF)
|
|
continue;
|
|
|
|
// If the destination isn't a broadcast on this NTE, skip him.
|
|
if (!IS_BCAST_DEST(IsBCastOnNTE(Destination, TempNTE)))
|
|
continue;
|
|
|
|
// if this NTE is P2P then always add him to bcast list.
|
|
if ((TempNTE->nte_if)->if_flags & IF_FLAGS_P2P) {
|
|
j = NetsToSend;
|
|
} else {
|
|
//
|
|
// Go through the list we've already build, looking for a match.
|
|
//
|
|
for (j = 0; j < NetsToSend; j++) {
|
|
|
|
//
|
|
// if P2P NTE then skip it - we want to send bcasts to all
|
|
// P2P interfaces in addition to 1 non P2P interface even
|
|
// if they are on the same subnet.
|
|
//
|
|
if ((SendList[j].bsl_if)->if_flags & IF_FLAGS_P2P)
|
|
continue;
|
|
|
|
if ((SendList[j].bsl_if)->if_flags & IF_FLAGS_P2MP)
|
|
continue;
|
|
|
|
if (IP_ADDR_EQUAL(SendList[j].bsl_addr & TempNTE->nte_mask, TempAddr)
|
|
|| SendList[j].bsl_if == TempNTE->nte_if) {
|
|
|
|
// He matches this send list element. Shrink the MSS if
|
|
// we need to, and then break out.
|
|
SendList[j].bsl_mtu = MIN(SendList[j].bsl_mtu, TempNTE->nte_mss);
|
|
if ((DataSize + OptionSize) > SendList[j].bsl_mtu)
|
|
NeedFragment = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (j == NetsToSend) {
|
|
// This is a new one. Fill him in, and bump NetsToSend.
|
|
|
|
SendList[j].bsl_addr = TempNTE->nte_addr;
|
|
SendList[j].bsl_if = TempNTE->nte_if;
|
|
SendList[j].bsl_mtu = TempNTE->nte_mss;
|
|
SendList[j].bsl_flags = TempNTE->nte_flags;
|
|
SendList[j].bsl_if_refs++;
|
|
|
|
ASSERT(SendList[j].bsl_if_refs <= 1);
|
|
|
|
LOCKED_REFERENCE_IF(TempNTE->nte_if);
|
|
|
|
if ((DataSize + OptionSize) > SendList[j].bsl_mtu)
|
|
NeedFragment = TRUE;
|
|
NetsToSend++;
|
|
}
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, LockHandle);
|
|
|
|
if (NetsToSend == 0) {
|
|
CTEFreeMem(SendList);
|
|
if (PContext->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet,Buffer);
|
|
FreeIPPacket(Packet, TRUE, IP_SUCCESS);
|
|
}
|
|
return IP_SUCCESS; // Nothing to send on.
|
|
|
|
}
|
|
// OK, we've got the list. If we've got more than one interface to send
|
|
// on or we need to fragment, get a BufferReference.
|
|
if (NetsToSend > 1 || NeedFragment) {
|
|
if ((BR = CTEAllocMemN(sizeof(BufferReference), 'siCT')) ==
|
|
(BufferReference *) NULL) {
|
|
FreeBCastSendList(SendList, SendListSize);
|
|
if (PContext->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet,Buffer);
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
}
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
BR->br_buffer = Buffer;
|
|
BR->br_refcount = 0;
|
|
CTEInitLock(&BR->br_lock);
|
|
PContext->pc_br = BR;
|
|
BR->br_userbuffer = PContext->pc_hdrincl;
|
|
TempBuffer = BR->br_buffer;
|
|
PC_Firewall = PContext->pc_firewall;
|
|
PC_Firewall2 = PContext->pc_firewall2;
|
|
|
|
pIpsecCtx = PContext->pc_common.pc_IpsecCtx;
|
|
if (pIpsecCtx && (pIpsecCtx->Flags & SCF_FLUSH)) {
|
|
PC_reinject = TRUE;
|
|
PC_context = PContext->pc_context;
|
|
}
|
|
xmitdone = PContext->pc_pi->pi_xmitdone;
|
|
|
|
} else {
|
|
BR = NULL;
|
|
PContext->pc_br = NULL;
|
|
}
|
|
|
|
//
|
|
// We need to pass up the options and IP hdr in a contiguous buffer.
|
|
// Allocate the buffer once and re-use later.
|
|
//
|
|
if (RefPtrValid(&FilterRefPtr)) {
|
|
if (Options == NULL) {
|
|
#if FWD_DBG
|
|
DbgPrint("Options==NULL\n");
|
|
#endif
|
|
Temp = IPH;
|
|
} else {
|
|
Temp = CTEAllocMemN(sizeof(IPHeader) + OptionSize, 'tiCT');
|
|
if (Temp == NULL) {
|
|
FreeBCastSendList(SendList, SendListSize);
|
|
if (PContext->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet,Buffer);
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
}
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
*Temp = *IPH;
|
|
#if FWD_DBG
|
|
DbgPrint("Options!=NULL : alloced temp @ %lx\n", Temp);
|
|
#endif
|
|
|
|
//
|
|
// done later...
|
|
// RtlCopyMemory((uchar *)(Temp + 1), Options, OptionSize);
|
|
}
|
|
}
|
|
// Now, loop through the list. For each entry, send.
|
|
// Header fixup is needed in FreeIPPacket called within this loop
|
|
// If number of nets is one
|
|
|
|
|
|
for (i = 0; i < NetsToSend; i++) {
|
|
|
|
//
|
|
// For all nets except the last one we're going to send on we need
|
|
// to make a copy of the header, packet, buffers, and any options.
|
|
// On the last net we'll use the user provided information.
|
|
//
|
|
|
|
if (i != (NetsToSend - 1)) {
|
|
PVOID CancelId;
|
|
|
|
if ((NewHeader = GetIPHeader(&NewPacket)) == (IPHeader *) NULL) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
continue; // Couldn't get a header, skip this send.
|
|
|
|
}
|
|
|
|
NewUserBuffer = IPCopyBuffer(Buffer, 0, DataSize);
|
|
|
|
if (NewUserBuffer == NULL) {
|
|
|
|
// Couldn't get user buffer copied.
|
|
|
|
FreeIPPacket(NewPacket, FALSE, IP_NO_RESOURCES);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
continue;
|
|
}
|
|
|
|
PacketOwner = ((PacketContext *) NewPacket->ProtocolReserved)->pc_common.pc_owner;
|
|
*(PacketContext *) NewPacket->ProtocolReserved = *PContext;
|
|
|
|
*NewHeader = *IPH;
|
|
(*(PacketContext *) NewPacket->ProtocolReserved).pc_common.pc_flags |= PACKET_FLAG_IPBUF;
|
|
(*(PacketContext *) NewPacket->ProtocolReserved).pc_common.pc_flags &= ~PACKET_FLAG_FW;
|
|
(*(PacketContext *) NewPacket->ProtocolReserved).pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS;
|
|
(*(PacketContext *) NewPacket->ProtocolReserved).pc_common.pc_owner = PacketOwner;
|
|
|
|
if (Options) {
|
|
// We have options, make a copy.
|
|
if ((NewOptions = CTEAllocMemN(OptionSize, 'uiCT')) == (uchar *) NULL) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
FreeIPPacket(NewPacket, FALSE, IP_NO_RESOURCES);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
continue;
|
|
}
|
|
RtlCopyMemory(NewOptions, Options, OptionSize);
|
|
} else {
|
|
NewOptions = NULL;
|
|
}
|
|
|
|
#if !MILLEN
|
|
// Set the cancel requestID from parent packet.
|
|
CancelId = NDIS_GET_PACKET_CANCEL_ID(Packet);
|
|
NDIS_SET_PACKET_CANCEL_ID(NewPacket, CancelId);
|
|
#endif
|
|
|
|
} else {
|
|
NewHeader = IPH;
|
|
NewPacket = Packet;
|
|
NewOptions = Options;
|
|
NewUserBuffer = Buffer;
|
|
}
|
|
|
|
UpdateOptions(NewOptions, Index, SendList[i].bsl_addr);
|
|
|
|
// See if we need to filter this packet. If we
|
|
// do, call the filter routine to see if it's
|
|
// OK to send it.
|
|
if (RefPtrValid(&FilterRefPtr)) {
|
|
//
|
|
// Copy over the options.
|
|
//
|
|
if (NewOptions) {
|
|
RtlCopyMemory((uchar *) (Temp + 1), NewOptions, OptionSize);
|
|
}
|
|
pvBuffer = TcpipBufferVirtualAddress(NewUserBuffer, NormalPagePriority);
|
|
|
|
if (pvBuffer == NULL) {
|
|
|
|
if (i != (NetsToSend - 1)) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (NewOptions) {
|
|
CTEFreeMem(NewOptions);
|
|
}
|
|
}
|
|
FreeIPPacket(NewPacket, FALSE, IP_GENERAL_FAILURE);
|
|
continue;
|
|
}
|
|
if ((SendList[i].bsl_if)->if_flags & IF_FLAGS_P2MP) {
|
|
|
|
if ((SendList[i].bsl_if)->if_flags & IF_FLAGS_NOLINKBCST) {
|
|
|
|
// what filtercontext to use ?
|
|
#if FWD_DBG
|
|
DbgPrint("FilterPtr not called for IF %lx since IF_FLAGS_NOLINKBCST not set\n", SendList[i].bsl_if);
|
|
#endif
|
|
Action = FORWARD;
|
|
|
|
} else {
|
|
//scan all the links on this interface and deliver them to the forwardfilter
|
|
|
|
Interface *IF = SendList[i].bsl_if;
|
|
LinkEntry *tmpLink = IF->if_link;
|
|
|
|
// ASSERT(tmpLink);
|
|
while (tmpLink) {
|
|
|
|
tmpLink->link_Action = FORWARD;
|
|
FilterPtr = AcquireRefPtr(&FilterRefPtr);
|
|
Action = (*FilterPtr) (Temp,
|
|
pvBuffer,
|
|
NdisBufferLength(NewUserBuffer),
|
|
INVALID_IF_INDEX,
|
|
IF->if_index,
|
|
NULL_IP_ADDR,
|
|
tmpLink->link_NextHop);
|
|
ReleaseRefPtr(&FilterRefPtr);
|
|
tmpLink->link_Action = Action;
|
|
tmpLink = tmpLink->link_next;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
FilterPtr = AcquireRefPtr(&FilterRefPtr);
|
|
Action = (*FilterPtr) (Temp,
|
|
pvBuffer,
|
|
NdisBufferLength(NewUserBuffer),
|
|
INVALID_IF_INDEX,
|
|
SendList[i].bsl_if->if_index,
|
|
NULL_IP_ADDR, NULL_IP_ADDR);
|
|
ReleaseRefPtr(&FilterRefPtr);
|
|
|
|
}
|
|
|
|
#if FWD_DBG
|
|
DbgPrint("FilterPtr: %lx, FORWARD is %lx\n", Action, FORWARD);
|
|
#endif
|
|
|
|
if (!(SendList[i].bsl_if->if_flags & IF_FLAGS_P2MP) ||
|
|
(SendList[i].bsl_if->if_flags & IF_FLAGS_P2MP) &&
|
|
(SendList[i].bsl_if->if_flags & IF_FLAGS_NOLINKBCST)) {
|
|
|
|
if (Action != FORWARD) {
|
|
if (i != (NetsToSend - 1)) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
if (NewOptions) {
|
|
CTEFreeMem(NewOptions);
|
|
}
|
|
}
|
|
FreeIPPacket(NewPacket, FALSE, IP_GENERAL_FAILURE);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if ((SendList[i].bsl_if->if_flags & IF_FLAGS_P2MP) &&
|
|
(SendList[i].bsl_if->if_flags & IF_FLAGS_NOLINKBCST)) {
|
|
|
|
//Determine the minimum MTU
|
|
|
|
Interface *tmpIF = SendList[i].bsl_if;
|
|
LinkEntry *tmpLink = tmpIF->if_link;
|
|
// int mtu;
|
|
|
|
if (!tmpLink) {
|
|
if (i != (NetsToSend - 1)) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
if (NewOptions) {
|
|
CTEFreeMem(NewOptions);
|
|
}
|
|
}
|
|
FreeIPPacket(NewPacket, FALSE, IP_GENERAL_FAILURE);
|
|
|
|
continue;
|
|
}
|
|
ASSERT(tmpLink);
|
|
mtu = tmpLink->link_mtu;
|
|
|
|
while (tmpLink) {
|
|
|
|
if (tmpLink->link_mtu < mtu)
|
|
mtu = tmpLink->link_mtu;
|
|
tmpLink = tmpLink->link_next;
|
|
}
|
|
|
|
if ((DataSize + OptionSize) > mtu) { // This is too big
|
|
//
|
|
// Don't need to update Sent when fragmenting, as IPFragment
|
|
// will update the br_refcount field itself. It will also free
|
|
// the option buffer.
|
|
//
|
|
|
|
Status = IPFragment(SendList[i].bsl_if, mtu,
|
|
Destination, NewPacket, NewHeader,
|
|
NewUserBuffer, DataSize, NewOptions,
|
|
OptionSize, &Sent, FALSE, NULL);
|
|
|
|
//
|
|
// IPFragment is done with the descriptor chain, so if this is
|
|
// a locally allocated chain free it now.
|
|
//
|
|
if (i != (NetsToSend - 1))
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
} else {
|
|
NewHeader->iph_xsum = 0;
|
|
|
|
// Do not free the packet in SendIPPacket, as we may need
|
|
// to chain the buffer in case of IP_NO_RESOURCES
|
|
|
|
|
|
Status = SendIPPacket(SendList[i].bsl_if, Destination,
|
|
NewPacket, NewUserBuffer, NewHeader,
|
|
NewOptions, OptionSize, FALSE, NULL, TRUE);
|
|
|
|
if (Status == IP_PENDING) {
|
|
Sent++;
|
|
} else {
|
|
if (Status == IP_NO_RESOURCES) {
|
|
// SendIPPacket has not chained the buffer..
|
|
NdisChainBufferAtBack(NewPacket, NewUserBuffer);
|
|
}
|
|
if (NetsToSend == 1) {
|
|
FreeIPPacket(NewPacket, TRUE, Status);
|
|
} else {
|
|
FreeIPPacket(NewPacket, FALSE, Status);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (SendList[i].bsl_if->if_flags & IF_FLAGS_P2MP) {
|
|
// broadcast on all the links
|
|
|
|
Interface *tmpIF = SendList[i].bsl_if;
|
|
LinkEntry *tmpLink = tmpIF->if_link;
|
|
|
|
ASSERT(!(SendList[i].bsl_if->if_flags & IF_FLAGS_NOLINKBCST));
|
|
|
|
if (!tmpLink) {
|
|
if (i != (NetsToSend - 1)) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
if (NewOptions) {
|
|
CTEFreeMem(NewOptions);
|
|
}
|
|
}
|
|
FreeIPPacket(NewPacket, FALSE, IP_GENERAL_FAILURE);
|
|
continue;
|
|
}
|
|
ASSERT(tmpLink);
|
|
while (tmpLink) {
|
|
//
|
|
//Go thru the send motion for all the links
|
|
//Passing the link context and checking whether it was
|
|
//forward for that link. For all link except the last one
|
|
//we're going to send on we need to make a copy of the header,
|
|
//packet, buffers, and any options.
|
|
// On the last net we'll use the user provided information.
|
|
//
|
|
|
|
if (tmpLink->link_next) {
|
|
if ((NewHeader2 = GetIPHeader(&NewPacket2)) == (IPHeader *) NULL) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
// free the packet etc. we made for the interface
|
|
|
|
if (i != (NetsToSend - 1)) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
if (NewOptions) {
|
|
CTEFreeMem(NewOptions);
|
|
}
|
|
}
|
|
FreeIPPacket(NewPacket, FALSE, IP_NO_RESOURCES);
|
|
tmpLink = tmpLink->link_next;
|
|
continue; // Couldn't get a header, skip this send.
|
|
|
|
}
|
|
NewUserBuffer2 = IPCopyBuffer(Buffer, 0, DataSize);
|
|
if (NewUserBuffer2 == NULL) {
|
|
|
|
// Couldn't get user buffer copied.
|
|
|
|
FreeIPPacket(NewPacket2, FALSE, IP_NO_RESOURCES);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
|
|
if (i != (NetsToSend - 1)) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
if (NewOptions) {
|
|
CTEFreeMem(NewOptions);
|
|
}
|
|
}
|
|
tmpLink = tmpLink->link_next;
|
|
continue;
|
|
}
|
|
PacketOwner = ((PacketContext *) NewPacket2->ProtocolReserved)->pc_common.pc_owner;
|
|
|
|
*(PacketContext *) NewPacket2->ProtocolReserved = *PContext;
|
|
|
|
*NewHeader2 = *IPH;
|
|
(*(PacketContext *) NewPacket2->ProtocolReserved).pc_common.pc_flags |= PACKET_FLAG_IPBUF;
|
|
(*(PacketContext *) NewPacket2->ProtocolReserved).pc_common.pc_flags &= ~PACKET_FLAG_FW;
|
|
(*(PacketContext *) NewPacket2->ProtocolReserved).pc_common.pc_owner = PacketOwner;
|
|
|
|
if (Options) {
|
|
// We have options, make a copy.
|
|
if ((NewOptions2 = CTEAllocMemN(OptionSize, 'viCT')) == (uchar *) NULL) {
|
|
FreeIPBufferChain(NewUserBuffer2);
|
|
FreeIPPacket(NewPacket2, FALSE, IP_NO_RESOURCES);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
|
|
if (i != (NetsToSend - 1)) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
if (NewOptions) {
|
|
CTEFreeMem(NewOptions);
|
|
}
|
|
}
|
|
tmpLink = tmpLink->link_next;
|
|
continue;
|
|
}
|
|
RtlCopyMemory(NewOptions2, Options, OptionSize);
|
|
} else {
|
|
NewOptions2 = NULL;
|
|
}
|
|
} else { // last link
|
|
|
|
NewHeader2 = NewHeader;
|
|
NewPacket2 = NewPacket;
|
|
NewOptions2 = NewOptions;
|
|
NewUserBuffer2 = NewUserBuffer;
|
|
}
|
|
|
|
UpdateOptions(NewOptions2, Index, SendList[i].bsl_addr);
|
|
|
|
if (tmpLink->link_Action) {
|
|
|
|
if ((DataSize + OptionSize) > tmpLink->link_mtu) {
|
|
|
|
//
|
|
// This is too big
|
|
// Don't need to update Sent when fragmenting, as
|
|
// IPFragment will update the br_refcount field itself.
|
|
// It will also free the option buffer.
|
|
//
|
|
|
|
Status = IPFragment(SendList[i].bsl_if,
|
|
tmpLink->link_mtu,
|
|
Destination, NewPacket2,
|
|
NewHeader2, NewUserBuffer2,
|
|
DataSize,
|
|
NewOptions2, OptionSize, &Sent,
|
|
FALSE, tmpLink->link_arpctxt);
|
|
|
|
//
|
|
// IPFragment is done with the descriptor chain, so
|
|
// if this is a locally allocated chain free it now.
|
|
//
|
|
|
|
if ((i != (NetsToSend - 1)) || (tmpLink->link_next))
|
|
FreeIPBufferChain(NewUserBuffer2);
|
|
} else {
|
|
NewHeader2->iph_xsum = 0;
|
|
|
|
// Do not free the packet in SendIPPacket, as we may need
|
|
// to chain the buffer in case of IP_NO_RESOURCES
|
|
|
|
Status = SendIPPacket(SendList[i].bsl_if,
|
|
Destination, NewPacket2,
|
|
NewUserBuffer2, NewHeader2,
|
|
NewOptions2, OptionSize, FALSE,
|
|
tmpLink->link_arpctxt, TRUE);
|
|
|
|
if (Status == IP_PENDING) {
|
|
Sent++;
|
|
} else {
|
|
if (Status == IP_NO_RESOURCES) {
|
|
// SendIPPacket has not chained the buffer..
|
|
NdisChainBufferAtBack(NewPacket2, NewUserBuffer2);
|
|
}
|
|
if (NetsToSend == 1) {
|
|
FreeIPPacket(NewPacket2, TRUE, Status);
|
|
} else {
|
|
FreeIPPacket(NewPacket2, FALSE, Status);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else { // Action != FORWARD
|
|
|
|
if ((i != (NetsToSend - 1)) || (tmpLink->link_next)) {
|
|
FreeIPBufferChain(NewUserBuffer2);
|
|
if (NewOptions2) {
|
|
CTEFreeMem(NewOptions2);
|
|
}
|
|
}
|
|
tmpLink = tmpLink->link_next;
|
|
continue;
|
|
}
|
|
tmpLink = tmpLink->link_next;
|
|
}
|
|
} else { // Normal path
|
|
|
|
if ((DataSize + OptionSize) > SendList[i].bsl_mtu) {
|
|
//
|
|
// This is too big
|
|
// Don't need to update Sent when fragmenting, as IPFragment
|
|
// will update the br_refcount field itself. It will also free
|
|
// the option buffer.
|
|
//
|
|
|
|
Status = IPFragment(SendList[i].bsl_if,
|
|
SendList[i].bsl_mtu,
|
|
Destination, NewPacket, NewHeader,
|
|
NewUserBuffer, DataSize,
|
|
NewOptions, OptionSize, &Sent, FALSE, NULL);
|
|
|
|
//
|
|
// IPFragment is done with the descriptor chain, so if this is
|
|
// a locally allocated chain free it now.
|
|
//
|
|
if (i != (NetsToSend - 1)) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
}
|
|
|
|
} else {
|
|
NewHeader->iph_xsum = 0;
|
|
|
|
// Do not free the packet in SendIPPacket, as we may need
|
|
// to chain the buffer in case of IP_NO_RESOURCES
|
|
|
|
Status = SendIPPacket(SendList[i].bsl_if,
|
|
Destination, NewPacket,
|
|
NewUserBuffer, NewHeader, NewOptions,
|
|
OptionSize, FALSE, NULL, TRUE);
|
|
|
|
if (Status == IP_PENDING) {
|
|
Sent++;
|
|
} else {
|
|
|
|
if (Status == IP_NO_RESOURCES) {
|
|
// SendIPPacket has not chained the buffer..
|
|
NdisChainBufferAtBack(NewPacket, NewUserBuffer);
|
|
}
|
|
if (NetsToSend == 1) {
|
|
FreeIPPacket(NewPacket, TRUE, Status);
|
|
} else {
|
|
FreeIPPacket(NewPacket, FALSE, Status);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Temp && Temp != IPH) {
|
|
CTEFreeMem(Temp);
|
|
}
|
|
//
|
|
// Alright, we've sent everything we need to. We'll adjust the reference
|
|
// count by the number we've sent. IPFragment may also have put some
|
|
// references on it. If the reference count goes to 0, we're done and
|
|
// we'll free the BufferReference structure.
|
|
//
|
|
|
|
if (BR != NULL) {
|
|
if (ReferenceBuffer(BR, Sent) == 0) {
|
|
|
|
FreeBCastSendList(SendList, SendListSize);
|
|
|
|
// Need to undo ipsec/firewall/Hdrincl header munging
|
|
|
|
if (PC_reinject) {
|
|
|
|
//
|
|
// the only remaining way is calling xmitdone
|
|
// since ipsendcomplete won't have called xmit done as
|
|
// refcount would be -ve
|
|
//
|
|
|
|
(*IPSecSendCmpltPtr)(NULL, TempBuffer, pIpsecCtx, IP_SUCCESS,
|
|
&TempBuffer);
|
|
|
|
(*xmitdone) (PC_context, TempBuffer, IP_SUCCESS);
|
|
} else {
|
|
|
|
// Need to undo ipsec, firewall and then
|
|
// header include changes to the buffer list.
|
|
|
|
if (pIpsecCtx) {
|
|
(*IPSecSendCmpltPtr)(NULL, TempBuffer, pIpsecCtx,
|
|
IP_SUCCESS, &TempBuffer);
|
|
}
|
|
|
|
// If this is user header include packet,
|
|
// relink the original user buffer if necessary
|
|
|
|
if (PC_Firewall) {
|
|
BR->br_buffer = PC_Firewall;
|
|
}
|
|
|
|
if (BR->br_userbuffer) {
|
|
FreeIPPayloadBuffer(BR->br_buffer, BR->br_userbuffer);
|
|
}
|
|
}
|
|
|
|
|
|
CTEFreeMem(BR); // Reference is 0, free the BR structure.
|
|
|
|
return IP_SUCCESS;
|
|
} else {
|
|
FreeBCastSendList(SendList, SendListSize);
|
|
return IP_PENDING;
|
|
}
|
|
} else {
|
|
// Had only one I/F to send on. Just return the status.
|
|
FreeBCastSendList(SendList, SendListSize);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//** IPCancelPacket - Cancels packets that are pending
|
|
//
|
|
// Called by upper layer, when a send request is cancelled.
|
|
// Check for validity of the interface and call link layer
|
|
// cancel routine, if it is registered.
|
|
//
|
|
// Entry: IPIF - Interface on which the cancel needs to be issued
|
|
// Ctxt - Pointer to the cancel ID.
|
|
//
|
|
// Returns: None
|
|
//
|
|
VOID
|
|
IPCancelPackets(void *IPIF, void * Ctxt)
|
|
{
|
|
Interface *IF;
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
|
|
if ((Interface *)IPIF != NULL) {
|
|
|
|
IF = IFList;
|
|
|
|
if (IPIF != BCAST_IF_CTXT) {
|
|
|
|
while(IF && (IF != IPIF)) {
|
|
IF= IF->if_next;
|
|
}
|
|
|
|
if (IF && !(IF->if_flags & IF_FLAGS_DELETING) && IF->if_cancelpackets) {
|
|
|
|
LOCKED_REFERENCE_IF(IF);
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
(*(IF->if_cancelpackets)) (IF->if_lcontext, Ctxt);
|
|
DerefIF(IF);
|
|
} else {
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
//Bcast cancel!. Issue cancel on all interfaces
|
|
|
|
uint CancelListSize, CancelIFs,i=0;
|
|
Interface **CancelList;
|
|
|
|
CancelListSize = sizeof(Interface *)*(NumIF +1);
|
|
|
|
CancelList = CTEAllocMemN(CancelListSize, 'riCT');
|
|
|
|
if (!CancelList) {
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return;
|
|
}
|
|
|
|
//refcnt valid interfaces
|
|
|
|
while(IF){
|
|
if (IF->if_refcount && IF->if_cancelpackets) {
|
|
LOCKED_REFERENCE_IF(IF);
|
|
CancelList[++i] = IF;
|
|
}
|
|
IF = IF->if_next;
|
|
}
|
|
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
|
|
//call cancel and deref if
|
|
|
|
CancelIFs = i;
|
|
|
|
while (i) {
|
|
(*(CancelList[i]->if_cancelpackets))(CancelList[i]->if_lcontext, Ctxt);
|
|
i--;
|
|
}
|
|
|
|
while (CancelIFs) {
|
|
DerefIF(CancelList[CancelIFs]);
|
|
CancelIFs--;
|
|
}
|
|
|
|
CTEFreeMem(CancelList);
|
|
|
|
}
|
|
} else {
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
}
|
|
}
|
|
|
|
|
|
IP_STATUS
|
|
ARPResolve(IPAddr Dest, IPAddr Source, ARPControlBlock * controlBlock,
|
|
ArpRtn Callback)
|
|
{
|
|
NDIS_STATUS status;
|
|
Interface *DestIF;
|
|
IPAddr NextHop;
|
|
uint MTU, size;
|
|
|
|
status = IP_DEST_HOST_UNREACHABLE;
|
|
|
|
DestIF = LookupNextHop(Dest, Source, &NextHop, &MTU);
|
|
|
|
if (DestIF == &LoopInterface) {
|
|
|
|
Interface *IF = NULL;
|
|
NetTableEntry *NTE;
|
|
NetTableEntry *NetTableList = NewNetTableList[NET_TABLE_HASH(Dest)];
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if (NTE != LoopNTE && IP_ADDR_EQUAL(NTE->nte_addr, Dest)) {
|
|
// Found one. Save it and break out.
|
|
IF = NTE->nte_if;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (IF) {
|
|
|
|
if (controlBlock->PhyAddrLen < IF->if_addrlen) {
|
|
size = controlBlock->PhyAddrLen;
|
|
status = IP_NO_RESOURCES;
|
|
|
|
} else {
|
|
size = IF->if_addrlen;
|
|
status = IP_SUCCESS;
|
|
}
|
|
//
|
|
// Update the address length.
|
|
//
|
|
controlBlock->PhyAddrLen = size;
|
|
|
|
RtlCopyMemory(controlBlock->PhyAddr, IF->if_addr, size);
|
|
|
|
}
|
|
DerefIF(DestIF);
|
|
return status;
|
|
|
|
}
|
|
controlBlock->CompletionRtn = Callback;
|
|
|
|
if (DestIF != NULL) {
|
|
|
|
if (!DestIF->if_arpresolveip) {
|
|
|
|
DerefIF(DestIF);
|
|
return IP_GENERAL_FAILURE;
|
|
|
|
}
|
|
if (!IP_ADDR_EQUAL(NextHop, Dest)) {
|
|
//We do not arp on non local address(via gateway)
|
|
|
|
DerefIF(DestIF);
|
|
return IP_BAD_DESTINATION;
|
|
|
|
}
|
|
status = (*(DestIF->if_arpresolveip)) (DestIF->if_lcontext, Dest,
|
|
controlBlock);
|
|
if (NDIS_STATUS_PENDING == status) {
|
|
|
|
status = IP_PENDING;
|
|
|
|
} else if (NDIS_STATUS_SUCCESS == status) {
|
|
|
|
status = IP_SUCCESS;
|
|
|
|
} else {
|
|
status = IP_GENERAL_FAILURE;
|
|
}
|
|
|
|
DerefIF(DestIF);
|
|
}
|
|
return status;
|
|
|
|
}
|
|
|
|
//** IPLargeXmit - Large Send
|
|
//
|
|
// This is the main transmit routine called by the upper layer. Conceptually,
|
|
// we process any options, look up the route to the destination, fragment the
|
|
// packet if needed, and send it. In reality, we use an RCE to cache the best
|
|
// route, and we have special case code here for dealing with the common
|
|
// case of no options, with everything fitting into one buffer.
|
|
//
|
|
// Entry: Context - Pointer to ProtInfo struc for protocol.
|
|
// SendContext - User provided send context, passed back on send cmplt.
|
|
// Protocol - Protocol field for packet.
|
|
// Buffer - NDIS_BUFFER chain of data to be sent.
|
|
// DataSize - Size in bytes of data to be sent.
|
|
// OptInfo - Pointer to optinfo structure.
|
|
// Dest - Destination to send to.
|
|
// Source - Source address to use.
|
|
// RCE - Pointer to an RCE structure that caches info. about path.
|
|
// SentBytes - pointer to return the number of bytes xmited
|
|
//
|
|
// Returns: Status of transmit command.
|
|
//
|
|
IP_STATUS
|
|
IPLargeXmit(void *Context, void *SendContext, PNDIS_BUFFER Buffer, uint DataSize,
|
|
IPAddr Dest, IPAddr Source, IPOptInfo * OptInfo, RouteCacheEntry * RCE,
|
|
uchar Protocol, ulong * SentBytes, ulong mss)
|
|
{
|
|
ProtInfo *PInfo = (ProtInfo *) Context;
|
|
PacketContext *pc;
|
|
Interface *DestIF; // Outgoing interface to use.
|
|
IPAddr FirstHop; // First hop address of
|
|
// destination.
|
|
NDIS_STATUS Status = IP_GENERAL_FAILURE;
|
|
IPHeader *IPH;
|
|
PNDIS_PACKET Packet;
|
|
PNDIS_BUFFER HeaderBuffer;
|
|
PNDIS_BUFFER OptBuffer = NULL;
|
|
CTELockHandle LockHandle;
|
|
uchar *Options;
|
|
uint OptionSize = 0;
|
|
RouteTableEntry *RTE;
|
|
IP_STATUS SendStatus;
|
|
uint FirewallMode = 0;
|
|
|
|
IPSInfo.ipsi_outrequests++;
|
|
|
|
//
|
|
// Allocate a packet that we need for all cases, and fill
|
|
// in the common stuff. If everything goes well, we'll send it
|
|
// here. Otherwise we'll break out into special case code for
|
|
// broadcasts, fragments, etc.
|
|
//
|
|
|
|
// Make sure that we have an RCE, that it's valid, etc.
|
|
|
|
FirewallMode = ProcessFirewallQ();
|
|
|
|
if (RefPtrValid(&FilterRefPtr) || FirewallMode) {
|
|
return Status;
|
|
}
|
|
if (RCE != NULL) {
|
|
|
|
// We have an RCE. Make sure it's valid.
|
|
|
|
if ((Packet = GetIPPacket()) != (PNDIS_PACKET) NULL) { // Got a packet.
|
|
|
|
PNDIS_PACKET_EXTENSION PktExt;
|
|
|
|
pc = (PacketContext *) Packet->ProtocolReserved;
|
|
pc->pc_br = (BufferReference *) NULL;
|
|
pc->pc_pi = PInfo;
|
|
pc->pc_context = SendContext;
|
|
ASSERT(pc->pc_if == NULL);
|
|
ASSERT(pc->pc_iflink == NULL);
|
|
|
|
CTEGetLock(&RCE->rce_lock, &LockHandle);
|
|
|
|
if (RCE->rce_flags == RCE_ALL_VALID) {
|
|
|
|
// The RTE is valid.
|
|
|
|
CTEInterlockedIncrementLong(&RCE->rce_usecnt);
|
|
RTE = RCE->rce_rte;
|
|
FirstHop = ADDR_FROM_RTE(RTE, Dest);
|
|
DestIF = IF_FROM_RTE(RTE);
|
|
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
|
|
if (RCE->rce_dtype != DEST_BCAST) {
|
|
|
|
if (!OptInfo->ioi_options) {
|
|
|
|
// Construct the IP header in the backfill space
|
|
// provided by the transport
|
|
|
|
NdisAdjustBufferLength(Buffer, NdisBufferLength(Buffer) + sizeof(IPHeader));
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
IPH = (IPHeader *)TcpipBufferVirtualAddress(Buffer, NormalPagePriority);
|
|
} else {
|
|
|
|
// Allocate a separate buffer for the IP header
|
|
// and chain to it the packet, followed by a separate
|
|
// buffer allocated for the packet's IP options.
|
|
|
|
OptionSize = OptInfo->ioi_optlength;
|
|
HeaderBuffer = GetIPHdrBuffer(&IPH);
|
|
if (HeaderBuffer) {
|
|
|
|
pc->pc_common.pc_flags |= PACKET_FLAG_IPHDR;
|
|
NdisChainBufferAtBack(Packet, HeaderBuffer);
|
|
|
|
Options = CTEAllocMemN(OptionSize, 'xiCT');
|
|
if (!Options) {
|
|
IPH = NULL;
|
|
} else {
|
|
NDIS_STATUS Status;
|
|
|
|
// Copy the options to the allocated block
|
|
// and obtain an NDIS_BUFFER to map the block.
|
|
|
|
RtlCopyMemory(Options, OptInfo->ioi_options,
|
|
OptionSize);
|
|
|
|
NdisAllocateBuffer(&Status, &OptBuffer,
|
|
BufferPool, Options,
|
|
OptionSize);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
CTEFreeMem(Options);
|
|
IPH = NULL;
|
|
} else {
|
|
uchar* ULData;
|
|
|
|
// Mark the packet as carrying options,
|
|
// and chain both the options-buffer and
|
|
// the application data to the packet.
|
|
|
|
pc->pc_common.pc_flags |=
|
|
PACKET_FLAG_OPTIONS;
|
|
NdisChainBufferAtBack(Packet, OptBuffer);
|
|
|
|
// Copy the upper layer data forward.
|
|
// Note that the upper-layer header is
|
|
// assumed to be in non-paged pool, so
|
|
// TcpipBufferVirtualAddress cannot fail.
|
|
|
|
ULData = TcpipBufferVirtualAddress(Buffer, NormalPagePriority);
|
|
RtlMoveMemory(ULData,
|
|
ULData + sizeof(IPHeader),
|
|
NdisBufferLength(Buffer));
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IPH == NULL) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
IPH->iph_protocol = Protocol;
|
|
IPH->iph_xsum = 0;
|
|
if (IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR)) {
|
|
IPH->iph_dest = Dest;
|
|
} else {
|
|
IPH->iph_dest = OptInfo->ioi_addr;
|
|
}
|
|
IPH->iph_src = Source;
|
|
IPH->iph_ttl = OptInfo->ioi_ttl;
|
|
|
|
if (OptInfo->ioi_ttl == 0) {
|
|
NdisSetPacketFlags(Packet, NDIS_FLAGS_LOOPBACK_ONLY);
|
|
} else if (!DestIF->if_promiscuousmode) {
|
|
// Set DONT_LOOPBACK flags for unicast packets
|
|
// to save few cycles in ndis
|
|
NdisSetPacketFlags(Packet, NDIS_FLAGS_DONT_LOOPBACK);
|
|
}
|
|
|
|
IPH->iph_tos = OptInfo->ioi_tos;
|
|
IPH->iph_offset = net_short((OptInfo->ioi_flags & IP_FLAG_DF) << 13);
|
|
|
|
IPH->iph_id =
|
|
(ushort) InterlockedExchangeAdd(
|
|
(PLONG) &IPIDCacheLine.Value,
|
|
(DataSize + mss - 1) / mss);
|
|
IPH->iph_id = net_short(IPH->iph_id);
|
|
|
|
IPH->iph_verlen = (UCHAR)
|
|
(IP_VERSION + ((OptionSize + (uint) sizeof(IPHeader)) >> 2));
|
|
IPH->iph_length =
|
|
net_short(DataSize + OptionSize + sizeof(IPHeader));
|
|
|
|
|
|
PktExt = NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
|
|
PktExt->NdisPacketInfo[TcpLargeSendPacketInfo] = UlongToPtr(mss);
|
|
#if GPC
|
|
if (OptInfo->ioi_GPCHandle) {
|
|
PktExt->NdisPacketInfo[ClassificationHandlePacketInfo] =
|
|
IntToPtr(OptInfo->ioi_GPCHandle);
|
|
}
|
|
#endif
|
|
Status = (*(DestIF->if_xmit)) (DestIF->if_lcontext,
|
|
&Packet, 1, FirstHop, RCE, NULL);
|
|
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
|
|
if (Status != NDIS_STATUS_PENDING) {
|
|
|
|
*SentBytes = PtrToUlong(PktExt->NdisPacketInfo[TcpLargeSendPacketInfo]);
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Largesend status not pending!\n"));
|
|
|
|
SendStatus = (Status == NDIS_STATUS_SUCCESS)
|
|
? IP_SUCCESS : IP_GENERAL_FAILURE;
|
|
FreeIPPacket(Packet, TRUE, SendStatus);
|
|
return SendStatus;
|
|
|
|
} else {
|
|
|
|
return IP_PENDING;
|
|
}
|
|
|
|
}
|
|
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
|
|
} else {
|
|
//
|
|
// Large send is not possible.
|
|
//
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
|
|
}
|
|
Status = IP_GENERAL_FAILURE;
|
|
FreeIPPacket(Packet, TRUE, Status);
|
|
|
|
} else {
|
|
//
|
|
// Could not get the packet.
|
|
//
|
|
Status = IP_NO_RESOURCES;
|
|
}
|
|
|
|
} //RCE NULL
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
#define FREE_RESOURCES(status) \
|
|
if (pc->pc_hdrincl) { \
|
|
NdisChainBufferAtBack(Packet, Buffer); \
|
|
} \
|
|
FreeIPPacket(Packet, TRUE, status);\
|
|
if (Link) { \
|
|
DerefLink(Link); \
|
|
} \
|
|
if (RoutedIF != NULL) { \
|
|
DerefIF(RoutedIF); \
|
|
} else { \
|
|
ASSERT(RoutedRCE); \
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt); \
|
|
} \
|
|
if (Options) { \
|
|
CTEFreeMem(Options); \
|
|
} \
|
|
if (IPSecHandlerPtr && OptBuffer) { \
|
|
NdisFreeBuffer(OptBuffer); \
|
|
} \
|
|
IPSInfo.ipsi_outdiscards++; \
|
|
|
|
|
|
//** IPTransmit - Transmit a packet.
|
|
//
|
|
// This is the main transmit routine called by the upper layer. Conceptually,
|
|
// we process any options, look up the route to the destination, fragment the
|
|
// packet if needed, and send it. In reality, we use an RCE to cache the best
|
|
// route, and we have special case code here for dealing with the common
|
|
// case of no options, with everything fitting into one buffer.
|
|
//
|
|
// Entry: Context - Pointer to ProtInfo struc for protocol.
|
|
// SendContext - User provided send context, passed back on send cmplt.
|
|
// Protocol - Protocol field for packet.
|
|
// Buffer - NDIS_BUFFER chain of data to be sent.
|
|
// DataSize - Size in bytes of data to be sent.
|
|
// OptInfo - Pointer to optinfo structure.
|
|
// Dest - Destination to send to.
|
|
// Source - Source address to use.
|
|
// RCE - Pointer to an RCE structure that caches info. about path.
|
|
// Protocol - Transport layer protcol number
|
|
// Irp - Pointer to Irp which generated this request, used
|
|
// for cancellation purpose
|
|
//
|
|
// Returns: Status of transmit command.
|
|
//
|
|
IP_STATUS
|
|
IPTransmit(void *Context, void *SendContext, PNDIS_BUFFER Buffer, uint DataSize,
|
|
IPAddr Dest, IPAddr Source, IPOptInfo *OptInfo, RouteCacheEntry *RCE,
|
|
uchar Protocol, IRP *Irp)
|
|
{
|
|
ProtInfo *PInfo = (ProtInfo *) Context;
|
|
PacketContext *pc;
|
|
Interface *DestIF = NULL; // Outgoing interface to use.
|
|
IPAddr FirstHop; // First hop address of destination.
|
|
uint MTU = 0; // MTU of route.
|
|
NDIS_STATUS Status;
|
|
IPHeader *IPH;
|
|
UCHAR saveIPH[MAX_IP_HDR_SIZE + ICMP_HEADER_SIZE];
|
|
IPAddr SrcRouteOrigDest = 0;
|
|
IPAddr SrcRouteFirstHop = 0;
|
|
BOOLEAN fSrcRoute = FALSE;
|
|
ULONG ipsecFlags = 0;
|
|
PNDIS_PACKET Packet;
|
|
PNDIS_BUFFER HeaderBuffer = NULL;
|
|
PNDIS_BUFFER OptBuffer = NULL;
|
|
CTELockHandle LockHandle;
|
|
uchar *Options = NULL;
|
|
uint OptionSize = 0;
|
|
BufferReference *BR;
|
|
RouteTableEntry *RTE;
|
|
uchar DType = 0;
|
|
IP_STATUS SendStatus;
|
|
Interface *RoutedIF;
|
|
BOOLEAN fIpsec; // is this an IPSEC generated packet?
|
|
FORWARD_ACTION Action = FORWARD;
|
|
IPPacketFilterPtr FilterPtr;
|
|
ULONG ipsecByteCount = 0;
|
|
ULONG ipsecMTU;
|
|
PNDIS_BUFFER newBuf = NULL;
|
|
IPRcvBuf *pInRcvBuf = NULL;
|
|
uint FirewallMode = 0;
|
|
uint FirewallRef;
|
|
Queue* FirewallQ;
|
|
uint BufferChanged = 0; // used by firewall
|
|
UINT HdrInclOptions = FALSE;
|
|
LinkEntry *Link = NULL;
|
|
IPAddr LinkNextHop;
|
|
void *ArpCtxt = NULL;
|
|
RouteCacheEntry *RoutedRCE = NULL;
|
|
void *pvTmpBuffer;
|
|
uint ConstrainIF;
|
|
|
|
IPSInfo.ipsi_outrequests++;
|
|
|
|
|
|
// Check the request length. If it is > max that can be sent
|
|
// in IP fail this request.
|
|
|
|
if (OptInfo->ioi_hdrincl) {
|
|
//
|
|
// In the case of header include, DataSize includes
|
|
// IP header length and option length.
|
|
//
|
|
if ((int)DataSize > MAX_TOTAL_LENGTH) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_PACKET_TOO_BIG;
|
|
}
|
|
} else if ((int)DataSize >
|
|
(MAX_TOTAL_LENGTH - (sizeof(IPHeader) +
|
|
(OptInfo->ioi_options ? OptInfo->ioi_optlength : 0)))) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_PACKET_TOO_BIG;
|
|
}
|
|
|
|
if ((DataSize == 0) && OptInfo->ioi_hdrincl) {
|
|
// There is nothing to send, not even just IP header!
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_SUCCESS;
|
|
}
|
|
|
|
FirewallMode = ProcessFirewallQ();
|
|
|
|
DEBUGMSG(DBG_TRACE && DBG_IP && DBG_TX,
|
|
(DTEXT("+IPTransmit(%x, %x, %x, %d, %x, %x, %x, %x, %x)\n"),
|
|
Context, SendContext, Buffer, DataSize, Dest, Source,
|
|
OptInfo, RCE, Protocol));
|
|
|
|
//
|
|
// fIpsec is set if and only if this is called by IPSec driver.
|
|
//
|
|
fIpsec = (OptInfo->ioi_flags & IP_FLAG_IPSEC);
|
|
|
|
//
|
|
// Allocate a packet that we need for all cases, and fill
|
|
// in the common stuff. If everything goes well, we'll send it
|
|
// here. Otherwise we'll break out into special case code for
|
|
// broadcasts, fragments, etc.
|
|
//
|
|
Packet = GetIPPacket();
|
|
if (Packet == NULL) {
|
|
// Need to call ipsec's xmitdone since it expects us to do so
|
|
if (fIpsec) {
|
|
(PInfo->pi_xmitdone)(SendContext, Buffer, IP_NO_RESOURCES);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
#if !MILLEN
|
|
//Enable this in Millennium when ndis5.1 is checked in
|
|
|
|
SET_CANCELID(Irp, Packet);
|
|
|
|
#endif
|
|
|
|
pc = (PacketContext *) Packet->ProtocolReserved;
|
|
|
|
ASSERT(pc->pc_firewall == NULL);
|
|
ASSERT(pc->pc_firewall2 == NULL);
|
|
pc->pc_br = (BufferReference *) NULL;
|
|
pc->pc_pi = PInfo;
|
|
pc->pc_context = SendContext;
|
|
ASSERT(pc->pc_if == NULL);
|
|
ASSERT(pc->pc_iflink == NULL);
|
|
pc->pc_firewall = NULL;
|
|
pc->pc_firewall2 = NULL;
|
|
pc->pc_ipsec_flags = 0;
|
|
pc->pc_hdrincl = NULL;
|
|
|
|
//
|
|
// This might be called from IPSEC also; in this case, Protocol will
|
|
// indicate so. The entire IP packet is in Buffer and all we need to
|
|
// do is find the best route and ship it.
|
|
//
|
|
if (fIpsec) {
|
|
ULONG len;
|
|
|
|
ASSERT(Context);
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_IP && DBG_TX,
|
|
(DTEXT("IPTransmit: ipsec....\n")));
|
|
|
|
pc->pc_common.pc_IpsecCtx = SendContext;
|
|
pc->pc_common.pc_flags |= PACKET_FLAG_IPHDR;
|
|
FirstHop = NULL_IP_ADDR;
|
|
|
|
//
|
|
// IPH is at head of first buffer
|
|
//
|
|
TcpipQueryBuffer(Buffer, (PVOID) & IPH, (PUINT) &len, NormalPagePriority);
|
|
|
|
if (IPH == NULL) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
|
|
//
|
|
// Save packet header in the reinject case for potential
|
|
// Path MTU discovery use. We need to save the original IPH since
|
|
// the header can be modified when going through IPSEC again.
|
|
//
|
|
if (IPH->iph_offset & IP_DF_FLAG) {
|
|
PUCHAR pTpt;
|
|
ULONG tptLen;
|
|
ULONG HeaderLength;
|
|
|
|
*((IPHeader *) saveIPH) = *IPH;
|
|
|
|
HeaderLength = (IPH->iph_verlen & (uchar) ~ IP_VER_FLAG) << 2;
|
|
if (HeaderLength > sizeof(IPHeader)) {
|
|
TcpipQueryBuffer(NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE(Buffer)),
|
|
&pTpt,
|
|
(PUINT) &tptLen,
|
|
NormalPagePriority);
|
|
|
|
} else {
|
|
TcpipQueryBuffer(NDIS_BUFFER_LINKAGE(Buffer),
|
|
&pTpt,
|
|
(PUINT) &tptLen,
|
|
NormalPagePriority);
|
|
}
|
|
|
|
if (pTpt == NULL) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Copy Options if any.
|
|
//
|
|
|
|
if (OptInfo->ioi_options) {
|
|
ASSERT(HeaderLength == (sizeof(IPHeader) + OptInfo->ioi_optlength));
|
|
RtlCopyMemory(saveIPH + sizeof(IPHeader),
|
|
OptInfo->ioi_options,
|
|
OptInfo->ioi_optlength);
|
|
}
|
|
|
|
RtlCopyMemory((PUCHAR) saveIPH + HeaderLength,
|
|
pTpt,
|
|
MIN(ICMP_HEADER_SIZE, tptLen));
|
|
}
|
|
//
|
|
// Attach the IPSecPktInfo and/or TcpipPktInfo passed in to Packet's
|
|
// NDIS extension structure.
|
|
//
|
|
if (OptInfo->ioi_options) {
|
|
|
|
PNDIS_PACKET_EXTENSION PktExt;
|
|
|
|
PktExt = NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
PktExt->NdisPacketInfo[IpSecPacketInfo] =
|
|
((PNDIS_PACKET_EXTENSION) OptInfo->ioi_options)->
|
|
NdisPacketInfo[IpSecPacketInfo];
|
|
|
|
PktExt->NdisPacketInfo[TcpIpChecksumPacketInfo] =
|
|
((PNDIS_PACKET_EXTENSION) OptInfo->ioi_options)->
|
|
NdisPacketInfo[TcpIpChecksumPacketInfo];
|
|
|
|
OptInfo->ioi_options = NULL;
|
|
}
|
|
goto ipsec_jump;
|
|
} else {
|
|
pc->pc_common.pc_IpsecCtx = NULL;
|
|
}
|
|
|
|
// Make sure that we have an RCE, that it's valid, etc.
|
|
|
|
#if GPC
|
|
// Check GPC handle
|
|
|
|
if (OptInfo->ioi_GPCHandle) {
|
|
|
|
NDIS_PER_PACKET_INFO_FROM_PACKET(Packet,
|
|
ClassificationHandlePacketInfo) = IntToPtr(OptInfo->ioi_GPCHandle);
|
|
|
|
//tos info is handled in protocol
|
|
}
|
|
#endif
|
|
|
|
if ((RCE != NULL) && !(RCE->rce_flags & RCE_LINK_DELETED)) {
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_IP && DBG_TX,
|
|
(DTEXT("IPTransmit: RCE %x\n"), RCE));
|
|
|
|
// We have an RCE. Make sure it's valid.
|
|
CTEGetLock(&RCE->rce_lock, &LockHandle);
|
|
if (RCE->rce_flags == RCE_ALL_VALID) {
|
|
|
|
ASSERT(RCE->rce_cnt > 0);
|
|
|
|
// The RTE is valid.
|
|
CTEInterlockedIncrementLong(&RCE->rce_usecnt);
|
|
RTE = RCE->rce_rte;
|
|
FirstHop = ADDR_FROM_RTE(RTE, Dest);
|
|
DestIF = IF_FROM_RTE(RTE);
|
|
RoutedRCE = RCE;
|
|
|
|
if (DestIF->if_flags & IF_FLAGS_P2MP) {
|
|
Link = RTE->rte_link;
|
|
if (!Link) {
|
|
ASSERT(Link);
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
FreeIPPacket(Packet, TRUE, IP_GENERAL_FAILURE);
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
ArpCtxt = Link->link_arpctxt;
|
|
MTU = MIN(Link->link_mtu, DestIF->if_mtu);
|
|
|
|
// pc_iflink stores a pointer to Link since sendcomplete
|
|
// has to deref it
|
|
//
|
|
pc->pc_iflink = Link;
|
|
CTEInterlockedIncrementLong(&Link->link_refcount);
|
|
} else {
|
|
MTU = MTU_FROM_RTE(RTE);
|
|
}
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
|
|
//
|
|
// Check that we have no options, this isn't a broadcast, and
|
|
// that everything will fit into one link level MTU. If this
|
|
// is the case, we'll send it in a hurry.
|
|
|
|
// if FirewallMode is set, bail out to slow path. The reason
|
|
// is that if firewall hook adds options or increases the
|
|
// buffer size to more than MTU in fast path, we have to go to
|
|
// slow path and things becomes messy.
|
|
//
|
|
|
|
if ((OptInfo->ioi_options == (uchar *) NULL) &&
|
|
(!(*IPSecQueryStatusPtr)(OptInfo->ioi_GPCHandle)) &&
|
|
(!FirewallMode)) {
|
|
if (!IS_BCAST_DEST(RCE->rce_dtype)) {
|
|
if (DataSize <= MTU) {
|
|
|
|
// update mcast counters
|
|
|
|
if (IS_MCAST_DEST(RCE->rce_dtype)){
|
|
|
|
DestIF->if_OutMcastPkts++;
|
|
DestIF->if_OutMcastOctets += DataSize;
|
|
|
|
} else if (OptInfo->ioi_ttl &&
|
|
!DestIF->if_promiscuousmode) {
|
|
|
|
// Tell NDIS not to loop the packet back to us
|
|
// on our binding, since:
|
|
// * it's a unicast transmission
|
|
// * its TTL is non-zero
|
|
// * its outgoing interface is not in promiscuous
|
|
// mode.
|
|
|
|
NdisSetPacketFlags(Packet,
|
|
NDIS_FLAGS_DONT_LOOPBACK);
|
|
}
|
|
|
|
// Check if user is supplying the IP header
|
|
|
|
if (!OptInfo->ioi_hdrincl) {
|
|
|
|
NdisAdjustBufferLength(Buffer,
|
|
NdisBufferLength(Buffer) + sizeof(IPHeader));
|
|
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
IPH = (IPHeader *) TcpipBufferVirtualAddress(Buffer,
|
|
NormalPagePriority);
|
|
|
|
if (IPH == NULL) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
IPH->iph_protocol = Protocol;
|
|
IPH->iph_xsum = 0;
|
|
IPH->iph_dest = Dest;
|
|
IPH->iph_src = Source;
|
|
IPH->iph_ttl = OptInfo->ioi_ttl;
|
|
|
|
if (OptInfo->ioi_ttl == 0) {
|
|
NdisSetPacketFlags(Packet,
|
|
NDIS_FLAGS_LOOPBACK_ONLY);
|
|
}
|
|
IPH->iph_tos = OptInfo->ioi_tos;
|
|
IPH->iph_offset = net_short((OptInfo->ioi_flags & IP_FLAG_DF) << 13);
|
|
|
|
IPH->iph_id = (ushort) InterlockedExchangeAdd(
|
|
(PLONG) &IPIDCacheLine.Value, 1);
|
|
IPH->iph_id = net_short(IPH->iph_id);
|
|
|
|
IPH->iph_verlen = DEFAULT_VERLEN;
|
|
IPH->iph_length = net_short(DataSize + sizeof(IPHeader));
|
|
|
|
if (!IPSecStatus) {
|
|
RCE->rce_OffloadFlags = DestIF->if_OffloadFlags;
|
|
} else {
|
|
RCE->rce_OffloadFlags = 0;
|
|
}
|
|
|
|
if (IPSecStatus ||
|
|
!(DestIF->if_OffloadFlags & IP_XMT_CHECKSUM_OFFLOAD)) {
|
|
|
|
IPH->iph_xsum = ~xsum(IPH, sizeof(IPHeader));
|
|
|
|
}
|
|
if (!IPSecStatus &&
|
|
((DestIF->if_OffloadFlags & IP_XMT_CHECKSUM_OFFLOAD) ||
|
|
(DestIF->if_OffloadFlags & TCP_XMT_CHECKSUM_OFFLOAD))) {
|
|
|
|
PNDIS_PACKET_EXTENSION PktExt;
|
|
NDIS_TCP_IP_CHECKSUM_PACKET_INFO ChksumPktInfo;
|
|
|
|
PktExt = NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
|
|
ChksumPktInfo.Value = 0;
|
|
ChksumPktInfo.Transmit.NdisPacketChecksumV4 = 1;
|
|
|
|
if (DestIF->if_OffloadFlags & IP_XMT_CHECKSUM_OFFLOAD) {
|
|
ChksumPktInfo.Transmit.NdisPacketIpChecksum = 1;
|
|
}
|
|
if (OptInfo->ioi_TcpChksum) {
|
|
ChksumPktInfo.Transmit.NdisPacketTcpChecksum = 1;
|
|
}
|
|
|
|
PktExt->NdisPacketInfo[TcpIpChecksumPacketInfo]
|
|
= UlongToPtr(ChksumPktInfo.Value);
|
|
#if DBG
|
|
DbgIPSendHwChkSum++;
|
|
#endif
|
|
}
|
|
|
|
} else { //hdrincl
|
|
|
|
PNDIS_BUFFER UserBuffer;
|
|
uint len;
|
|
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
|
|
UserBuffer = NDIS_BUFFER_LINKAGE(Buffer);
|
|
|
|
DataSize -= NdisBufferLength(Buffer);
|
|
ASSERT((long)DataSize >= 0);
|
|
|
|
NdisAdjustBufferLength(Buffer, 0);
|
|
|
|
ASSERT(UserBuffer != NULL);
|
|
|
|
IPH = (IPHeader *) TcpipBufferVirtualAddress(UserBuffer,
|
|
NormalPagePriority);
|
|
|
|
if (IPH != NULL &&
|
|
DataSize >= sizeof(IPHeader) &&
|
|
((len = IPH->iph_verlen & 0xf)*4) <=
|
|
DataSize ) {
|
|
|
|
if (!IPH->iph_id) {
|
|
|
|
IPH->iph_id =
|
|
(ushort)InterlockedExchangeAdd(
|
|
(PLONG) &IPIDCacheLine.Value, 1);
|
|
IPH->iph_id = net_short(IPH->iph_id);
|
|
}
|
|
|
|
IPH->iph_length = net_short(DataSize);
|
|
IPH->iph_tos = OptInfo->ioi_tos;
|
|
IPH->iph_xsum = 0;
|
|
IPH->iph_xsum = ~xsum(IPH, len * 4);
|
|
|
|
} else {
|
|
|
|
SendStatus = (IPH == NULL) ? IP_NO_RESOURCES
|
|
: IP_GENERAL_FAILURE;
|
|
FreeIPPacket(Packet, TRUE, SendStatus);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
return SendStatus;
|
|
|
|
}
|
|
|
|
IPH->iph_id = (ushort)InterlockedExchangeAdd(
|
|
(PLONG) &IPIDCacheLine.Value, 1);
|
|
|
|
IPH->iph_id = net_short(IPH->iph_id);
|
|
|
|
|
|
len = IPH->iph_verlen & 0xf;
|
|
|
|
((IPHeader UNALIGNED*)IPH)->iph_length = net_short(DataSize);
|
|
IPH->iph_tos = OptInfo->ioi_tos;
|
|
((IPHeader UNALIGNED*)IPH)->iph_xsum = 0;
|
|
((IPHeader UNALIGNED*)IPH)->iph_xsum = ~xsum(IPH, len * 4);
|
|
|
|
ASSERT(!dbg_hdrincl);
|
|
}
|
|
|
|
// See if we need to filter this packet. If we
|
|
// do, call the filter routine to see if it's
|
|
// OK to send it.
|
|
|
|
if (!RefPtrValid(&FilterRefPtr)) {
|
|
|
|
// Set the cancellation context
|
|
// Once link level call is made,
|
|
// Irp can go away any time
|
|
|
|
SET_CANCEL_CONTEXT(Irp, DestIF);
|
|
|
|
|
|
Status = (*(DestIF->if_xmit)) (DestIF->if_lcontext,
|
|
&Packet, 1, FirstHop,
|
|
RCE, ArpCtxt);
|
|
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
|
|
if (Status != NDIS_STATUS_PENDING) {
|
|
SendStatus = (Status == NDIS_STATUS_FAILURE)
|
|
? IP_GENERAL_FAILURE : IP_SUCCESS;
|
|
FreeIPPacket(Packet, TRUE, SendStatus);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
return SendStatus;
|
|
}
|
|
|
|
return IP_PENDING;
|
|
|
|
} else {
|
|
PNDIS_BUFFER pDataBuffer;
|
|
PVOID pvBuf = NULL;
|
|
ULONG cbBuf = 0;
|
|
|
|
if (DestIF->if_flags & IF_FLAGS_P2MP) {
|
|
LinkNextHop = Link->link_NextHop;
|
|
} else {
|
|
LinkNextHop = NULL_IP_ADDR;
|
|
}
|
|
|
|
//
|
|
// There are three cases which need to be
|
|
// taken care of here:
|
|
// 1) Normal path. Buffer contains both
|
|
// IPHeader and header from TCP/UDP, etc.
|
|
// 2) Raw. Buffer contains IPHeader only.
|
|
// Need to get next data in chain from
|
|
// linked buffer.
|
|
// 3) Raw - iphdrinclude. Buffer length is
|
|
// 0. Need to get IPHeader and next
|
|
// header from linked buffer.
|
|
//
|
|
// Use the byte count of the first buffer
|
|
// to determine the case to handle.
|
|
//
|
|
|
|
if (NdisBufferLength(Buffer) > sizeof(IPHeader)) {
|
|
// Case 1.
|
|
pvBuf = (PVOID) (IPH + 1);
|
|
cbBuf = NdisBufferLength(Buffer) - sizeof(IPHeader);
|
|
} else {
|
|
// Need to skip to the next buffer.
|
|
NdisGetNextBuffer(Buffer, &pDataBuffer);
|
|
|
|
if (pDataBuffer) {
|
|
if (NdisBufferLength(Buffer) == 0) {
|
|
|
|
// Case 3.
|
|
cbBuf = NdisBufferLength(pDataBuffer) - sizeof(IPHeader);
|
|
pvBuf = (PVOID) (IPH + 1);
|
|
} else {
|
|
// Case 2.
|
|
ASSERT(NdisBufferLength(Buffer)
|
|
== sizeof(IPHeader));
|
|
cbBuf = NdisBufferLength(pDataBuffer);
|
|
pvBuf = TcpipBufferVirtualAddress(
|
|
pDataBuffer,
|
|
NormalPagePriority);
|
|
}
|
|
} else {
|
|
// Should always have two buffers in
|
|
// chain at this point!
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
if (pvBuf == NULL) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
FilterPtr = AcquireRefPtr(&FilterRefPtr);
|
|
Action = (*FilterPtr) (IPH,
|
|
pvBuf, cbBuf,
|
|
INVALID_IF_INDEX,
|
|
DestIF->if_index,
|
|
NULL_IP_ADDR,
|
|
LinkNextHop);
|
|
ReleaseRefPtr(&FilterRefPtr);
|
|
|
|
if (Action == FORWARD) {
|
|
// Set the cancellation context
|
|
// Once link level call is made,
|
|
// Irp can go away any time
|
|
|
|
SET_CANCEL_CONTEXT(Irp, DestIF);
|
|
|
|
Status = (*(DestIF->if_xmit)) (
|
|
DestIF->if_lcontext,
|
|
&Packet, 1, FirstHop,
|
|
RCE, ArpCtxt);
|
|
} else {
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
IPSInfo.ipsi_outdiscards++;
|
|
} // if (Action == FORWARD)
|
|
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
|
|
if (Status != NDIS_STATUS_PENDING) {
|
|
SendStatus = (Status == NDIS_STATUS_FAILURE)
|
|
? IP_GENERAL_FAILURE : IP_SUCCESS;
|
|
FreeIPPacket(Packet, TRUE, SendStatus);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
return SendStatus;
|
|
}
|
|
return IP_PENDING;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RCE && IPSecStatus) {
|
|
RCE->rce_OffloadFlags = 0;
|
|
}
|
|
// CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
DType = RCE->rce_dtype;
|
|
} else {
|
|
|
|
uint IPHdrSize, BufLength;
|
|
|
|
IPHdrSize = sizeof(IPHeader);
|
|
|
|
|
|
//If user supplied header, account for it.
|
|
//This is to satisfy DoDcallout
|
|
//may not be necessary...
|
|
|
|
if (OptInfo->ioi_hdrincl) {
|
|
IPHdrSize = 0;
|
|
}
|
|
|
|
|
|
// We have an RCE, but there is no RTE for it. Call the
|
|
// routing code to fix this.
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
|
|
BufLength = NdisBufferLength(Buffer);
|
|
|
|
if ((BufLength == 0) && DataSize) {
|
|
|
|
PNDIS_BUFFER NextBuffer = NULL;
|
|
|
|
// Get the virtual address of user buffer
|
|
// which is after null transport header
|
|
|
|
NdisGetNextBuffer(Buffer, &NextBuffer);
|
|
ASSERT(NextBuffer != NULL);
|
|
pvTmpBuffer = TcpipBufferVirtualAddress(NextBuffer, NormalPagePriority);
|
|
BufLength = NdisBufferLength(NextBuffer);
|
|
|
|
// Since this is raw socket, just pass the raw data
|
|
// to Dod Callout, instead of pointing beyond header
|
|
// size.
|
|
|
|
IPHdrSize = 0;
|
|
|
|
} else {
|
|
pvTmpBuffer = TcpipBufferVirtualAddress(Buffer, NormalPagePriority);
|
|
BufLength = NdisBufferLength(Buffer);
|
|
}
|
|
|
|
|
|
if (pvTmpBuffer == NULL) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
if (!AttachRCEToRTE(RCE, Protocol,
|
|
(uchar *) pvTmpBuffer + IPHdrSize,
|
|
BufLength)) {
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
FreeIPPacket(Packet, TRUE, IP_DEST_HOST_UNREACHABLE);
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
// See if the RCE is now valid.
|
|
CTEGetLock(&RCE->rce_lock, &LockHandle);
|
|
if (RCE->rce_flags == RCE_ALL_VALID) {
|
|
|
|
// The RCE is now valid, so use his info.
|
|
RTE = RCE->rce_rte;
|
|
FirstHop = ADDR_FROM_RTE(RTE, Dest);
|
|
DestIF = IF_FROM_RTE(RTE);
|
|
RoutedRCE = RCE;
|
|
CTEInterlockedIncrementLong(&RCE->rce_usecnt);
|
|
|
|
if (DestIF->if_flags & IF_FLAGS_P2MP) {
|
|
LinkEntry* PrevLink;
|
|
PrevLink = Link;
|
|
Link = RTE->rte_link;
|
|
if (!Link) {
|
|
ASSERT(Link);
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
FreeIPPacket(Packet, TRUE, IP_GENERAL_FAILURE);
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
//
|
|
// Dereferenc previous link before we return.
|
|
//
|
|
if (PrevLink) {
|
|
DerefLink(PrevLink);
|
|
}
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
ArpCtxt = Link->link_arpctxt;
|
|
MTU = MIN(Link->link_mtu, DestIF->if_mtu);
|
|
|
|
pc->pc_iflink = Link;
|
|
CTEInterlockedIncrementLong(&Link->link_refcount);
|
|
} else {
|
|
MTU = MTU_FROM_RTE(RTE);
|
|
}
|
|
|
|
DType = RCE->rce_dtype;
|
|
|
|
if (RTE->rte_if) {
|
|
RCE->rce_TcpDelAckTicks = RTE->rte_if->if_TcpDelAckTicks;
|
|
RCE->rce_TcpAckFrequency = RTE->rte_if->if_TcpAckFrequency;
|
|
} else {
|
|
RCE->rce_TcpDelAckTicks = 0;
|
|
RCE->rce_TcpAckFrequency = 0;
|
|
}
|
|
|
|
if (!IPSecStatus) {
|
|
RCE->rce_OffloadFlags = RTE->rte_if->if_OffloadFlags;
|
|
} else {
|
|
RCE->rce_OffloadFlags = 0;
|
|
}
|
|
|
|
} else
|
|
FirstHop = NULL_IP_ADDR;
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
}
|
|
} else {
|
|
// We had no RCE, so we'll have to look it up the hard way.
|
|
FirstHop = NULL_IP_ADDR;
|
|
}
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_IP && DBG_TX,
|
|
(DTEXT("IPTransmit: Bailed to slow path.\n")));
|
|
|
|
// We bailed out of the fast path for some reason. Allocate a header
|
|
// buffer, and copy the data in the first buffer forward. Then figure
|
|
// out why we're off the fast path, and deal with it. If we don't have
|
|
// the next hop info, look it up now.
|
|
|
|
//If user has supplied the IP header, assume that he is taken care
|
|
//of options too.
|
|
|
|
NdisReinitializePacket(Packet);
|
|
|
|
if (!OptInfo->ioi_hdrincl) {
|
|
|
|
HeaderBuffer = GetIPHdrBuffer(&IPH);
|
|
if (HeaderBuffer == NULL) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
DEBUGMSG(DBG_WARN && DBG_IP && DBG_TX,
|
|
(DTEXT("IPTransmit: failure to allocate IP hdr.\n")));
|
|
return IP_NO_RESOURCES;
|
|
} else {
|
|
uchar *Temp1, *Temp2;
|
|
|
|
// Got a buffer, copy the upper layer data forward.
|
|
|
|
Temp1 = TcpipBufferVirtualAddress(Buffer, NormalPagePriority);
|
|
|
|
if (Temp1 == NULL) {
|
|
FreeIPHdrBuffer(HeaderBuffer);
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
Temp2 = Temp1 + sizeof(IPHeader);
|
|
RtlMoveMemory(Temp1, Temp2, NdisBufferLength(Buffer));
|
|
}
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_IP && DBG_TX,
|
|
(DTEXT("IPTransmit: Pkt %x IPBuf %x IPH %x\n"),
|
|
Packet, HeaderBuffer, IPH));
|
|
|
|
NdisChainBufferAtBack(Packet, HeaderBuffer);
|
|
|
|
IPH->iph_protocol = Protocol;
|
|
IPH->iph_xsum = 0;
|
|
IPH->iph_src = Source;
|
|
IPH->iph_ttl = OptInfo->ioi_ttl;
|
|
|
|
if (OptInfo->ioi_ttl == 0) {
|
|
NdisSetPacketFlags(Packet, NDIS_FLAGS_LOOPBACK_ONLY);
|
|
}
|
|
IPH->iph_tos = OptInfo->ioi_tos;
|
|
IPH->iph_offset = net_short((OptInfo->ioi_flags & IP_FLAG_DF) << 13);
|
|
IPH->iph_id = (ushort) InterlockedExchangeAdd(
|
|
(PLONG) &IPIDCacheLine.Value, 1);
|
|
IPH->iph_id = net_short(IPH->iph_id);
|
|
pc = (PacketContext *) Packet->ProtocolReserved;
|
|
pc->pc_common.pc_flags |= PACKET_FLAG_IPHDR;
|
|
|
|
if (IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR)) {
|
|
IPH->iph_dest = Dest;
|
|
} else {
|
|
if (IPSecHandlerPtr) {
|
|
UCHAR Length;
|
|
ULONG Index = 0;
|
|
PUCHAR pOptions = OptInfo->ioi_options;
|
|
|
|
//
|
|
// Search for the last hop gateway address in strict
|
|
// or loose source routing option.
|
|
//
|
|
while (Index < OptInfo->ioi_optlength) {
|
|
switch (*pOptions) {
|
|
case IP_OPT_EOL:
|
|
Index = OptInfo->ioi_optlength;
|
|
break;
|
|
|
|
case IP_OPT_NOP:
|
|
Index++;
|
|
pOptions++;
|
|
break;
|
|
|
|
case IP_OPT_LSRR:
|
|
case IP_OPT_SSRR:
|
|
Length = pOptions[IP_OPT_LENGTH];
|
|
pOptions += Length;
|
|
fSrcRoute = TRUE;
|
|
SrcRouteOrigDest = *((IPAddr UNALIGNED *)(pOptions - sizeof(IPAddr)));
|
|
Index = OptInfo->ioi_optlength;
|
|
break;
|
|
|
|
case IP_OPT_RR:
|
|
case IP_OPT_TS:
|
|
case IP_OPT_ROUTER_ALERT:
|
|
case IP_OPT_SECURITY:
|
|
default:
|
|
Length = pOptions[IP_OPT_LENGTH];
|
|
Index += Length;
|
|
pOptions += Length;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have a source route, so we need to redo the
|
|
// destination and first hop information.
|
|
//
|
|
Dest = OptInfo->ioi_addr;
|
|
IPH->iph_dest = Dest;
|
|
|
|
if (RCE != NULL) {
|
|
// We have an RCE. Make sure it's valid.
|
|
CTEGetLock(&RCE->rce_lock, &LockHandle);
|
|
|
|
if (RCE->rce_flags == RCE_ALL_VALID) {
|
|
|
|
// The RTE is valid.
|
|
RTE = RCE->rce_rte;
|
|
FirstHop = ADDR_FROM_RTE(RTE, Dest);
|
|
DestIF = IF_FROM_RTE(RTE);
|
|
if (!RoutedRCE) {
|
|
CTEInterlockedIncrementLong(&RCE->rce_usecnt);
|
|
RoutedRCE = RCE;
|
|
}
|
|
if (DestIF->if_flags & IF_FLAGS_P2MP) {
|
|
LinkEntry* PrevLink;
|
|
PrevLink = Link;
|
|
Link = RTE->rte_link;
|
|
if (!Link) {
|
|
ASSERT(Link);
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
FreeIPPacket(Packet, TRUE, IP_GENERAL_FAILURE);
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
if (PrevLink) {
|
|
DerefLink(PrevLink);
|
|
}
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
ArpCtxt = Link->link_arpctxt;
|
|
|
|
MTU = MIN(Link->link_mtu, DestIF->if_mtu);
|
|
pc->pc_iflink = Link;
|
|
CTEInterlockedIncrementLong(&Link->link_refcount);
|
|
} else {
|
|
MTU = MTU_FROM_RTE(RTE);
|
|
}
|
|
|
|
} else {
|
|
FirstHop = NULL_IP_ADDR;
|
|
}
|
|
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
}
|
|
}
|
|
} else { //hdrincl option
|
|
PNDIS_BUFFER UserBuffer, NewBuffer = NULL, NextBuf = NULL;
|
|
uint len;
|
|
NDIS_STATUS NewStatus;
|
|
|
|
UserBuffer = NDIS_BUFFER_LINKAGE(Buffer);
|
|
ASSERT(UserBuffer != NULL);
|
|
|
|
HeaderBuffer = GetIPHdrBuffer(&IPH);
|
|
if (HeaderBuffer == NULL) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
return IP_NO_RESOURCES;
|
|
} else {
|
|
uchar *UserData;
|
|
|
|
// Got a buffer, copy the upper layer data forward.
|
|
UserData = TcpipBufferVirtualAddress(UserBuffer, NormalPagePriority);
|
|
|
|
if (UserData == NULL || (DataSize < sizeof(IPHeader))) {
|
|
FreeIPHdrBuffer(HeaderBuffer);
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(IPH, UserData, sizeof(IPHeader));
|
|
NdisAdjustBufferLength(HeaderBuffer, sizeof(IPHeader));
|
|
}
|
|
|
|
pc = (PacketContext *) Packet->ProtocolReserved;
|
|
pc->pc_common.pc_flags |= PACKET_FLAG_IPHDR;
|
|
|
|
NdisChainBufferAtBack(Packet, HeaderBuffer);
|
|
|
|
// find the header length (in bytes) specified in IPHeader
|
|
len = (IPH->iph_verlen & 0xf) << 2;
|
|
|
|
if (len < sizeof(IPHeader)) {
|
|
|
|
// Fixup of headers is not needed as this is headerinclude
|
|
// packet and header include operation is not done yet
|
|
|
|
FreeIPPacket(Packet, FALSE, IP_GENERAL_FAILURE);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
|
|
if (len > sizeof(IPHeader)) {
|
|
uchar *Temp1;
|
|
|
|
// we have options in HDR_INCL
|
|
HdrInclOptions = TRUE;
|
|
// find the length of options.
|
|
OptionSize = len - sizeof(IPHeader);
|
|
Options = CTEAllocMemN(OptionSize, 'wiCT');
|
|
if (Options == (uchar *) NULL) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
// Got a buffer, copy the options in Options Buffer
|
|
Temp1 = TcpipBufferVirtualAddress(UserBuffer, NormalPagePriority);
|
|
|
|
// Assume first user buffer contains complete IP header
|
|
if (Temp1 == NULL ||
|
|
NdisBufferLength(UserBuffer) < len) {
|
|
SendStatus = (Temp1 == NULL) ? IP_NO_RESOURCES
|
|
: IP_GENERAL_FAILURE;
|
|
FreeIPPacket(Packet, TRUE, SendStatus);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
CTEFreeMem(Options);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
return SendStatus;
|
|
}
|
|
RtlCopyMemory(Options, Temp1 + sizeof(IPHeader), OptionSize);
|
|
}
|
|
DataSize -= NdisBufferLength(Buffer) + len;
|
|
|
|
//
|
|
// Map out the post-IP header portion
|
|
//
|
|
|
|
pvTmpBuffer = TcpipBufferVirtualAddress(UserBuffer, NormalPagePriority);
|
|
|
|
if (pvTmpBuffer == NULL) {
|
|
NewStatus = NDIS_STATUS_RESOURCES;
|
|
} else {
|
|
|
|
// If user header buffer is just the length of IP header
|
|
// check for NextBuf
|
|
|
|
NextBuf = NDIS_BUFFER_LINKAGE(UserBuffer);
|
|
|
|
if ((NdisBufferLength(UserBuffer) - len)) {
|
|
NdisAllocateBuffer(&NewStatus, &NewBuffer, BufferPool,
|
|
((uchar *) pvTmpBuffer) + len,
|
|
NdisBufferLength(UserBuffer) - len);
|
|
} else {
|
|
|
|
if (NextBuf) {
|
|
pvTmpBuffer = TcpipBufferVirtualAddress(NextBuf, NormalPagePriority);
|
|
|
|
if (!pvTmpBuffer) {
|
|
NewStatus = NDIS_STATUS_RESOURCES;
|
|
} else {
|
|
NdisAllocateBuffer(&NewStatus, &NewBuffer, BufferPool,
|
|
((uchar *) pvTmpBuffer),
|
|
NdisBufferLength(NextBuf));
|
|
//
|
|
// Advance the NextBuf pointer to next buffer in chain.
|
|
//
|
|
NextBuf = NDIS_BUFFER_LINKAGE(NextBuf);
|
|
}
|
|
} else {
|
|
NewStatus = NDIS_STATUS_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NewStatus != NDIS_STATUS_SUCCESS) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (HdrInclOptions) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
// Remember the orignal usermdl
|
|
// Once this ip allocated mdl is chained,
|
|
// original chain needs to be restored
|
|
// in all the completion paths.
|
|
|
|
pc->pc_hdrincl = UserBuffer;
|
|
NDIS_BUFFER_LINKAGE(Buffer) = NewBuffer;
|
|
NDIS_BUFFER_LINKAGE(NewBuffer) = NextBuf;
|
|
NdisAdjustBufferLength(Buffer, 0);
|
|
|
|
if (!IPH->iph_id) {
|
|
IPH->iph_id = (ushort) InterlockedExchangeAdd(
|
|
(PLONG) &IPIDCacheLine.Value, 1);
|
|
IPH->iph_id = net_short(IPH->iph_id);
|
|
}
|
|
|
|
IPH->iph_length = net_short(DataSize + len);
|
|
IPH->iph_tos = OptInfo->ioi_tos;
|
|
IPH->iph_xsum = 0;
|
|
|
|
ASSERT(!dbg_hdrincl);
|
|
}
|
|
|
|
ipsec_jump:
|
|
|
|
if (RCE) {
|
|
#if 0
|
|
//
|
|
//If we take slow path for TCP, offload is meaningless
|
|
//let this packet go with xsum error
|
|
//rexmitted packet will be okay, if it takes slow path again.
|
|
//
|
|
RCE->rce_OffloadFlags = 0;
|
|
#else
|
|
if (!fIpsec && OptInfo->ioi_TcpChksum &&
|
|
(RCE->rce_OffloadFlags & TCP_XMT_CHECKSUM_OFFLOAD)) {
|
|
PNDIS_PACKET_EXTENSION PktExt =
|
|
NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
PNDIS_TCP_IP_CHECKSUM_PACKET_INFO ChksumPktInfo =
|
|
(PNDIS_TCP_IP_CHECKSUM_PACKET_INFO)
|
|
&PktExt->NdisPacketInfo[TcpIpChecksumPacketInfo];
|
|
ChksumPktInfo->Value = 0;
|
|
ChksumPktInfo->Transmit.NdisPacketChecksumV4 = 1;
|
|
ChksumPktInfo->Transmit.NdisPacketTcpChecksum = 1;
|
|
}
|
|
#endif
|
|
}
|
|
if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR)) {
|
|
if (OptInfo->ioi_mcastif) {
|
|
//
|
|
// mcastif is set to unnumbered interface, we won't do any
|
|
// lookup in this case
|
|
//
|
|
CTELockHandle TableLock; // Lock handle for routing table.
|
|
Interface *pIf;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &TableLock);
|
|
for (pIf = IFList; pIf != NULL; pIf = pIf->if_next) {
|
|
if ((pIf->if_refcount != 0) &&
|
|
(pIf->if_index == OptInfo->ioi_mcastif))
|
|
break;
|
|
}
|
|
if (pIf && !(pIf->if_iftype & DONT_ALLOW_MCAST)) {
|
|
LOCKED_REFERENCE_IF(pIf);
|
|
FirstHop = Dest;
|
|
MTU = pIf->if_mtu;
|
|
Link = NULL;
|
|
DestIF = pIf;
|
|
} else {
|
|
DestIF = NULL;
|
|
}
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
} else {
|
|
pvTmpBuffer = TcpipBufferVirtualAddress(Buffer, NormalPagePriority);
|
|
|
|
if (pvTmpBuffer == NULL) {
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (HdrInclOptions)
|
|
CTEFreeMem(Options);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
// Decide whether to do a strong or weak host lookup
|
|
ConstrainIF = GetIfConstraint(Dest, Source, OptInfo, fIpsec);
|
|
|
|
DestIF = LookupNextHopWithBuffer(Dest, Source, &FirstHop, &MTU,
|
|
PInfo->pi_protocol,
|
|
(uchar *) NdisBufferVirtualAddress(Buffer),
|
|
NdisBufferLength(Buffer), NULL, &Link,
|
|
Source, ConstrainIF);
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_IP && DBG_TX,
|
|
(DTEXT("IPTransmit: LookupNextHopWithBuffer returned %x\n"),
|
|
DestIF));
|
|
}
|
|
|
|
pc->pc_if = DestIF;
|
|
RoutedIF = DestIF;
|
|
|
|
if (DestIF == NULL) {
|
|
// Lookup failed. Return an error.
|
|
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_DEST_HOST_UNREACHABLE);
|
|
if (HdrInclOptions)
|
|
CTEFreeMem(Options);
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
if (DestIF->if_flags & IF_FLAGS_P2MP) {
|
|
|
|
if (!Link) {
|
|
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_GENERAL_FAILURE);
|
|
if (HdrInclOptions)
|
|
CTEFreeMem(Options);
|
|
DerefIF(DestIF);
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
// NextHopCtxt = Link->link_NextHop;
|
|
ArpCtxt = Link->link_arpctxt;
|
|
pc->pc_iflink = Link;
|
|
}
|
|
if (!OptInfo->ioi_hdrincl) {
|
|
if ((DestIF->if_flags & IF_FLAGS_NOIPADDR) &&
|
|
IP_ADDR_EQUAL(Source, NULL_IP_ADDR)) {
|
|
IPH->iph_src = g_ValidAddr;
|
|
if (IP_ADDR_EQUAL(g_ValidAddr, NULL_IP_ADDR)) {
|
|
FreeIPPacket(Packet, TRUE, IP_DEST_HOST_UNREACHABLE);
|
|
if (HdrInclOptions)
|
|
CTEFreeMem(Options);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
DerefIF(DestIF);
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
} else {
|
|
IPH->iph_src = Source;
|
|
}
|
|
}
|
|
DType = GetAddrType(Dest);
|
|
ASSERT(DType != DEST_INVALID);
|
|
|
|
} else {
|
|
RoutedIF = NULL;
|
|
}
|
|
|
|
//
|
|
// See if we have any options. If we do, copy them now.
|
|
//
|
|
|
|
//
|
|
// If user is giving us IP hdr, just assume he has done Options too.
|
|
//
|
|
|
|
if ((!OptInfo->ioi_hdrincl &&
|
|
(OptInfo->ioi_options != NULL) &&
|
|
OptInfo->ioi_optlength) || HdrInclOptions) {
|
|
// if HdrInclOptions is TRUE we have already created Options Buffer
|
|
if (!HdrInclOptions) {
|
|
|
|
//
|
|
// If we have a SSRR, make sure that we're sending straight to
|
|
// the first hop.
|
|
//
|
|
if (OptInfo->ioi_flags & IP_FLAG_SSRR) {
|
|
if (!IP_ADDR_EQUAL(Dest, FirstHop)) {
|
|
FreeIPPacket(Packet, TRUE, IP_DEST_HOST_UNREACHABLE);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
}
|
|
Options = CTEAllocMemN(OptionSize = OptInfo->ioi_optlength, 'xiCT');
|
|
if (Options == (uchar *) NULL) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
RtlCopyMemory(Options, OptInfo->ioi_options, OptionSize);
|
|
}
|
|
//
|
|
// Allocate the MDL for options too
|
|
//
|
|
if (IPSecHandlerPtr) {
|
|
NdisAllocateBuffer(&Status, &OptBuffer, BufferPool, Options,
|
|
OptionSize);
|
|
if (Status != NDIS_STATUS_SUCCESS) { // Couldn't get the
|
|
// needed option buffer.
|
|
|
|
CTEFreeMem(Options);
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
}
|
|
} else {
|
|
Options = (uchar *) NULL;
|
|
OptionSize = 0;
|
|
}
|
|
|
|
if (!OptInfo->ioi_hdrincl) {
|
|
if (!fIpsec) {
|
|
//
|
|
// The options have been taken care of. Now see if it's some
|
|
// sort of broadcast.
|
|
//
|
|
IPH->iph_verlen = (UCHAR) (IP_VERSION + ((OptionSize + (uint) sizeof(IPHeader)) >> 2));
|
|
IPH->iph_length = net_short(DataSize + OptionSize + sizeof(IPHeader));
|
|
}
|
|
}
|
|
|
|
// Call the firewall hooks
|
|
|
|
if (FirewallMode) {
|
|
|
|
IPRcvBuf *pRcvBuf, *tmpRcvBuf;
|
|
IPRcvBuf *pOutRcvBuf;
|
|
FIREWALL_CONTEXT_T FrCtx;
|
|
PNDIS_BUFFER pBuf;
|
|
Queue *CurrQ;
|
|
FIREWALL_HOOK *CurrHook;
|
|
uint SrcIFIndex = LOCAL_IF_INDEX;
|
|
uint DestIFIndex = DestIF->if_index;
|
|
uchar DestinationType = DType;
|
|
IPHeader *Temp;
|
|
KIRQL OldIrql;
|
|
BOOLEAN SkipHeader = TRUE;
|
|
PNDIS_PACKET_EXTENSION PktExt =
|
|
NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
PNDIS_TCP_IP_CHECKSUM_PACKET_INFO ChksumPktInfo =
|
|
(PNDIS_TCP_IP_CHECKSUM_PACKET_INFO)
|
|
&PktExt->NdisPacketInfo[TcpIpChecksumPacketInfo];
|
|
|
|
//
|
|
// Temp will be used to contain complete IPHeader (including
|
|
// options) When we pass the RcvBuf chain to Firewall hook, its
|
|
// assumed that whole IPHeader is contained in the first buffer
|
|
//
|
|
|
|
Temp = CTEAllocMemN(sizeof(IPHeader) + OptionSize, 'yiCT');
|
|
if (Temp == NULL) {
|
|
FREE_RESOURCES(IP_NO_RESOURCES);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
*Temp = *IPH;
|
|
if (Options) {
|
|
RtlCopyMemory((uchar *) (Temp + 1), Options, OptionSize);
|
|
}
|
|
|
|
// the context we pass to the firewall hook
|
|
|
|
FrCtx.Direction = IP_TRANSMIT;
|
|
FrCtx.NTE = NULL; //not required
|
|
|
|
FrCtx.LinkCtxt = NULL;
|
|
|
|
//
|
|
// Convert MDL chain to IPRcvBuf chain
|
|
// and pass it to the firewall hook
|
|
//
|
|
|
|
// attach the IP header
|
|
pRcvBuf = (IPRcvBuf *) (CTEAllocMemN(sizeof(IPRcvBuf), 'ziCT'));
|
|
if (!pRcvBuf) {
|
|
CTEFreeMem(Temp);
|
|
FREE_RESOURCES(IP_NO_RESOURCES);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
RtlZeroMemory(pRcvBuf, sizeof(IPRcvBuf));
|
|
pRcvBuf->ipr_buffer = (uchar *) Temp;
|
|
pRcvBuf->ipr_size = sizeof(IPHeader) + OptionSize;
|
|
pRcvBuf->ipr_owner = IPR_OWNER_IP;
|
|
if (ChksumPktInfo->Value) {
|
|
pRcvBuf->ipr_flags |= IPR_FLAG_CHECKSUM_OFFLOAD;
|
|
}
|
|
pInRcvBuf = pRcvBuf;
|
|
|
|
// convert the MDL chain of buffers to RcvBuf chain
|
|
// firewall hook understands RcvBuf chain only
|
|
|
|
for (pBuf = Buffer; pBuf != NULL; pBuf = pBuf->Next) {
|
|
IPRcvBuf *tmpRcvBuf;
|
|
|
|
if (fIpsec && SkipHeader) {
|
|
//
|
|
// The first buffer contains IPHeader.
|
|
// In ipsec re-inject case, pRcvBuf already
|
|
// points to this. So, skip the first buffer
|
|
// in Buffer chain.
|
|
//
|
|
SkipHeader = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (NdisBufferLength(pBuf) == 0)
|
|
continue;
|
|
tmpRcvBuf = (IPRcvBuf *) (CTEAllocMemN(sizeof(IPRcvBuf), '1iCT'));
|
|
if (!tmpRcvBuf) {
|
|
IPFreeBuff(pInRcvBuf);
|
|
CTEFreeMem(Temp);
|
|
FREE_RESOURCES(IP_NO_RESOURCES);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(tmpRcvBuf, sizeof(IPRcvBuf));
|
|
tmpRcvBuf->ipr_buffer = TcpipBufferVirtualAddress(pBuf,
|
|
NormalPagePriority);
|
|
|
|
if (tmpRcvBuf->ipr_buffer == NULL) {
|
|
CTEFreeMem(tmpRcvBuf);
|
|
IPFreeBuff(pInRcvBuf);
|
|
CTEFreeMem(Temp);
|
|
FREE_RESOURCES(IP_NO_RESOURCES);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
pRcvBuf->ipr_next = tmpRcvBuf;
|
|
tmpRcvBuf->ipr_size = NdisBufferLength(pBuf);
|
|
ASSERT(tmpRcvBuf->ipr_buffer != NULL);
|
|
ASSERT(tmpRcvBuf->ipr_size != 0);
|
|
tmpRcvBuf->ipr_owner = IPR_OWNER_IP;
|
|
if (ChksumPktInfo->Value) {
|
|
tmpRcvBuf->ipr_flags |= IPR_FLAG_CHECKSUM_OFFLOAD;
|
|
}
|
|
pRcvBuf = tmpRcvBuf;
|
|
}
|
|
|
|
pRcvBuf->ipr_next = NULL;
|
|
|
|
pOutRcvBuf = NULL;
|
|
|
|
pc = (PacketContext *) Packet->ProtocolReserved;
|
|
|
|
// scan the Queue from rear
|
|
// we scannned the Queue from front in rcv path
|
|
|
|
#if MILLEN
|
|
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
|
#else // MILLEN
|
|
OldIrql = KeRaiseIrqlToDpcLevel();
|
|
#endif // MILLEN
|
|
FirewallRef = RefFirewallQ(&FirewallQ);
|
|
CurrQ = QPREV(FirewallQ);
|
|
|
|
while (CurrQ != QEND(FirewallQ)) {
|
|
CurrHook = QSTRUCT(FIREWALL_HOOK, CurrQ, hook_q);
|
|
|
|
// pOutRcvBuf has to be NULL before we call the firewallhook
|
|
// pInRcvBuf contains the input buffer chain
|
|
pOutRcvBuf = NULL;
|
|
|
|
if (fIpsec) {
|
|
pInRcvBuf->ipr_flags |= IPR_FLAG_IPSEC_TRANSFORMED;
|
|
}
|
|
|
|
Action = (*CurrHook->hook_Ptr) (&pInRcvBuf,
|
|
SrcIFIndex,
|
|
&DestIFIndex,
|
|
&DestinationType,
|
|
&FrCtx,
|
|
sizeof(FrCtx),
|
|
&pOutRcvBuf);
|
|
|
|
if (Action == DROP) {
|
|
DerefFirewallQ(FirewallRef);
|
|
KeLowerIrql(OldIrql);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
|
|
if (pInRcvBuf != NULL) {
|
|
IPFreeBuff(pInRcvBuf);
|
|
}
|
|
if (pOutRcvBuf != NULL) {
|
|
IPFreeBuff(pOutRcvBuf);
|
|
}
|
|
CTEFreeMem(Temp);
|
|
FREE_RESOURCES(IP_DEST_HOST_UNREACHABLE);
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
} else {
|
|
ASSERT(Action == FORWARD);
|
|
if (pOutRcvBuf != NULL) {
|
|
// free the old buffer if non NULL
|
|
if (pInRcvBuf != NULL) {
|
|
IPFreeBuff(pInRcvBuf);
|
|
}
|
|
pInRcvBuf = pOutRcvBuf;
|
|
BufferChanged = 1;
|
|
}
|
|
} // Action == FORWARD
|
|
|
|
CurrQ = QPREV(CurrQ);
|
|
}
|
|
DerefFirewallQ(FirewallRef);
|
|
KeLowerIrql(OldIrql);
|
|
|
|
ASSERT(Action == FORWARD);
|
|
|
|
if (BufferChanged) {
|
|
// At least one of the firewall hook touched the buffer
|
|
|
|
PNDIS_BUFFER CurrentBuffer;
|
|
PNDIS_BUFFER tmpBuffer;
|
|
int Status;
|
|
uint hlen;
|
|
|
|
//
|
|
// It is assumed that if first buffer contained just ipheader
|
|
// before the hook is called, this holds after firewall also
|
|
//
|
|
|
|
ASSERT(pInRcvBuf->ipr_buffer != NULL);
|
|
RtlCopyMemory((uchar *) IPH, pInRcvBuf->ipr_buffer, sizeof(IPHeader));
|
|
|
|
//
|
|
// we recompute it later on anyway: so if firewall has
|
|
// recomputed make it 0
|
|
//
|
|
IPH->iph_xsum = 0;
|
|
|
|
//
|
|
// find the header length (in bytes) specified in IPHeader
|
|
//
|
|
hlen = (IPH->iph_verlen & 0xf) << 2;
|
|
ASSERT(pInRcvBuf->ipr_size == hlen);
|
|
OptionSize = hlen - sizeof(IPHeader);
|
|
if (Options) {
|
|
// we will allocate a new one anyway
|
|
CTEFreeMem(Options);
|
|
if (IPSecHandlerPtr) {
|
|
NdisFreeBuffer(OptBuffer);
|
|
OptBuffer = NULL;
|
|
}
|
|
}
|
|
if (OptionSize) {
|
|
Options = CTEAllocMemN(OptionSize, '2iCT');
|
|
if (Options == NULL) {
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
CTEFreeMem(Temp);
|
|
IPFreeBuff(pInRcvBuf);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
RtlCopyMemory(Options, pInRcvBuf->ipr_buffer + sizeof(IPHeader),
|
|
OptionSize);
|
|
|
|
if (IPSecHandlerPtr) {
|
|
NdisAllocateBuffer(&Status, &OptBuffer,
|
|
BufferPool, Options, OptionSize);
|
|
//
|
|
// If we couldn't get the needed options buffer
|
|
//
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
|
|
CTEFreeMem(Options);
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
CTEFreeMem(Temp);
|
|
IPFreeBuff(pInRcvBuf);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
}
|
|
} else {
|
|
Options = NULL;
|
|
}
|
|
|
|
// if packet touched compute the new length: DataSize
|
|
DataSize = 0;
|
|
tmpRcvBuf = pInRcvBuf->ipr_next; // First buffer contains
|
|
// header + options
|
|
|
|
while (tmpRcvBuf != NULL) {
|
|
ASSERT(tmpRcvBuf->ipr_buffer != NULL);
|
|
DataSize += tmpRcvBuf->ipr_size;
|
|
tmpRcvBuf = tmpRcvBuf->ipr_next;
|
|
}
|
|
|
|
// Convert the IPRcvBuf chain to MDL chain
|
|
// form the buffer chain again
|
|
|
|
tmpRcvBuf = pInRcvBuf->ipr_next; // first buffer contains
|
|
// just IP Header +
|
|
// options, if any
|
|
|
|
ASSERT(tmpRcvBuf->ipr_buffer != NULL);
|
|
ASSERT(tmpRcvBuf->ipr_size != 0);
|
|
NdisAllocateBuffer(&Status, &tmpBuffer, BufferPool,
|
|
tmpRcvBuf->ipr_buffer, tmpRcvBuf->ipr_size);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
|
|
if (Options) {
|
|
// option buffer.
|
|
CTEFreeMem(Options);
|
|
if (IPSecHandlerPtr) {
|
|
NdisFreeBuffer(OptBuffer);
|
|
}
|
|
}
|
|
IPFreeBuff(pInRcvBuf);
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
CTEFreeMem(Temp);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
tmpBuffer->Next = (PNDIS_BUFFER) NULL;
|
|
|
|
//
|
|
// save these 2 in the packet context: will be used in
|
|
// freeippacket/ipsendcomplete
|
|
//
|
|
pc->pc_firewall = Buffer;
|
|
pc->pc_firewall2 = pInRcvBuf;
|
|
|
|
// Convert the RcvBuf chain back to MDL chain
|
|
Buffer = tmpBuffer;
|
|
CurrentBuffer = Buffer;
|
|
|
|
for (tmpRcvBuf = tmpRcvBuf->ipr_next;
|
|
tmpRcvBuf != NULL;
|
|
tmpRcvBuf = tmpRcvBuf->ipr_next) {
|
|
|
|
ASSERT(tmpRcvBuf->ipr_buffer != NULL);
|
|
ASSERT(tmpRcvBuf->ipr_size != 0);
|
|
|
|
if (tmpRcvBuf->ipr_size == 0)
|
|
continue;
|
|
|
|
NdisAllocateBuffer(&Status, &tmpBuffer, BufferPool,
|
|
tmpRcvBuf->ipr_buffer, tmpRcvBuf->ipr_size);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
|
|
if (Options) {
|
|
// option buffer.
|
|
CTEFreeMem(Options);
|
|
if (IPSecHandlerPtr) {
|
|
NdisFreeBuffer(OptBuffer);
|
|
}
|
|
}
|
|
CTEFreeMem(Temp);
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
} else {
|
|
FreeIPBufferChain(Buffer);
|
|
}
|
|
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
CurrentBuffer->Next = tmpBuffer;
|
|
CurrentBuffer = tmpBuffer;
|
|
CurrentBuffer->Next = (PNDIS_BUFFER) NULL;
|
|
}
|
|
|
|
ASSERT(CurrentBuffer->Next == NULL);
|
|
|
|
if (DestinationType == DEST_INVALID) {
|
|
// recompute DestIF by doing a lookup again
|
|
Dest = IPH->iph_dest;
|
|
|
|
// Decide whether to do a strong or weak host lookup
|
|
ConstrainIF = GetIfConstraint(Dest, Source, OptInfo, fIpsec);
|
|
|
|
if (!ConstrainIF) {
|
|
//
|
|
// if this option is set, we want to send on the
|
|
// address we are bound to so don't recompute the
|
|
// Source address from IP header
|
|
//
|
|
Source = IPH->iph_src;
|
|
}
|
|
DType = GetAddrType(Dest);
|
|
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
// Make sure that pc_iflink is also initialized
|
|
pc->pc_iflink = NULL;
|
|
Link = NULL;
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(DestIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
RoutedRCE = NULL;
|
|
}
|
|
|
|
pvTmpBuffer = TcpipBufferVirtualAddress(Buffer,
|
|
NormalPagePriority);
|
|
|
|
if (pvTmpBuffer == NULL) {
|
|
if (Options) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
} else {
|
|
if (BufferChanged) {
|
|
FreeIPBufferChain(Buffer);
|
|
}
|
|
|
|
}
|
|
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
|
|
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
// Decide whether to do a strong or weak host lookup
|
|
ConstrainIF = GetIfConstraint(Dest, Source, OptInfo, fIpsec);
|
|
|
|
DestIF = LookupNextHopWithBuffer(Dest, Source, &FirstHop, &MTU,
|
|
PInfo->pi_protocol,
|
|
(uchar *) NdisBufferVirtualAddress(Buffer),
|
|
NdisBufferLength(Buffer), NULL, &Link,
|
|
Source, ConstrainIF);
|
|
|
|
pc->pc_if = DestIF;
|
|
RoutedIF = DestIF;
|
|
if (DestIF == NULL) {
|
|
// Lookup failed. Return an error.
|
|
if (Options) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
|
|
if (IPSecHandlerPtr && OptBuffer) {
|
|
NdisFreeBuffer(OptBuffer);
|
|
}
|
|
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
} else {
|
|
if (BufferChanged) {
|
|
FreeIPBufferChain(Buffer);
|
|
}
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_DEST_HOST_UNREACHABLE);
|
|
|
|
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
if (DestIF->if_flags & IF_FLAGS_P2MP) {
|
|
if (!Link) {
|
|
ASSERT(Link);
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
|
|
if (Options) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
|
|
if (IPSecHandlerPtr && OptBuffer) {
|
|
NdisFreeBuffer(OptBuffer);
|
|
}
|
|
|
|
FreeIPPacket(Packet, TRUE, IP_GENERAL_FAILURE);
|
|
if (HdrInclOptions) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
DerefIF(DestIF);
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
// NextHopCtxt = Link->link_NextHop;
|
|
ArpCtxt = Link->link_arpctxt;
|
|
pc->pc_iflink = Link;
|
|
}
|
|
}
|
|
|
|
// Finally, clear the checksum-request option in the packet,
|
|
// if it was set. The firewall-hook is responsible for ensuring
|
|
// that the checksum has now been computed correctly.
|
|
|
|
ChksumPktInfo->Value = 0;
|
|
} // BufferChanged
|
|
|
|
else { // Buffer not changed
|
|
|
|
if (pInRcvBuf != NULL) {
|
|
IPFreeBuff(pInRcvBuf);
|
|
}
|
|
}
|
|
CTEFreeMem(Temp);
|
|
}
|
|
|
|
if (RefPtrValid(&FilterRefPtr)) {
|
|
IPHeader *Temp;
|
|
PNDIS_BUFFER pDataBuffer;
|
|
PVOID pvBuf = NULL;
|
|
ULONG cbBuf = 0;
|
|
|
|
//
|
|
// See if we need to filter this packet. If we
|
|
// do, call the filter routine to see if it's
|
|
// OK to send it.
|
|
//
|
|
|
|
if (Options == NULL) {
|
|
Temp = IPH;
|
|
} else {
|
|
Temp = CTEAllocMemN(sizeof(IPHeader) + OptionSize, '3iCT');
|
|
if (Temp == NULL) {
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
} else {
|
|
if (BufferChanged) {
|
|
FreeIPBufferChain(Buffer);
|
|
}
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
|
|
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
CTEFreeMem(Options);
|
|
if (IPSecHandlerPtr && OptBuffer) {
|
|
NdisFreeBuffer(OptBuffer);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
*Temp = *IPH;
|
|
RtlCopyMemory((uchar *) (Temp + 1), Options, OptionSize);
|
|
}
|
|
if (DestIF->if_flags & IF_FLAGS_P2MP) {
|
|
LinkNextHop = Link->link_NextHop;
|
|
} else {
|
|
LinkNextHop = NULL_IP_ADDR;
|
|
}
|
|
|
|
//
|
|
// There are some cases where the first buffer in the chain
|
|
// of data does not contain any data. This includes ICMP,
|
|
// and iphdrinclude. If the first buffer is zero length,
|
|
// then we skip and give the second buffer. Really the
|
|
// filter api should take an MDL chain.
|
|
//
|
|
// Also, in the case of Ipsec Re-inject path, need to skip
|
|
// the first buffer as IPH is already pointing to it.
|
|
//
|
|
|
|
if ((NdisBufferLength(Buffer) == 0) || fIpsec) {
|
|
|
|
NdisGetNextBuffer(Buffer, &pDataBuffer);
|
|
|
|
if (pDataBuffer) {
|
|
cbBuf = NdisBufferLength(pDataBuffer);
|
|
pvBuf = TcpipBufferVirtualAddress(pDataBuffer,
|
|
NormalPagePriority);
|
|
}
|
|
} else {
|
|
pvBuf = TcpipBufferVirtualAddress(Buffer, NormalPagePriority);
|
|
cbBuf = NdisBufferLength(Buffer);
|
|
}
|
|
|
|
if (pvBuf == NULL) {
|
|
|
|
if (Options) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
|
|
if (IPSecHandlerPtr && OptBuffer) {
|
|
NdisFreeBuffer(OptBuffer);
|
|
}
|
|
|
|
// Need to chain buffers correctly to packet before calling
|
|
// FreeIPPacket.
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
} else {
|
|
if (BufferChanged) {
|
|
FreeIPBufferChain(Buffer);
|
|
}
|
|
}
|
|
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
|
|
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
FilterPtr = AcquireRefPtr(&FilterRefPtr);
|
|
Action = (*FilterPtr) (Temp, pvBuf, cbBuf, INVALID_IF_INDEX,
|
|
DestIF->if_index, NULL_IP_ADDR, LinkNextHop);
|
|
ReleaseRefPtr(&FilterRefPtr);
|
|
|
|
if (Options != NULL) {
|
|
CTEFreeMem(Temp);
|
|
}
|
|
|
|
if (Action != FORWARD) {
|
|
|
|
//
|
|
// If this is a bcast pkt, dont fail the send here since we might
|
|
// send this pkt over some other NTE; instead, let SendIPBCast
|
|
// deal with the Filtering for broadcast pkts.
|
|
//
|
|
// NOTE: We shd actually not call into FilterPtr here at
|
|
// all since we deal with it in BCast, but we do so in order to
|
|
// avoid a check above and hence
|
|
// take a double call hit in the bcast case.
|
|
//
|
|
if (DType != DEST_BCAST) {
|
|
|
|
if (Options) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
|
|
if (IPSecHandlerPtr && OptBuffer) {
|
|
NdisFreeBuffer(OptBuffer);
|
|
}
|
|
|
|
// Need to chain buffers correctly to packet before calling
|
|
// FreeIPPacket.
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
} else {
|
|
if (BufferChanged) {
|
|
FreeIPBufferChain(Buffer);
|
|
}
|
|
}
|
|
|
|
FreeIPPacket(Packet, TRUE, IP_DEST_HOST_UNREACHABLE);
|
|
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
#if FWD_DBG
|
|
else {
|
|
DbgPrint("IPTransmit: ignoring return %lx\n", Action);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
if (IPSecHandlerPtr) {
|
|
//
|
|
// See if IPSEC is enabled, see if it needs to do anything with this
|
|
// packet - we need to construct the full IP header in the first MDL
|
|
// before we call out to IPSEC.
|
|
//
|
|
IPSEC_ACTION Action;
|
|
ulong csum;
|
|
PacketContext *pc = (PacketContext *) Packet->ProtocolReserved;
|
|
|
|
//
|
|
// dont re-xsum if this came from IPSEC.
|
|
//
|
|
if (fIpsec) {
|
|
|
|
HeaderBuffer = Buffer;
|
|
if (OptBuffer) {
|
|
NdisFreeBuffer(OptBuffer);
|
|
}
|
|
OptBuffer = NULL;
|
|
|
|
} else {
|
|
IPH->iph_xsum = 0;
|
|
csum = xsum(IPH, sizeof(IPHeader));
|
|
|
|
//
|
|
// Link the header buffer to the options buffer before we
|
|
// indicate to IPSEC
|
|
//
|
|
if (OptBuffer) {
|
|
NDIS_BUFFER_LINKAGE(HeaderBuffer) = OptBuffer;
|
|
NDIS_BUFFER_LINKAGE(OptBuffer) = Buffer;
|
|
|
|
//
|
|
// update the xsum in the IP header
|
|
//
|
|
pc->pc_common.pc_flags |= PACKET_FLAG_OPTIONS;
|
|
csum += xsum(Options, OptionSize);
|
|
csum = (csum >> 16) + (csum & 0xffff);
|
|
csum += (csum >> 16);
|
|
} else {
|
|
NDIS_BUFFER_LINKAGE(HeaderBuffer) = Buffer;
|
|
}
|
|
|
|
IPH->iph_xsum = ~(ushort) csum;
|
|
}
|
|
|
|
if ((DataSize + OptionSize) < MTU) {
|
|
ipsecByteCount = MTU - (DataSize + OptionSize);
|
|
}
|
|
ipsecMTU = MTU;
|
|
|
|
//
|
|
// Pass the original dest address if source routing.
|
|
//
|
|
if (fSrcRoute) {
|
|
SrcRouteFirstHop = IPH->iph_dest;
|
|
IPH->iph_dest = SrcRouteOrigDest;
|
|
ipsecFlags |= IPSEC_FLAG_SSRR;
|
|
}
|
|
if (DestIF == &LoopInterface) {
|
|
ipsecFlags |= IPSEC_FLAG_LOOPBACK;
|
|
}
|
|
|
|
Action = (*IPSecHandlerPtr) ((PUCHAR) IPH,
|
|
(PVOID) HeaderBuffer,
|
|
DestIF,
|
|
Packet,
|
|
&ipsecByteCount,
|
|
&ipsecMTU,
|
|
(PVOID) & newBuf,
|
|
&ipsecFlags,
|
|
DType);
|
|
|
|
//
|
|
// Put back the dest address for source routing.
|
|
//
|
|
if (fSrcRoute) {
|
|
IPH->iph_dest = SrcRouteFirstHop;
|
|
}
|
|
|
|
if (Action != eFORWARD) {
|
|
IP_STATUS ipStatus;
|
|
|
|
//
|
|
// If this is a bcast pkt, dont fail the send here since we
|
|
// might send this pkt over some other NTE; instead, let
|
|
// SendIPBCast deal with the Filtering
|
|
// for broadcast pkts.
|
|
// Since Options are linked already, FreeIPPacket will do
|
|
// the right thing.
|
|
//
|
|
|
|
if (ipsecMTU) {
|
|
ipStatus = IP_PACKET_TOO_BIG;
|
|
FreeIPPacket(Packet, TRUE, ipStatus);
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPTransmit: MTU %lx, ipsecMTU %lx\n", MTU, ipsecMTU));
|
|
|
|
if (fIpsec) {
|
|
SendICMPIPSecErr(DestIF->if_nte->nte_addr,
|
|
(IPHeader *) saveIPH,
|
|
ICMP_DEST_UNREACH,
|
|
FRAG_NEEDED,
|
|
net_long(ipsecMTU + sizeof(IPHeader)));
|
|
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPTransmit: Sent ICMP frag_needed to %lx, "
|
|
"from src: %lx\n",
|
|
((IPHeader *) saveIPH)->iph_src,
|
|
DestIF->if_nte->nte_addr));
|
|
|
|
} else if (RCE) {
|
|
RCE->rce_newmtu = ipsecMTU;
|
|
}
|
|
} else {
|
|
if (Action == eABSORB && Protocol == PROTOCOL_ICMP) {
|
|
ipStatus = IP_NEGOTIATING_IPSEC;
|
|
} else {
|
|
ipStatus = IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
FreeIPPacket(Packet, TRUE, ipStatus);
|
|
}
|
|
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
} else {
|
|
ASSERT(RoutedRCE);
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
|
|
return ipStatus;
|
|
} else {
|
|
//
|
|
// Reset newmtu if we don't need IPSec. Otherwise if this RCE
|
|
// was applied IPSec previously but not now and link MTU gets
|
|
// changed, we won't be able to adjust MTU anymore in TCPSend.
|
|
//
|
|
if (!pc->pc_common.pc_IpsecCtx && RCE) {
|
|
RCE->rce_newmtu = 0;
|
|
}
|
|
|
|
//
|
|
// Use the new buffer chain - IPSEC will restore the old one
|
|
// on send complete
|
|
//
|
|
if (newBuf) {
|
|
NdisReinitializePacket(Packet);
|
|
NdisChainBufferAtBack(Packet, newBuf);
|
|
}
|
|
DataSize += ipsecByteCount;
|
|
}
|
|
}
|
|
//
|
|
// If this is a broadcast address, call our broadcast send handler
|
|
// to deal with this. The broadcast address handler will free the
|
|
// option buffer for us, if needed. Otherwise if it's a fragment, call
|
|
// the fragmentation handler.
|
|
//
|
|
if (DType == DEST_BCAST) {
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_IP && DBG_TX,
|
|
(DTEXT("IPTransmit: DEST_BCAST, source %x\n"), Source));
|
|
|
|
//Note the fact that this is bcast pkt,in the irp,
|
|
//used for cancelling the requests
|
|
//Irp can go away any time
|
|
|
|
SET_CANCEL_CONTEXT(Irp, BCAST_IF_CTXT);
|
|
|
|
if (IP_ADDR_EQUAL(Source, NULL_IP_ADDR)) {
|
|
SendStatus = SendDHCPPacket(Dest, Packet, Buffer, IPH, ArpCtxt);
|
|
|
|
if ((Link) && (SendStatus != IP_PENDING)) {
|
|
DerefLink(Link);
|
|
}
|
|
if (SendStatus != IP_PENDING && RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
return SendStatus;
|
|
} else {
|
|
SendStatus = SendIPBCast(NULL, Dest, Packet, IPH, Buffer, DataSize,
|
|
Options, OptionSize,
|
|
OptInfo->ioi_limitbcasts, NULL);
|
|
|
|
if ((Link) && (SendStatus != IP_PENDING)) {
|
|
DerefLink(Link);
|
|
}
|
|
if (SendStatus != IP_PENDING && RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
// In the case of header include, SendIPBcast will handle
|
|
// the cleanup.
|
|
|
|
return SendStatus;
|
|
}
|
|
}
|
|
// Not a broadcast. If it needs to be fragmented, call our
|
|
// fragmenter to do it. The fragmentation routine needs a
|
|
// BufferReference structure, so we'll need one of those first.
|
|
if ((DataSize + OptionSize) > MTU) {
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_IP && DBG_TX,
|
|
(DTEXT("IPTransmit: fragmentation needed.\n")));
|
|
|
|
BR = CTEAllocMemN(sizeof(BufferReference), '4iCT');
|
|
if (BR == (BufferReference *) NULL) {
|
|
// Couldn't get a BufferReference
|
|
|
|
//
|
|
// If options are already linked in, dont free them.
|
|
// FreeIPPacket will.
|
|
//
|
|
if (Options) {
|
|
if (!(pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS)) {
|
|
CTEFreeMem(Options);
|
|
} else if (newBuf) {
|
|
//
|
|
// Option has been copied by IPSEC (in the tunneling
|
|
// case); free the original option and clear the
|
|
// FLAG_OPTIONS so that FreeIPPacket will not try to
|
|
// free options again.
|
|
//
|
|
ASSERT(IPSecHandlerPtr);
|
|
NdisFreeBuffer(OptBuffer);
|
|
CTEFreeMem(Options);
|
|
pc->pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Just before caling ipsec we had chained
|
|
// the Buffer to the Packet. If this is not
|
|
// ipsec case chain it here before calling FreeIPPacket,
|
|
// which will free the firewall and hdrincl buffers.
|
|
//
|
|
|
|
if (!IPSecHandlerPtr) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
BR->br_buffer = Buffer;
|
|
BR->br_refcount = 0;
|
|
CTEInitLock(&BR->br_lock);
|
|
pc->pc_br = BR;
|
|
BR->br_userbuffer = pc->pc_hdrincl;
|
|
|
|
//
|
|
// setup so IPSEC headers appear just as first part of the data.
|
|
//
|
|
|
|
if (IPSecHandlerPtr) {
|
|
//
|
|
// If this is a reinjected packet from IPSEC, then, allocate
|
|
// another IP header here.
|
|
//
|
|
// This is to ensure that in fragmented packets, the send
|
|
// completes happen properly vis-a-vis IPSEC.
|
|
//
|
|
// When packet comes in it looks like this: [IP]->[ULP]
|
|
// We allocate another IP header [IP'] and nuke [IP] length
|
|
// to 0 so that it is ignored and [IP'] is used instead.
|
|
//
|
|
if (fIpsec) {
|
|
|
|
PNDIS_BUFFER UserBuffer;
|
|
int hdrLen;
|
|
|
|
UserBuffer = Buffer;
|
|
|
|
HeaderBuffer = GetIPHdrBuffer(&IPH);
|
|
if (HeaderBuffer == NULL) {
|
|
pc->pc_common.pc_flags &= ~PACKET_FLAG_IPHDR;
|
|
|
|
pc->pc_ipsec_flags |= (IPSEC_FLAG_FRAG_DONE |
|
|
IPSEC_FLAG_FLUSH);
|
|
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
CTEFreeMem(BR);
|
|
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
} else {
|
|
uchar *UserData;
|
|
|
|
// Got a buffer, copy the upper layer data forward.
|
|
UserData = TcpipBufferVirtualAddress(UserBuffer,
|
|
NormalPagePriority);
|
|
|
|
if (UserData == NULL) {
|
|
FreeIPHdrBuffer(HeaderBuffer);
|
|
|
|
pc->pc_common.pc_flags &= ~PACKET_FLAG_IPHDR;
|
|
|
|
pc->pc_ipsec_flags |= (IPSEC_FLAG_FRAG_DONE |
|
|
IPSEC_FLAG_FLUSH);
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
CTEFreeMem(BR);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
RtlCopyMemory(IPH, UserData, sizeof(IPHeader));
|
|
NdisAdjustBufferLength(HeaderBuffer, sizeof(IPHeader));
|
|
}
|
|
|
|
pc = (PacketContext *) Packet->ProtocolReserved;
|
|
pc->pc_common.pc_flags |= PACKET_FLAG_IPHDR;
|
|
|
|
NdisAdjustBufferLength(Buffer, 0);
|
|
|
|
//
|
|
// Handle options by using the same method as above:
|
|
// i.e. link our own options buffer; copy out the input
|
|
// options and nuke the input buffer.
|
|
//
|
|
hdrLen = (IPH->iph_verlen & (uchar) ~ IP_VER_FLAG) << 2;
|
|
|
|
if (hdrLen > sizeof(IPHeader)) {
|
|
PNDIS_BUFFER InOptionBuf;
|
|
ULONG InOptionSize;
|
|
PUCHAR InOptions;
|
|
|
|
InOptionBuf = NDIS_BUFFER_LINKAGE(UserBuffer);
|
|
ASSERT(InOptionBuf);
|
|
TcpipQueryBuffer(InOptionBuf, &InOptions,
|
|
(PUINT) &InOptionSize,
|
|
NormalPagePriority);
|
|
|
|
Options = CTEAllocMemN(InOptionSize, '5iCT');
|
|
if (Options == NULL || InOptions == NULL) {
|
|
|
|
pc->pc_common.pc_flags &= ~PACKET_FLAG_IPHDR;
|
|
|
|
if (Options) {
|
|
CTEFreeMem(Options);
|
|
}
|
|
pc->pc_ipsec_flags |= (IPSEC_FLAG_FRAG_DONE |
|
|
IPSEC_FLAG_FLUSH);
|
|
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
CTEFreeMem(BR);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
//
|
|
// Got a buffer, copy the options.
|
|
//
|
|
OptionSize = InOptionSize;
|
|
RtlCopyMemory(Options, InOptions, OptionSize);
|
|
NdisAdjustBufferLength(InOptionBuf, 0);
|
|
}
|
|
} else {
|
|
Buffer = NDIS_BUFFER_LINKAGE(HeaderBuffer);
|
|
//
|
|
// This is to ensure that options are freed appropriately.
|
|
// In the fragment code, the first fragment inherits the
|
|
// options of the entire packet; but these packets have
|
|
// no IPSEC context, hence cannot be freed appropriately.
|
|
// So, we allocate temporary options here and use these to
|
|
// represent the real options.
|
|
// These are freed when the first fragment is freed and
|
|
// the real options are freed here.
|
|
//
|
|
if (Options) {
|
|
PUCHAR tmpOptions;
|
|
|
|
if (newBuf) {
|
|
//
|
|
// if a new buffer chain was returned above by
|
|
// IPSEC, then it is most prob. a tunnel =>
|
|
// options were copied, hence get rid of ours.
|
|
//
|
|
NdisFreeBuffer(OptBuffer);
|
|
CTEFreeMem(Options);
|
|
Options = NULL;
|
|
OptionSize = 0;
|
|
} else {
|
|
Buffer = NDIS_BUFFER_LINKAGE(OptBuffer);
|
|
tmpOptions = CTEAllocMemN(OptionSize, '6iCT');
|
|
if (!tmpOptions) {
|
|
|
|
if (pc->pc_hdrincl) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
CTEFreeMem(BR);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
NdisFreeBuffer(OptBuffer);
|
|
RtlCopyMemory(tmpOptions, Options, OptionSize);
|
|
CTEFreeMem(Options);
|
|
Options = tmpOptions;
|
|
}
|
|
pc->pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS;
|
|
}
|
|
}
|
|
|
|
NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL;
|
|
NdisReinitializePacket(Packet);
|
|
NdisChainBufferAtBack(Packet, HeaderBuffer);
|
|
IPH->iph_xsum = 0;
|
|
}
|
|
|
|
// Mark Irp with the destif
|
|
// Once link level call is made,
|
|
// Irp can go away any time
|
|
|
|
SET_CANCEL_CONTEXT(Irp, DestIF);
|
|
|
|
SendStatus = IPFragment(DestIF, MTU, FirstHop, Packet, IPH,
|
|
Buffer, DataSize, Options, OptionSize,
|
|
NULL, FALSE, ArpCtxt);
|
|
|
|
//
|
|
// If IPFragment returns IP_PACKET_TOO_BIG (meaning DF bit is set)
|
|
// and we are in the IPSEC reinject path, send an ICMP error
|
|
// message including the MTU back so the source host can perform
|
|
// Path MTU discovery.
|
|
//
|
|
if ((SendStatus == IP_PACKET_TOO_BIG) && fIpsec) {
|
|
|
|
ASSERT(IPSecHandlerPtr);
|
|
SendICMPIPSecErr(DestIF->if_nte->nte_addr,
|
|
(IPHeader *) saveIPH,
|
|
ICMP_DEST_UNREACH,
|
|
FRAG_NEEDED,
|
|
net_long(MTU + sizeof(IPHeader)));
|
|
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPTransmit: Sent ICMP frag_needed to %lx, "
|
|
"from src: %lx\n",
|
|
((IPHeader *) saveIPH)->iph_src,
|
|
DestIF->if_nte->nte_addr));
|
|
}
|
|
|
|
if ((Link) && (SendStatus != IP_PENDING)) {
|
|
DerefLink(Link);
|
|
}
|
|
if (SendStatus != IP_PENDING && RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
// If this is a headerinclude packet and status != pending, IPFragment takes
|
|
// care of clean up.
|
|
|
|
|
|
return SendStatus;
|
|
}
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_IP && DBG_TX,
|
|
(DTEXT("IPTransmit: Calling SendIPPacket...\n")));
|
|
|
|
//
|
|
// If we've reached here, we aren't sending a broadcast and don't
|
|
// need to fragment anything. Presumably we got here because we have
|
|
// options. In any case, we're ready now.
|
|
//
|
|
|
|
if (IPH->iph_ttl == 0) {
|
|
NdisSetPacketFlags(Packet, NDIS_FLAGS_LOOPBACK_ONLY);
|
|
}
|
|
|
|
// Mark Irp with outgoing interface
|
|
// Once link level call is made,
|
|
// Irp can go away any time
|
|
|
|
SET_CANCEL_CONTEXT(Irp, DestIF);
|
|
|
|
// Do not free the packet in SendIPPacket, as we may need
|
|
// to chain the buffer in case of IP_NO_RESOURCES
|
|
|
|
SendStatus = SendIPPacket(DestIF, FirstHop, Packet, Buffer, IPH,
|
|
Options, OptionSize, (BOOLEAN) (IPSecHandlerPtr != NULL),
|
|
ArpCtxt, TRUE);
|
|
|
|
if ((Link) && (SendStatus != IP_PENDING)) {
|
|
DerefLink(Link);
|
|
}
|
|
if (SendStatus != IP_PENDING && RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
if (RoutedRCE) {
|
|
CTEInterlockedDecrementLong(&RoutedRCE->rce_usecnt);
|
|
}
|
|
if (SendStatus != IP_PENDING) {
|
|
|
|
if (SendStatus == IP_NO_RESOURCES) {
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
}
|
|
|
|
FreeIPPacket(Packet, TRUE, SendStatus);
|
|
}
|
|
|
|
return SendStatus;
|
|
}
|
|
|
|
|