#include "allinc.h" DWORD RtmEventCallback ( IN RTM_ENTITY_HANDLE hRtmHandle, IN RTM_EVENT_TYPE retEvent, IN PVOID pContext1, IN PVOID pContext2 ) /*++ Routine Description: This callback is given by RTM when we have changed dests to process. We just queue a work item to process changed destinations. Arguments: hRtmHandle - Handle that we got during registration retEvent - Event type - we only handle events of type "more changes available" for now pContext1 - Notification handle on which changes are available pContext2 - Context supplied during notification registration time Return Value: Status of the operation. --*/ { DWORD dwResult; // Only "change notifications available" is supported if (retEvent != RTM_CHANGE_NOTIFICATION) { return ERROR_NOT_SUPPORTED; } return ((HANDLE) pContext1) == g_hNotification ? ProcessChanges(g_hNotification) : ProcessDefaultRouteChanges( g_hDefaultRouteNotification ); } DWORD WINAPI ProcessChanges ( IN HANDLE hNotifyHandle ) /*++ Routine Description: Upon learning that we have changed destinations to process, this function gets called. We retrieve all destinations to process and take appropriate action. Arguments: hRtmHandle - RTM registration handle hNotifyHandle - Handle correponding to the change notification that is being signalled Return Value: Status of the operation. --*/ { PRTM_DEST_INFO pDestInfo; PRTM_ROUTE_INFO pRouteInfo; DWORD dwDests; DWORD dwResult; BOOL bMark = FALSE; TraceEnter("ProcessChanges"); pRouteInfo = HeapAlloc( IPRouterHeap, 0, RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); if (pRouteInfo == NULL) { Trace1( ERR, "ProcessChanges : error allocating %d bytes for " "route info", RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); TraceLeave("ProcessChanges"); return ERROR_NOT_ENOUGH_MEMORY; } pDestInfo = HeapAlloc( IPRouterHeap, 0, RTM_SIZE_OF_DEST_INFO(g_rtmProfile.NumberOfViews) ); if (pDestInfo == NULL) { Trace1( ERR, "ProcessChanges : error allocating %d bytes for " "dest. info", RTM_SIZE_OF_DEST_INFO(g_rtmProfile.NumberOfViews) ); HeapFree(IPRouterHeap, 0, pRouteInfo); TraceLeave("ProcessChanges"); return ERROR_NOT_ENOUGH_MEMORY; } dwDests = 1; // Get each changed dest from the table do { RtmGetChangedDests(g_hLocalRoute, hNotifyHandle, &dwDests, pDestInfo); if (dwDests < 1) { break; } // // For default routes, mark the route so that future changes // are managed by ProcessDefaultRouteChanges. // // We need to do this here so that default routes added by // routing protocols RIP/OSPF are marked for change notification // These default routes are added by entities other than // RouterManager. Default routes added by RM i.e STATIC, // AUTO-STATIC and NETMGMT default routes are already marked for // change notification when they are added by RM. // // By marking routing protocol default routes here we make sure // that all default routes are subsequently handled by marked changed // mechanism (ProcessDefaultRouteChanges). // if (pDestInfo->DestAddress.NumBits is 0) { TraceRoute2( ROUTE, "Checking dest %d.%d.%d.%d/%d is marked", PRINT_IPADDR(*(ULONG *)pDestInfo->DestAddress.AddrBits), PRINT_IPADDR(pDestInfo->DestAddress.NumBits) ); dwResult = RtmIsMarkedForChangeNotification( g_hNetMgmtRoute, g_hDefaultRouteNotification, pDestInfo->DestHandle, &bMark ); if (dwResult is NO_ERROR) { if (bMark) { // // default route is already marked, nothing further // to do here. This default route change will be // handled by ProcessDefaultRouteChanges // TraceRoute0( ROUTE, "ProcessChanges : Route 0/0 is already marked" ); RtmReleaseChangedDests(g_hLocalRoute, hNotifyHandle, dwDests, pDestInfo); continue; } // // Default route is not marked, mark it // dwResult = RtmMarkDestForChangeNotification( g_hNetMgmtRoute, g_hDefaultRouteNotification, pDestInfo->DestHandle, TRUE ); if (dwResult isnot NO_ERROR) { // // Failed to mark 0/0 route. The consequence is that // only best route changes are processed. We will // have to live with the fact that we cannot // install multiple NETMGMT default routes since // this is performed by the mark dest. change // processing (in ProcessDefaultRouteChanges) // Trace1( ERR, "ProcessChanges: error %d marking default route", dwResult ); } } else { // // Failed to check is 0/0 destination has been // marked for change notification // - Refer previous comment // Trace1( ERR, "ProcessChanges: error %d checking if default route " "marked", dwResult ); } } // Check if we have a route in Unicast view if (pDestInfo->BelongsToViews & RTM_VIEW_MASK_UCAST) { // This is either a new or update route // Update the same route in KM Frwder ASSERT(pDestInfo->ViewInfo[0].ViewId is RTM_VIEW_ID_UCAST); dwResult = RtmGetRouteInfo(g_hLocalRoute, pDestInfo->ViewInfo[0].Route, pRouteInfo, NULL); // An error mean route just got deleted // Ignore this change as it is obsolete if (dwResult is NO_ERROR) { ChangeRouteWithForwarder(&pDestInfo->DestAddress, pRouteInfo, TRUE, TRUE); RtmReleaseRouteInfo(g_hLocalRoute, pRouteInfo); } } else { // The last UCAST route has been deleted // Delete the same route from KM Frwder // // Check to make sure that the route was added to // the forwarder. If not there is no need to // ChangeRouteWithForwarder. // // This rather kludgy fix was done for 446075 // The problem here is really a fallout of // of RTMv2 behavior and how IPRTRMGR uses // RTM. // // Routes added to RTM and not to the TCP/IP // stack are marked as such in the Flags1 field // of the RTM_ROUTE_INFO struture. (they have // the IP_STACK_ROUTE bit turned off). // // The problem arises when a particular destination // is no longer reachable i.e. all routes to it have // been deleted. When a change notification for this // is processed by IPRTRMGR, it no longer has access // to the RTM_ROUTE_INFO STUCTURE for the last route // to the destination and consequently no access to // the flags field mentioned above. Hence routes // were being deleted from the the TCP/IP stack that // had not been added by IPRTRMGR. // // In the normal case this is not a problem. However // for host routes added by PPTP directly to TCP/IP stack // a problem arises as follows: // // 1. Host route to VPN server Route added by PPTP to TCP // 2. IPRTRMGR notified of route by TCP. // 3. IPRTRMGR added route to RTM with stack bit cleared // as we do not want to readd this route to stack. // 4. RTM notifies IPRTRMGR to new best route. // 5. IPRTRMGR skips adding this route to the stack as // its stack bit is cleared. // // 6. Host route to VPN server Route deleted by PPTP to TCP // 7. IPRTRMGR notified of route deletion by TCP // 8. IPRTRMGR deleted route from RTM // 9. RTM notified IPRTRMGR of route/dest deletion // 10. IPRTRMGR having no idea if this was added to TCP // deletes this route from TCP. // // Somewhere between steps 7 and 10, PPTP adds the route // to TCP again. Step 10 deleted the new route in TCP in // response to the old route being deleted thereby // disabling the new PPTP connection. // // To get around this problem we set state in the // destination RTM_DEST_INFO to flag this condition. This // is not a complete fix, allows IPRTRMGR to not delete // routes from TCP that were added by it in the first // place. // // Refer AddRtmRoute to figure out for which routes this // state is set. // TraceRoute2( ROUTE, "Route delete notification for " "%d.%d.%d.%d/%d", PRINT_IPADDR(*(ULONG *)pDestInfo->DestAddress.AddrBits), pDestInfo->DestAddress.NumBits ); // // if this is a host route // if(pDestInfo->DestAddress.NumBits is HOST_MASK_LENGTH) { PBYTE pbOpaque; do { // // 1. Get the opaque pointer for NETMGMT // dwResult = RtmGetOpaqueInformationPointer( g_hNetMgmtRoute, pDestInfo->DestHandle, &pbOpaque ); if(dwResult isnot NO_ERROR) { Trace1( ERR, "AddRtmRoute : error %d retrieving opaque " "info", dwResult ); break; } // // 2. Check if this route was set to stack // if(*((PDWORD) pbOpaque) isnot RTM_NOT_STACK_ROUTE) { TraceRoute1( ROUTE, "Host route with Stack bit 0x%x", *((PDWORD) pbOpaque) ); // // 3.1 If so delete it. // ChangeRouteWithForwarder( &pDestInfo->DestAddress, NULL, FALSE, TRUE ); } else { // // 3.2 Otherwise move on. // TraceRoute1( ROUTE, "Stack bit not set on host " "route, Skipping deleting 0x%x", *((PDWORD) pbOpaque) ); } }while( FALSE ); } else { TraceRoute0(ROUTE, "Deleting a subnet route" ); ChangeRouteWithForwarder(&pDestInfo->DestAddress, NULL, FALSE, TRUE); } } RtmReleaseChangedDests(g_hLocalRoute, hNotifyHandle, dwDests, pDestInfo); } while (TRUE); HeapFree(IPRouterHeap, 0, pRouteInfo); HeapFree(IPRouterHeap, 0, pDestInfo); TraceLeave("ProcessChanges"); return NO_ERROR; } DWORD WINAPI ProcessDefaultRouteChanges( IN HANDLE hNotifyHandle ) /*++ Routine Description: This function is invoked in response to changes to the default route. If the best default route is owned by protocol PROTO_IP_NETMGMT enumerate all PROTO_IP_NETMGMT routes for default route 0/0 and set them as one multihop route to the forwarder Arguments: hRtmHandle - RTM registration handle hNotifyHandle - Handle correponding to the change notification that is being signalled Return Value: NO_ERROR - Success System error code - Otherwise --*/ { PRTM_DEST_INFO pDestInfo; PRTM_ROUTE_INFO pRouteInfo; DWORD dwDests; DWORD dwResult; TraceEnter("ProcessDefaultRouteChanges"); pRouteInfo = HeapAlloc( IPRouterHeap, 0, RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); if (pRouteInfo == NULL) { Trace1( ERR, "ProcessDefaultRouteChanges : error allocating %d bytes for " "route info", RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); TraceLeave("ProcessDefaultRouteChanges"); return ERROR_NOT_ENOUGH_MEMORY; } pDestInfo = HeapAlloc( IPRouterHeap, 0, RTM_SIZE_OF_DEST_INFO(g_rtmProfile.NumberOfViews) ); if (pDestInfo == NULL) { Trace1( ERR, "ProcessDefaultRouteChanges : error allocating %d bytes for " "dest. info", RTM_SIZE_OF_DEST_INFO(g_rtmProfile.NumberOfViews) ); HeapFree(IPRouterHeap, 0, pRouteInfo); TraceLeave("ProcessDefaultRouteChanges"); return ERROR_NOT_ENOUGH_MEMORY; } do { // // retreive changed dests // dwDests = 1; dwResult = RtmGetChangedDests( g_hNetMgmtRoute, hNotifyHandle, &dwDests, pDestInfo ); if ((dwResult isnot NO_ERROR) and (dwResult isnot ERROR_NO_MORE_ITEMS)) { Trace1( ERR, "ProcessDefaultRouteChanges: error %d retrieving changed dests", dwResult ); break; } if (dwDests < 1) { // // no more dests to enumerate // break; } do { // // Make sure this the default route 0/0. This functions // only processes default route changes. // if ((pDestInfo->DestAddress.NumBits isnot 0) or (*((ULONG *)pDestInfo->DestAddress.AddrBits) isnot 0)) { Trace2( ERR, "ProcessDefaultRouteChanges: Not default route %d.%d.%d.%d/%d", PRINT_IPADDR(*((ULONG *)pDestInfo->DestAddress.AddrBits)), pDestInfo->DestAddress.NumBits ); break; } // // If all routes to 0/0 have been deleted, // delete it from the forwarder too. // if (!(pDestInfo->BelongsToViews & RTM_VIEW_MASK_UCAST)) { dwResult = ChangeRouteWithForwarder( &(pDestInfo->DestAddress), NULL, FALSE, TRUE ); break; } // // A route to 0/0 was added/updated // if (pDestInfo->ViewInfo[0].Owner isnot g_hNetMgmtRoute) { // // Default route is not owned by PROTO_IP_NETMGT // Add only the best route to forwarder // TraceRoute1( ROUTE, "ProcessDefaultRouteChanges: Adding non-NetMgmt" " route to forwarder, owner RTM handle 0x%x", pDestInfo->ViewInfo[0].Owner ); dwResult = RtmGetRouteInfo( g_hNetMgmtRoute, pDestInfo->ViewInfo[0].Route, pRouteInfo, NULL ); if (dwResult is NO_ERROR) { ChangeRouteWithForwarder( &pDestInfo->DestAddress, pRouteInfo, TRUE, TRUE ); dwResult = RtmReleaseRouteInfo( g_hNetMgmtRoute, pRouteInfo ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "ProcessDefaultRouteChanges: Failed " "to release route info", dwResult ); } } break; } // // Default route owned by PROTO_IP_NETMGMT // // // First delete existing 0/0 from the TCP/IP forwarder // dwResult = ChangeRouteWithForwarder( &(pDestInfo->DestAddress), NULL, FALSE, TRUE ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "ProcessDefaultRouteChanges: error %d deleting " "old NetMgmt default routes from forwarder", dwResult ); // break; } // // Second add all NETMGMT 0/0 to the TCP/IP forwarder // AddNetmgmtDefaultRoutesToForwarder(pDestInfo); } while( FALSE ); // // release handles to changed destinations // dwResult = RtmReleaseChangedDests( g_hNetMgmtRoute, hNotifyHandle, dwDests, pDestInfo ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "ProcessDefaultRouteChanges: error %d releasing dests ", dwResult ); } } while ( TRUE ); HeapFree(IPRouterHeap, 0, pRouteInfo); HeapFree(IPRouterHeap, 0, pDestInfo); TraceLeave("ProcessDefaultRouteChanges"); return dwResult; } DWORD WINAPI AddNetmgmtDefaultRoutesToForwarder( PRTM_DEST_INFO pDestInfo ) /*++ Routine Description: This routine enumerates the routes to 0/0 added by protocol PROTO_IP_NETMGT and adds them to the forwarder. This routine is invoked in response to any change to the default route If the best default route is owned by PROTO_IP_NETMGMT, all PROTO_IP_NETMGMT default routes are added to the TCP/IP forwarder. This is required since the TCP/IP stack does dead gateway detection and that required multiple default routes if present to be installed in the stack. An implicit assumption here is that PROTO_IP_NETMGMT routes alone merit this treatment. In case of static or other protocol generated 0/0 routes, only the best route is added to the stack. It is assumed that in the later case(s) the administrator (for static routes) or the protocol has a better idea of routing and so the dead gateway detection is suppressed in the stack by the addition of the best route to 0/0 alone. Arguments: pDestInfo - RTM destination info structure of 0/0 route Return Value : NO_ERROR - Sucess Win32 error code - Otherwise --*/ { DWORD dwResult, dwNumHandles = 0, i; BOOL bRelEnum = FALSE, bRelRoutes = FALSE; PRTM_ROUTE_INFO pRouteInfo; PRTM_ROUTE_HANDLE pHandles; RTM_ENUM_HANDLE hRouteEnum; dwNumHandles = pDestInfo->ViewInfo[0].NumRoutes; pHandles = HeapAlloc( IPRouterHeap, 0, dwNumHandles * sizeof(RTM_ROUTE_HANDLE) ); if (pHandles == NULL) { Trace1( ERR, "AddNetmgmtDefaultRoutesToForwarder: error allocating %d bytes" "for route handles", dwNumHandles * sizeof(RTM_ROUTE_HANDLE) ); return ERROR_NOT_ENOUGH_MEMORY; } pRouteInfo = HeapAlloc( IPRouterHeap, 0, RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); if (pRouteInfo == NULL) { Trace1( ERR, "AddNetmgmtDefaultRoutesToForwarder: error allocating %d bytes" "for route info", RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); HeapFree(IPRouterHeap, 0, pHandles); return ERROR_NOT_ENOUGH_MEMORY; } do { // // Enumerate and add all NETMGMT routes to the forwarder // dwResult = RtmCreateRouteEnum( g_hNetMgmtRoute, pDestInfo->DestHandle, RTM_VIEW_MASK_UCAST, RTM_ENUM_OWN_ROUTES, NULL, 0, NULL, 0, &hRouteEnum ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "AddNetmgmtDefaultRoutesToForwarder: error %d creating route " "enumeration", dwResult ); break; } bRelEnum = TRUE; dwResult = RtmGetEnumRoutes( g_hNetMgmtRoute, hRouteEnum, &dwNumHandles, pHandles ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "ProcessDefaultRouteChanges:error %d enumerating " "routes", dwResult ); break; } bRelRoutes = TRUE; // // Change route with the forwarder // for (i = 0; i < dwNumHandles; i++) { dwResult = RtmGetRouteInfo( g_hNetMgmtRoute, pHandles[i], pRouteInfo, NULL ); if (dwResult is NO_ERROR) { ChangeRouteWithForwarder( &(pDestInfo->DestAddress), pRouteInfo, TRUE, FALSE ); dwResult = RtmReleaseRouteInfo( g_hNetMgmtRoute, pRouteInfo ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "ProcessDefaultRouteChanges: error %d releasing " "route info ", dwResult ); } } else { Trace2( ERR, "ProcessDefaultRouteChanges: error %d getting route " "info for route %d", dwResult, i ); } } } while( FALSE ); // // Release handles // if (bRelRoutes) { Trace0(ROUTE, "Releasing routes to 0/0"); dwResult = RtmReleaseRoutes( g_hNetMgmtRoute, dwNumHandles, pHandles ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "ProcessDefaultRouteChanges: error %d deleting enum " "handle", dwResult ); } } if (bRelEnum) { Trace0(ROUTE, "Releasing route enum for 0/0"); dwResult = RtmDeleteEnumHandle( g_hNetMgmtRoute, hRouteEnum ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "ProcessDefaultRouteChanges: error %d deleting enum " "handle", dwResult ); } } HeapFree(IPRouterHeap, 0, pHandles); HeapFree(IPRouterHeap, 0, pRouteInfo); return dwResult; } DWORD AddRtmRoute ( IN HANDLE hRtmHandle, IN PINTERFACE_ROUTE_INFO pRtInfo, IN DWORD dwRouteFlags, IN DWORD dwNextHopMask, IN DWORD dwTimeToLive, OUT HANDLE *phRtmRoute ) /*++ Routine Description: Adds a route to RTM with the specified route information. Arguments: hRtmHandle - RTM registration handle used in RTM calls pRtInfo - dwNextHopMask - dwTimeToLive - Time for which the route is kept in RTM before being deleted (value is seconds). Return Value: Status of the operation. --*/ { PRTM_NET_ADDRESS pDestAddr; PRTM_ROUTE_INFO pRouteInfo; RTM_NEXTHOP_INFO rniInfo; DWORD dwFlags; DWORD dwResult; HANDLE hNextHopHandle; PADAPTER_INFO pBinding; // Initialize output before caling ops if (ARGUMENT_PRESENT(phRtmRoute)) { *phRtmRoute = NULL; } pDestAddr = HeapAlloc( IPRouterHeap, 0, sizeof(RTM_NET_ADDRESS) ); if (pDestAddr == NULL) { Trace1( ERR, "AddRtmRoute : error allocating %d bytes" "for dest. address", RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); return ERROR_NOT_ENOUGH_MEMORY; } pRouteInfo = HeapAlloc( IPRouterHeap, 0, RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); if (pRouteInfo == NULL) { Trace1( ERR, "AddRtmRoute : error allocating %d bytes" "for route info", RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); HeapFree(IPRouterHeap, 0, pDestAddr); return ERROR_NOT_ENOUGH_MEMORY; } // // Add a next hop if not already present // RTM_IPV4_MAKE_NET_ADDRESS(&rniInfo.NextHopAddress, pRtInfo->dwRtInfoNextHop, 32); rniInfo.InterfaceIndex = pRtInfo->dwRtInfoIfIndex; rniInfo.EntitySpecificInfo = (PVOID) (ULONG_PTR)dwNextHopMask; rniInfo.Flags = 0; rniInfo.RemoteNextHop = NULL; hNextHopHandle = NULL; dwResult = RtmAddNextHop(hRtmHandle, &rniInfo, &hNextHopHandle, &dwFlags); if (dwResult is NO_ERROR) { TraceRoute2( ROUTE, "Route to %d.%d.%d.%d/%d.%d.%d.%d", PRINT_IPADDR(pRtInfo->dwRtInfoDest), PRINT_IPADDR(pRtInfo->dwRtInfoMask) ); TraceRoute4( ROUTE, "Next Hop %d.%d.%d.%d/%d.%d.%d.%d, If 0x%x, handle is 0x%x", PRINT_IPADDR(pRtInfo->dwRtInfoNextHop), PRINT_IPADDR(dwNextHopMask), pRtInfo->dwRtInfoIfIndex, hNextHopHandle ); dwResult = ConvertRouteInfoToRtm(hRtmHandle, pRtInfo, hNextHopHandle, dwRouteFlags, pDestAddr, pRouteInfo); if (dwResult is NO_ERROR) { // // If we are adding a non-dod route we should // adjust the state of the route to match // that of the interface it is being added on // if ((hRtmHandle == g_hNonDodRoute) || (hRtmHandle == g_hNetMgmtRoute)) { // // Find the binding given the interface id // ENTER_READER(BINDING_LIST); pBinding = GetInterfaceBinding(pRtInfo->dwRtInfoIfIndex); if ((!pBinding) || (!pBinding->bBound)) { // Interface has been deleted meanwhile // or is not bound at this point - quit EXIT_LOCK(BINDING_LIST); return ERROR_INVALID_PARAMETER; } } // // Convert TimeToLive from secs to millisecs // if (dwTimeToLive != INFINITE) { if (dwTimeToLive < (INFINITE / 1000)) { dwTimeToLive *= 1000; } else { dwTimeToLive = INFINITE; } } dwFlags = 0; // // Add the new route using the RTMv2 API call // dwResult = RtmAddRouteToDest(hRtmHandle, phRtmRoute, pDestAddr, pRouteInfo, dwTimeToLive, NULL, 0, NULL, &dwFlags); if ((hRtmHandle == g_hNonDodRoute) || (hRtmHandle == g_hNetMgmtRoute)) { EXIT_LOCK(BINDING_LIST); } // // check if route is 0/0 and route protocol is // PROTO_IP_NETMGMT. If so mark for change notification // if ((pRtInfo->dwRtInfoDest is 0) and (pRtInfo->dwRtInfoMask is 0)) { RTM_DEST_INFO rdi; BOOL bMark; BOOL bRelDest = FALSE; do { TraceRoute2( ROUTE, "Checking dest %d.%d.%d.%d/%d for mark", PRINT_IPADDR(*(ULONG *)pDestAddr->AddrBits), PRINT_IPADDR(pDestAddr->NumBits) ); dwResult = RtmGetExactMatchDestination( g_hNetMgmtRoute, pDestAddr, RTM_THIS_PROTOCOL, RTM_VIEW_MASK_UCAST, &rdi ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "AddRtmRoute: error %d failed to get " "destination 0/0 for change notification", dwResult ); break; } bRelDest = TRUE; dwResult = RtmIsMarkedForChangeNotification( g_hNetMgmtRoute, g_hDefaultRouteNotification, rdi.DestHandle, &bMark ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "AddRtmRoute: error %d failed to check " "destination 0/0 for change notification", dwResult ); break; } if (!bMark) { dwResult = RtmMarkDestForChangeNotification( g_hNetMgmtRoute, g_hDefaultRouteNotification, rdi.DestHandle, TRUE ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "AddRtmRoute: error %d failed to nark " "destination 0/0 for change notification", dwResult ); break; } // // Add route once more, to force marked dest // change notifications to be issued for this // change // dwFlags = 0; dwResult = RtmAddRouteToDest( hRtmHandle, phRtmRoute, pDestAddr, pRouteInfo, dwTimeToLive, NULL, 0, NULL, &dwFlags ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "AddRtmRoute: error %d added route after " "marking destination", dwResult ); break; } TraceRoute2( ROUTE, "Marked dest %d.%d.%d.%d/%d and added", PRINT_IPADDR(*(ULONG *)pDestAddr->AddrBits), pDestAddr->NumBits ); } } while (FALSE); if (bRelDest) { RtmReleaseDestInfo( g_hNetMgmtRoute, &rdi ); } } // // for host routes, added by NETMGMT // if they are not added to the stack // if((pRtInfo->dwRtInfoMask is HOST_ROUTE_MASK) and (pRtInfo->dwRtInfoProto is PROTO_IP_NETMGMT) and (!(dwRouteFlags & IP_STACK_ROUTE))) { RTM_DEST_INFO rdi; BOOL bRelDest = FALSE; PBYTE pbOpaque = NULL; TraceRoute2( ROUTE, "Non-stack Netmgmt host route " "%d.%d.%d.%d/%d", PRINT_IPADDR(*(ULONG *)pDestAddr->AddrBits), pDestAddr->NumBits ); do { // // Retrieve destination // dwResult = RtmGetExactMatchDestination( g_hNetMgmtRoute, pDestAddr, RTM_THIS_PROTOCOL, RTM_VIEW_MASK_UCAST, &rdi ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "AddRtmRoute : error %d retriveing host route " "destination", dwResult ); break; } bRelDest = TRUE; // // get opaque info ptr. // dwResult = RtmLockDestination( g_hNetMgmtRoute, rdi.DestHandle, TRUE, TRUE ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "AddRtmRoute : error %d locking host route " "destination", dwResult ); break; } dwResult = RtmGetOpaqueInformationPointer( g_hNetMgmtRoute, rdi.DestHandle, &pbOpaque ); if(dwResult isnot NO_ERROR) { Trace1( ERR, "AddRtmRoute : error %d retrieving opaque " "info", dwResult ); break; } *((PDWORD) pbOpaque) = RTM_NOT_STACK_ROUTE; dwResult = RtmLockDestination( g_hNetMgmtRoute, rdi.DestHandle, TRUE, FALSE ); if (dwResult isnot NO_ERROR) { Trace1( ERR, "AddRtmRoute : error %d unlocking dest", dwResult ); break; } }while( FALSE ); if (bRelDest) { RtmReleaseDestInfo( g_hNetMgmtRoute, &rdi ); } TraceRoute3( ROUTE, "Non-stack Netmgmt host route " "%d.%d.%d.%d/%d opaq info set, res == %d", PRINT_IPADDR(*(ULONG *)pDestAddr->AddrBits), pDestAddr->NumBits, dwResult ); } } // Release the next hop handle obtained above RtmReleaseNextHops(hRtmHandle, 1, &hNextHopHandle); } HeapFree(IPRouterHeap, 0, pDestAddr); HeapFree(IPRouterHeap, 0, pRouteInfo); return dwResult; } DWORD DeleteRtmRoute ( IN HANDLE hRtmHandle, IN PINTERFACE_ROUTE_INFO pRtInfo ) /*++ Routine Description: Deletes an RTM route with the specified route information. Arguments: hRtmHandle - RTM registration handle used in RTM calls pRtInfo - Return Value: Status of the operation. --*/ { PRTM_NET_ADDRESS pDestAddr; PRTM_ROUTE_INFO pRouteInfo; RTM_NEXTHOP_INFO rniInfo; DWORD dwFlags; DWORD dwResult; HANDLE hRouteHandle; HANDLE hNextHopHandle; pRouteInfo = HeapAlloc( IPRouterHeap, 0, RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); if (pRouteInfo == NULL) { Trace1( ERR, "DeleteRtmRoute : error allocating %d bytes" "for route info", RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); return ERROR_NOT_ENOUGH_MEMORY; } pDestAddr = HeapAlloc( IPRouterHeap, 0, sizeof(RTM_NET_ADDRESS) ); if (pDestAddr == NULL) { Trace1( ERR, "AddRtmRoute : error allocating %d bytes" "for dest. address", RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); HeapFree(IPRouterHeap, 0, pRouteInfo); return ERROR_NOT_ENOUGH_MEMORY; } // // Obtain a handle to the next hop in the route // RTM_IPV4_MAKE_NET_ADDRESS(&rniInfo.NextHopAddress, pRtInfo->dwRtInfoNextHop, 32); rniInfo.InterfaceIndex = pRtInfo->dwRtInfoIfIndex; rniInfo.NextHopOwner = hRtmHandle; dwResult = RtmFindNextHop(hRtmHandle, &rniInfo, &hNextHopHandle, NULL); if (dwResult isnot NO_ERROR) { HeapFree(IPRouterHeap, 0, pDestAddr); HeapFree(IPRouterHeap, 0, pRouteInfo); return dwResult; } // // We can get this route by matching the route's // net addr, its owner and neighbour learnt from // ConvertRouteInfoToRtm(hRtmHandle, pRtInfo, hNextHopHandle, 0, pDestAddr, pRouteInfo); dwResult = RtmGetExactMatchRoute(hRtmHandle, pDestAddr, RTM_MATCH_OWNER | RTM_MATCH_NEIGHBOUR, pRouteInfo, 0, 0, &hRouteHandle); if (dwResult is NO_ERROR) { // // Delete the route found above using the handle // dwResult = RtmDeleteRouteToDest(hRtmHandle, hRouteHandle, &dwFlags); if (dwResult isnot NO_ERROR) { // If delete successful, deref is automatic RtmReleaseRoutes(hRtmHandle, 1, &hRouteHandle); } // Release the route information obtained above RtmReleaseRouteInfo(hRtmHandle, pRouteInfo); } // Release the next hop handle obtained above RtmReleaseNextHops(hRtmHandle, 1, &hNextHopHandle); HeapFree(IPRouterHeap, 0, pDestAddr); HeapFree(IPRouterHeap, 0, pRouteInfo); return dwResult; } DWORD ConvertRouteInfoToRtm( IN HANDLE hRtmHandle, IN PINTERFACE_ROUTE_INFO pRtInfo, IN HANDLE hNextHopHandle, IN DWORD dwRouteFlags, OUT PRTM_NET_ADDRESS pDestAddr, OUT PRTM_ROUTE_INFO pRouteInfo ) { DWORD dwAddrLen; // Fill the destination addr structure RTM_IPV4_LEN_FROM_MASK(dwAddrLen, pRtInfo->dwRtInfoMask); RTM_IPV4_MAKE_NET_ADDRESS(pDestAddr, pRtInfo->dwRtInfoDest, dwAddrLen); // Fill in the route information now ZeroMemory(pRouteInfo, sizeof(RTM_ROUTE_INFO)); pRouteInfo->RouteOwner = hRtmHandle; pRouteInfo->Neighbour = hNextHopHandle; pRouteInfo->PrefInfo.Metric = pRtInfo->dwRtInfoMetric1; pRouteInfo->PrefInfo.Preference = pRtInfo->dwRtInfoPreference; pRouteInfo->BelongsToViews = pRtInfo->dwRtInfoViewSet; // // BUG BUG BUG BUG : // This is broken for future references // if(g_pLoopbackInterfaceCb && pRtInfo->dwRtInfoIfIndex is g_pLoopbackInterfaceCb->dwIfIndex) { pRouteInfo->BelongsToViews &= ~RTM_VIEW_MASK_MCAST; } pRouteInfo->NextHopsList.NumNextHops = 1; pRouteInfo->NextHopsList.NextHops[0] = hNextHopHandle; // an unsigned integer is converted to a shorter // unsigned integer by truncating the high-order bits! pRouteInfo->Flags1 = (UCHAR) dwRouteFlags; pRouteInfo->Flags = (USHORT) (dwRouteFlags >> 16); // Get the preference for this route return ValidateRouteForProtocol(pRtInfo->dwRtInfoProto, pRouteInfo, pDestAddr); // The following information is lost // // dwForwardMetric2,3 // dwForwardPolicy // dwForwardType // dwForwardAge // dwForwardNextHopAS } VOID ConvertRtmToRouteInfo ( IN DWORD ownerProtocol, IN PRTM_NET_ADDRESS pDestAddr, IN PRTM_ROUTE_INFO pRouteInfo, IN PRTM_NEXTHOP_INFO pNextHop, OUT PINTERFACE_ROUTE_INFO pRtInfo ) { pRtInfo->dwRtInfoDest = *(ULONG *)pDestAddr->AddrBits; pRtInfo->dwRtInfoMask = RTM_IPV4_MASK_FROM_LEN(pDestAddr->NumBits); pRtInfo->dwRtInfoIfIndex = pNextHop->InterfaceIndex; pRtInfo->dwRtInfoNextHop = *(ULONG *)pNextHop->NextHopAddress.AddrBits; pRtInfo->dwRtInfoProto = ownerProtocol; pRtInfo->dwRtInfoMetric1 = pRtInfo->dwRtInfoMetric2 = pRtInfo->dwRtInfoMetric3 = pRouteInfo->PrefInfo.Metric; pRtInfo->dwRtInfoPreference = pRouteInfo->PrefInfo.Preference; pRtInfo->dwRtInfoViewSet = pRouteInfo->BelongsToViews; pRtInfo->dwRtInfoPolicy = 0; pRtInfo->dwRtInfoType = 0; pRtInfo->dwRtInfoAge = 0; pRtInfo->dwRtInfoNextHopAS = 0; return; } PINTERFACE_ROUTE_INFO ConvertMibRouteToRouteInfo( IN PMIB_IPFORWARDROW pMibRow ) { PINTERFACE_ROUTE_INFO pRouteInfo = (PINTERFACE_ROUTE_INFO)pMibRow; pMibRow->dwForwardMetric2 = 0; pMibRow->dwForwardMetric3 = 0; pMibRow->dwForwardMetric4 = 0; pMibRow->dwForwardMetric5 = 0; // Make sure Metric1 isn't 0 if (pRouteInfo->dwRtInfoMetric1 is 0) { pRouteInfo->dwRtInfoMetric1 = 1; } // By default put it in both views pRouteInfo->dwRtInfoViewSet = RTM_VIEW_MASK_UCAST | RTM_VIEW_MASK_MCAST; return pRouteInfo; } PMIB_IPFORWARDROW ConvertRouteInfoToMibRoute( IN PINTERFACE_ROUTE_INFO pRouteInfo ) { PMIB_IPFORWARDROW pMibRoute = (PMIB_IPFORWARDROW) pRouteInfo; pMibRoute->dwForwardMetric2 = pMibRoute->dwForwardMetric3 = pMibRoute->dwForwardMetric4 = pMibRoute->dwForwardMetric5 = IRE_METRIC_UNUSED; pMibRoute->dwForwardAge = INFINITE; pMibRoute->dwForwardPolicy = 0; pMibRoute->dwForwardNextHopAS = 0; pMibRoute->dwForwardType = IRE_TYPE_INDIRECT; return pMibRoute; } VOID ConvertRouteNotifyOutputToRouteInfo( IN PIPRouteNotifyOutput pirno, OUT PINTERFACE_ROUTE_INFO pRtInfo ) { ZeroMemory(pRtInfo, sizeof(INTERFACE_ROUTE_INFO)); pRtInfo->dwRtInfoDest = pirno->irno_dest; pRtInfo->dwRtInfoMask = pirno->irno_mask; pRtInfo->dwRtInfoIfIndex = pirno->irno_ifindex; pRtInfo->dwRtInfoNextHop = pirno->irno_nexthop; pRtInfo->dwRtInfoProto = pirno->irno_proto; pRtInfo->dwRtInfoMetric1 = pRtInfo->dwRtInfoMetric2 = pRtInfo->dwRtInfoMetric3 = pirno->irno_metric; pRtInfo->dwRtInfoPreference = ComputeRouteMetric(pirno->irno_proto); pRtInfo->dwRtInfoViewSet = RTM_VIEW_MASK_UCAST | RTM_VIEW_MASK_MCAST; pRtInfo->dwRtInfoType = (pirno->irno_proto == PROTO_IP_LOCAL) ? MIB_IPROUTE_TYPE_DIRECT : MIB_IPROUTE_TYPE_OTHER; pRtInfo->dwRtInfoAge = INFINITE; pRtInfo->dwRtInfoNextHopAS = 0; pRtInfo->dwRtInfoPolicy = 0; return; } DWORD BlockConvertRoutesToStatic ( IN HANDLE hRtmHandle, IN DWORD dwIfIndex, IN DWORD dwProtocolId ) { HANDLE hRtmEnum; RTM_ENTITY_INFO reiInfo; RTM_NET_ADDRESS rnaDest; PRTM_ROUTE_INFO pRouteInfo1; PRTM_ROUTE_INFO pRouteInfo2; RTM_NEXTHOP_INFO nhiInfo; RTM_NEXTHOP_HANDLE hNextHop; PHANDLE hRoutes; DWORD dwHandles; DWORD dwFlags; DWORD dwNumBytes; DWORD i, j, k; BOOL fDeleted; DWORD dwResult; hRoutes = HeapAlloc( IPRouterHeap, 0, g_rtmProfile.MaxHandlesInEnum * sizeof(HANDLE) ); if (hRoutes == NULL) { Trace1( ERR, "BlockConvertRoutesToStatic : error allocating %d bytes" "for route handes", g_rtmProfile.MaxHandlesInEnum * sizeof(HANDLE) ); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return ERROR_NOT_ENOUGH_MEMORY; } dwNumBytes = RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute); pRouteInfo1 = HeapAlloc( IPRouterHeap, 0, dwNumBytes ); if (pRouteInfo1 == NULL) { Trace1( ERR, "BlockConvertRoutesToStatic : error allocating %d bytes" "for route info", dwNumBytes ); HeapFree(IPRouterHeap, 0, hRoutes); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return ERROR_NOT_ENOUGH_MEMORY; } pRouteInfo2 = HeapAlloc( IPRouterHeap, 0, dwNumBytes ); if (pRouteInfo2 == NULL) { Trace1( ERR, "BlockConvertRoutesToStatic : error allocating %d bytes" "for route info", dwNumBytes ); HeapFree(IPRouterHeap, 0, hRoutes); HeapFree(IPRouterHeap, 0, pRouteInfo1); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return ERROR_NOT_ENOUGH_MEMORY; } // // Enum all routes on the interface that we need // dwResult = RtmCreateRouteEnum(hRtmHandle, NULL, RTM_VIEW_MASK_ANY, RTM_ENUM_ALL_ROUTES, NULL, RTM_MATCH_INTERFACE, NULL, dwIfIndex, &hRtmEnum); if(dwResult isnot NO_ERROR) { Trace2(ERR, "BlockConvertRoutesToStatic: Error %d creating handle for %d\n", dwResult, hRtmHandle); HeapFree(IPRouterHeap, 0, hRoutes); HeapFree(IPRouterHeap, 0, pRouteInfo1); HeapFree(IPRouterHeap, 0, pRouteInfo2); return dwResult; } do { dwHandles = g_rtmProfile.MaxHandlesInEnum; dwResult = RtmGetEnumRoutes(hRtmHandle, hRtmEnum, &dwHandles, hRoutes); for (i = 0; i < dwHandles; i++) { fDeleted = FALSE; // Get the route info from the handle if (RtmGetRouteInfo(hRtmHandle, hRoutes[i], pRouteInfo1, &rnaDest) is NO_ERROR) { // Does this match the routing protocol we want ? if ((RtmGetEntityInfo(hRtmHandle, pRouteInfo1->RouteOwner, &reiInfo) is NO_ERROR) && (reiInfo.EntityId.EntityProtocolId is dwProtocolId)) { // // Add new static route with same information // CopyMemory(pRouteInfo2, pRouteInfo1, sizeof(RTM_ROUTE_INFO)); // Adjust the preference to confirm to protocol pRouteInfo2->PrefInfo.Preference = ComputeRouteMetric(PROTO_IP_NT_AUTOSTATIC); // Adjust the neighbour to corr to new protocol if (pRouteInfo1->Neighbour) { // In case we cant get convert the neighbour pRouteInfo2->Neighbour = NULL; if (RtmGetNextHopInfo(hRtmHandle, pRouteInfo1->Neighbour, &nhiInfo) is NO_ERROR) { // Add the same neigbour using new protocol hNextHop = NULL; if (RtmAddNextHop(hRtmHandle, &nhiInfo, &hNextHop, &dwFlags) is NO_ERROR) { pRouteInfo2->Neighbour = hNextHop; } RtmReleaseNextHopInfo(hRtmHandle, &nhiInfo); } } // Adjust the next hops to corr to new protocol for (j = k = 0; j < pRouteInfo1->NextHopsList.NumNextHops; j++) { if (RtmGetNextHopInfo(hRtmHandle, pRouteInfo1->NextHopsList.NextHops[j], &nhiInfo) is NO_ERROR) { // Add the same nexthop using new protocol hNextHop = NULL; if (RtmAddNextHop(hRtmHandle, &nhiInfo, &hNextHop, &dwFlags) is NO_ERROR) { pRouteInfo2->NextHopsList.NextHops[k++] = hNextHop; } RtmReleaseNextHopInfo(hRtmHandle, &nhiInfo); } } pRouteInfo2->NextHopsList.NumNextHops = (USHORT) k; // Add the new route with the next hop information if (k > 0) { dwFlags = 0; if (RtmAddRouteToDest(hRtmHandle, NULL, &rnaDest, pRouteInfo2, INFINITE, NULL, 0, NULL, &dwFlags) is NO_ERROR) { // Route add is successful - delete old route if (RtmDeleteRouteToDest(pRouteInfo1->RouteOwner, hRoutes[i], &dwFlags) is NO_ERROR) { fDeleted = TRUE; } } RtmReleaseNextHops(hRtmHandle, k, pRouteInfo2->NextHopsList.NextHops); } } RtmReleaseRouteInfo(hRtmHandle, pRouteInfo1); } if (!fDeleted) { RtmReleaseRoutes(hRtmHandle, 1, &hRoutes[i]); } } } while (dwResult is NO_ERROR); RtmDeleteEnumHandle(hRtmHandle, hRtmEnum); HeapFree(IPRouterHeap, 0, hRoutes); HeapFree(IPRouterHeap, 0, pRouteInfo1); HeapFree(IPRouterHeap, 0, pRouteInfo2); return NO_ERROR; } DWORD DeleteRtmRoutes ( IN HANDLE hRtmHandle, IN DWORD dwIfIndex, IN BOOL fDeleteAll ) { HANDLE hRtmEnum; PHANDLE hRoutes; DWORD dwHandles; DWORD dwFlags; DWORD i; DWORD dwResult; hRoutes = HeapAlloc( IPRouterHeap, 0, g_rtmProfile.MaxHandlesInEnum * sizeof(HANDLE) ); if (hRoutes == NULL) { Trace1(ERR, "DeleteRtmRoutes: Error allocating %d bytes", g_rtmProfile.MaxHandlesInEnum * sizeof(HANDLE)); return ERROR_NOT_ENOUGH_MEMORY; } dwFlags = fDeleteAll ? 0: RTM_MATCH_INTERFACE; dwResult = RtmCreateRouteEnum(hRtmHandle, NULL, RTM_VIEW_MASK_ANY, RTM_ENUM_OWN_ROUTES, NULL, dwFlags, NULL, dwIfIndex, &hRtmEnum); if(dwResult isnot NO_ERROR) { Trace2(ERR, "DeleteRtmRoutes: Error %d creating handle for %d\n", dwResult, hRtmHandle); HeapFree(IPRouterHeap, 0, hRoutes); return dwResult; } do { dwHandles = g_rtmProfile.MaxHandlesInEnum; dwResult = RtmGetEnumRoutes(hRtmHandle, hRtmEnum, &dwHandles, hRoutes); for (i = 0; i < dwHandles; i++) { if (RtmDeleteRouteToDest(hRtmHandle, hRoutes[i], &dwFlags) isnot NO_ERROR) { // If delete is successful, this is automatic RtmReleaseRoutes(hRtmHandle, 1, &hRoutes[i]); } } } while (dwResult is NO_ERROR); RtmDeleteEnumHandle(hRtmHandle, hRtmEnum); HeapFree(IPRouterHeap, 0, hRoutes); return NO_ERROR; } DWORD DeleteRtmNexthops ( IN HANDLE hRtmHandle, IN DWORD dwIfIndex, IN BOOL fDeleteAll ) { PRTM_NEXTHOP_INFO pNexthop; PHANDLE hNexthops; HANDLE hRtmEnum; DWORD dwHandles; DWORD i; DWORD dwResult; hNexthops = HeapAlloc( IPRouterHeap, 0, g_rtmProfile.MaxHandlesInEnum * sizeof(HANDLE) ); if (hNexthops == NULL) { Trace1(ERR, "DeleteRtmNextHops: Error allocating %d bytes", g_rtmProfile.MaxHandlesInEnum * sizeof(HANDLE)); return ERROR_NOT_ENOUGH_MEMORY; } dwResult = RtmCreateNextHopEnum(hRtmHandle, 0, NULL, &hRtmEnum); if(dwResult isnot NO_ERROR) { Trace2(ERR, "DeleteAllNexthops: Error %d creating handle for %d\n", dwResult, hRtmHandle); HeapFree(IPRouterHeap, 0, hNexthops); return dwResult; } do { dwHandles = g_rtmProfile.MaxHandlesInEnum; dwResult = RtmGetEnumNextHops(hRtmHandle, hRtmEnum, &dwHandles, hNexthops); for (i = 0; i < dwHandles; i++) { if (!fDeleteAll) { // // Make sure that the interface matches // if ((RtmGetNextHopPointer(hRtmHandle, hNexthops[i], &pNexthop) isnot NO_ERROR) || (pNexthop->InterfaceIndex != dwIfIndex)) { RtmReleaseNextHops(hRtmHandle, 1, &hNexthops[i]); continue; } } // We need to delete this next hop here if (RtmDeleteNextHop(hRtmHandle, hNexthops[i], NULL) isnot NO_ERROR) { // If delete is successful, this is automatic RtmReleaseNextHops(hRtmHandle, 1, &hNexthops[i]); } } } while (dwResult is NO_ERROR); RtmDeleteEnumHandle(hRtmHandle, hRtmEnum); HeapFree(IPRouterHeap, 0, hNexthops); return NO_ERROR; }