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 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_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.