From the work around it can be determined that it has something to do with "forwarding" and "routing".
Taking a look at windows nt 3.5 source code, iproute.c is my main suspect containing the bug.
Most likely the bug is in this piece of source code:
"
//** IPForward - Forward a packet.
//
// The routine called when we need to forward a packet. We check if we're supposed
// to act as a gateway, and if we are and the incoming packet is a bcast we check
// and see if we're supposed to forward broadcasts. Assuming we're supposed to
// forward it, we will process any options. If we find some, we do some validation
// to make sure everything is good. After that, we look up the next hop. If we can't
// find one, we'll issue an error. Then we get a packet and buffers, and send it.
//
// Input: SrcNTE - NTE for net on which we received this.
// Header - Pointer to received IPheader.
// HeaderLength - Length of header.
// Data - Pointer to data to be forwarded.
// BufferLength - Length in bytes available in the buffer.
// DestType - Type of destination.
//
// Returns: Nothing.
//
void
IPForward(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header, uint HeaderLength,
void *Data, uint BufferLength, NDIS_HANDLE LContext1, uint LContext2,
uchar DestType)
{
uchar *Options;
uchar OptLength;
OptIndex Index;
IPAddr DestAddr; // IP address we're routing towards.
uchar SendOnSource = FALSE;
IPAddr NextHop; // Next hop IP address.
PNDIS_PACKET Packet;
FWContext *FWC;
IPHeader *NewHeader; // New header.
NDIS_STATUS Status;
uint DataLength;
CTELockHandle TableHandle;
uchar ErrIndex;
IPAddr OutAddr; // Address of interface we're send out on.
Interface *IF; // Interface we're sending out on.
uint MTU;
if (ForwardPackets) {
DestAddr = Header->iph_dest;
// If it's a broadcast, see if we can forward it. We won't forward it if broadcast
// forwarding is turned off, or the destination if the local (all one's) broadcast,
// or it's a multicast (Class D address). We'll pass through subnet broadcasts in
// case there's a source route. This would be odd - maybe we should disable this?
if (IS_BCAST_DEST(DestType)) {
if (!ForwardBCast) {
if (DestType > DEST_REMOTE)
IPSInfo.ipsi_inaddrerrors++;
return;
}
if ((DestAddr == IP_LOCAL_BCST) ||
(DestAddr == IP_ZERO_BCST) ||
(DestType == DEST_SN_BCAST) ||
CLASSD_ADDR(DestAddr)) {
return;
}
} else
if (DestType == DEST_REMOTE) {
SrcNTE = BestNTEForIF(Header->iph_src, SrcNTE->nte_if);
if (SrcNTE == NULL) {
// Something bad happened.
CTEAssert(FALSE);
return;
}
}
// If the TTL would expire, send a message.
if (Header->iph_ttl <= 1) {
IPSInfo.ipsi_inhdrerrors++;
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_TIME_EXCEED, TTL_IN_TRANSIT,0);
return;
}
DataLength = net_short(Header->iph_length) - HeaderLength;
Index.oi_srtype = NO_SR; // So we know we don't have a source route.
Index.oi_srindex = MAX_OPT_SIZE;
Index.oi_rrindex = MAX_OPT_SIZE;
Index.oi_tsindex = MAX_OPT_SIZE;
// Now check for options, and process any we find.
if (HeaderLength != sizeof(IPHeader)) {
IPOptInfo OptInfo;
OptInfo.ioi_options = (uchar *)(Header + 1);
OptInfo.ioi_optlength = HeaderLength - sizeof(IPHeader);
// Validate options, and set up indices.
if ((ErrIndex = ParseRcvdOptions(&OptInfo, &Index)) < MAX_OPT_SIZE) {
IPSInfo.ipsi_inhdrerrors++;
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM, PTR_VALID,
net_long((ulong)ErrIndex + sizeof(IPHeader)));
return;
}
Options = CTEAllocMem(OptInfo.ioi_optlength);
if (!Options) {
IPSInfo.ipsi_outdiscards++;
return; // Couldn't get an
} // option buffer, return;
// Now copy into our buffer.
CTEMemCopy(Options, OptInfo.ioi_options, OptLength = OptInfo.ioi_optlength);
// See if we have a source routing option, and if so we may need to process it. If
// we have one, and the destination in the header is us, we need to update the
// route and the header.
if (Index.oi_srindex != MAX_OPT_SIZE) {
if (DestType >= DEST_REMOTE) { // Not for us.
if (Index.oi_srtype == IP_OPT_SSRR) {
// This packet is strict source routed, but we're not the destination!
// We can't continue from here - perhaps we should send an ICMP, but
// I'm not sure which one it would be.
CTEFreeMem(Options);
IPSInfo.ipsi_inaddrerrors++;
return;
}
Index.oi_srindex = MAX_OPT_SIZE; // Don't need to update this.
} else { // This came here, we need to update the destination address.
uchar *SROpt = Options + Index.oi_srindex;
uchar Pointer;
Pointer = SROpt[IP_OPT_PTR] - 1; // Index starts from one.
// Get the next hop address, and see if it's a broadcast.
DestAddr = *(IPAddr UNALIGNED *)&SROpt[Pointer];
DestType = GetAddrType(DestAddr); // Find address type.
if (DestType == DEST_INVALID) {
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, SR_FAILED, 0);
IPSInfo.ipsi_inhdrerrors++;
CTEFreeMem(Options);
return;
}
// If we came through here, any sort of broadcast needs to be sent out
// the way it came, so update that flag.
SendOnSource = TRUE;
}
}
} else { // No options.
Options = (uchar *)NULL;
OptLength = 0;
}
IPSInfo.ipsi_forwdatagrams++;
// We've processed the options. Now look up the next hop. If we can't
// find one, send back an error.
IF = LookupNextHop(DestAddr, SrcNTE->nte_addr, &NextHop, &MTU);
if (IF == NULL) {
// Couldn't find an outgoing route.
IPSInfo.ipsi_outnoroutes++;
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
HOST_UNREACH, 0);
if (Options)
CTEFreeMem(Options);
return;
}
// If we have a strict source route and the next hop is not the one
// specified, send back an error.
if (Index.oi_srtype == IP_OPT_SSRR)
if (DestAddr != NextHop) {
IPSInfo.ipsi_outnoroutes++;
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
SR_FAILED, 0);
CTEFreeMem(Options);
return;
}
// Update the options, if we can and we need to.
if ((DestType != DEST_BCAST) && Options != NULL) {
NetTableEntry *OutNTE;
// Need to find a valid source address for the outgoing interface.
CTEGetLock(&RouteTableLock, &TableHandle);
OutNTE = BestNTEForIF(DestAddr, IF);
if (OutNTE == NULL) {
// No NTE for this IF. Something's wrong, just bail out.
CTEFreeLock(&RouteTableLock, TableHandle);
CTEFreeMem(Options);
return;
} else {
OutAddr = OutNTE->nte_addr;
CTEFreeLock(&RouteTableLock, TableHandle);
}
ErrIndex = UpdateOptions(Options, &Index,
(IP_LOOPBACK(OutAddr) ? DestAddr : OutAddr));
if (ErrIndex != MAX_OPT_SIZE) {
IPSInfo.ipsi_inhdrerrors++;
SendICMPErr(OutAddr, Header, ICMP_PARAM_PROBLEM, PTR_VALID,
net_long((ulong)ErrIndex + sizeof(IPHeader)));
CTEFreeMem(Options);
return;
}
}
// Send a redirect, if we need to. We'll send a redirect if the packet
// is going out on the interface it came in on and the next hop address
// is on the same subnet as the NTE we received it on, and if there
// are no source route options. We also need to make sure that the
// source of the datagram is on the I/F we received it on, so we don't
// send a redirect to another gateway.
// SendICMPErr will check and not send a redirect if this is a broadcast.
if ((SrcNTE->nte_if == IF) &&
IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask,
NextHop & SrcNTE->nte_mask) &&
IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask,
Header->iph_src & SrcNTE->nte_mask)) {
if (Index.oi_srindex == MAX_OPT_SIZE)
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_REDIRECT,
REDIRECT_HOST, NextHop);
}
// We have the next hop. Now get a forwarding packet.
if ((NewHeader = GetFWPacket(&Packet, IF, DestType)) !=
(IPHeader *)NULL) {
// Got the header. Fill it in.
*NewHeader = *Header;
NewHeader->iph_dest = DestAddr;
NewHeader->iph_ttl = Header->iph_ttl - 1;
NewHeader->iph_xsum = 0;
// Save the packet forwarding context info.
FWC = (FWContext *)Packet->ProtocolReserved;
FWC->fc_options = Options;
FWC->fc_optlength = OptLength;
FWC->fc_if = IF;
FWC->fc_mtu = MTU;
FWC->fc_srcnte = SrcNTE;
FWC->fc_nexthop = NextHop;
FWC->fc_sos = SendOnSource;
FWC->fc_dtype = DestType;
FWC->fc_index = Index;
// Now that we have a packet, go ahead and transfer data the
// data in if we need to.
Status = GetFWBuffer(SrcNTE, Packet, Data, DataLength, BufferLength,
HeaderLength, LContext1, LContext2);
// If the status is pending, don't do anything now. Otherwise,
// if the status is success send the packet.
if (Status != NDIS_STATUS_PENDING)
if (Status == NDIS_STATUS_SUCCESS) {
SendFWPacket(Packet, Status, DataLength);
} else {
// Some sort of failure. Free the packet.
IPSInfo.ipsi_outdiscards++;
FreeFWPacket(Packet);
}
} else { // Couldn't get a packet, so drop this.
IPSInfo.ipsi_outdiscards++;
if (Options)
CTEFreeMem(Options);
}
} else { // Forward called, but forwarding
// turned off.
if (DestType != DEST_BCAST && DestType != DEST_SN_BCAST) {
// No need to go through here for strictly broadcast packets,
// although we want to bump the counters for remote bcast stuff.
IPSInfo.ipsi_inaddrerrors++;
if (!IS_BCAST_DEST(DestType)) {
if (DestType == DEST_LOCAL) // Called when local, must be SR.
SendICMPErr(SrcNTE->nte_addr, Header,
ICMP_DEST_UNREACH, SR_FAILED, 0);
else // Not a source route, just HOST_UNREACH
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
HOST_UNREACH, 0);
}
}
}
}
"
In case I am wrong I will also post other option related code from this "unit" / c file:
"
//** OpenRCE - Open an RCE for a specific route.
//
// Called by the upper layer to open an RCE. We look up the type of the address
// - if it's invalid, we return 'Destination invalid'. If not, we look up the
// route, fill in the RCE, and link it on the correct RTE.
//
// As an added bonus, this routine will return the local address to use
// to reach the destination.
//
// Entry: Address - Address for which we are to open an RCE.
// Src - Source address we'll be using.
// RCE - Pointer to where to return pointer to RCE.
// Type - Pointer to where to return destination type.
// MSS - Pointer to where to return MSS for route.
// OptInfo - Pointer to option information, such as TOS and
// any source routing info.
//
// Returns: Source IP address to use. This will be NULL_IP_ADDR if the
// specified destination is unreachable for any reason.
//
IPAddr
OpenRCE(IPAddr Address, IPAddr Src, RouteCacheEntry **RCE, uchar *Type,
ushort *MSS, IPOptInfo *OptInfo)
{
RouteTableEntry *RTE; // Pointer to RTE to put RCE on.
CTELockHandle TableLock;
uchar LocalType;
if (!IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR))
Address = OptInfo->ioi_addr;
CTEGetLock(&RouteTableLock, &TableLock);
// Make sure we're not in DHCP update.
if (DHCPActivityCount != 0) {
// We are updating DHCP. Just fail this now, since we're in an
// indeterminate state.
CTEFreeLock(&RouteTableLock, TableLock);
return NULL_IP_ADDR;
}
LocalType = GetAddrType(Address);
*Type = LocalType;
// If the specified address isn't invalid, continue.
if (LocalType != DEST_INVALID) {
RouteCacheEntry *NewRCE;
// If he's specified a source address, loop through the NTE table
// now and make sure it's valid.
if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) {
NetTableEntry *NTE;
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
if ((NTE->nte_flags & NTE_VALID) &&
IP_ADDR_EQUAL(Src, NTE->nte_addr))
break;
if (NTE == NULL) {
// Didn't find a match.
CTEFreeLock(&RouteTableLock, TableLock);
return NULL_IP_ADDR;
}
}
// Find the route for this guy. If we can't find one, return NULL.
RTE = LookupRTE(Address, Src, HOST_ROUTE_PRI);
if (RTE != (RouteTableEntry *)NULL) {
IPAddr NewSrc;
CTELockHandle RCEHandle;
RouteCacheEntry *OldRCE;
NetTableEntry *SrcNTE;
// We found one.
*MSS = (ushort)RTE->rte_mtu; // Return the route MTU.
if (IP_LOOPBACK_ADDR(Src) && (RTE->rte_if != &LoopInterface)) {
// The upper layer is sending from a loopback address, but the
// destination isn't reachable through the loopback interface.
// Fail the request.
CTEFreeLock(&RouteTableLock, TableLock);
return NULL_IP_ADDR;
}
// We have the RTE. Fill in the RCE, and link it on the RTE.
if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL))
*Type |= DEST_OFFNET_BIT; // Tell upper layer it's off
// net.
// First, see if an RCE already exists for this.
if ((OldRCE = FindRCE(RTE, Address, Src)) == NULL) {
// If the destination is local to this host (i.e.
// loopback), we'll use the destination address as the
// source.
if (LocalType == DEST_LOCAL)
NewSrc = Address;
else {
SrcNTE = BestNTEForIF(Address, RTE->rte_if);
if (SrcNTE == NULL) {
// Can't find an address! Fail the request.
CTEFreeLock(&RouteTableLock, TableLock);
return NULL_IP_ADDR;
}
NewSrc = SrcNTE->nte_addr;
}
// Don't have an existing RCE. See if we can get a new one,
// and fill it in.
NewRCE = CTEAllocMem(sizeof(RouteCacheEntry));
*RCE = NewRCE;
if (NewRCE != NULL) {
CTEMemSet(NewRCE, 0, sizeof(RouteCacheEntry));
// Save the source address for this one. We use the
// caller specified address if provided, otherwise we
// use the best local address.
if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR))
NewRCE->rce_src = NewSrc;
else
NewRCE->rce_src = Src;
NewRCE->rce_dtype = LocalType;
NewRCE->rce_cnt = 1;
CTEInitLock(&NewRCE->rce_lock);
NewRCE->rce_dest = Address;
NewRCE->rce_rte = RTE;
NewRCE->rce_valid = TRUE;
NewRCE->rce_next = RTE->rte_rcelist;
RTE->rte_rcelist = NewRCE;
}
CTEFreeLock(&RouteTableLock, TableLock);
return NewSrc;
} else {
// We have an existing RCE. We'll return his source as the
// valid source, bump the reference count, free the locks
// and return.
CTEGetLock(&OldRCE->rce_lock, &RCEHandle);
NewSrc = OldRCE->rce_src;
OldRCE->rce_cnt++;
*RCE = OldRCE;
CTEFreeLock(&OldRCE->rce_lock, RCEHandle);
CTEFreeLock(&RouteTableLock, TableLock);
return NewSrc;
}
} else {
CTEFreeLock(&RouteTableLock, TableLock);
return NULL_IP_ADDR;
}
}
CTEFreeLock(&RouteTableLock, TableLock);
return NULL_IP_ADDR;
}
//* FreeFWPacket - Free a forwarding packet when we're done with it.
//
//
// Input: Packet - Packet to be freed.
//
// Returns: Nothing.
//
void
FreeFWPacket(PNDIS_PACKET Packet)
{
CTELockHandle Handle;
FWContext *FWC;
// BUGBUG - Portability issue
#ifdef VXD
Packet->Private.Head = (PNDIS_BUFFER)NULL;
Packet->Private.Count = 0;
Packet->Private.PhysicalCount = 0;
Packet->Private.TotalLength = 0;
#else // VXD
#ifdef NT
//
// BUGBUG: This is inefficient. Need something better.
//
NdisReinitializePacket(Packet);
#else // NT
#error Need portable way to do this.
#endif // NT
#endif // VXD
FWC = (FWContext *)Packet->ProtocolReserved;
if (FWC->fc_options) {
CTEFreeMem(FWC->fc_options);
FWC->fc_options = (uchar *)NULL;
}
if (FWC->fc_buffhead) {
CTEGetLock(&FWBufFreeLock, &Handle);
FWC->fc_bufftail->Next = FWBufFree; // BUGBUG more portable.
FWBufFree = FWC->fc_buffhead;
CTEFreeLock(&FWBufFreeLock, Handle);
FWC->fc_buffhead = (PNDIS_BUFFER)NULL;
}
CTEGetLock(&FWPacketFreeLock, &Handle);
FWC->fc_pc.pc_common.pc_link = FWPacketFree;
FWPacketFree = Packet;
CTEFreeLock(&FWPacketFreeLock, Handle);
}
//* TransmitFWPacket - Transmit a forwarded packet on a link.
//
// Called when we know we can send a packet. We fix up the header, and send it.
//
// Input: Packet - Packet to be sent.
// DataLength - Length of data.
//
// Returns: Nothing.
//
void
TransmitFWPacket(PNDIS_PACKET Packet, uint DataLength)
{
FWContext *FC = (FWContext *)Packet->ProtocolReserved;
PNDIS_BUFFER HBuffer, Buffer;
IP_STATUS Status;
PVOID VirtualAddress;
UINT BufLen;
// Fix up the packet. Remove the existing buffer chain, and put our header on
// the front.
// BUGBUG - Get NDIS fixed to make this portable.
#ifdef VXD
Buffer = Packet->Private.Head;
HBuffer = FC->fc_hndisbuff;
Packet->Private.Head = HBuffer;
Packet->Private.Tail = HBuffer;
HBuffer->Next = (PNDIS_BUFFER)NULL;
Packet->Private.TotalLength = sizeof(IPHeader);
Packet->Private.Count = 1;
Packet->Private.PhysicalCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(HBuffer->VirtualAddress,
sizeof(IPHeader));
#else // VXD
#ifdef NT
Buffer = Packet->Private.Head;
HBuffer = FC->fc_hndisbuff;
Packet->Private.Head = HBuffer;
Packet->Private.Tail = HBuffer;
NDIS_BUFFER_LINKAGE(HBuffer) = (PNDIS_BUFFER)NULL;
Packet->Private.TotalLength = sizeof(IPHeader);
Packet->Private.Count = 1;
NdisQueryBuffer(Buffer, &VirtualAddress, &BufLen);
Packet->Private.PhysicalCount =
ADDRESS_AND_SIZE_TO_SPAN_PAGES(
VirtualAddress,
sizeof(IPHeader)
);
#else // NT
#error HELP! Need to make this code portable.
#endif // NT
#endif // VXD
// Figure out how to send it. If it's not a broadcast we'll either send it or
// have it fragmented. If it is a broadcast we'll let our send broadcast routine
// handle it.
if (FC->fc_dtype != DEST_BCAST) {
if ((DataLength + (uint)FC->fc_optlength) <= FC->fc_mtu)
Status = SendIPPacket(FC->fc_if, FC->fc_nexthop, Packet, Buffer,
FC->fc_hbuff, FC->fc_options, (uint)FC->fc_optlength);
else { // Need to fragment this.
BufferReference *BR = CTEAllocMem(sizeof(BufferReference));
if (BR == (BufferReference *)NULL) { // Couldn't get a BufferReference
FWSendComplete(Packet, Buffer);
return;
}
BR->br_buffer = Buffer;
BR->br_refcount = 0;
CTEInitLock(&BR->br_lock);
FC->fc_pc.pc_br = BR;
Status = IPFragment(FC->fc_if, FC->fc_mtu, FC->fc_nexthop, Packet,
FC->fc_hbuff, Buffer, DataLength, FC->fc_options,
(uint)FC->fc_optlength, (int *)NULL);
if (Status == IP_PACKET_TOO_BIG) // Couldn't fragment.
// For MTU discovery pass MTU back here.
SendICMPErr(FC->fc_srcnte->nte_addr, FC->fc_hbuff,
ICMP_DEST_UNREACH, FRAG_NEEDED, 0);
}
} else
Status = SendIPBCast(FC->fc_srcnte, FC->fc_nexthop, Packet, FC->fc_hbuff,
Buffer, DataLength, FC->fc_options, (uint)FC->fc_optlength,
FC->fc_sos, &FC->fc_index);
if (Status != IP_PENDING)
FWSendComplete(Packet, Buffer);
}
"
Unfortunately the comp.lang.c newsgroup is banned on google groups, now I can see another reason why ! ;) =D
If you do manage to find the bug, I would like to know about it ! ;)
Even better would be a "proof of concept" code to exploit/test it.
Bye for now,
Skybuck.