diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/tcpip/ip/ipxmit.c | 1949 |
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; +} + + + |