Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

745 lines
20 KiB

/*******************************************************************/
/* Copyright(c) 1993 Microsoft Corporation */
/*******************************************************************/
//***
//
// Filename: ripproc.c
//
// Description: process rip packets
//
// Author: Stefan Solomon (stefans) October 11, 1993.
//
// Revision History:
//
//***
#include "rtdefs.h"
VOID
RipRequest(PPACKET_TAG pktp);
VOID
RipResponse(PPACKET_TAG pktp);
VOID
SetNetworkEntry(PUCHAR nep,
PIPX_ROUTE_ENTRY rtep);
//***
//
// Function: ProcessRipPacket
//
// Descr:
//
// Params: Packet
//
// Returns: none
//
//***
VOID
ProcessRipPacket(PPACKET_TAG pktp)
{
USHORT opcode;
PUCHAR hdrp; // ptr to the packet header
// get a ptr to the packet header
hdrp = pktp->DataBufferp;
// check the RIP operation type
GETSHORT2USHORT(&opcode, hdrp + RIP_OPCODE);
switch(opcode) {
case RIP_REQUEST:
RipRequest(pktp);
break;
case RIP_RESPONSE:
RipResponse(pktp);
break;
default:
// this is an invalid frame
RtPrint(DBG_NOTIFY, ("IpxRouter: ProcessRipPacket: Reject invalid frame\n"));
FreeRcvPkt(pktp);
break;
}
}
//***
//
// Function: RipRequest
//
// Descr: process the RIP request
//
//***
VOID
RipRequest(PPACKET_TAG pktp)
{
USHORT reqlen; // offset to get next request
USHORT resplen; // offset to put next response
USHORT pktlen; // packet length
PUCHAR hdrp; // ptr to the packet header
PNICCB niccbp; // ptr to nic ctrl blk that received this packet
PNICCB rtniccbp;
PIPX_ROUTE_ENTRY rtep;
KIRQL oldirql;
PRIP_SNDREQ respcbp;
UINT segment;
BOOLEAN PingRouter = FALSE;
RtPrint(DBG_RIP, ("IpxRouter: RipRequest: Entered\n"));
// get a ptr to the packet owner Nic
niccbp = pktp->PacketOwnerNicCbp;
// if there is no network number for this NIC we don't reply to this RIP
// request
if(!memcmp(niccbp->Network, nulladdress, IPX_NET_LEN)) {
RtPrint(DBG_NOTIFY, ("IpxRouter: Cannot reply to RIP request on unnumbered net for NIC %d\n",
niccbp->NicId));
// free the packet buffer and return
FreeRcvPkt(pktp);
return;
}
// get a ptr to the packet header
hdrp = pktp->DataBufferp;
// get IPX packet length
GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH);
// We may have one or more network entry requests in the packet.
// If one network entry is 0xFFFFFFFF, then a general RIP response is
// requested.
// for each network entry, try to get the answer from our routing table
for(reqlen = resplen = RIP_INFO;
reqlen < pktlen;
reqlen += NE_ENTRYSIZE) {
// check if a general response is requested
if(!memcmp(hdrp + reqlen + NE_NETNUMBER, bcastaddress, IPX_NET_LEN)) {
// queue a req for rip gen response for this net and free the packet.
// the req will be dequeued and processed by the rip response
// thread.
if((respcbp = ExAllocatePool(NonPagedPool,
sizeof(RIP_SNDREQ))) != NULL) {
// set up the send request
respcbp->SndReqId = RIP_GEN_RESPONSE;
respcbp->SendOnAllNics = FALSE; // send only to requesting node
// fill in the sending node address, to be used in the response
memcpy(respcbp->DestNode, hdrp + IPXH_SRCNODE, 6);
// fill in the sending socket to be used in the response
GETSHORT2USHORT(&respcbp->DestSock, hdrp + IPXH_SRCSOCK);
respcbp->DoNotSendNicCbp = NULL;
respcbp->SenderNicCbp = niccbp;
respcbp->SndCompleteEventp = NULL;
if(!RipQueueSndReqAtNic(niccbp, respcbp)) {
// can't queue this request
ExFreePool(respcbp);
respcbp = NULL;
}
}
FreeRcvPkt(pktp);
return;
}
//*** a specific response is requested. ***
// if the requested network number is 0, we replace it with
// the network segment the packet was received on:
if(!memcmp(hdrp + reqlen + NE_NETNUMBER, nulladdress, IPX_NET_LEN)) {
RtPrint(DBG_RIP, ("IpxRouter: RipRequest: request info on directly attached net\n"));
memcpy(hdrp + reqlen + NE_NETNUMBER, niccbp->Network, IPX_NET_LEN);
PingRouter = TRUE;
}
segment = IpxGetSegment(hdrp + reqlen + NE_NETNUMBER);
// LOCK THE ROUTING TABLE
ExAcquireSpinLock(&SegmentLocksTable[segment], &oldirql);
if(rtep = IpxGetRoute(segment, hdrp + reqlen + NE_NETNUMBER)) {
// check if we can route the packet
// the route should be on a different nic id than the received
// packet. For the global WAN net, rtep->NicId = 0xFFFE !
if(rtep->NicId != niccbp->NicId) {
// if the response will be sent on a WAN link then there
// is some filtering to do
if(niccbp->DeviceType == NdisMediumWan) {
// check if the target net is the global WAN net
if(!(rtep->Flags & IPX_ROUTER_GLOBAL_WAN_NET)) {
// This is a request received on WAN and the target is not
// the global WAN net
rtniccbp = NicCbPtrTab[rtep->NicId];
// check if the target net is visible via a WAN-Disabled LAN
if((rtniccbp->DeviceType != NdisMediumWan) &&
(rtniccbp->WanRoutingDisabled)) {
// the target is a LAN disabled for WAN traffic
// skip it!
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
continue;
}
// check if the nic to send on is a WAN client.
if(niccbp->WanConnectionClient) {
// Check if LAN-WAN-LAN connectivity is enabled
if(!LanWanLan) {
// this node can only inform about its virtual net
if(rtniccbp->NicId != VirtualNicId) {
// skip it!
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
continue;
}
}
}
}
else
{
// this is a request received on WAN and the target is the
// global WAN net.
// check if the nic to send on doesn't have the same WAN address
if(!memcmp(rtep->Network, niccbp->Network, 4)) {
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
continue;
}
// check if the nic to send on is a WAN client.
if(niccbp->WanConnectionClient) {
// Check if LAN-WAN-LAN connectivity is enabled
if(!LanWanLan) {
// this node can only inform about its virtual net
// and the global net is not the virtual net
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
continue;
}
}
}
}
else
{
// The response will be sent on a LAN.
// if LAN to LAN routing is disabled and if the target
// route is from a LAN Nic, we do not repond to it
if(!EnableLanRouting) {
// LAN routing is disabled -> the only routes we can
// respond with are the virtual net or any WAN net
if(!(rtep->Flags & IPX_ROUTER_GLOBAL_WAN_NET)) {
// the target net is not the global wan net
// check if it is not the virtual net
if(rtep->NicId != VirtualNicId) {
// the target net is not the virtual net
// check the it is not a LAN net
if(NicCbPtrTab[rtep->NicId]->DeviceType != NdisMediumWan) {
// The target net is NOT:
// 1. the global wan net
// 2. the vitual net
// 3. a WAN net
// I.e. -> target is a LAN net -> do not answer
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
continue;
}
}
}
}
// if the destination nic is WAN and has a client role and
// LAN-WAN-LAN traffic is disabled, do not respond to it
if(!LanWanLan) {
if(!(rtep->Flags & IPX_ROUTER_GLOBAL_WAN_NET)) {
if( (NicCbPtrTab[rtep->NicId]->DeviceType == NdisMediumWan) &&
(NicCbPtrTab[rtep->NicId]->WanConnectionClient)) {
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
continue;
}
}
}
}
// we can route it -> answer to it
// fill in the network entry structure in the packet with the
// info from the route entry
SetNetworkEntry(hdrp + resplen, rtep);
// increment the response length to the next response entry
resplen += NE_ENTRYSIZE;
}
if(PingRouter) {
// answer to the ping request
SetNetworkEntry(hdrp + resplen, rtep);
// increment the response length to the next response entry
resplen += NE_ENTRYSIZE;
}
}
PingRouter = FALSE;
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
}
// We are done answering this request.
// Check if any response has been generated
if(resplen == RIP_INFO) {
// no response generated for this packet
RtPrint(DBG_RIP, ("IpxRouter: RipRequest: no response send for this request\n"));
FreeRcvPkt(pktp);
return;
}
// Turn the packet around and send it. This is done by changing the
// packet's src and dest nodes and sockets. (src and dst net are the same)
memcpy(hdrp + IPXH_DESTNODE, hdrp + IPXH_SRCNODE, 6);
memcpy(hdrp + IPXH_SRCNODE, niccbp->Node, 6);
memcpy(hdrp + IPXH_DESTSOCK, hdrp + IPXH_SRCSOCK, 2);
PUTUSHORT2SHORT(hdrp + IPXH_SRCSOCK, IPX_RIP_SOCKET);
// change the packet type to RIP response
PUTUSHORT2SHORT(hdrp + RIP_OPCODE, RIP_RESPONSE);
// set the new packet length
PUTUSHORT2SHORT(hdrp + IPXH_LENGTH, resplen);
// prepare remote address structure in the packet tag to send the packet
pktp->RemoteAddress.NicId = niccbp->NicId;
memcpy(pktp->RemoteAddress.MacAddress, hdrp + IPXH_DESTNODE, 6);
// Send the packet. The packet will be freed when send completes.
RtPrint(DBG_RIP, ("IpxRouter: RipRequest: send response send for this request\n"));
SendPacket(pktp);
}
//***
//
// Function: RipResponse
//
// Descr: Updates the routing table with the response info
//
// Params: Packet
//
// Returns: none
//
//***
VOID
RipResponse(PPACKET_TAG pktp)
{
USHORT resplen; // offset of the next response network entry
USHORT pktlen; // IPX packet length
PUCHAR hdrp; // ptr to the packet header
PNICCB niccbp; // ptr to the nic ctrl blk the packet that
// received the packet
PIPX_ROUTE_ENTRY oldrtep, newrtep; // new and old routing tab entries
KIRQL oldirql;
USHORT nrofhops;
BOOLEAN RouteDown;
PRIP_UPDATE_SNDREQ respcbp = NULL; // ptr to changes response to bcast
PRIP_SNDREQ sndreqp;
PUCHAR sndhdrp;
USHORT sndpktlen;
UINT segment;
USHORT tickcount;
#if DBG
UCHAR b[6];
#endif
RtPrint(DBG_RIP, ("IpxRouter: RipResponse: Entered\n"));
// get a ptr to this Nic
niccbp = pktp->PacketOwnerNicCbp;
// get a ptr to the received response packet header
hdrp = pktp->DataBufferp;
// get received response packet length
GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH);
// For each network entry, check if we have it in our routing table.
// If we do not have this entry or if
// it is a better entry than what we have, we add it to the routing table
// !!! for this primary version, we don't care about better entries !!!
for(resplen = RIP_INFO;
resplen < pktlen;
resplen += NE_ENTRYSIZE) {
newrtep = NULL;
// check if the network route is up or down
GETSHORT2USHORT(&nrofhops, hdrp + resplen + NE_NROFHOPS);
if(nrofhops < 16) {
RouteDown = FALSE;
}
else
{
RouteDown = TRUE;
}
segment = IpxGetSegment(hdrp + resplen + NE_NETNUMBER);
// LOCK THE ROUTING TABLE
ExAcquireSpinLock(&SegmentLocksTable[segment], &oldirql);
// check if the entry exists.
if((oldrtep = IpxGetRoute(segment, hdrp + resplen + NE_NETNUMBER)) == NULL) {
//*** This route does not exist ***
// if this is a route down bcast and we didn't have this route
// we skip this information
if(RouteDown) {
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
continue;
}
// if this is a route with 15 hops, we choose for now to ignore
// it.
if(nrofhops == 15) {
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
continue;
}
// This is a new route. We add this route to the routing table
if(newrtep = ExAllocatePool(NonPagedPool, sizeof(IPX_ROUTE_ENTRY))) {
// set it up
memcpy(newrtep->Network, hdrp + resplen + NE_NETNUMBER, IPX_NET_LEN);
newrtep->NicId = niccbp->NicId;
memcpy(newrtep->NextRouter, hdrp + IPXH_SRCNODE, IPX_NODE_LEN);
newrtep->Flags = 0;
newrtep->Timer = 0; // TTL of this route entry is 3 min
newrtep->Segment = segment;
GETSHORT2USHORT(&newrtep->TickCount, hdrp + resplen + NE_NROFTICKS);
GETSHORT2USHORT(&newrtep->HopCount, hdrp + resplen + NE_NROFHOPS);
// increment the hop count to reflect our new router in the path
// the tick count received from the other router includes the
// nr of ticks on the network segment it was sent, so no adjust
// is necessary
newrtep->HopCount++;
InitializeListHead(&newrtep->AlternateRoute);
// add it to the table
IpxAddRoute(segment, newrtep);
}
}
else
{
//
//*** This route exists in our routing table ***
//
// first check if the response is coming from
// the same router as the one which we got the route from
if(memcmp(oldrtep->NextRouter, hdrp + IPXH_SRCNODE, IPX_NODE_LEN)) {
//
//** This is a response from another router for the same network
//
// check if the response is : route unreachable
if(RouteDown) {
// useless response -> discard it!
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
#if DBG
memcpy(b, hdrp + resplen + NE_NETNUMBER, 4);
#endif
continue;
}
// if the route is not down but this is a better route, add it
// instead.
// Check if this is a better route
GETSHORT2USHORT(&tickcount, hdrp + resplen + NE_NROFTICKS);
if(tickcount >= oldrtep->TickCount) {
// same or worse route, ignore it!
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
#if DBG
memcpy(b, hdrp + resplen + NE_NETNUMBER, 4);
#endif
continue;
}
// Ignore this response if it refers to a permanent/local
// net
if((oldrtep->Flags & IPX_ROUTER_PERMANENT_ENTRY) ||
(oldrtep->Flags & IPX_ROUTER_LOCAL_NET)) {
// skip this route!
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
continue;
}
// We have a better route
// Just copy the new route over the old route entry
// we do not modify the network and segment fields
oldrtep->NicId = niccbp->NicId;
memcpy(oldrtep->NextRouter, hdrp + IPXH_SRCNODE, IPX_NODE_LEN);
oldrtep->Flags = 0;
oldrtep->Timer = 0; // TTL of this route entry is 3 min
GETSHORT2USHORT(&oldrtep->TickCount, hdrp + resplen + NE_NROFTICKS);
GETSHORT2USHORT(&oldrtep->HopCount, hdrp + resplen + NE_NROFHOPS);
// increment the hop count to reflect our new router in the path
// the tick count received from the other router includes the
// nr of ticks on the network segment it was sent, so no adjust
// is necessary
oldrtep->HopCount++;
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
#if DBG
memcpy(b, hdrp + resplen + NE_NETNUMBER, 4);
#endif
continue;
}
//
//*** This response comes from the same router as the one which gave
//*** us this route entry initially
//
// First -> reset the aging timer
oldrtep->Timer = 0;
// if the info tells us that the route is down, we should delete
// the route from the routing table and inform all other routers
// of the change
if(RouteDown) {
// this may be a bogus packet from the wire
if((oldrtep->Flags & IPX_ROUTER_PERMANENT_ENTRY) ||
(oldrtep->Flags & IPX_ROUTER_LOCAL_NET)) {
#if DBG
memcpy(b, hdrp + IPXH_SRCNODE, 6);
#endif
RtPrint(DBG_NOTIFY, ("IpxRouter: RipResponse: Bogus resp permanent route down from %x-%x-%x-%x-%x-%x !!\n",
b[0],b[1],b[2],b[3],b[4],b[5]));
RouteDown = FALSE;
}
else
{
IpxDeleteRoute(segment, oldrtep);
// set the nr of hops to 16 in the route entry; used later to
// bcast the change
oldrtep->HopCount = 16;
}
}
else
{
// update hop and tick counts
GETSHORT2USHORT(&oldrtep->TickCount, hdrp + resplen + NE_NROFTICKS);
GETSHORT2USHORT(&oldrtep->HopCount, hdrp + resplen + NE_NROFHOPS);
// increment the hop count to reflect our new router in the path
// the tick count received from the other router includes the
// nr of ticks on the network segment it was sent, so no adjust
// is necessary
oldrtep->HopCount++;
}
}
// UNLOCK THE ROUTING TABLE
ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
// if the rip response packet is bigger than the standard length we will
// fragment the bcast update and broadcast it with the rip timer
if(pktlen > RIP_RESPONSE_PACKET_LEN) {
if(oldrtep && RouteDown) {
ExFreePool(oldrtep);
}
continue;
}
// if the RIP response packet comes from a LAN and if the LAN to LAN
// routing is disabled, we won't broadcast any update. This is so because:
// 1. We don't broadcast updates on WAN
// 2. We broadcast only WAN updates on LAN if LAN routing disabled
if((!EnableLanRouting) &&
(niccbp->DeviceType != NdisMediumWan)) {
if(oldrtep && RouteDown) {
ExFreePool(oldrtep);
}
continue;
}
// check if we will broadcast any change now and if a bcast packet
// request + buffer have been allocated
if (newrtep || (oldrtep && RouteDown)) {
// check if a send bcast req struct has already been allocated
if(respcbp == NULL) {
// allocate a send request struct to bcast changes in the routing table
respcbp = ExAllocatePool(NonPagedPool,
sizeof(RIP_UPDATE_SNDREQ) + RIP_SNDPKT_MAXLEN);
if(respcbp != NULL) {
// get the ipx hdr ptr for the send packet
sndhdrp = (PUCHAR)respcbp->RipSndPktBuff.IpxPacket;
// set the initial length
sndpktlen = RIP_INFO;
}
}
}
// check if we have added a new route or deleted an old one
if(newrtep) {
// write the network entry for this new route in the packet to be
// sent
if(respcbp) {
// fill in the network entry structure in the packet with the
// info from the route entry
SetNetworkEntry(sndhdrp + sndpktlen, newrtep);
// increment the send packet length to the next network entry
sndpktlen += NE_ENTRYSIZE;
}
}
else
{
// check if an old route has been deleted
if(oldrtep && RouteDown) {
// write the network entry for this old route in the packet to be
// sent
if(respcbp) {
// fill in the network entry structure in the packet with the
// info from the route entry
SetNetworkEntry(sndhdrp + sndpktlen, oldrtep);
// increment the send packet length to the next network entry
sndpktlen += NE_ENTRYSIZE;
ExFreePool(oldrtep);
}
}
}
}
// if we have added or deleted some routes, we make a request to the rip bcast
// thread to have them bcasted over to the other nics
if(respcbp) {
// Send a bcast update with the changes to all the nics except this one
sndreqp = &respcbp->RipSndReq;
// set the new packet length
PUTUSHORT2SHORT(sndhdrp + IPXH_LENGTH, sndpktlen);
BroadcastRipUpdate(sndreqp, niccbp, NULL);
}
// free the packet
FreeRcvPkt(pktp);
}
//***
//
// Function: SetNetworkEntry
//
// Descr:
//
//***
VOID
SetNetworkEntry(PUCHAR nep, // points to the network entry in the
// RIP packet
PIPX_ROUTE_ENTRY rtep)
{
memcpy(nep + NE_NETNUMBER, rtep->Network, IPX_NET_LEN);
PUTUSHORT2SHORT(nep + NE_NROFHOPS, rtep->HopCount);
PUTUSHORT2SHORT(nep + NE_NROFTICKS, rtep->TickCount);
}