mirror of https://github.com/lianthony/NT4.0
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.
1949 lines
61 KiB
1949 lines
61 KiB
/********************************************************************/
|
|
/** Microsoft LAN Manager **/
|
|
/** Copyright(c) Microsoft Corp., 1990-1992 **/
|
|
/********************************************************************/
|
|
/* :ts=4 */
|
|
|
|
//*** ipxmit.c - IP transmit routines.
|
|
//
|
|
// This module contains all transmit related IP routines.
|
|
//
|
|
|
|
#include "oscfg.h"
|
|
#include "cxport.h"
|
|
#include "ndis.h"
|
|
#include "ip.h"
|
|
#include "ipdef.h"
|
|
#include "ipinit.h"
|
|
#include "info.h"
|
|
#include "iproute.h"
|
|
#include "iprtdef.h"
|
|
#include "ipfilter.h"
|
|
|
|
typedef struct NdisResEntry {
|
|
struct NdisResEntry *nre_next;
|
|
NDIS_HANDLE nre_handle;
|
|
uchar *nre_buffer;
|
|
} NdisResEntry;
|
|
|
|
extern BufferReference *GetBufferReference(void);
|
|
|
|
extern NetTableEntry *NetTableList; // Pointer to the net table.
|
|
extern NetTableEntry *LoopNTE; // Pointer to loopback NTE.
|
|
extern NetTableEntry *DHCPNTE; // Pointer to NTE currently being
|
|
// DHCP'd.
|
|
|
|
extern ulong TimeStamp; // Starting timestamp.
|
|
extern ulong TSFlag; // Mask to use on this.
|
|
extern uint NumNTE;
|
|
|
|
//* Global variables for buffers and packets.
|
|
DEFINE_LOCK_STRUCTURE(HeaderLock)
|
|
#ifdef NT
|
|
SLIST_HEADER PacketList;
|
|
SLIST_HEADER HdrBufList;
|
|
#else
|
|
PNDIS_PACKET PacketList;
|
|
PNDIS_BUFFER HdrBufList = NULL;
|
|
#endif
|
|
|
|
NdisResEntry *PacketPoolList = NULL;
|
|
NdisResEntry *HdrPoolList = NULL;
|
|
|
|
uint CurrentPacketCount = 0;
|
|
uint MaxPacketCount = 0xfffffff;
|
|
|
|
uint CurrentHdrBufCount = 0;
|
|
uint MaxHdrBufCount = 0xffffffff;
|
|
|
|
NDIS_HANDLE BufferPool;
|
|
|
|
#define HDR_BUF_GROW_COUNT 16
|
|
#define PACKET_GROW_COUNT 16
|
|
|
|
//* Global IP ID.
|
|
ulong IPID;
|
|
|
|
//** FreeIPHdrBuffer - Free a buffer back to the pool.
|
|
//
|
|
// Input: Buffer - Hdr buffer to be freed.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
FreeIPHdrBuffer(PNDIS_BUFFER Buffer)
|
|
{
|
|
|
|
#ifdef VXD
|
|
NDIS_BUFFER_LINKAGE(Buffer) = HdrBufList;
|
|
HdrBufList = Buffer;
|
|
#else
|
|
|
|
ExInterlockedPushEntrySList(
|
|
&HdrBufList,
|
|
STRUCT_OF(SINGLE_LIST_ENTRY, &(Buffer->Next), Next),
|
|
&HeaderLock
|
|
);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
//** 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;
|
|
}
|
|
}
|
|
|
|
//* 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.
|
|
//
|
|
// 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)
|
|
{
|
|
PNDIS_BUFFER NextBuffer, OldBuffer;
|
|
PacketContext *pc = (PacketContext *)Packet->ProtocolReserved;
|
|
|
|
|
|
// BUGBUG - Get NDIS fixed to make this portable.
|
|
#ifdef VXD
|
|
NextBuffer = Packet->Private.Head;
|
|
#else // VXD
|
|
NdisQueryPacket(Packet, NULL, NULL, &NextBuffer, NULL);
|
|
#endif // VXD
|
|
|
|
// 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))) {
|
|
CTEAssert(pc->pc_common.pc_flags == 0);
|
|
|
|
NdisReinitializePacket(Packet);
|
|
|
|
#ifdef VXD
|
|
pc->pc_common.pc_link = PacketList;
|
|
PacketList = Packet;
|
|
#else
|
|
ExInterlockedPushEntrySList(
|
|
&PacketList,
|
|
STRUCT_OF(SINGLE_LIST_ENTRY, &(pc->pc_common.pc_link), Next),
|
|
&HeaderLock
|
|
);
|
|
#endif
|
|
|
|
return NextBuffer;
|
|
}
|
|
|
|
pc->pc_common.pc_flags &= ~PACKET_FLAG_IPHDR;
|
|
|
|
OldBuffer = NextBuffer;
|
|
CTEAssert(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;
|
|
CTEAssert(OptBuffer != NULL);
|
|
|
|
NdisGetNextBuffer(OptBuffer,&NextBuffer);
|
|
|
|
CTEAssert(NextBuffer != NULL);
|
|
|
|
NdisQueryBuffer(OptBuffer, &Options, &OptSize);
|
|
// 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))
|
|
CTEFreeMem(Options);
|
|
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);
|
|
NdisReinitializePacket(Packet);
|
|
#ifdef _PNP_POWER
|
|
pc->pc_if = NULL;
|
|
#endif
|
|
|
|
#ifdef VXD
|
|
pc->pc_common.pc_link = PacketList;
|
|
PacketList = Packet;
|
|
#else
|
|
ExInterlockedPushEntrySList(
|
|
&PacketList,
|
|
STRUCT_OF(SINGLE_LIST_ENTRY, &(pc->pc_common.pc_link), Next),
|
|
&HeaderLock
|
|
);
|
|
#endif
|
|
}
|
|
|
|
return NextBuffer;
|
|
}
|
|
|
|
//** GrowIPPacketList - Grow the number of packets in our list.
|
|
//
|
|
// Called when we need to grow the number of packets in our list. We assume
|
|
// this routine is called with the HeaderLock held. We check to see if
|
|
// we've reached our limit on the number of packets, and if we haven't we'll
|
|
// grow the free list.
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: Pointer to newly allocated packet, or NULL if this faild.
|
|
//
|
|
PNDIS_PACKET
|
|
GrowIPPacketList(void)
|
|
{
|
|
NdisResEntry *NewEntry;
|
|
NDIS_STATUS Status;
|
|
PNDIS_PACKET Packet, ReturnPacket;
|
|
uint i;
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&HeaderLock, &Handle);
|
|
|
|
if (CurrentPacketCount >= MaxPacketCount)
|
|
goto failure;
|
|
|
|
// First, allocate a tracking structure.
|
|
NewEntry = CTEAllocMem(sizeof(NdisResEntry));
|
|
if (NewEntry == NULL)
|
|
goto failure;
|
|
|
|
// Got a tracking structure. Now allocate a packet pool.
|
|
NdisAllocatePacketPool(&Status, &NewEntry->nre_handle, PACKET_GROW_COUNT,
|
|
sizeof(PacketContext));
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
CTEFreeMem(NewEntry);
|
|
goto failure;
|
|
}
|
|
|
|
// We've allocated the pool. Now initialize the packets, and link them
|
|
// on the free list.
|
|
ReturnPacket = NULL;
|
|
|
|
// Link the new NDIS resource tracker entry onto the list.
|
|
NewEntry->nre_next = PacketPoolList;
|
|
PacketPoolList = NewEntry;
|
|
CurrentPacketCount += PACKET_GROW_COUNT;
|
|
CTEFreeLock(&HeaderLock, Handle);
|
|
|
|
for (i = 0; i < PACKET_GROW_COUNT; i++) {
|
|
PacketContext *PC;
|
|
|
|
NdisAllocatePacket(&Status, &Packet, NewEntry->nre_handle);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
CTEAssert(FALSE);
|
|
break;
|
|
}
|
|
|
|
CTEMemSet(Packet->ProtocolReserved, 0, sizeof(PacketContext));
|
|
PC = (PacketContext *)Packet->ProtocolReserved;
|
|
PC->pc_common.pc_owner = PACKET_OWNER_IP;
|
|
if (i != 0) {
|
|
(void)FreeIPPacket(Packet);
|
|
} else
|
|
ReturnPacket = Packet;
|
|
|
|
}
|
|
|
|
// We've put all but the first one on the list. Return the first one.
|
|
return ReturnPacket;
|
|
|
|
failure:
|
|
CTEFreeLock(&HeaderLock, Handle);
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
//** GrowHdrBufList - Grow the our IP header buffer list.
|
|
//
|
|
// Called when we need to grow our header buffer list. We allocate a tracking
|
|
// structure, a buffer pool and a bunch of buffers. Put them all together
|
|
// and link them on the list.
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: Pointer to newly header buffer, or NULL if this faild.
|
|
//
|
|
PNDIS_BUFFER
|
|
GrowHdrBufList(void)
|
|
{
|
|
NdisResEntry *NewEntry;
|
|
NDIS_STATUS Status;
|
|
PNDIS_BUFFER Buffer, ReturnBuffer;
|
|
uchar *Hdr;
|
|
uint i;
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&HeaderLock, &Handle);
|
|
|
|
// Make sure we can grow.
|
|
if (CurrentHdrBufCount >= MaxHdrBufCount)
|
|
goto failure;
|
|
|
|
// First, allocate a tracking structure.
|
|
NewEntry = CTEAllocMem(sizeof(NdisResEntry));
|
|
if (NewEntry == NULL)
|
|
goto failure;
|
|
|
|
// Got a tracking structure. Now allocate a buffer pool.
|
|
NdisAllocateBufferPool(&Status, &NewEntry->nre_handle, HDR_BUF_GROW_COUNT);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
CTEFreeMem(NewEntry);
|
|
goto failure;
|
|
}
|
|
|
|
// We've allocated the pool. Now allocate memory for the buffers.
|
|
Hdr = CTEAllocMem(sizeof(IPHeader) * HDR_BUF_GROW_COUNT);
|
|
if (Hdr == NULL) {
|
|
// Couldn't get memory for the headers.
|
|
NdisFreeBufferPool(NewEntry->nre_handle);
|
|
CTEFreeMem(NewEntry);
|
|
goto failure;
|
|
}
|
|
|
|
NewEntry->nre_buffer = Hdr;
|
|
|
|
NewEntry->nre_next = HdrPoolList;
|
|
HdrPoolList = NewEntry;
|
|
ReturnBuffer = NULL;
|
|
CurrentHdrBufCount += HDR_BUF_GROW_COUNT;
|
|
CTEFreeLock(&HeaderLock, Handle);
|
|
|
|
for (i = 0; i < HDR_BUF_GROW_COUNT; i++) {
|
|
|
|
NdisAllocateBuffer(&Status, &Buffer, NewEntry->nre_handle,
|
|
Hdr, sizeof(IPHeader));
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
CTEAssert(FALSE);
|
|
break;
|
|
}
|
|
if (i != 0) {
|
|
FreeIPHdrBuffer(Buffer);
|
|
} else
|
|
ReturnBuffer = Buffer;
|
|
|
|
Hdr += sizeof(IPHeader);
|
|
|
|
}
|
|
|
|
// Update the count for any we didn't actually allocate.
|
|
CTEInterlockedAddUlong(&CurrentHdrBufCount, i - HDR_BUF_GROW_COUNT,
|
|
&HeaderLock);
|
|
|
|
// We've put all but the first one on the list. Return the first one.
|
|
return ReturnBuffer;
|
|
|
|
failure:
|
|
CTEFreeLock(&HeaderLock, Handle);
|
|
return 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;
|
|
|
|
|
|
#ifdef VXD
|
|
Packet = PacketList;
|
|
if (Packet != (PNDIS_PACKET)NULL) {
|
|
PacketContext *PC;
|
|
|
|
PC = (PacketContext *)Packet->ProtocolReserved;
|
|
PacketList = PC->pc_common.pc_link;
|
|
return Packet;
|
|
#else
|
|
PSINGLE_LIST_ENTRY Link;
|
|
PacketContext *PC;
|
|
struct PCCommon *Common;
|
|
|
|
Link = ExInterlockedPopEntrySList(
|
|
&PacketList,
|
|
&HeaderLock
|
|
);
|
|
if (Link != NULL) {
|
|
Common = STRUCT_OF(struct PCCommon, Link, pc_link);
|
|
PC = STRUCT_OF(PacketContext, Common, pc_common);
|
|
Packet = STRUCT_OF(NDIS_PACKET, PC, ProtocolReserved);
|
|
|
|
return Packet;
|
|
#endif
|
|
|
|
|
|
} else {
|
|
// Couldn't get a packet. Try to grow the list.
|
|
Packet = GrowIPPacketList();
|
|
}
|
|
|
|
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.
|
|
//
|
|
PNDIS_BUFFER
|
|
GetIPHdrBuffer(void)
|
|
{
|
|
PNDIS_BUFFER Buffer;
|
|
|
|
#ifdef VXD
|
|
Buffer = HdrBufList;
|
|
if (Buffer != NULL) {
|
|
|
|
HdrBufList = NDIS_BUFFER_LINKAGE(Buffer);
|
|
NDIS_BUFFER_LINKAGE(Buffer) = NULL;
|
|
#else
|
|
PSINGLE_LIST_ENTRY BufferLink;
|
|
|
|
BufferLink = ExInterlockedPopEntrySList(
|
|
&HdrBufList,
|
|
&HeaderLock
|
|
);
|
|
if (BufferLink != NULL) {
|
|
Buffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next);
|
|
NDIS_BUFFER_LINKAGE(Buffer) = NULL;
|
|
|
|
return Buffer;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
Buffer = GrowHdrBufList();
|
|
}
|
|
|
|
return Buffer;
|
|
|
|
}
|
|
|
|
|
|
//** 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;
|
|
|
|
|
|
Packet = GetIPPacket();
|
|
if (Packet != NULL) {
|
|
Buffer = GetIPHdrBuffer();
|
|
if (Buffer != NULL) {
|
|
PacketContext *PC = (PacketContext *)Packet->ProtocolReserved;
|
|
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
*PacketPtr = Packet;
|
|
PC->pc_common.pc_flags |= PACKET_FLAG_IPHDR;
|
|
return (IPHeader *)NdisBufferVirtualAddress(Buffer);
|
|
|
|
} else
|
|
FreeIPPacket(Packet);
|
|
}
|
|
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;
|
|
|
|
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
|
|
IPSendComplete(void *Context, PNDIS_PACKET Packet, NDIS_STATUS Status)
|
|
{
|
|
NetTableEntry *NTE = (NetTableEntry *)Context;
|
|
PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved;
|
|
PNDIS_BUFFER Buffer;
|
|
void (*xmitdone)(void *, PNDIS_BUFFER); // Pointer to xmit done routine.
|
|
void *UContext; // Upper layer context.
|
|
BufferReference *BufRef; // Buffer reference, if any.
|
|
#ifdef _PNP_POWER
|
|
Interface *IF; // The interface on which this
|
|
// completed.
|
|
#endif
|
|
|
|
xmitdone = PContext->pc_pi->pi_xmitdone; // Copy useful information from packet.
|
|
UContext = PContext->pc_context;
|
|
BufRef = PContext->pc_br;
|
|
#ifdef _PNP_POWER
|
|
IF = PContext->pc_if;
|
|
#endif
|
|
|
|
Buffer = FreeIPPacket(Packet);
|
|
if (BufRef == (BufferReference *)NULL) {
|
|
#ifdef DEBUG
|
|
if (!Buffer)
|
|
DEBUGCHK;
|
|
#endif
|
|
(*xmitdone)(UContext, Buffer);
|
|
} else {
|
|
if (!ReferenceBuffer(BufRef, -1)) {
|
|
Buffer = BufRef->br_buffer;
|
|
#ifdef DEBUG
|
|
if (!Buffer)
|
|
DEBUGCHK;
|
|
#endif
|
|
CTEFreeMem(BufRef);
|
|
(*xmitdone)(UContext, Buffer);
|
|
} else {
|
|
#ifdef _PNP_POWER
|
|
// We're not done with the send yet, so NULL the IF to
|
|
// prevent dereferencing it.
|
|
IF = NULL;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef _PNP_POWER
|
|
// We're done with the packet now, we may need to dereference
|
|
// the interface.
|
|
if (IF == NULL) {
|
|
return;
|
|
} else {
|
|
DerefIF(IF);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
#ifndef NT
|
|
|
|
//** xsum - Checksum a flat buffer.
|
|
//
|
|
// This is the lowest level checksum routine. It returns the uncomplemented
|
|
// checksum of a flat buffer.
|
|
//
|
|
// Entry: Buffer - Buffer to be checksummed.
|
|
// Size - Size in bytes of Buffer.
|
|
//
|
|
// Returns: The uncomplemented checksum of buffer.
|
|
//
|
|
ushort
|
|
xsum(void *Buffer, int Size)
|
|
{
|
|
ushort UNALIGNED *Buffer1 = (ushort UNALIGNED *)Buffer; // Buffer expressed as shorts.
|
|
ulong csum = 0;
|
|
|
|
while (Size > 1) {
|
|
csum += *Buffer1++;
|
|
Size -= sizeof(ushort);
|
|
}
|
|
|
|
if (Size)
|
|
csum += *(uchar *)Buffer1; // For odd buffers, add in last byte.
|
|
|
|
csum = (csum >> 16) + (csum & 0xffff);
|
|
csum += (csum >> 16);
|
|
return (ushort)csum;
|
|
}
|
|
|
|
#endif // NT
|
|
|
|
|
|
//** 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)
|
|
{
|
|
ulong csum;
|
|
NDIS_STATUS Status;
|
|
|
|
|
|
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);
|
|
FreeIPPacket(Packet);
|
|
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);
|
|
|
|
Status = (*(IF->if_xmit))(IF->if_lcontext, Packet, FirstHop, NULL);
|
|
|
|
if (Status == NDIS_STATUS_PENDING)
|
|
return IP_PENDING;
|
|
|
|
// Status wasn't pending. Free the packet, and map the status.
|
|
FreeIPPacket(Packet);
|
|
if (Status == NDIS_STATUS_SUCCESS)
|
|
return IP_SUCCESS;
|
|
else
|
|
return IP_HW_ERROR;
|
|
}
|
|
|
|
//* 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)
|
|
{
|
|
if (DHCPNTE != NULL && ((DHCPNTE->nte_flags & (NTE_VALID | NTE_ACTIVE))
|
|
== NTE_ACTIVE)) {
|
|
// The DHCP NTE is currently invalid, and active. Send on that
|
|
// interface.
|
|
return SendIPPacket(DHCPNTE->nte_if, Dest, Packet, Buffer, IPH, NULL,
|
|
0);
|
|
}
|
|
|
|
// Didn't find an invalid NTE! Free the resources, and return the failure.
|
|
FreeIPPacket(Packet);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Macros needed by IpCopyBuffer
|
|
//
|
|
#ifdef VXD
|
|
|
|
#define NdisBufferLength(Buffer) (Buffer)->Length
|
|
#define NdisBufferVirtualAddress(Buffer) (Buffer)->VirtualAddress
|
|
|
|
#else // VXD
|
|
#ifdef NT
|
|
|
|
#define NdisBufferLength(Buffer) MmGetMdlByteCount(Buffer)
|
|
#define NdisBufferVirtualAddress(Buffer) MmGetSystemAddressForMdl(Buffer)
|
|
|
|
#else // NT
|
|
|
|
#error Need appropriate NDIS macros here
|
|
|
|
#endif NT
|
|
#endif // VXD
|
|
//* 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; // 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.
|
|
|
|
// 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
|
|
);
|
|
NdisAllocateBuffer(&NewStatus, NewBuffer, BufferPool,
|
|
((uchar *)NdisBufferVirtualAddress(CurrentBuffer)) + 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).
|
|
//
|
|
// 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)
|
|
{
|
|
BufferReference *BR; // Buffer reference we'll use.
|
|
PacketContext *PContext = (PacketContext *)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.
|
|
|
|
MaxSend = (MTU - OptionSize) & ~7; // Determine max send size.
|
|
|
|
#ifdef DEBUG
|
|
if (MaxSend >= DataSize)
|
|
DEBUGCHK;
|
|
#endif
|
|
|
|
BR = PContext->pc_br; // Get the buffer reference we'll need.
|
|
|
|
#ifdef DEBUG
|
|
if (!BR)
|
|
DEBUGCHK;
|
|
#endif
|
|
|
|
if (Header->iph_offset & IP_DF_FLAG) { // Don't fragment flag set.
|
|
// Error out.
|
|
FreeIPPacket(Packet);
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
if (SentCount == (int *)NULL) // No sent count is to be
|
|
// returned.
|
|
CTEFreeMem(BR);
|
|
IPSInfo.ipsi_fragfails++;
|
|
return IP_PACKET_TOO_BIG;
|
|
}
|
|
|
|
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.
|
|
CTEMemSet(FragmentOptions, IP_OPT_EOL, MAX_OPT_SIZE);
|
|
while ((TempOptions[IP_OPT_TYPE] != IP_OPT_EOL) &&
|
|
(TempOptions < EndOptions)) {
|
|
if (TempOptions[IP_OPT_TYPE] & IP_OPT_COPIED) { // This option needs
|
|
// to be copied.
|
|
uint TempOptSize;
|
|
|
|
TempOptSize = TempOptions[IP_OPT_LENGTH];
|
|
CTEMemCopy(&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);
|
|
}
|
|
|
|
PContext->pc_common.pc_flags |= PACKET_FLAG_IPBUF;
|
|
|
|
// Now, while we can build maximum size fragments, do so.
|
|
do {
|
|
if ((CurrentHeader = GetIPHeader(&CurrentPacket)) == (IPHeader *)NULL) {
|
|
// Couldn't get a buffer. Break out, since no point in sending others.
|
|
Error = TRUE;
|
|
break;
|
|
}
|
|
|
|
// Copy the buffer into a new one, if we can.
|
|
CurrentBuffer = IPCopyBuffer(Buffer, Offset, MaxSend);
|
|
if (CurrentBuffer == NULL) { // No buffer, free resources and
|
|
// break.
|
|
FreeIPPacket(CurrentPacket);
|
|
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;
|
|
*CurrentContext = *PContext;
|
|
*CurrentHeader = *Header;
|
|
CurrentContext->pc_common.pc_flags &= ~PACKET_FLAG_FW;
|
|
CurrentHeader->iph_verlen = IP_VERSION +
|
|
((OptionSize + 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;
|
|
|
|
SendStatus = SendIPPacket(DestIF, FirstHop, CurrentPacket,
|
|
CurrentBuffer, CurrentHeader, Options, OptionSize);
|
|
if (SendStatus == IP_PENDING)
|
|
PendingSends++;
|
|
|
|
IPSInfo.ipsi_fragcreates++;
|
|
Offset += MaxSend;
|
|
DataSize -= MaxSend;
|
|
|
|
// If we have any fragmented options, set up to use them next time.
|
|
if (FragOptSize) {
|
|
Options = CTEAllocMem(OptionSize = FragOptSize);
|
|
if (Options == (uchar *)NULL) { // Can't get an option
|
|
// buffer.
|
|
Error = TRUE;
|
|
break;
|
|
}
|
|
CTEMemCopy(Options, FragmentOptions, OptionSize);
|
|
} else {
|
|
Options = (uchar *)NULL;
|
|
OptionSize = 0;
|
|
}
|
|
} while (DataSize > MaxSend);
|
|
|
|
// 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
|
|
// and stop.
|
|
if (Options)
|
|
CTEFreeMem(Options); // Free the option buffer
|
|
FreeIPPacket(Packet);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
} else { // Everything's OK, send it.
|
|
Header->iph_verlen = IP_VERSION + ((OptionSize + 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);
|
|
SendStatus = SendIPPacket(DestIF, FirstHop, Packet,
|
|
CurrentBuffer, Header, Options, OptionSize);
|
|
if (SendStatus == IP_PENDING)
|
|
PendingSends++;
|
|
IPSInfo.ipsi_fragcreates++;
|
|
IPSInfo.ipsi_fragoks++;
|
|
}
|
|
} else { // We had some sort of error.
|
|
// Free resources.
|
|
FreeIPPacket(Packet);
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
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)) {
|
|
CTEFreeMem(BR);
|
|
return IP_SUCCESS;
|
|
}
|
|
return IP_PENDING;
|
|
} else
|
|
*SentCount += PendingSends;
|
|
|
|
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) // Not maxed.
|
|
LocalOption[IP_TS_OVFLAGS] += IP_TS_INC; // So increment it.
|
|
else
|
|
return LocalIndex; // Would have overflowed.
|
|
}
|
|
}
|
|
}
|
|
return MAX_OPT_SIZE;
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
IPAddr bsl_addr;
|
|
Interface *bsl_if;
|
|
uint bsl_mtu;
|
|
ushort bsl_flags;
|
|
} BCastSendList;
|
|
|
|
//** 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;
|
|
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;
|
|
FORWARD_ACTION Action;
|
|
|
|
|
|
SendList = CTEAllocMem(sizeof(BCastSendList) * NumNTE);
|
|
|
|
if (SendList == NULL) {
|
|
return(IP_NO_RESOURCES);
|
|
}
|
|
|
|
CTEMemSet(SendList, 0, sizeof(BCastSendList) * NumNTE);
|
|
|
|
// If SendOnSource, initalize SrcAddr and SrcIF to be non-matching.
|
|
// Otherwise initialize them to the masked source address and source
|
|
// interface.
|
|
if (SendOnSource) {
|
|
SrcAddr = NULL_IP_ADDR;
|
|
SrcIF = NULL;
|
|
} else {
|
|
CTEAssert(SrcNTE != NULL);
|
|
SrcAddr = (SrcNTE->nte_addr & SrcNTE->nte_mask);
|
|
SrcIF = SrcNTE->nte_if;
|
|
}
|
|
|
|
|
|
NeedFragment = FALSE;
|
|
// Loop through the NTE table, making a list of interfaces and
|
|
// corresponding addresses to send on.
|
|
for (NetsToSend = 0, TempNTE = NetTableList; 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;
|
|
|
|
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 (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;
|
|
if ((DataSize + OptionSize) > SendList[j].bsl_mtu)
|
|
NeedFragment = TRUE;
|
|
NetsToSend++;
|
|
}
|
|
|
|
}
|
|
|
|
if (NetsToSend == 0) {
|
|
CTEFreeMem(SendList);
|
|
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 = CTEAllocMem(sizeof(BufferReference))) ==
|
|
(BufferReference *)NULL) {
|
|
CTEFreeMem(SendList);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
BR->br_buffer = Buffer;
|
|
BR->br_refcount = 0;
|
|
CTEInitLock(&BR->br_lock);
|
|
PContext->pc_br = BR;
|
|
} 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 (ForwardFilterPtr != NULL) {
|
|
if (Options == NULL) {
|
|
#if FWD_DBG
|
|
DbgPrint("Options==NULL\n");
|
|
#endif
|
|
Temp = IPH;
|
|
} else {
|
|
Temp = CTEAllocMem(sizeof(IPHeader) + OptionSize);
|
|
if (Temp == NULL) {
|
|
CTEFreeMem(SendList);
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
*Temp = *IPH;
|
|
#if FWD_DBG
|
|
DbgPrint("Options!=NULL : alloced temp @ %lx\n", Temp);
|
|
#endif
|
|
|
|
//
|
|
// done later...
|
|
// CTEMemCopy((uchar *)(Temp + 1), Options, OptionSize);
|
|
}
|
|
}
|
|
|
|
// Now, loop through the list. For each entry, send.
|
|
|
|
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)) {
|
|
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);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
continue;
|
|
}
|
|
|
|
*(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;
|
|
if (Options) {
|
|
// We have options, make a copy.
|
|
if ((NewOptions = CTEAllocMem(OptionSize)) == (uchar *)NULL) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
FreeIPPacket(NewPacket);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
continue;
|
|
}
|
|
CTEMemCopy(NewOptions, Options, OptionSize);
|
|
}
|
|
else {
|
|
NewOptions = NULL;
|
|
}
|
|
} 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 (ForwardFilterPtr != NULL) {
|
|
//
|
|
// Copy over the options.
|
|
//
|
|
if (NewOptions) {
|
|
CTEMemCopy((uchar *)(Temp + 1), NewOptions, OptionSize);
|
|
}
|
|
|
|
Action = (*ForwardFilterPtr)(Temp,
|
|
NdisBufferVirtualAddress(NewUserBuffer),
|
|
NdisBufferLength(NewUserBuffer),
|
|
NULL, SendList[i].bsl_if->if_filtercontext);
|
|
|
|
#if FWD_DBG
|
|
DbgPrint("ForwardFilterPtr: %lx, FORWARD is %lx\n", Action, FORWARD);
|
|
#endif
|
|
|
|
if (Action != FORWARD) {
|
|
if (i != (NetsToSend - 1)) {
|
|
FreeIPBufferChain(NewUserBuffer);
|
|
if (NewOptions) {
|
|
CTEFreeMem(NewOptions);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
// 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 {
|
|
Status = SendIPPacket(SendList[i].bsl_if, Destination, NewPacket,
|
|
NewUserBuffer, NewHeader, NewOptions, OptionSize);
|
|
if (Status == IP_PENDING)
|
|
Sent++;
|
|
}
|
|
}
|
|
|
|
if (ForwardFilterPtr && Options) {
|
|
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)) {
|
|
CTEFreeMem(SendList);
|
|
CTEFreeMem(BR); // Reference is 0, free the BR structure.
|
|
return IP_SUCCESS;
|
|
} else {
|
|
CTEFreeMem(SendList);
|
|
return IP_PENDING;
|
|
}
|
|
} else {
|
|
// Had only one I/F to send on. Just return the status.
|
|
CTEFreeMem(SendList);
|
|
return Status;
|
|
}
|
|
|
|
}
|
|
|
|
//** 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.
|
|
//
|
|
// 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)
|
|
{
|
|
ProtInfo *PInfo = (ProtInfo *)Context;
|
|
PacketContext *pc;
|
|
Interface *DestIF; // Outgoing interface to use.
|
|
IPAddr FirstHop; // First hop address of
|
|
// destination.
|
|
uint MTU; // MTU of route.
|
|
NDIS_STATUS Status;
|
|
IPHeader *IPH;
|
|
PNDIS_PACKET Packet;
|
|
PNDIS_BUFFER HeaderBuffer;
|
|
CTELockHandle LockHandle;
|
|
uchar *Options;
|
|
uint OptionSize;
|
|
BufferReference *BR;
|
|
RouteTableEntry *RTE;
|
|
uchar DType;
|
|
IP_STATUS SendStatus;
|
|
#ifdef _PNP_POWER
|
|
Interface *RoutedIF;
|
|
#endif
|
|
|
|
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.
|
|
if ((Packet = GetIPPacket()) != (PNDIS_PACKET)NULL) { // Got a packet.
|
|
pc = (PacketContext *)Packet->ProtocolReserved;
|
|
pc->pc_br = (BufferReference *)NULL;
|
|
pc->pc_pi = PInfo;
|
|
pc->pc_context = SendContext;
|
|
#ifdef _PNP_POWER
|
|
CTEAssert(pc->pc_if == NULL);
|
|
#endif
|
|
|
|
// Make sure that we have an RCE, that it's valid, etc.
|
|
|
|
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.
|
|
CTEInterlockedIncrementLong(&RCE->rce_usecnt);
|
|
RTE = RCE->rce_rte;
|
|
FirstHop = ADDR_FROM_RTE(RTE, Dest);
|
|
DestIF = IF_FROM_RTE(RTE);
|
|
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 (OptInfo->ioi_options == (uchar *)NULL) {
|
|
if (RCE->rce_dtype != DEST_BCAST) {
|
|
if (DataSize <= MTU) {
|
|
|
|
|
|
NdisBufferLength(Buffer) += sizeof(IPHeader);
|
|
NdisChainBufferAtBack(Packet, Buffer);
|
|
IPH = (IPHeader *)NdisBufferVirtualAddress(Buffer);
|
|
|
|
IPH->iph_protocol = Protocol;
|
|
IPH->iph_xsum = 0;
|
|
IPH->iph_dest = Dest;
|
|
IPH->iph_src = Source;
|
|
IPH->iph_ttl = OptInfo->ioi_ttl;
|
|
IPH->iph_tos = OptInfo->ioi_tos;
|
|
IPH->iph_offset =
|
|
net_short(((OptInfo->ioi_flags & IP_FLAG_DF)
|
|
<< 13));
|
|
IPH->iph_id =
|
|
(ushort)CTEInterlockedExchangeAdd(&IPID, 1);
|
|
IPH->iph_verlen = DEFAULT_VERLEN;
|
|
IPH->iph_length = net_short(DataSize+sizeof(IPHeader));
|
|
IPH->iph_xsum = ~xsum(IPH, sizeof(IPHeader));
|
|
|
|
// 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 (ForwardFilterPtr == NULL) {
|
|
Status = (*(DestIF->if_xmit))(DestIF->if_lcontext,
|
|
Packet, FirstHop, RCE);
|
|
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
|
|
if (Status != NDIS_STATUS_PENDING) {
|
|
FreeIPPacket(Packet);
|
|
return IP_SUCCESS; // BUGBUG - should map error
|
|
// code.
|
|
}
|
|
return IP_PENDING;
|
|
|
|
} else {
|
|
FORWARD_ACTION Action;
|
|
|
|
Action = (*ForwardFilterPtr)(IPH,
|
|
(uchar *)(IPH + 1),
|
|
NdisBufferLength(Buffer) -
|
|
sizeof(IPHeader),
|
|
NULL, DestIF->if_filtercontext);
|
|
|
|
if (Action == FORWARD) {
|
|
Status = (*(DestIF->if_xmit))(
|
|
DestIF->if_lcontext,
|
|
Packet, FirstHop, RCE);
|
|
} else {
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
IPSInfo.ipsi_outdiscards++;
|
|
}
|
|
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
|
|
if (Status != NDIS_STATUS_PENDING) {
|
|
FreeIPPacket(Packet);
|
|
return IP_SUCCESS; // BUGBUG - should map error
|
|
// code.
|
|
}
|
|
return IP_PENDING;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CTEInterlockedDecrementLong(&RCE->rce_usecnt);
|
|
DType = RCE->rce_dtype;
|
|
} else {
|
|
// We have an RCE, but there is no RTE for it. Call the
|
|
// routing code to fix this.
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
if (!AttachRCEToRTE(RCE, PInfo->pi_protocol,
|
|
(uchar *)NdisBufferVirtualAddress(Buffer) + sizeof(IPHeader),
|
|
NdisBufferLength(Buffer))) {
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
FreeIPPacket(Packet);
|
|
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);
|
|
MTU = MTU_FROM_RTE(RTE);
|
|
DType = RCE->rce_dtype;
|
|
} 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;
|
|
}
|
|
|
|
// 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.
|
|
|
|
HeaderBuffer = GetIPHdrBuffer();
|
|
if (HeaderBuffer == NULL) {
|
|
FreeIPPacket(Packet);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
} else {
|
|
uchar *Temp1, *Temp2;
|
|
|
|
// Got a buffer, copy the upper layer data forward.
|
|
|
|
Temp1 = (uchar *)NdisBufferVirtualAddress(Buffer);
|
|
Temp2 = Temp1 + sizeof(IPHeader);
|
|
CTEMemCopy(Temp1, Temp2, NdisBufferLength(Buffer));
|
|
}
|
|
|
|
NdisChainBufferAtBack(Packet, HeaderBuffer);
|
|
|
|
IPH = (IPHeader *)NdisBufferVirtualAddress(HeaderBuffer);
|
|
IPH->iph_protocol = Protocol;
|
|
IPH->iph_xsum = 0;
|
|
IPH->iph_src = Source;
|
|
IPH->iph_ttl = OptInfo->ioi_ttl;
|
|
IPH->iph_tos = OptInfo->ioi_tos;
|
|
IPH->iph_offset = net_short(((OptInfo->ioi_flags & IP_FLAG_DF) << 13));
|
|
IPH->iph_id = (ushort)CTEInterlockedExchangeAdd(&IPID, 1);
|
|
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 {
|
|
//
|
|
// 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);
|
|
MTU = MTU_FROM_RTE(RTE);
|
|
}
|
|
else {
|
|
FirstHop = NULL_IP_ADDR;
|
|
}
|
|
|
|
CTEFreeLock(&RCE->rce_lock, LockHandle);
|
|
}
|
|
}
|
|
|
|
if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR)) {
|
|
DestIF = LookupNextHopWithBuffer(Dest, Source, &FirstHop, &MTU,
|
|
PInfo->pi_protocol, (uchar *)NdisBufferVirtualAddress(Buffer),
|
|
NdisBufferLength(Buffer));
|
|
#ifdef _PNP_POWER
|
|
pc->pc_if = DestIF;
|
|
RoutedIF = DestIF;
|
|
#endif
|
|
if (DestIF == NULL) {
|
|
// Lookup failed. Return an error.
|
|
FreeIPPacket(Packet);
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
|
|
DType = GetAddrType(Dest);
|
|
#ifdef DEBUG
|
|
if (DType == DEST_INVALID)
|
|
DEBUGCHK;
|
|
#endif
|
|
} else {
|
|
#ifdef _PNP_POWER
|
|
RoutedIF = NULL;
|
|
#endif
|
|
}
|
|
|
|
// See if we have any options. If we do, copy them now.
|
|
if (OptInfo->ioi_options != NULL) {
|
|
// 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);
|
|
#ifdef _PNP_POWER
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
#endif
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
}
|
|
Options = CTEAllocMem(OptionSize = OptInfo->ioi_optlength);
|
|
if (Options == (uchar *)NULL) {
|
|
FreeIPPacket(Packet);
|
|
#ifdef _PNP_POWER
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
#endif
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
CTEMemCopy(Options, OptInfo->ioi_options, OptionSize);
|
|
} else {
|
|
Options = (uchar *)NULL;
|
|
OptionSize = 0;
|
|
}
|
|
|
|
// The options have been taken care of. Now see if it's some sort
|
|
// of broadcast.
|
|
IPH->iph_verlen = IP_VERSION + ((OptionSize + sizeof(IPHeader)) >> 2);
|
|
IPH->iph_length = net_short(DataSize+OptionSize+sizeof(IPHeader));
|
|
|
|
// 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 (ForwardFilterPtr != NULL) {
|
|
IPHeader *Temp;
|
|
FORWARD_ACTION Action;
|
|
|
|
if (Options == NULL) {
|
|
Temp = IPH;
|
|
} else {
|
|
Temp = CTEAllocMem(sizeof(IPHeader) + OptionSize);
|
|
if (Temp == NULL) {
|
|
FreeIPPacket(Packet);
|
|
#ifdef _PNP_POWER
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
#endif
|
|
CTEFreeMem(Options);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
*Temp = *IPH;
|
|
CTEMemCopy((uchar *)(Temp + 1), Options, OptionSize);
|
|
}
|
|
|
|
Action = (*ForwardFilterPtr)(Temp,
|
|
NdisBufferVirtualAddress(Buffer),
|
|
NdisBufferLength(Buffer),
|
|
NULL, DestIF->if_filtercontext);
|
|
|
|
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 ForwardFilterPtr 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);
|
|
FreeIPPacket(Packet);
|
|
|
|
#ifdef _PNP_POWER
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
#endif
|
|
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_DEST_HOST_UNREACHABLE;
|
|
}
|
|
#if FWD_DBG
|
|
else {
|
|
DbgPrint("IPTransmit: ignoring return %lx\n", Action);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
if (IP_ADDR_EQUAL(Source, NULL_IP_ADDR)) {
|
|
SendStatus = SendDHCPPacket(Dest, Packet, Buffer, IPH);
|
|
|
|
#ifdef _PNP_POWER
|
|
if (SendStatus != IP_PENDING && RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
#endif
|
|
|
|
return SendStatus;
|
|
} else {
|
|
SendStatus= SendIPBCast(NULL, Dest, Packet, IPH, Buffer, DataSize,
|
|
Options, OptionSize, TRUE, NULL);
|
|
|
|
#ifdef _PNP_POWER
|
|
if (SendStatus != IP_PENDING && RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
#endif
|
|
|
|
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) {
|
|
BR = CTEAllocMem(sizeof(BufferReference));
|
|
if (BR == (BufferReference *)NULL) {
|
|
// Couldn't get a BufferReference
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
FreeIPPacket(Packet);
|
|
|
|
#ifdef _PNP_POWER
|
|
if (RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
#endif
|
|
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
BR->br_buffer = Buffer;
|
|
BR->br_refcount = 0;
|
|
CTEInitLock(&BR->br_lock);
|
|
pc->pc_br = BR;
|
|
SendStatus = IPFragment(DestIF, MTU, FirstHop, Packet, IPH, Buffer,
|
|
DataSize, Options, OptionSize, (int *)NULL);
|
|
|
|
#ifdef _PNP_POWER
|
|
if (SendStatus != IP_PENDING && RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
#endif
|
|
|
|
return SendStatus;
|
|
}
|
|
|
|
// 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.
|
|
|
|
SendStatus = SendIPPacket(DestIF, FirstHop, Packet, Buffer, IPH, Options,
|
|
OptionSize);
|
|
|
|
#ifdef _PNP_POWER
|
|
if (SendStatus != IP_PENDING && RoutedIF != NULL) {
|
|
DerefIF(RoutedIF);
|
|
}
|
|
#endif
|
|
|
|
return SendStatus;
|
|
}
|
|
|
|
// Couldn't get a buffer. Return 'no resources'
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
|
|
|