windows-nt/Source/XPSP1/NT/net/rras/ip/rtrmgr/rtmif.c
2020-09-26 16:20:57 +08:00

988 lines
26 KiB
C

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
routing\ip\rtrmgr\init.c
Abstract:
IP Router Manager code
Revision History:
Gurdeep Singh Pall 6/14/95 Created
--*/
#include "allinc.h"
// ChangeRouteWithForwarder()
//
// Function: If addroute is TRUE this function adds an IP route. If addroute is FALSE
// this function deletes the given route with the forwarder.
//
// Returns: Nothing
//
//
DWORD
ChangeRouteWithForwarder(
PRTM_NET_ADDRESS pDestAddr,
PRTM_ROUTE_INFO pRoute,
BOOL bAddRoute,
BOOL bDelOld
)
{
IPMultihopRouteEntry *pMultiRouteEntry;
IPRouteEntry *pRouteEntry;
IPRouteNextHopEntry *pNexthopEntry;
RTM_ENTITY_INFO entityInfo;
RTM_NEXTHOP_INFO nhiInfo;
PADAPTER_INFO pBinding;
UINT numnexthops, i;
ULONG numbytes;
DWORD dwAddr, dwMask;
UINT dwLen;
ULONG ifindex, nexthop, type;
BOOL bValidNHop;
DWORD context;
DWORD dwLocalNet, dwLocalMask;
DWORD dwResult;
TraceEnter("ChangeRouteWithForwarder");
if(!g_bSetRoutesToStack)
{
Trace0(ROUTE,
"ChangeRouteWithForwarder: SetRoutesToStack is FALSE");
TraceLeave("ChangeRouteWithForwarder");
return NO_ERROR;
}
if (bAddRoute)
{
//
// Ensure that the stack bit is set
//
if (!pRoute || !IsRouteStack(pRoute))
{
if (!pRoute )
{
Trace0(ROUTE,
"Error adding route, route == NULL"
);
}
else
{
Trace1(ROUTE,
"Error adding route, Stack bit == %d",
IsRouteStack(pRoute)
);
}
TraceLeave("ChangeRouteWithForwarder");
return ERROR_INVALID_PARAMETER;
}
// We should have atleast one nexthop
numnexthops = pRoute->NextHopsList.NumNextHops;
if (numnexthops == 0)
{
Trace0(ROUTE,
"Error adding route, no nexthops");
TraceLeave("ChangeRouteWithForwarder");
return ERROR_INVALID_PARAMETER;
}
numbytes = sizeof(IPMultihopRouteEntry) +
(numnexthops - 1) *
sizeof(IPRouteNextHopEntry);
}
else
{
//
// for routes to be deleted, they should be stack
// routes
//
// We do not have any next hops here
numbytes = sizeof(IPMultihopRouteEntry);
}
__try
{
pMultiRouteEntry = _alloca(numbytes);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
pRouteEntry = &pMultiRouteEntry->imre_routeinfo;
//
// Fill the dest and mask for the current route
//
RTM_IPV4_GET_ADDR_AND_LEN(pRouteEntry->ire_dest,
dwLen,
pDestAddr);
pRouteEntry->ire_mask = RTM_IPV4_MASK_FROM_LEN(dwLen);
TraceRoute2(ROUTE,
"route to %d.%d.%d.%d/%d.%d.%d.%d",
PRINT_IPADDR(pRouteEntry->ire_dest),
PRINT_IPADDR(pRouteEntry->ire_mask));
if (!bAddRoute)
{
//
// Prepare to delete old information on dest
//
pRouteEntry->ire_type = IRE_TYPE_INVALID;
pMultiRouteEntry->imre_numnexthops = 0;
Trace2(ROUTE,
"ChangeRouteWithForwarder: Deleting all " \
"routes to %d.%d.%d.%d/%d.%d.%d.%d",
PRINT_IPADDR(pRouteEntry->ire_dest),
PRINT_IPADDR(pRouteEntry->ire_mask));
dwResult = SetIpMultihopRouteEntryToStack(pMultiRouteEntry);
TraceLeave("ChangeRouteWithForwarder");
return dwResult;
}
//
// Get the routing protocol of the route's owner
//
dwResult = RtmGetEntityInfo(g_hLocalRoute,
pRoute->RouteOwner,
&entityInfo);
if (dwResult isnot NO_ERROR)
{
Trace1(ROUTE,
"Error %d retrieving entity info from RTM",
dwResult);
TraceLeave("ChangeRouteWithForwarder");
return dwResult;
}
//
// Prepare to add a multihop route on this dest
//
// Prepare information common to all nexthops
pRouteEntry->ire_proto = entityInfo.EntityId.EntityProtocolId;
pRouteEntry->ire_metric1 = pRoute->PrefInfo.Metric;
pRouteEntry->ire_metric2 = IRE_METRIC_UNUSED;
pRouteEntry->ire_metric3 = IRE_METRIC_UNUSED;
pRouteEntry->ire_metric4 = IRE_METRIC_UNUSED;
pRouteEntry->ire_metric5 = IRE_METRIC_UNUSED;
pRouteEntry->ire_age = 0;
numnexthops = 0;
for (i = 0; i < pRoute->NextHopsList.NumNextHops; i++)
{
// Get and release next hop info as we got a copy
dwResult = RtmGetNextHopInfo(g_hLocalRoute,
pRoute->NextHopsList.NextHops[i],
&nhiInfo);
if (dwResult isnot NO_ERROR)
{
Trace1(ROUTE,
"Error %d retrieving next hop info from RTM",
dwResult);
continue;
}
RtmReleaseNextHopInfo(g_hLocalRoute, &nhiInfo);
// Get the next hop address from the nexthop info
RTM_IPV4_GET_ADDR_AND_LEN(nexthop,
dwLen,
&nhiInfo.NextHopAddress);
TraceRoute3(
ROUTE, "Next Hop %d.%d.%d.%d, If 0x%x, handle is 0x%x",
PRINT_IPADDR(nexthop),
nhiInfo.InterfaceIndex,
pRoute->NextHopsList.NextHops[i]
);
ENTER_READER(BINDING_LIST);
//
// find the binding given the interface id
//
pBinding = GetInterfaceBinding(nhiInfo.InterfaceIndex);
if(!(pBinding))
{
//
// The interface was deleted so lets just get out
//
EXIT_LOCK(BINDING_LIST);
TraceRoute2(ERR,
"**Warning** tried to %s route with interface %d which "
"is no longer present",
bAddRoute?"add":"delete",
nhiInfo.InterfaceIndex);
continue;
}
//
// set adapter index - this is 0xffffffff
// if the nexthop interface is not MAPPED
//
ifindex = pBinding->bBound ? pBinding->dwIfIndex : INVALID_IF_INDEX;
if(((pRouteEntry->ire_proto is PROTO_IP_NT_STATIC) or
(pRouteEntry->ire_proto is PROTO_IP_NT_AUTOSTATIC)) and
(pBinding->ritType is ROUTER_IF_TYPE_FULL_ROUTER))
{
context = pBinding->dwSeqNumber;
TraceRoute1(ROUTE,
"route context : ICB == %d\n\n",
pBinding->dwSeqNumber);
}
else
{
context = 0;
if(ifindex is INVALID_IF_INDEX)
{
Trace3(ERR,
"**Error** Tried to %s route to %d.%d.%d.%d over %d as DOD\n",
bAddRoute?"add":"delete",
PRINT_IPADDR(pRouteEntry->ire_dest),
pBinding->dwIfIndex);
EXIT_LOCK(BINDING_LIST);
continue;
}
}
//
// First we figure out the correct nexthop for p2p links
// For all other links, we take the whatever is given to us
//
if(IsIfP2P(pBinding->ritType))
{
if(pBinding->bBound)
{
TraceRoute2(
ROUTE, "Next Hop %d.%d.%d.%d, remote address %d.%d.%d.%d, "
"bound p2p",
PRINT_IPADDR(nexthop),
PRINT_IPADDR(pBinding->dwRemoteAddress)
);
if (nexthop is 0)
{
nexthop = pBinding->dwRemoteAddress;
}
}
else
{
nexthop = 0;
}
}
//
// Now we figure out if the route is a direct route or indirect routes
// Routes over unconnected demand dial routes are marked OTHER
//
//
// For connected WAN interfaces (P2P with mask of 255.255.255.255) we
// do two checks:
// The next hop should be local address or remote address.
// AR: We used to do the above check but removed it because when
// we set a route over a disconnected interface, we dont
// know the address of the remote endpoint
// If the dest is remote address, then the MASK must be all ONES
// We mark all valid routes as DIRECT
//
//
// For LAN interfaces and WAN with non all ones mask, we check the
// following:
// A direct route to a host must have the Destination as the NextHop
// A direct route to a network must have the the NextHop as one of the
// local interfaces
// The next hop must be on the same subnet as one of the bindings
//
type = IRE_TYPE_OTHER;
if(pBinding->bBound)
{
if((pBinding->dwNumAddresses is 1) and
(pBinding->rgibBinding[0].dwMask is ALL_ONES_MASK))
{
//
// route over P2P link or P2MP link, possibly unnumbered.
//
if(((pBinding->dwRemoteAddress isnot 0) and
(pRouteEntry->ire_dest is pBinding->dwRemoteAddress)) or
(pRouteEntry->ire_dest is nexthop))
{
type = IRE_TYPE_DIRECT;
}
else
{
type = IRE_TYPE_INDIRECT;
}
}
else
{
//
// A route over a non P2P link or a bay style p2p link which
// has a /30 mask
//
bValidNHop = FALSE;
type = IRE_TYPE_INDIRECT;
for(i = 0; i < pBinding->dwNumAddresses; i++)
{
dwLocalMask = pBinding->rgibBinding[i].dwMask;
dwLocalNet = pBinding->rgibBinding[i].dwAddress & dwLocalMask;
if((dwLocalNet is (pRouteEntry->ire_dest & dwLocalMask)) or
(nexthop is IP_LOOPBACK_ADDRESS) or
(nexthop is pBinding->rgibBinding[i].dwAddress))
{
//
// Route to local net or over loopback
//
type = IRE_TYPE_DIRECT;
}
if(((nexthop & dwLocalMask) is dwLocalNet) or
((nexthop is IP_LOOPBACK_ADDRESS)))
{
//
// Next hop is on local net or loopback
// That is good
//
bValidNHop = TRUE;
break;
}
}
if(!bValidNHop and
(pBinding->dwNumAddresses isnot 0) and
(pBinding->ritType isnot ROUTER_IF_TYPE_INTERNAL))
{
Trace0(ERR,
"ERROR - Nexthop not on same network");
for(i = 0; i < pBinding->dwNumAddresses; i ++)
{
Trace3(ROUTE,"AdapterId: %d, %d.%d.%d.%d/%d.%d.%d.%d",
pBinding->dwIfIndex,
PRINT_IPADDR(pBinding->rgibBinding[i].dwAddress),
PRINT_IPADDR(pBinding->rgibBinding[i].dwMask));
}
EXIT_LOCK(BINDING_LIST);
// PrintRoute(ERR, ipRoute);
continue;
}
}
}
EXIT_LOCK(BINDING_LIST);
#if 0
// DGT workaround for bug where stack won't accept
// nexthop of 0.0.0.0. Until Chait fixes this, we'll
// set nexthop to the ifindex.
if (!nexthop)
{
nexthop = ifindex;
}
#endif
//
// Fill the current nexthop info into the route
//
if (numnexthops)
{
// Copy to the next posn in the route
pNexthopEntry =
&pMultiRouteEntry->imre_morenexthops[numnexthops - 1];
pNexthopEntry->ine_iretype = type;
pNexthopEntry->ine_ifindex = ifindex;
pNexthopEntry->ine_nexthop = nexthop;
pNexthopEntry->ine_context = context;
}
else
{
// Copy to the first posn in the route
pRouteEntry->ire_type = type;
pRouteEntry->ire_index = ifindex;
pRouteEntry->ire_nexthop = nexthop;
pRouteEntry->ire_context = context;
}
numnexthops++;
}
pMultiRouteEntry->imre_numnexthops = numnexthops;
pMultiRouteEntry->imre_flags = bDelOld ? IMRE_FLAG_DELETE_DEST : 0;
if (numnexthops > 0)
{
dwResult = SetIpMultihopRouteEntryToStack(pMultiRouteEntry);
if(dwResult isnot NO_ERROR)
{
if(pRouteEntry->ire_nexthop != IP_LOOPBACK_ADDRESS)
{
Trace1(ERR,
"Route addition failed with %x for", dwResult);
PrintRoute(ERR, pMultiRouteEntry);
}
Trace1(ERR,
"Route addition failed with %x for local route", dwResult);
TraceLeave("ChangeRouteWithForwarder");
return dwResult;
}
else
{
Trace0(ROUTE,
"Route addition succeeded for");
PrintRoute(ROUTE, pMultiRouteEntry);
}
}
else
{
Trace0(ERR, "Route not added since there are no next hops" );
PrintRoute(ROUTE, pMultiRouteEntry);
}
TraceLeave("ChangeRouteWithForwarder");
return NO_ERROR;
}
DWORD
WINAPI
ValidateRouteForProtocol(
IN DWORD dwProtoId,
IN PVOID pRouteInfo,
IN PVOID pDestAddr OPTIONAL
)
/*++
Routine Description:
This function is called by the router Manger (and indirectly by routing
protocols) to validate the route info. We set the preference and the
type of the route
Locks:
Acquires the binding lock
This function CAN NOT acquire the ICB lock
Arguments:
dwProtoId Protocols Id
pRoute
Return Value:
NO_ERROR
RtmError code
--*/
{
RTM_DEST_INFO destInfo;
PRTM_ROUTE_INFO pRoute;
RTM_NEXTHOP_INFO nextHop;
PADAPTER_INFO pBinding;
HANDLE hNextHop;
BOOL bValidNHop;
DWORD dwIfIndex;
DWORD dwLocalNet;
DWORD dwLocalMask;
DWORD destAddr;
DWORD destMask;
DWORD nexthop;
DWORD nhopMask;
DWORD dwType;
DWORD dwResult;
UINT i, j;
pRoute = (PRTM_ROUTE_INFO)pRouteInfo;
if (pRoute->PrefInfo.Preference is 0)
{
//
// Map the metric weight based on the weight assigned by admin
//
pRoute->PrefInfo.Preference = ComputeRouteMetric(dwProtoId);
}
//
// This validation applies to the unicast routes only.
// [ This does not apply to INACTIVE routes and such ]
//
if (!(pRoute->BelongsToViews & RTM_VIEW_MASK_UCAST))
{
return NO_ERROR;
}
//
// Get the destination address if it is not specified
//
if (!ARGUMENT_PRESENT(pDestAddr))
{
//
// Get the destination information from the route
//
dwResult = RtmGetDestInfo(g_hLocalRoute,
pRoute->DestHandle,
RTM_BEST_PROTOCOL,
RTM_VIEW_MASK_UCAST,
&destInfo);
if (dwResult != NO_ERROR)
{
Trace0(ERR,
"**ERROR:ValidateRoute: Invalid destination");
return dwResult;
}
pDestAddr = &destInfo.DestAddress;
RtmReleaseDestInfo(g_hLocalRoute, &destInfo);
}
RTM_IPV4_GET_ADDR_AND_MASK(destAddr,
destMask,
(PRTM_NET_ADDRESS)pDestAddr);
//
// If the dest&Mask != dest then the stack will not set this route
// Hence lets do the check here
//
if((destAddr & destMask) isnot destAddr)
{
#if TRACE_DBG
Trace2(ROUTE,
"**ERROR:ValidateRoute: called with Dest %d.%d.%d.%d and Mask %d.%d.%d.%d - This will fail**",
PRINT_IPADDR(destAddr),
PRINT_IPADDR(destMask));
#endif // TRACE_DBG
return ERROR_INVALID_PARAMETER;
}
if((((DWORD)(destAddr & 0x000000FF)) >= (DWORD)0x000000E0) and
(destAddr isnot ALL_ONES_BROADCAST) and
(destAddr isnot LOCAL_NET_MULTICAST))
{
//
// This will catch the CLASS D/E but allow all 1's bcast
//
Trace1(ERR,
"**ERROR:ValidateRoute: Dest %d.%d.%d.%d is invalid",
PRINT_IPADDR(destAddr));
return ERROR_INVALID_PARAMETER;
}
#if 0
// Removed this since metric=0 is legal for routes to the loopback
// interface.
if(pRoute->PrefInfo.Metric is 0)
{
Trace0(ERR,
"**ERROR:ValidateRoute: Metric cant be 0");
return ERROR_INVALID_PARAMETER;
}
#endif
if (pRoute->NextHopsList.NumNextHops == 0)
{
Trace0(ERR,
"**ERROR:ValidateRoute: Zero next hops");
return ERROR_INVALID_PARAMETER;
}
// Make sure each next hop on the route is a valid one
for (i = 0; i < pRoute->NextHopsList.NumNextHops; i++)
{
hNextHop = pRoute->NextHopsList.NextHops[i];
dwResult = RtmGetNextHopInfo(g_hLocalRoute,
hNextHop,
&nextHop);
if (dwResult != NO_ERROR)
{
Trace0(ERR,
"**ERROR:ValidateRoute: Invalid next hop");
return dwResult;
}
dwIfIndex = nextHop.InterfaceIndex;
RTM_IPV4_GET_ADDR_AND_MASK(nexthop,
nhopMask,
(PRTM_NET_ADDRESS)&nextHop.NextHopAddress);
RtmReleaseNextHopInfo(g_hLocalRoute, &nextHop);
// *** Exclusion Begin ***
ENTER_READER(BINDING_LIST);
//
// find the interface given the interface id
//
pBinding = GetInterfaceBinding(dwIfIndex);
if(!pBinding)
{
EXIT_LOCK(BINDING_LIST);
#if TRACE_DBG
Trace0(ERR,
"**ERROR:ValidateRoute: Interface block doesnt exist");
#endif // TRACE_DBG
return ERROR_INVALID_PARAMETER;
}
//
// Set whether the route is P2P
//
if(IsIfP2P(pBinding->ritType))
{
// Note that in the multihop case, we just overwrite value
SetRouteP2P(pRoute);
/*
ipRoute->RR_NextHopAddress.N_NetNumber =
RtlUlongByteSwap(pBinding->dwIfIndex);
ipRoute->RR_NextHopAddress.N_NetMask = ALL_ONES_MASK;
*/
}
/*
//
// If the next hop mask is not set, we need to do so now. Normally
// this shouldnt happen
//
#if ROUTE_DBG
if(!(ipRoute->RR_NextHopAddress.N_NetMask))
{
Trace0(ERR,
"**WARNING:Route doesnt seem to have any next hop mask**");
}
#endif // ROUTE_DBG
*/
//
// Now we figure out if the route is a direct route or indirect routes
// Routes over unconnected demand dial routes are marked OTHER
//
//
// For connected WAN interfaces (P2P with mask of 255.255.255.255) we
// do two checks:
// The next hop should be local address or remote address.
// AR: We used to do the above check but removed it because when
// we set a route over a disconnected interface, we dont
// know the address of the remote endpoint
// If the dest is remote address, then the MASK must be all ONES
// We mark all valid routes as DIRECT
//
//
// For LAN interfaces and WAN with non all ones mask, we check the
// following:
// A direct route to a host to must have the Destination as the NextHop
// A direct route to a network to must have the the NextHop as one of the
// local interfaces
// The next hop must be on the same subnet as one of the bindings
//
dwType = IRE_TYPE_OTHER;
if(pBinding->bBound and IsRouteValid(pRoute))
{
//
// Comment this block out - as we validate this
// next hop for the LAN case below, and dont care
// what it is in the WAN case as we set it to 0.
//
/*
//
// So outgoing interface has a valid address
// and next hop should have been plumbed in
//
if(nexthop && (destAddr is nexthop))
{
//
// A direct host route
//
if(destMask isnot HOST_ROUTE_MASK)
{
EXIT_LOCK(BINDING_LIST);
Trace0(ERR,
"ValidateRoute: Host route with wrong mask");
return ERROR_INVALID_PARAMETER;
}
dwType = IRE_TYPE_DIRECT;
}
*/
if((pBinding->dwNumAddresses is 1) and
(pBinding->rgibBinding[0].dwMask is ALL_ONES_MASK))
{
//
// route over P2P link.
// Set it to indirect and mark it as a P2P route
//
dwType = IRE_TYPE_DIRECT;
//IpRtAssert(IsRouteP2P(pRoute));
}
else
{
//
// A route over a non P2P link possibly unnumbered
//
bValidNHop = FALSE;
dwType = IRE_TYPE_INDIRECT;
for(j = 0; j < pBinding->dwNumAddresses; j++)
{
dwLocalMask = pBinding->rgibBinding[j].dwMask;
dwLocalNet = pBinding->rgibBinding[j].dwAddress & dwLocalMask;
if((dwLocalNet is (destAddr & dwLocalMask)) or
(nexthop is IP_LOOPBACK_ADDRESS) or
//(nexthop is dwLocalNet) or
(nexthop is pBinding->rgibBinding[i].dwAddress))
{
//
// Route to local net or over loopback
//
dwType = IRE_TYPE_DIRECT;
}
if(((nexthop & dwLocalMask) is dwLocalNet) or
((nexthop is IP_LOOPBACK_ADDRESS)))
{
//
// Next hop is on local net or loopback
// That is good
//
bValidNHop = TRUE;
break;
}
}
if(!bValidNHop and
pBinding->dwNumAddresses and
(pBinding->ritType isnot ROUTER_IF_TYPE_INTERNAL))
{
Trace1(ERR,
"ValidateRoute: Nexthop %d.%d.%d.%d not on network",
PRINT_IPADDR(nexthop));
for(j = 0; j < pBinding->dwNumAddresses; j++)
{
Trace3(ROUTE,"AdapterId: %d, %d.%d.%d.%d/%d.%d.%d.%d",
pBinding->dwIfIndex,
PRINT_IPADDR(pBinding->rgibBinding[j].dwAddress),
PRINT_IPADDR(pBinding->rgibBinding[j].dwMask));
}
EXIT_LOCK(BINDING_LIST);
return ERROR_INVALID_PARAMETER;
}
}
}
/*
#if ROUTE_DBG
if(ipRoute->RR_NextHopAddress.N_NetMask isnot dwLocalMask)
{
Trace0(ERR,
"**WARNING:Route doesnt seem to have the right next hop mask**");
PrintRoute(ERR,ipRoute);
ipRoute->RR_NextHopAddress.N_NetMask = dwLocalMask;
}
#endif // ROUTE_DBG
*/
// *** Exclusion End ***
EXIT_LOCK(BINDING_LIST);
}
//
// Set the appropriate route flags in the route - stack flag etc.
//
// pRoute->Flags1 |= IP_STACK_ROUTE;
g_LastUpdateTable[IPFORWARDCACHE] = 0;
return NO_ERROR;
}
DWORD
WINAPI
ValidateRouteForProtocolEx(
IN DWORD dwProtoId,
IN PVOID pRouteInfo,
IN PVOID pDestAddr OPTIONAL
)
/*++
Routine Description:
This function is called by the router Manger (and indirectly by routing
protocols) to validate the route info. We set the preference and the
type of the route
Locks:
Acquires the binding lock
This function CAN NOT acquire the ICB lock
Arguments:
dwProtoId Protocols Id
pRoute
Return Value:
NO_ERROR
RtmError code
--*/
{
DWORD dwResult;
dwResult = ValidateRouteForProtocol(
dwProtoId,
pRouteInfo,
pDestAddr
);
if (dwResult is NO_ERROR)
{
((PRTM_ROUTE_INFO)pRouteInfo)->Flags1 |= IP_STACK_ROUTE;
}
return dwResult;
}