summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/ip/ipxmit.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/tcpip/ip/ipxmit.c1949
1 files changed, 1949 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/ip/ipxmit.c b/private/ntos/tdi/tcpip/ip/ipxmit.c
new file mode 100644
index 000000000..61ee32b90
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/ipxmit.c
@@ -0,0 +1,1949 @@
+/********************************************************************/
+/** 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;
+}
+
+
+