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.
635 lines
15 KiB
635 lines
15 KiB
/*++
|
|
|
|
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));
|
|
}
|
|
}
|