windows-nt/Source/XPSP1/NT/net/rras/ipx/rip/ripproc.c

636 lines
15 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
ripproc.c
Abstract:
RIP processing functions
Author:
Stefan Solomon 09/01/1995
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
VOID
RipRequest(PWORK_ITEM wip);
VOID
RipResponse(PWORK_ITEM wip);
VOID
StartGenResponse(PICB icbp,
PUCHAR dstnodep,
PUCHAR dstsocket);
ULONG
RouteTimeToLiveSecs(PICB icbp);
/*++
Function: ProcessReceivedPacket
Descr: increments the receive if statistics
does rip processing
Remark: >> called with the if lock held <<
--*/
VOID
ProcessReceivedPacket(PWORK_ITEM wip)
{
USHORT opcode;
PUCHAR hdrp; // ptr to the packet header
PICB icbp;
USHORT pktlen;
icbp = wip->icbp;
// check that the interface is up
if(icbp->IfStats.RipIfOperState != OPER_STATE_UP) {
return;
}
// get a ptr to the packet header
hdrp = wip->Packet;
// check if this is a looped back packet
if(!memcmp(hdrp + IPXH_SRCNODE, icbp->AdapterBindingInfo.LocalNode, 6)) {
return;
}
// update the interface receive statistics
icbp->IfStats.RipIfInputPackets++;
// check the packet length
GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH);
if(pktlen > MAX_PACKET_LEN) {
// bad length RIP packet
return;
}
// check the RIP operation type
GETSHORT2USHORT(&opcode, hdrp + RIP_OPCODE);
switch(opcode) {
case RIP_REQUEST:
RipRequest(wip);
break;
case RIP_RESPONSE:
RipResponse(wip);
break;
default:
// this is a bad opcode RIP packet
break;
}
}
//***
//
// Function: RipRequest
//
// Descr: process the RIP request
//
// Remark: >> called with the if lock held <<
//
//***
VOID
RipRequest(PWORK_ITEM wip)
{
USHORT reqlen; // offset to get next request
USHORT resplen; // offset to put next response
USHORT pktlen; // packet length
PUCHAR hdrp; // ptr to the received packet header
PUCHAR resphdrp; // ptr to the response packet header
PICB icbp;
USHORT srcsocket;
IPX_ROUTE IpxRoute;
PWORK_ITEM respwip = NULL; // response packet
ULONG network;
icbp = wip->icbp;
Trace(RIP_REQUEST_TRACE, "RipRequest: Entered on if # %d", icbp->InterfaceIndex);
if(icbp->IfConfigInfo.Supply != ADMIN_STATE_ENABLED) {
Trace(RIP_REQUEST_TRACE,
"RIP request discarded on if %d because Supply is DISABLED\n",
icbp->InterfaceIndex);
return;
}
// get a ptr to the packet header
hdrp = wip->Packet;
// get IPX packet length
GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH);
// if the packet is too long, discard
if(pktlen > MAX_PACKET_LEN) {
Trace(RIP_REQUEST_TRACE,
"RIP request discarded on if %d because the packet size %d > max packet len %d\n",
icbp->InterfaceIndex,
pktlen,
MAX_PACKET_LEN);
return;
}
// 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+NE_ENTRYSIZE) <= pktlen;
reqlen += NE_ENTRYSIZE) {
// check if a general response is requested
if(!memcmp(hdrp + reqlen + NE_NETNUMBER, bcastnet, 4)) {
//*** a general response is requested ***
// create the initial general response packet (work item).
// when the send completes for this packet, the work item will
// contain the RTM enumeration handle which is used to continue
// for the creation of the next succesive gen response packets
StartGenResponse(icbp,
hdrp + IPXH_SRCNODE,
hdrp + IPXH_SRCSOCK);
return;
}
//*** a specific response is requested. ***
// allocate a response packet if none allocated yet
if(respwip == NULL) {
if((respwip = AllocateWorkItem(SEND_PACKET_TYPE)) == NULL) {
// give up!
Trace(RIP_REQUEST_TRACE,
"RIP request discarded on if %d because cannot allocate response packet\n",
icbp->InterfaceIndex);
return;
}
else
{
// init the send packet
respwip->icbp = icbp;
respwip->AdapterIndex = icbp->AdapterBindingInfo.AdapterIndex;
resphdrp = respwip->Packet;
}
}
if(IsRoute(hdrp + reqlen + NE_NETNUMBER, &IpxRoute)) {
// check if we can route the packet
// the route should be on a different interface index than the
// received packet. For the global WAN net, the interface index is
// the GLOBAL_INTERFACE_INDEX
if(IsRouteAdvertisable(icbp, &IpxRoute)) {
// we can route it -> answer to it
// fill in the network entry structure in the packet with the
// info from the route entry
SetNetworkEntry(resphdrp + resplen, &IpxRoute, icbp->LinkTickCount);
// increment the response length to the next response entry
resplen += NE_ENTRYSIZE;
}
}
else
{
GETLONG2ULONG(&network, hdrp + reqlen + NE_NETNUMBER);
Trace(RIP_REQUEST_TRACE,
"RIP Request on if %d : Route not found for net %x\n",
icbp->InterfaceIndex,
network);
}
}
// We are done answering this request.
// Check if any response has been generated
if(resplen == RIP_INFO) {
// no response generated for this packet
if(respwip != NULL) {
FreeWorkItem(respwip);
}
return;
}
// set the response packet header (src becomes dest)
SetRipIpxHeader(resphdrp,
icbp, // sets the src&dst net, src node and src socket
hdrp + IPXH_SRCNODE,
hdrp + IPXH_SRCSOCK,
RIP_RESPONSE);
// set the new packet length
PUTUSHORT2SHORT(resphdrp + IPXH_LENGTH, resplen);
// send the response
if(SendSubmit(respwip) != NO_ERROR) {
FreeWorkItem(respwip);
}
}
//***
//
// Function: RipResponse
//
// Descr: Updates the routing table with the response info
//
// Params: Packet
//
// Returns: none
//
// Remark: >> called with the interface lock held <<
//
//***
VOID
RipResponse(PWORK_ITEM wip)
{
PICB icbp;
USHORT resplen; // offset of the next response network entry
USHORT pktlen; // IPX packet length
PUCHAR hdrp; // ptr to the packet header
USHORT nrofhops;
USHORT tickcount;
IPX_ROUTE IpxRoute;
ULONG i;
// get a ptr to this ICB
icbp = wip->icbp;
// check if LISTEN TO RIP UPDATES is enabled on this interface
if(icbp->IfConfigInfo.Listen != ADMIN_STATE_ENABLED) {
Trace(RIP_RESPONSE_TRACE,
"RIP Response on if %d : discard response packet because LISTEN is DISABLED\n",
icbp->InterfaceIndex);
return;
}
// get a ptr to the received response packet header
hdrp = wip->Packet;
// get received response packet length
GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH);
// check the source address of the sender. If different then what is locally
// configured log an error.
if(memcmp(hdrp + IPXH_SRCNET, icbp->AdapterBindingInfo.Network, 4)) {
Trace(RIP_ALERT,
"The router at %.2x%.2x%.2x%.2x%.2x%.2x claims the local interface # %d has network number %.2x%.2x%.2x%.2x !\n",
*(hdrp + IPXH_SRCNODE),
*(hdrp + IPXH_SRCNODE + 1),
*(hdrp + IPXH_SRCNODE + 2),
*(hdrp + IPXH_SRCNODE + 3),
*(hdrp + IPXH_SRCNODE + 4),
*(hdrp + IPXH_SRCNODE + 5),
icbp->InterfaceIndex,
*(hdrp + IPXH_SRCNET),
*(hdrp + IPXH_SRCNET + 1),
*(hdrp + IPXH_SRCNET + 2),
*(hdrp + IPXH_SRCNET + 3));
IF_LOG (EVENTLOG_WARNING_TYPE) {
LPWSTR pname[1] = {icbp->InterfaceName};
RouterLogWarningDataW (RipEventLogHdl,
ROUTERLOG_IPXRIP_LOCAL_NET_NUMBER_CONFLICT,
1, pname,
10, hdrp+IPXH_SRCNET);
}
return;
}
// For each network entry:
// chcek if it passes the acceptance filter and if yes:
// add it to our routing table if route not down
// delete it from the routing table if route down
for(resplen = RIP_INFO, i = 0;
((resplen+NE_ENTRYSIZE) <= pktlen) && (i < 50);
resplen += NE_ENTRYSIZE, i++) {
// check if there is an entry left in the packet
if(resplen + NE_ENTRYSIZE > pktlen) {
Trace(RIP_ALERT, "RipResponse: Invalid length for last network entry in the packet, discard entry!\n");
continue;
}
// check if it passes the acceptance filter
if(!PassRipListenFilter(icbp, hdrp + resplen + NE_NETNUMBER)) {
Trace(RIP_RESPONSE_TRACE,
"RIP Response on if %d : do not accept net %.2x%.2x%.2x%.2x because of LISTEN filter\n",
icbp->InterfaceIndex,
*(hdrp + IPXH_SRCNET),
*(hdrp + IPXH_SRCNET + 1),
*(hdrp + IPXH_SRCNET + 2),
*(hdrp + IPXH_SRCNET + 3));
continue;
}
// check if the network route is up or down
GETSHORT2USHORT(&nrofhops, hdrp + resplen + NE_NROFHOPS);
if(nrofhops < 16) {
// pmay: U270476. Disregard routes with 0 hop count
//
if (nrofhops == 0)
{
continue;
}
// if there is a bogus network number advertised in this packet
// like 0 or FFFFFFFF ignore it.
if(!memcmp(hdrp + resplen + NE_NETNUMBER, nullnet, 4)) {
continue;
}
if(!memcmp(hdrp + resplen + NE_NETNUMBER, bcastnet, 4)) {
continue;
}
// should not accept route for a directly connected net
if(IsRoute(hdrp + resplen + NE_NETNUMBER, &IpxRoute) &&
(IpxRoute.Protocol == IPX_PROTOCOL_LOCAL)) {
continue;
}
// should not accept the route if it has a bad number of ticks
// like 0 or > 60000.
GETSHORT2USHORT(&IpxRoute.TickCount, hdrp + resplen + NE_NROFTICKS);
if((IpxRoute.TickCount == 0) ||
(IpxRoute.TickCount > 60000)) {
continue;
}
// Add (update) this route to the routing table
IpxRoute.InterfaceIndex = icbp->InterfaceIndex;
IpxRoute.Protocol = IPX_PROTOCOL_RIP;
memcpy(IpxRoute.Network, hdrp + resplen + NE_NETNUMBER, 4);
// if this route is learned over a point to point wan, next hop doesn't
// make sense
if(icbp->InterfaceType == PERMANENT) {
memcpy(IpxRoute.NextHopMacAddress, hdrp + IPXH_SRCNODE, 6);
}
else
{
memcpy(IpxRoute.NextHopMacAddress, bcastnode, 6);
}
GETSHORT2USHORT(&IpxRoute.HopCount, hdrp + resplen + NE_NROFHOPS);
if(IpxRoute.HopCount == 15) {
IpxRoute.Flags = DO_NOT_ADVERTISE_ROUTE;
AddRipRoute(&IpxRoute, RouteTimeToLiveSecs(icbp));
}
else
{
IpxRoute.Flags = 0;
// add it to the table
switch(icbp->InterfaceType) {
case REMOTE_WORKSTATION_DIAL:
// this RIP advertisment comes from a remote client
// we should accept it only if this is its internal net and if
// it doesn't conflict with a network we already have
if ((memcmp(icbp->RemoteWkstaInternalNet, nullnet, 4)==0)
|| (memcmp(icbp->RemoteWkstaInternalNet, IpxRoute.Network, 4)==0)) {
// none added so far as internal net for this client
if (!IsRoute(IpxRoute.Network, NULL)) {
// we assume this is its internal net and it will be
// cleaned up when interface is disconnected
AddRipRoute(&IpxRoute, INFINITE);
memcpy(icbp->RemoteWkstaInternalNet,
IpxRoute.Network,
4);
}
}
// do not accept any more advertisments from this client
return;
case LOCAL_WORKSTATION_DIAL:
// the interface is the local host dialed out.
// routes received by it should not be advertised over any
// interface but kept only for internal routing
if (!IsRoute(IpxRoute.Network, NULL)) {
IpxRoute.Flags = DO_NOT_ADVERTISE_ROUTE;
AddRipRoute(&IpxRoute, INFINITE);
}
break;
default:
AddRipRoute(&IpxRoute, RouteTimeToLiveSecs(icbp));
break;
}
}
}
else
{
// Delete this route from the routing table
IpxRoute.InterfaceIndex = icbp->InterfaceIndex;
IpxRoute.Protocol = IPX_PROTOCOL_RIP;
memcpy(IpxRoute.Network, hdrp + resplen + NE_NETNUMBER, 4);
memcpy(IpxRoute.NextHopMacAddress, hdrp + IPXH_SRCNODE, 6);
GETSHORT2USHORT(&IpxRoute.TickCount, hdrp + resplen + NE_NROFTICKS);
GETSHORT2USHORT(&IpxRoute.HopCount, hdrp + resplen + NE_NROFHOPS);
// delete it from the table
DeleteRipRoute(&IpxRoute);
}
}
}
/*++
Function: StartGenResponse
Descr: Creates a work item (packet) of type general response
Creates a RTM enumeration handle
Starts filling in a packet from RTM using split horizon
Sends the packet
--*/
VOID
StartGenResponse(PICB icbp,
PUCHAR dstnodep, // dst node to send gen resp
PUCHAR dstsocket) // dst socket to send gen resp
{
PWORK_ITEM wip;
HANDLE EnumHandle;
PUCHAR hdrp; // gen resp ipx packet header
// allocate a work item
if((wip = AllocateWorkItem(GEN_RESPONSE_PACKET_TYPE)) == NULL) {
return;
}
// init the work item
wip->icbp = icbp;
wip->AdapterIndex = icbp->AdapterBindingInfo.AdapterIndex;
// create an RTM enumeration handle
if((EnumHandle = CreateBestRoutesEnumHandle()) == NULL) {
FreeWorkItem(wip);
return;
}
wip->WorkItemSpecific.WIS_EnumRoutes.RtmEnumerationHandle = EnumHandle;
// make the first gen response packet
if(MakeRipGenResponsePacket(wip,
dstnodep,
dstsocket) == EMPTY_PACKET) {
// no routes to advertise for this general response
CloseEnumHandle(EnumHandle);
FreeWorkItem(wip);
return;
}
// Send the gen response on the associated adapter
if(IfRefSendSubmit(wip) != NO_ERROR) {
// !!!
CloseEnumHandle(EnumHandle);
FreeWorkItem(wip);
}
}
/*++
Function: IfCompleteGenResponse
Descr: Completes the gen response work item either by terminating it
if no more routes to advertise or by
getting the rest of the routes up to one packet and sending
the packet
--*/
VOID
IfCompleteGenResponse(PWORK_ITEM wip)
{
USHORT opcode;
PICB icbp;
HANDLE EnumHandle;
USHORT pktlen;
EnumHandle = wip->WorkItemSpecific.WIS_EnumRoutes.RtmEnumerationHandle;
// first off - check the interface status
icbp = wip->icbp;
GETSHORT2USHORT(&pktlen, wip->Packet + IPXH_LENGTH);
// check that:
// 1. the interface is up
// 2. this was not the last packet in the response
if((icbp->IfStats.RipIfOperState != OPER_STATE_UP) ||
(pktlen < FULL_PACKET)) {
CloseEnumHandle(EnumHandle);
FreeWorkItem(wip);
return;
}
// make the next gen response packet
if(MakeRipGenResponsePacket(wip,
wip->Packet + IPXH_DESTNODE,
wip->Packet + IPXH_DESTSOCK) == EMPTY_PACKET) {
// no more routes to advertise in this gen response
CloseEnumHandle(EnumHandle);
FreeWorkItem(wip);
return;
}
// Send the gen response on the associated adapter
if(IfRefSendSubmit(wip) != NO_ERROR) {
CloseEnumHandle(EnumHandle);
FreeWorkItem(wip);
}
}
ULONG
RouteTimeToLiveSecs(PICB icbp)
{
if(icbp->IfConfigInfo.PeriodicUpdateInterval == MAXULONG) {
return INFINITE;
}
else
{
return (AGE_INTERVAL_MULTIPLIER(icbp)) * (PERIODIC_UPDATE_INTERVAL_SECS(icbp));
}
}