/*++ Copyright (c) 1997 - 98, Microsoft Corporation Module Name: rtm1to2.c Abstract: Contains routines that wrap RTMv2 functions in the RTMv1 API. Author: Chaitanya Kodeboyina (chaitk) 13-Oct-1998 Revision History: --*/ #include "pchrtm.h" #pragma hdrstop #if WRAPPER #include "rtm1to2.h" // Wrapper Globals V1_GLOBAL_INFO V1Globals; DWORD RtmCreateRouteTable ( IN DWORD ProtocolFamily, IN PRTM_PROTOCOL_FAMILY_CONFIG Config ) /*++ Routine Description: Triggers the creation of a new route table corresponding to a protocol family (same as address family in RTMv2) in the default instance of RTMv2 by performing the very first registration in that protocol family. This default registration is also used for mapping RTMv1 operations that do not require a registration handle (V1 enums etc.) to their corresponding RTMv2 operations. Note that all RTMv2 calls require a registration handle. This call also creates a list of all V1 registrations at any time. This is used to automatically deregister all RTMv1 registrations before destroying this route table. We also set up the notification of changes in best routes to the router manager (RM) that invoked this function. Arguments: ProtocolFamily - Protocol Family (same as v2 address family) Config - Protocol family's router manager callbacks (Only the "route change callback" and the "validate route callback" funcs are used) Return Value: Status of the operation --*/ { HANDLE V1RegHandle; DWORD Status; // // Validate incoming parameters before action // if (ProtocolFamily >= RTM_NUM_OF_PROTOCOL_FAMILIES) { return ERROR_INVALID_PARAMETER; } if (V1Globals.PfRegInfo[ProtocolFamily]) { return ERROR_ALREADY_EXISTS; } // // Initialize the lock that guards regns list // try { InitializeCriticalSection(&V1Globals.PfRegnsLock[ProtocolFamily]); } except(EXCEPTION_EXECUTE_HANDLER) { return GetLastError(); } // Initialize list of regns on protocol family InitializeListHead(&V1Globals.PfRegistrations[ProtocolFamily]); // // Register on behalf of this protocol family // // This handle is also used for ops that // need a handle in RTM v2 but not in v1 // // We are also setting up best route change // notifications for RM using its callback // V1RegHandle = RtmpRegisterClient(ProtocolFamily, V1_WRAPPER_REGN_ID, Config->RPFC_Change, NULL, 0); if (V1RegHandle == NULL) { Status = GetLastError(); DeleteCriticalSection(&V1Globals.PfRegnsLock[ProtocolFamily]); return Status; } V1Globals.PfValidateRouteFunc[ProtocolFamily] = Config->RPFC_Validate; V1Globals.PfRegInfo[ProtocolFamily] = GET_POINTER_FROM_HANDLE(V1RegHandle); return NO_ERROR; } DWORD RtmDeleteRouteTable ( IN DWORD ProtocolFamily ) /*++ Routine Description: Deletes the route table for a particular address family after deregistering any active V1 registrations present. Note that atleast 1 registration (the wrapper's default registration) is active at this point. We assume that all RTMv2 protocols have deregistered by the time this function is called. We also assume that no RTMv1 protocols are trying to register or deregister while this function is executing, as we do not hold the lock that protects the list of registrations. Arguments: ProtocolFamily - Protocol Family whose table is deleted. Return Value: Status of the operation --*/ { PV1_REGN_INFO Regn; PLIST_ENTRY Regns; DWORD Status; // // Validate incoming parameters before action // if (ProtocolFamily >= RTM_NUM_OF_PROTOCOL_FAMILIES) { return ERROR_INVALID_PARAMETER; } if (V1Globals.PfRegInfo[ProtocolFamily] == NULL) { return ERROR_INVALID_PARAMETER; } // // Deregister existing regns on protocol family // including the default regn of the V1 wrapper // Regns = &V1Globals.PfRegistrations[ProtocolFamily]; // We have atleast the default regn available ASSERT(!IsListEmpty(Regns)); while (!IsListEmpty(Regns)) { Regn = CONTAINING_RECORD(Regns->Flink, V1_REGN_INFO, RegistrationsLE); Status = RtmDeregisterClient(MAKE_HANDLE_FROM_POINTER(Regn)); ASSERT(Status == NO_ERROR); } // Free the lock used to guard the regns list DeleteCriticalSection(&V1Globals.PfRegnsLock[ProtocolFamily]); V1Globals.PfRegInfo[ProtocolFamily] = NULL; return NO_ERROR; } HANDLE WINAPI RtmRegisterClient ( IN DWORD ProtocolFamily, IN DWORD RoutingProtocol, IN HANDLE ChangeEvent OPTIONAL, IN DWORD Flags ) /*++ Routine Description: Registers an RTMv1 client with the default instance and given protocol family in RTMv2. Also sets up notification of best route changes if caller asks for it. Arguments: ProtocolFamily - Protocol Family we are registering with. RoutingProtocol - Protocol ID of registering component. ChangeEvent - Event to indicate changes in best routes. Flags - RTM_PROTOCOL_SINGLE_ROUTE indicates that this protocol adds atmost one route per destination. Return Value: Registration Handle or NULL ( Use GetLastError() to get error ) --*/ { return RtmpRegisterClient(ProtocolFamily, RoutingProtocol, NULL, ChangeEvent, Flags); } HANDLE RtmpRegisterClient ( IN DWORD ProtocolFamily, IN DWORD RoutingProtocol, IN PROUTE_CHANGE_CALLBACK ChangeFunc OPTIONAL, IN HANDLE ChangeEvent OPTIONAL, IN DWORD Flags ) /*++ Routine Description: Registers an RTMv1 client with the default instance and given protocol family in RTMv2. Also sets up notification of best route changes if caller asks for it. Note that any protocol that needs to be indicated of best -route changes can either specify an event OR a callback for this purpose. Arguments: ProtocolFamily - Protocol Family we are registering with. RoutingProtocol - Protocol ID of registering component. ChangeFunc - Callback to indicates changes in best routes. ChangeEvent - Event to indicate changes in best routes. Flags - RTM_PROTOCOL_SINGLE_ROUTE indicates that this component keeps atmost one route per network (destination in RTMv2) in RTM. Return Value: Registration Handle or NULL ( Use GetLastError() to get error ) --*/ { PV1_REGN_INFO V1Regn; RTM_ENTITY_INFO EntityInfo; BOOL LockInited; BOOL Success; DWORD Status; // // Check parameters for validity (in v1 bounds) // if ((ProtocolFamily >= RTM_NUM_OF_PROTOCOL_FAMILIES) || (Flags & (~RTM_PROTOCOL_SINGLE_ROUTE))) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } // // Create a RTMv1->v2 registration wrapper // V1Regn = (PV1_REGN_INFO) AllocNZeroObject(sizeof(V1_REGN_INFO)); if (V1Regn == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return NULL; } LockInited = FALSE; do { #if DBG_HDL V1Regn->ObjectHeader.TypeSign = V1_REGN_ALLOC; #endif // // Register with RTMv2 after mapping input params to RTMv2 // // All v1 registrations fall in default Instance in RTMv2 EntityInfo.RtmInstanceId = 0; // We need to convert v1 protocol family id to winsock id EntityInfo.AddressFamily = ADDRESS_FAMILY[ProtocolFamily]; // All v1 protocols can register atmost once with RTMv2 // as they all will use the same "Protocol Instance Id" EntityInfo.EntityId.EntityProtocolId = RoutingProtocol; EntityInfo.EntityId.EntityInstanceId = V1_PROTOCOL_INSTANCE; Status = RtmRegisterEntity(&EntityInfo, (PRTM_ENTITY_EXPORT_METHODS) NULL, V2EventCallback, FALSE, &V1Regn->Rtmv2Profile, &V1Regn->Rtmv2RegHandle); if (Status != NO_ERROR) { break; } // // Cache RTMv1 specific params in RTMv1 regn // V1Regn->ProtocolFamily = ProtocolFamily; V1Regn->RoutingProtocol = RoutingProtocol; V1Regn->Flags = Flags; // // Store actual number of views in this regn // V1Regn->Rtmv2NumViews = V1Regn->Rtmv2Profile.NumberOfViews; // // Is caller interested in being notified of best route changes ? // if (/*ARGUMENT_PRESENT*/(ChangeFunc) || ARGUMENT_PRESENT(ChangeEvent)) { if (/*ARGUMENT_PRESENT*/(ChangeFunc)) { // The caller to be notified of changes directly V1Regn->NotificationFunc = ChangeFunc; } else { // Caller to be notified of changes with an event Success = ResetEvent(ChangeEvent); if (!Success) { Status = GetLastError(); break; } V1Regn->NotificationEvent = ChangeEvent; // Initialize lock to syncronize set/reset event try { InitializeCriticalSection(&V1Regn->NotificationLock); LockInited = TRUE; } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetLastError(); break; } } // // Register for change notifications with v2 // Status = RtmRegisterForChangeNotification(V1Regn->Rtmv2RegHandle, RTM_VIEW_MASK_UCAST, RTM_CHANGE_TYPE_ALL, (PVOID) V1Regn, &V1Regn->Rtmv2NotifyHandle); if (Status != NO_ERROR) { break; } } // // Stick it in the list of regns on protocol family // ACQUIRE_V1_REGNS_LOCK(ProtocolFamily); InsertHeadList(&V1Globals.PfRegistrations[ProtocolFamily], &V1Regn->RegistrationsLE); RELEASE_V1_REGNS_LOCK(ProtocolFamily); return MAKE_HANDLE_FROM_POINTER(V1Regn); } while (FALSE); // // Some error occured - clean up and return NULL // if (LockInited) { DeleteCriticalSection(&V1Regn->NotificationLock); } if (V1Regn->Rtmv2RegHandle) { ASSERT(RtmDeregisterEntity(V1Regn->Rtmv2RegHandle) == NO_ERROR); } #if DBG_HDL V1Regn->ObjectHeader.TypeSign = V1_REGN_FREED; #endif FreeObject(V1Regn); SetLastError(Status); return NULL; } DWORD WINAPI RtmDeregisterClient ( IN HANDLE ClientHandle ) /*++ Routine Description: Deregisters an RTMv1 client from the default instance and given protocol family in RTMv2. Also deletes any state that the RTMv1 caller left out - routes, nexthops etc. and deregisters any change notifications set up during registration time. Arguments: ClientHandle - RTMv1 registration handle being deregistered. Return Value: Status of the operation. --*/ { RTM_NEXTHOP_HANDLE EnumHandle; PV1_REGN_INFO V1Regn; HANDLE *Handles; UINT NumHandles, i; BOOL Success; DWORD Status; VALIDATE_V1_REGN_HANDLE(ClientHandle, &V1Regn); // // Remove the regn from the list of regns on protocol family // ACQUIRE_V1_REGNS_LOCK(V1Regn->ProtocolFamily); RemoveEntryList(&V1Regn->RegistrationsLE); RELEASE_V1_REGNS_LOCK(V1Regn->ProtocolFamily); do { // Allocate this var-size handles array on the stack Handles = ALLOC_HANDLES(V1Regn->Rtmv2Profile.MaxHandlesInEnum); // // Remove all the next-hops added by this client protocol // Status = RtmCreateNextHopEnum(V1Regn->Rtmv2RegHandle, 0, NULL, &EnumHandle); if (Status != NO_ERROR) { break; } do { NumHandles = V1Regn->Rtmv2Profile.MaxHandlesInEnum; Status = RtmGetEnumNextHops(V1Regn->Rtmv2RegHandle, EnumHandle, &NumHandles, Handles); for (i = 0; i < NumHandles; i++) { ASSERT(RtmDeleteNextHop(V1Regn->Rtmv2RegHandle, Handles[i], NULL) == NO_ERROR); } } while (Status == NO_ERROR); ASSERT(RtmDeleteEnumHandle(V1Regn->Rtmv2RegHandle, EnumHandle) == NO_ERROR); // // Clean up resources allocated for change processing // if (V1Regn->NotificationFunc || V1Regn->NotificationEvent) { // Stop the notification of changes to best routes Status = RtmDeregisterFromChangeNotification(V1Regn->Rtmv2RegHandle, V1Regn->Rtmv2NotifyHandle); if (Status != NO_ERROR) { break; } if (V1Regn->NotificationEvent) { // Free the lock that serves in syncronization DeleteCriticalSection(&V1Regn->NotificationLock); // Reset the event to indicate no more changes Success = ResetEvent(V1Regn->NotificationEvent); if (!Success) { Status = GetLastError(); break; } } } // // Deregister with RTMv2 using RTMv2 regn handle // Status = RtmDeregisterEntity(V1Regn->Rtmv2RegHandle); if (Status != NO_ERROR) { break; } // // Free resources allocated for the regn wrapper // #if DBG_HDL V1Regn->ObjectHeader.TypeSign = V1_REGN_FREED; #endif FreeObject(V1Regn); return NO_ERROR; } while (FALSE); // // Some error occured - clean up and return status // ASSERT(FALSE); return Status; } DWORD WINAPI RtmAddRoute ( IN HANDLE ClientHandle, IN PVOID Route, IN DWORD TimeToLive, OUT DWORD *Flags OPTIONAL, OUT PVOID CurBestRoute OPTIONAL, OUT PVOID PrevBestRoute OPTIONAL ) /*++ Routine Description: Adds a route to RTMv2 after converting the RTMv1 route to RTMv2 format. We create a next hop object if one does not exist, and add a route through it. Arguments: ClientHandle - RTMv1 registration handle of the caller. Route - Info for V1 route being added/updated. TimeToLive - Time for which the route is kept in RTM before being deleted (value is seconds). THE FOLLOWING PARAMETERS ARE OBSOLETE IN THIS WRAPPER Flags - Returns error if this param is not NULL. CurBestRoute - Returns error if this param is not NULL. PrevBestRoute - Returns error if this param is not NULL. Return Value: Status of the operation. --*/ { PV1_REGN_INFO V1Regn; RTM_NET_ADDRESS DestAddr; RTM_ROUTE_INFO V2RouteInfo; RTM_NEXTHOP_INFO V2NextHopInfo; RTM_NEXTHOP_HANDLE V2NextHop; DWORD ChangeFlags; DWORD Status; VALIDATE_V1_REGN_HANDLE(ClientHandle, &V1Regn); // Protocols specify Flags parameter but don't use it *Flags = RTM_NO_CHANGE; if (ARGUMENT_PRESENT(CurBestRoute) || ARGUMENT_PRESENT(PrevBestRoute)) { return ERROR_NOT_SUPPORTED; } // // Call back into RM to validate route, set priority // Status = V1Globals.PfValidateRouteFunc[V1Regn->ProtocolFamily](Route); if (Status != NO_ERROR) { return Status; } // // Create a new next-hop with this interface // (if this next-hop is not already present) // MakeV2NextHopFromV1Route(V1Regn, Route, &V2NextHopInfo); V2NextHop = NULL; Status = RtmAddNextHop(V1Regn->Rtmv2RegHandle, &V2NextHopInfo, &V2NextHop, &ChangeFlags); if (Status != NO_ERROR) { return Status; } // // Create a new route with the above nexthop // MakeV2RouteFromV1Route(V1Regn, Route, V2NextHop, &DestAddr, &V2RouteInfo); // // Convert TimeToLive for secs to ms // if (TimeToLive != INFINITE) { TimeToLive *= 1000; if (TimeToLive > (MAXTICKS/2-1)) { TimeToLive = MAXTICKS/2-1; } } // Setup flags that control RTMv2's add route ChangeFlags = (V1Regn->Flags & RTM_PROTOCOL_SINGLE_ROUTE) ? RTM_ROUTE_CHANGE_FIRST : 0; // // Add the new route using the RTMv2 API call // Status = RtmAddRouteToDest(V1Regn->Rtmv2RegHandle, NULL, &DestAddr, &V2RouteInfo, TimeToLive, NULL, 0, NULL, &ChangeFlags); // // Remove the handle ref we got on the nexthop above // ASSERT(RtmReleaseNextHops(V1Regn->Rtmv2RegHandle, 1, &V2NextHop) == NO_ERROR); return Status; } DWORD WINAPI RtmDeleteRoute ( IN HANDLE ClientHandle, IN PVOID Route, OUT DWORD *Flags OPTIONAL, OUT PVOID CurBestRoute OPTIONAL ) /*++ Routine Description: Deletes the route in RTMv2 that corresponds to input RTMv1 route. Arguments: ClientHandle - RTMv1 registration handle of the caller. Route - Info for V1 route being deleted in RTM. THE FOLLOWING PARAMETERS ARE OBSOLETE IN THIS WRAPPER Flags - Returns error if this param is not NULL. CurBestRoute - Returns error if this param is not NULL. Return Value: Status of the operation. --*/ { PV1_REGN_INFO V1Regn; RTM_NET_ADDRESS DestAddr; RTM_ROUTE_INFO V2RouteInfo; RTM_ROUTE_HANDLE V2Route; RTM_NEXTHOP_INFO V2NextHopInfo; RTM_NEXTHOP_HANDLE V2NextHop; DWORD ChangeFlags; DWORD Status; VALIDATE_V1_REGN_HANDLE(ClientHandle, &V1Regn); // Protocols specify Flags parameter but don't use it *Flags = RTM_NO_CHANGE; if (ARGUMENT_PRESENT(CurBestRoute)) { return ERROR_NOT_SUPPORTED; } // // Find the next-hop with this interface // MakeV2NextHopFromV1Route(V1Regn, Route, &V2NextHopInfo); V2NextHop = NULL; Status = RtmFindNextHop(V1Regn->Rtmv2RegHandle, &V2NextHopInfo, &V2NextHop, NULL); if (Status != NO_ERROR) { return Status; } // // Delete the route with the above nexthop // MakeV2RouteFromV1Route(V1Regn, Route, V2NextHop, &DestAddr, &V2RouteInfo); // // We can get this route by matching the route's // net addr, its owner and neighbour learnt from // Status = RtmGetExactMatchRoute(V1Regn->Rtmv2RegHandle, &DestAddr, RTM_MATCH_OWNER | RTM_MATCH_NEIGHBOUR, &V2RouteInfo, 0, 0, &V2Route); if (Status == NO_ERROR) { // // Delete the route found above using the handle // Status = RtmDeleteRouteToDest(V1Regn->Rtmv2RegHandle, V2Route, &ChangeFlags); if (Status != NO_ERROR) { // If delete was successful, this deref is automatic ASSERT(RtmReleaseRoutes(V1Regn->Rtmv2RegHandle, 1, &V2Route) == NO_ERROR); } ASSERT(RtmReleaseRouteInfo(V1Regn->Rtmv2RegHandle, &V2RouteInfo) == NO_ERROR); } // // Remove the handle ref we got on the nexthop // ASSERT(RtmReleaseNextHops(V1Regn->Rtmv2RegHandle, 1, &V2NextHop) == NO_ERROR); return Status; } DWORD WINAPI RtmDequeueRouteChangeMessage ( IN HANDLE ClientHandle, OUT DWORD *Flags, OUT PVOID CurBestRoute OPTIONAL, OUT PVOID PrevBestRoute OPTIONAL ) /*++ Routine Description: Removes a route change message (basically a dest that has changed recently) from the client's own queue of pending changes to be notified. If a best route exists on the dest, RTM_CURRENT_BEST_ROUTE is set in flags and CurBestRoute is filled with best info. If the dest has no best routes (in unicast view), then the flags are set to RTM_PREVIOUS_BEST_ROUTE, and a route with correct network address and rest of route info set to some dummy info is returned. At no point are both flags set (as was the case in RTMv1). Arguments: ClientHandle - RTMv1 registration handle of the caller. THESE HAVE A SLIGHTLY DIFFERENT MEANING IN THE WRAPPER Flags - RTM_NO_CHANGE, RTM_PREVIOUS_BEST_ROUTE or RTM_CURRENT_BEST_ROUTE CurBestRoute - Info for current best route is filled. ( See routine description just above ) PrevBestRoute - Info for previous best route is filled. ( See routine description just above ) Return Value: Status of the operation. --*/ { PV1_REGN_INFO V1Regn; PRTM_DEST_INFO DestInfo; PRTM_ROUTE_INFO V2RouteInfo; RTM_ROUTE_HANDLE V2RouteHandle; UINT NumDests; DWORD Status; DWORD Status1; VALIDATE_V1_REGN_HANDLE(ClientHandle, &V1Regn); *Flags = RTM_NO_CHANGE; // Allocate this var-size dest-info on the stack DestInfo = ALLOC_DEST_INFO(V1Regn->Rtmv2NumViews, 1); if (V1Regn->NotificationEvent) { // // This lock serves to make the RtmGetChangedDests // call and resetting of the "more changes" event // a single combined atomic operation, preventing // a case when a set gets lost due to a late reset. // ACQUIRE_V1_NOTIFY_LOCK(V1Regn); } Status = NO_ERROR; NumDests = 0; while (Status == NO_ERROR) { // // Release any destination we got in the prev loop // if (NumDests == 1) { ASSERT(RtmReleaseChangedDests(V1Regn->Rtmv2RegHandle, V1Regn->Rtmv2NotifyHandle, 1, DestInfo) == NO_ERROR); } // // Get the next changed destination for client // NumDests = 1; Status = RtmGetChangedDests(V1Regn->Rtmv2RegHandle, V1Regn->Rtmv2NotifyHandle, &NumDests, DestInfo); if (NumDests < 1) { break; } // // Get the current best route for this dest // V2RouteHandle = DestInfo->ViewInfo[0].Route; if (V2RouteHandle != NULL) { // // We have a best route on the changed dest // Give the caller the new best route info // if (ARGUMENT_PRESENT(CurBestRoute)) { // Get the route's information from RTMv2 V2RouteInfo = ALLOC_ROUTE_INFO(V1Regn->Rtmv2Profile.MaxNextHopsInRoute, 1); Status1 = RtmGetRouteInfo(V1Regn->Rtmv2RegHandle, V2RouteHandle, V2RouteInfo, NULL); if (Status1 != NO_ERROR) { // Best Route would have got deleted - get next change continue; }; Status1 = MakeV1RouteFromV2Route(V1Regn, V2RouteInfo, CurBestRoute); ASSERT(RtmReleaseRouteInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo) == NO_ERROR); if (Status1 != NO_ERROR) { // Best Route would have got changed - get next change continue; } } *Flags = RTM_CURRENT_BEST_ROUTE; } else { // // We have no best route on the changed dest, // Give dummy best route info with this dest // if (ARGUMENT_PRESENT(PrevBestRoute)) { MakeV1RouteFromV2Dest(V1Regn, DestInfo, PrevBestRoute); } *Flags = RTM_PREVIOUS_BEST_ROUTE; } // // Do we have more changes to process here ? // if (Status == ERROR_NO_MORE_ITEMS) { // We have no more changes to notify - reset event if present if (V1Regn->NotificationEvent) { ResetEvent(V1Regn->NotificationEvent); } Status = NO_ERROR; } else { // We have more changes to give out - indicate so in status ASSERT(Status == NO_ERROR); Status = ERROR_MORE_DATA; } break; } if (NumDests == 1) { ASSERT(SUCCESS(RtmReleaseChangedDests(V1Regn->Rtmv2RegHandle, V1Regn->Rtmv2NotifyHandle, 1, DestInfo))); } if (V1Regn->NotificationEvent) { RELEASE_V1_NOTIFY_LOCK(V1Regn); } return Status; } DWORD V2EventCallback ( IN RTM_ENTITY_HANDLE Rtmv2RegHandle, IN RTM_EVENT_TYPE EventType, IN PVOID Context1, IN PVOID Context2 ) /*++ Routine Description: This is the callback function that gets called when any RTMv2 event happens like change notification available, route's timed out etc. Context1 & Context2 contain event specific information. Arguments: Rtmv2RegHandle - Regn handle of entity being informed. EventType - Type of event that caused this call. Context1 - Context associated with this event. Context2 - Context associated with this event. Return Value: Status of the operation being returned to RTMv2 --*/ { PV1_REGN_INFO V1Regn; HANDLE V1RegHandle; V1_ROUTE_INFO CurBestRoute; V1_ROUTE_INFO PrevBestRoute; DWORD Flags; DWORD Status; UNREFERENCED_PARAMETER(Rtmv2RegHandle); UNREFERENCED_PARAMETER(Context1); switch(EventType) { case RTM_CHANGE_NOTIFICATION: V1Regn = (PV1_REGN_INFO) Context2; // // Signal availability of new changes using either callback or event // if (V1Regn->NotificationFunc) { V1RegHandle = MAKE_HANDLE_FROM_POINTER(V1Regn); do { // Get the next change in this regn's queue Status = RtmDequeueRouteChangeMessage(V1RegHandle, &Flags, &CurBestRoute, &PrevBestRoute); if (Status != ERROR_MORE_DATA) { break; } // Call the notification callback with data V1Regn->NotificationFunc(Flags, &CurBestRoute, &PrevBestRoute); } while (TRUE); // Give the final notification call if needed if (Status == NO_ERROR) { // Call the notification callback with data V1Regn->NotificationFunc(Flags, &CurBestRoute, &PrevBestRoute); } } else { // // Set event to signal availability of changes // ASSERT(V1Regn->NotificationEvent); ACQUIRE_V1_NOTIFY_LOCK(V1Regn); SetEvent(V1Regn->NotificationEvent); RELEASE_V1_NOTIFY_LOCK(V1Regn); } return NO_ERROR; case RTM_ROUTE_EXPIRED: // // Do not handle this route expiry notification. // This will automatically cause deletion of the // route and the appropriate change notification // generated and indicated to all the protocols. // return ERROR_NOT_SUPPORTED; } return ERROR_NOT_SUPPORTED; } HANDLE WINAPI RtmCreateEnumerationHandle ( IN DWORD ProtocolFamily, IN DWORD EnumerationFlags, IN PVOID CriteriaRoute ) /*++ Routine Description: Creates an enumeration on routes in RTM that match the appropriate criteria in the input route. This call does not need an RTMv1 registration handle, so we use the wrapper's default V1 registration with RTMv2 to make RTMv2 calls. Matching Routes are returned in the order governed the following fields - ( Dest Address and Mask, Route Preference and Metric ) Arguments: ProtocolFamily - Protocol family of the routes we want EnumerationFlags - Flags indicating the criteria to match CriteriaRoute - The route that we are matching against Return Value: Enumeration Handle or NULL ( GetLastError() to get error ) --*/ { PV1_REGN_INFO V1Regn; RTM_DEST_HANDLE DestHandle; PRTM_DEST_INFO DestInfo; RTM_NET_ADDRESS DestAddr; PV1_ENUM_INFO V1Enum; PVOID Network; RTM_VIEW_SET TargetViews; ULONG TempUlong; DWORD EnumFlags; DWORD MatchFlags; ULONG InterfaceIndex; DWORD Status; // // Validate incoming parameters before action // if ((ProtocolFamily >= RTM_NUM_OF_PROTOCOL_FAMILIES) || (EnumerationFlags & ~(RTM_ONLY_THIS_NETWORK | RTM_ONLY_THIS_PROTOCOL | RTM_ONLY_THIS_INTERFACE | RTM_ONLY_BEST_ROUTES | RTM_ONLY_OWND_ROUTES | RTM_INCLUDE_DISABLED_ROUTES))) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } // Specify Criteria if these flags are set if ((!ARGUMENT_PRESENT(CriteriaRoute)) && (EnumerationFlags & (RTM_ONLY_THIS_NETWORK | RTM_ONLY_THIS_PROTOCOL | RTM_ONLY_THIS_INTERFACE))) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } V1Regn = V1Globals.PfRegInfo[ProtocolFamily]; if (V1Regn == NULL) { SetLastError(ERROR_INVALID_HANDLE); return NULL; } // // If we don't need disabled routes, just use unicast view; // or use 0 views to get all routes including disabled ones // if (EnumerationFlags & RTM_INCLUDE_DISABLED_ROUTES) { TargetViews = RTM_VIEW_MASK_ANY; } else { TargetViews = RTM_VIEW_MASK_UCAST; } // // If enuming a certain n/w, check if corr. dest exists // DestHandle = NULL; #if WRN DestInfo = NULL; #endif if (EnumerationFlags & RTM_ONLY_THIS_NETWORK) { V1GetRouteNetwork(CriteriaRoute, ProtocolFamily, &Network); MakeNetAddress(Network, ProtocolFamily, TempUlong, &DestAddr); // Allocate this var-size dest-info on the stack DestInfo = ALLOC_DEST_INFO(V1Regn->Rtmv2NumViews, 1); Status = RtmGetExactMatchDestination(V1Regn->Rtmv2RegHandle, &DestAddr, RTM_BEST_PROTOCOL, TargetViews, DestInfo); if (Status != NO_ERROR) { SetLastError(Status); return NULL; } DestHandle = DestInfo->DestHandle; } do { // // Allocate a V1 enumeration wrapper structure // V1Enum = (PV1_ENUM_INFO) AllocNZeroObject(sizeof(V1_ENUM_INFO)); if (V1Enum == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } #if DBG_HDL V1Enum->ObjectHeader.TypeSign = V1_ENUM_ALLOC; #endif // // Cache RTMv1 specific params in RTMv1 enum // V1Enum->ProtocolFamily = ProtocolFamily; V1Enum->EnumFlags = EnumerationFlags; if (ARGUMENT_PRESENT(CriteriaRoute)) { // Convert the V1 criteria into V2 criteria V1CopyRoute(V1Enum->CriteriaRoute.Route, CriteriaRoute, ProtocolFamily); } // // Create a route enum on a dest or all dests // if (EnumerationFlags & RTM_ONLY_OWND_ROUTES) { EnumFlags = RTM_ENUM_OWN_ROUTES; } else { EnumFlags = RTM_ENUM_ALL_ROUTES; } MatchFlags = InterfaceIndex = 0; // Do we have to enum's routes on an interface if (EnumerationFlags & RTM_ONLY_THIS_INTERFACE) { MatchFlags = RTM_MATCH_INTERFACE; InterfaceIndex = ((PV1_ROUTE_INFO) CriteriaRoute)->XxRoute.RR_InterfaceID; } Status = RtmCreateRouteEnum(V1Regn->Rtmv2RegHandle, DestHandle, TargetViews, EnumFlags, NULL, MatchFlags, NULL, InterfaceIndex, &V1Enum->Rtmv2RouteEnum); if (Status != NO_ERROR) { break; } // // Initialize lock used to serialize enum ops // try { InitializeCriticalSection(&V1Enum->EnumLock); } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetLastError(); break; } // // Release the destination info as we are done // if (EnumerationFlags & RTM_ONLY_THIS_NETWORK) { ASSERT(SUCCESS(RtmReleaseDestInfo(V1Regn->Rtmv2RegHandle, DestInfo))); } return MAKE_HANDLE_FROM_POINTER(V1Enum); } while (FALSE); // // Some error occurred - clean up and return NULL // if (EnumerationFlags & RTM_ONLY_THIS_NETWORK) { ASSERT(SUCCESS(RtmReleaseDestInfo(V1Regn->Rtmv2RegHandle, DestInfo))); } if (V1Enum) { if (V1Enum->Rtmv2RouteEnum) { ASSERT(SUCCESS(RtmDeleteEnumHandle(V1Regn->Rtmv2RegHandle, V1Enum->Rtmv2RouteEnum))); } #if DBG_HDL V1Enum->ObjectHeader.TypeSign = V1_ENUM_FREED; #endif FreeObject(V1Enum); } SetLastError(Status); return NULL; } DWORD WINAPI RtmEnumerateGetNextRoute ( IN HANDLE EnumerationHandle, OUT PVOID Route ) /*++ Routine Description: Get the next route in the V1 enumeration (satisfying the enumeration critieria). Arguments: EnumerationHandle - Handle that identifies the enumeration Route - Next route is returned in this param Return Value: Status of the operation --*/ { PV1_REGN_INFO V1Regn; RTM_ROUTE_HANDLE V2Route; PV1_ENUM_INFO V1Enum; UINT NumRoutes; BOOL Match; DWORD Status; VALIDATE_V1_ENUM_HANDLE(EnumerationHandle, &V1Enum); V1Regn = V1Globals.PfRegInfo[V1Enum->ProtocolFamily]; // Acquire the enum lock to serialize requests ACQUIRE_V1_ENUM_LOCK(V1Enum); // // Do until you have a matching route or no more routes // Match = FALSE; do { // Get next route in enum, and check if it matches // // Routes are enum'ed in the following order, // Network Addr, Route Priority, Route Metric // NumRoutes = 1; Status = RtmGetEnumRoutes(V1Regn->Rtmv2RegHandle, V1Enum->Rtmv2RouteEnum, &NumRoutes, &V2Route); if (NumRoutes < 1) { break; } Match = MatchCriteriaAndCopyRoute(V1Regn, V2Route, V1Enum, Route); ASSERT(SUCCESS(RtmReleaseRoutes(V1Regn->Rtmv2RegHandle, 1, &V2Route))); } while (!Match); RELEASE_V1_ENUM_LOCK(V1Enum); return Match ? NO_ERROR : Status; } DWORD WINAPI RtmCloseEnumerationHandle ( IN HANDLE EnumerationHandle ) /*++ Routine Description: Closes the enumeration and releases its resources. Arguments: EnumerationHandle - Handle that identifies the enumeration Return Value: Status of the operation --*/ { PV1_REGN_INFO V1Regn; PV1_ENUM_INFO V1Enum; DWORD Status; VALIDATE_V1_ENUM_HANDLE(EnumerationHandle, &V1Enum); V1Regn = V1Globals.PfRegInfo[V1Enum->ProtocolFamily]; do { // // Free the RTMv2 route enumeration and resouces // if (V1Enum->Rtmv2RouteEnum) { Status = RtmDeleteEnumHandle(V1Regn->Rtmv2RegHandle, V1Enum->Rtmv2RouteEnum); ASSERT(Status == NO_ERROR); V1Enum->Rtmv2RouteEnum = NULL; } // // Free resources allocated for the enum wrapper // DeleteCriticalSection(&V1Enum->EnumLock); #if DBG_HDL V1Enum->ObjectHeader.TypeSign = V1_ENUM_FREED; #endif FreeObject(V1Enum); return NO_ERROR; } while (FALSE); ASSERT(FALSE); return Status; } DWORD WINAPI RtmGetFirstRoute ( IN DWORD ProtocolFamily, IN DWORD EnumerationFlags, IN OUT PVOID Route ) /*++ Routine Description: Returns the first route in the table that matches the criteria. This function just opens a new enumeration and gets the first route that matches enumeration critiria, and closes the enumeration. Arguments: ProtocolFamily - Protocol family of the route we want EnumerationFlags - Flags indicating the criteria to match CriteriaRoute - The route that we are matching against Return Value: Status of the operation --*/ { HANDLE V1EnumHandle; DWORD Status; // // Create an enumeration and return the first route in it // V1EnumHandle = RtmCreateEnumerationHandle(ProtocolFamily, EnumerationFlags, Route); if (V1EnumHandle == NULL) { return GetLastError(); } Status = RtmEnumerateGetNextRoute(V1EnumHandle, Route); ASSERT(SUCCESS(RtmCloseEnumerationHandle(V1EnumHandle))); return Status; } DWORD WINAPI RtmGetNextRoute ( IN DWORD ProtocolFamily, IN DWORD EnumerationFlags, OUT PVOID Route ) /*++ Routine Description: Returns the next route in the table that matches the criteria. The routes in RTMv2 are ordered using the following fields - (Dest Address and Mask, Route Preference and Metric) If we have 2 routes with identical values for all the above fields, then you have no way of knowing which of these routes you returned the last time this call was made. For this reason, this call is not supported in this wrapper. One should create an enumeration to actually get the next route in the table. Arguments: ProtocolFamily - Protocol family of the route we want EnumerationFlags - Flags indicating the criteria to match CriteriaRoute - The route that we are matching against Return Value: Status of the operation --*/ { UNREFERENCED_PARAMETER(ProtocolFamily); UNREFERENCED_PARAMETER(EnumerationFlags); UNREFERENCED_PARAMETER(Route); return ERROR_NOT_SUPPORTED; } DWORD WINAPI RtmBlockDeleteRoutes ( IN HANDLE ClientHandle, IN DWORD EnumerationFlags, IN PVOID CriteriaRoute ) /*++ Routine Description: Deletes all routes in the route table that match the criteria specified. Note that if we have multiple instances of a protocol running (say RIP), then each version can delete only the routes in owns. Arguments: ClientHandle - RTM v1 registration handle of caller EnumerationFlags - Flags indicating the criteria to match CriteriaRoute - The route that we are matching against Return Value: Status of the operation --*/ { PV1_REGN_INFO V1Regn; // // Check parameters for validity (in v1 bounds) // VALIDATE_V1_REGN_HANDLE(ClientHandle, &V1Regn); if (EnumerationFlags & ~(RTM_ONLY_THIS_NETWORK | RTM_ONLY_THIS_INTERFACE)) { return ERROR_INVALID_PARAMETER; } // // Deletes only routes of this instance of the protocol // Also include all disabled routes in the enumeration // EnumerationFlags |= (RTM_ONLY_OWND_ROUTES | RTM_INCLUDE_DISABLED_ROUTES); // // Call block operation to delete all matching routes // return BlockOperationOnRoutes(V1Regn, EnumerationFlags, CriteriaRoute, MatchCriteriaAndDeleteRoute); } BOOL MatchCriteriaAndDeleteRoute ( IN PV1_REGN_INFO V1Regn, IN PRTM_ROUTE_HANDLE V2RouteHandle, IN PV1_ENUM_INFO V1Enum ) /*++ Routine Description: Deletes input route if it matches the enumeration criteria. The enumeration criteria returns only routes owned by the caller. See RTM_ONLY_OWND_ROUTES in RtmBlockDeleteRoutes. Arguments: V1Regn - RTM v1 registration info of the caller V2RouteHandle - Handle of the route being considered V1Enum - Enum info that gives matching criteria Return Value: TRUE if you have released input route handle, FALSE if not --*/ { DWORD ChangeFlags; BOOL Match; DWORD Status; Match = MatchCriteria(V1Regn, V2RouteHandle, V1Enum); if (Match) { // Caller can delete the route only if he owns it Status = RtmDeleteRouteToDest(V1Regn->Rtmv2RegHandle, V2RouteHandle, &ChangeFlags); if (Status != NO_ERROR) { ASSERT(FALSE); Match = FALSE; } } return Match; } DWORD WINAPI RtmBlockSetRouteEnable ( IN HANDLE ClientHandle, IN DWORD EnumerationFlags, IN PVOID CriteriaRoute, IN BOOL Enable ) /*++ Routine Description: Enables or disables all routes in the route table that match the criteria specified. Disabling a route removes it from consideration in all best route computations. We do this by adding this route in "no" views in RTMv2. In other words, this route is not considered for best route computations in any views. Note that if we have multiple instances of a protocol running (say RIP), then each version can disable or enable only the routes it owns. Arguments: ClientHandle - RTM v1 registration handle of caller EnumerationFlags - Flags indicating the criteria to match CriteriaRoute - The route that we are matching against Enable - TRUE to enable, and FALSE to disable Return Value: Status of the operation --*/ { PV1_REGN_INFO V1Regn; DWORD *Flags; VALIDATE_V1_REGN_HANDLE(ClientHandle, &V1Regn); if (EnumerationFlags & ~(RTM_ONLY_THIS_NETWORK | RTM_ONLY_THIS_INTERFACE)) { return ERROR_INVALID_PARAMETER; } // Enable/Disable routes only of this instance of the protocol EnumerationFlags |= RTM_ONLY_OWND_ROUTES; // If we are enabling routes, get disable routes too if (Enable) { EnumerationFlags |= RTM_INCLUDE_DISABLED_ROUTES; } // // Set the enable/disable flag in the criteria route // V1GetRouteFlags(CriteriaRoute, V1Regn->ProtocolFamily, Flags); if (Enable) { (*Flags) |= IP_VALID_ROUTE; } else { (*Flags) &= ~IP_VALID_ROUTE; } // // Call block operation to enable/disable all matching routes // return BlockOperationOnRoutes(V1Regn, EnumerationFlags, CriteriaRoute, MatchCriteriaAndEnableRoute); } BOOL MatchCriteriaAndEnableRoute ( IN PV1_REGN_INFO V1Regn, IN PRTM_ROUTE_HANDLE V2RouteHandle, IN PV1_ENUM_INFO V1Enum ) /*++ Routine Description: Enables/disables route if it matches enumeration criteria. We enable or disable a route by adding or removing it from the unicast view, as we assume that v1 protocols understand only the unicast view. This will work only if we enable or disable route owned by caller. Enumeration criteria returns only routes owned by caller. See RTM_ONLY_OWND_ROUTES in RtmBlockSetRouteEnable. Arguments: V1Regn - RTM v1 registration info of the caller V2RouteHandle - Handle of the route being considered V1Enum - Enum info that gives matching criteria Return Value: TRUE if you have released input route handle, FALSE if not --*/ { PRTM_ROUTE_INFO V2RoutePointer; RTM_VIEW_SET Views; DWORD ChangeFlags; BOOL Match; DWORD *Flags; DWORD Status; Match = MatchCriteria(V1Regn, V2RouteHandle, V1Enum); if (!Match) { return FALSE; } do { // Do we need to enable or disable the route ? V1GetRouteFlags(&V1Enum->CriteriaRoute, V1Regn->ProtocolFamily, Flags); // // Remove route from unicast view to disable; // add it back to the unicast view to enable // if ((*Flags) & IP_VALID_ROUTE) { Views = RTM_VIEW_MASK_UCAST; } else { Views = RTM_VIEW_MASK_NONE; } // // Only the route's owner can lock and update it // Status = RtmLockRoute(V1Regn->Rtmv2RegHandle, V2RouteHandle, TRUE, &V2RoutePointer); if (Status != NO_ERROR) { break; } V2RoutePointer->BelongsToViews = Views; // Note that we are not preserving timeout value Status = RtmUpdateAndUnlockRoute(V1Regn->Rtmv2RegHandle, V2RouteHandle, INFINITE, NULL, 0, NULL, &ChangeFlags); if (Status != NO_ERROR) { break; } return FALSE; } while (FALSE); ASSERT(FALSE); return FALSE; } DWORD WINAPI RtmBlockConvertRoutesToStatic ( IN HANDLE ClientHandle, IN DWORD EnumerationFlags, IN PVOID CriteriaRoute ) /*++ Routine Description: Makes the caller the owner of all the routes matching the input criteria. Changing the owner is done by adding a new route for each matching route with the same info. The caller is the owner of the new route. Arguments: ClientHandle - RTM v1 registration handle of caller EnumerationFlags - Flags indicating the criteria to match CriteriaRoute - The route that we are matching against Return Value: Status of the operation --*/ { PV1_REGN_INFO V1Regn; VALIDATE_V1_REGN_HANDLE(ClientHandle, &V1Regn); // // In accordance with RTMv1, act only on enabled routes // EnumerationFlags &= ~RTM_INCLUDE_DISABLED_ROUTES; // // Call block op to add a new route for each matching one. // return BlockOperationOnRoutes(V1Regn, EnumerationFlags, CriteriaRoute, MatchCriteriaAndChangeOwner); } BOOL MatchCriteriaAndChangeOwner ( IN PV1_REGN_INFO V1Regn, IN PRTM_ROUTE_HANDLE V2RouteHandle, IN PV1_ENUM_INFO V1Enum ) /*++ Routine Description: Makes a copy of the route if it matches the enum criteria. The new copy of the route will have the caller as its owner. The matching route can then be deleted (if needed). Arguments: V1Regn - RTM v1 registration info of the caller V2RouteHandle - Handle of the route being considered V1Enum - Enum info that gives matching criteria Return Value: TRUE if you have released input route handle, FALSE if not --*/ { RTM_ENTITY_INFO EntityInfo; PRTM_DEST_INFO DestInfo; PRTM_ROUTE_INFO V2RouteInfo; RTM_NEXTHOP_HANDLE NextHopHandle; RTM_NEXTHOP_HANDLE OldNextHop; RTM_NEXTHOP_HANDLE OldNeighbour; RTM_NEXTHOP_INFO NextHopInfo; RTM_VIEW_SET BestInViews; ULONG Protocol; BOOL Match; DWORD ChangeFlags; DWORD Status; // // Get the route's information from RTMv2 // V2RouteInfo = ALLOC_ROUTE_INFO(V1Regn->Rtmv2Profile.MaxNextHopsInRoute, 1); Status = RtmGetRouteInfo(V1Regn->Rtmv2RegHandle, V2RouteHandle, V2RouteInfo, NULL); if (Status != NO_ERROR) { return FALSE; } Match = FALSE; do { // // Is the route owner already target protocol ? // if (V2RouteInfo->RouteOwner == V1Regn->Rtmv2RegHandle) { break; } // // Does it match the criteria route's protocol ? // if (V1Enum->EnumFlags & RTM_ONLY_THIS_PROTOCOL) { // // Get the protocol type for this route // Status = RtmGetEntityInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo->RouteOwner, &EntityInfo); if (Status != NO_ERROR) { break; } Protocol = EntityInfo.EntityId.EntityProtocolId; Status = RtmReleaseEntityInfo(V1Regn->Rtmv2RegHandle, &EntityInfo); ASSERT(Status == NO_ERROR); // Is this the routing protocol we need ? if (V1Enum->CriteriaRoute.XxRoute.RR_RoutingProtocol != Protocol) { break; } } // // And does it match other criteria in enum ? // if (V1Enum->EnumFlags & RTM_ONLY_BEST_ROUTES) { Status = RtmIsBestRoute(V1Regn->Rtmv2RegHandle, V2RouteHandle, &BestInViews); if ((BestInViews & RTM_VIEW_MASK_UCAST) == 0) { break; } } // // We are checking only the first next hop // as we expect this function to be used // only by V1 protocols on their own routes // ASSERT(V2RouteInfo->NextHopsList.NumNextHops == 1); Status = RtmGetNextHopInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo->NextHopsList.NextHops[0], &NextHopInfo); if (Status != NO_ERROR) { break; } #if DBG // Do we need to match the nexthop intf ? if (V1Enum->EnumFlags & RTM_ONLY_THIS_INTERFACE) { // Compare this next-hops interface with criteria if (NextHopInfo.InterfaceIndex == V1Enum->CriteriaRoute.XxRoute.RR_InterfaceID) { Match = TRUE; } // We have already done this filtering in RTM v2 ASSERT(Match == TRUE); } #endif // Add the same next hop with a different owner if (Match) { NextHopHandle = NULL; do { Status = RtmAddNextHop(V1Regn->Rtmv2RegHandle, &NextHopInfo, &NextHopHandle, &ChangeFlags); if (Status != NO_ERROR) { Match = FALSE; break; } // Allocate this var-size dest-info on the stack DestInfo = ALLOC_DEST_INFO(V1Regn->Rtmv2NumViews, 1); // // Get the destination address corr to handle // Status = RtmGetDestInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo->DestHandle, RTM_BEST_PROTOCOL, RTM_VIEW_ID_UCAST, DestInfo); if (Status != NO_ERROR) { Match = FALSE; break; } // // Add this route again with a different owner // ChangeFlags = (V1Regn->Flags & RTM_PROTOCOL_SINGLE_ROUTE) ? RTM_ROUTE_CHANGE_FIRST : 0; // Update route with new next hop neighbour OldNeighbour = V2RouteInfo->Neighbour; V2RouteInfo->Neighbour = NextHopHandle; // Update route with new forwarding next hop OldNextHop = V2RouteInfo->NextHopsList.NextHops[0]; V2RouteInfo->NextHopsList.NextHops[0] = NextHopHandle; // // Add the new route using the RTMv2 API call // Status = RtmAddRouteToDest(V1Regn->Rtmv2RegHandle, NULL, &DestInfo->DestAddress, V2RouteInfo, INFINITE, NULL, 0, NULL, &ChangeFlags); if (Status != NO_ERROR) { Match = FALSE; } // // Restore old information nexthop information // V2RouteInfo->Neighbour = OldNeighbour; V2RouteInfo->NextHopsList.NextHops[0] = OldNextHop; Status = RtmReleaseDestInfo(V1Regn->Rtmv2RegHandle, DestInfo); ASSERT(Status == NO_ERROR); } while (FALSE); // If we have a next hop handle, release it if (NextHopHandle) { Status = RtmReleaseNextHops(V1Regn->Rtmv2RegHandle, 1, &NextHopHandle); ASSERT(Status == NO_ERROR); } } Status = RtmReleaseNextHopInfo(V1Regn->Rtmv2RegHandle, &NextHopInfo); ASSERT(Status == NO_ERROR); } while (FALSE); #if DELETE_OLD // // Impersonate previous owner and delete his route // if (Match) { Status = RtmDeleteRouteToDest(V2RouteInfo->RouteOwner, V2RouteHandle, &ChangeFlags); if (Status != NO_ERROR) { // Must have been deleted meanwhile - ignore Match = FALSE; } } #else Match = FALSE; #endif ASSERT(SUCCESS(RtmReleaseRouteInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo))); return Match; } DWORD BlockOperationOnRoutes ( IN PV1_REGN_INFO V1Regn, IN DWORD EnumerationFlags, IN PVOID CriteriaRoute, IN PFUNC RouteOperation ) /*++ Routine Description: Performs the route operation specified on each route in the table that matches the enumeration criteria. The route operation is called with the route handle of each matching route. If the operation returns FALSE, then the route handle is released, else it is expected that the handle was released by the operation itself. Arguments: V1Regn - RTM v1 registration info of the caller EnumerationFlags - Flags indicating the criteria to match CriteriaRoute - The route that we are matching against RouteOperation - Operation performed on matching routes Return Value: Status of the operation --*/ { HANDLE V1EnumHandle; PV1_ENUM_INFO V1Enum; PRTM_ROUTE_HANDLE V2RouteHandles; UINT NumRoutes; UINT i; DWORD Status1; DWORD Status; // // Create an enumeration to get all matching routes // V1EnumHandle = RtmCreateEnumerationHandle(V1Regn->ProtocolFamily, EnumerationFlags, CriteriaRoute); if (V1EnumHandle == NULL) { return GetLastError(); } VALIDATE_V1_ENUM_HANDLE(V1EnumHandle, &V1Enum); // // Get list of all matching routes and call operation // // Allocate this var-size handles array on the stack V2RouteHandles = ALLOC_HANDLES(V1Regn->Rtmv2Profile.MaxHandlesInEnum); do { // Get next route in enum, and run operation on it NumRoutes = V1Regn->Rtmv2Profile.MaxHandlesInEnum; Status = RtmGetEnumRoutes(V1Regn->Rtmv2RegHandle, V1Enum->Rtmv2RouteEnum, &NumRoutes, V2RouteHandles); for (i = 0; i < NumRoutes; i++) { if (!RouteOperation(V1Regn, V2RouteHandles[i], V1Enum)) { // Operation not successful - release handle Status1 = RtmReleaseRoutes(V1Regn->Rtmv2RegHandle, 1, &V2RouteHandles[i]); ASSERT(SUCCESS(Status1)); } } } while (Status == NO_ERROR); ASSERT(SUCCESS(RtmCloseEnumerationHandle(V1EnumHandle))); return (Status == ERROR_NO_MORE_ROUTES) ? NO_ERROR : Status; } BOOL MatchCriteriaAndCopyRoute ( IN PV1_REGN_INFO V1Regn, IN PRTM_ROUTE_HANDLE V2RouteHandle, IN PV1_ENUM_INFO V1Enum OPTIONAL, OUT PVOID V1Route OPTIONAL ) /*++ Routine Description: If the input route matches enumeration criteria, it converts to a V1 route and copies it to the output buffer. Arguments: V1Regn - RTM v1 registration info of the caller V2RouteHandle - Handle of the route being considered V1Enum - Enum info that gives matching criteria V1Route - Buffer in which the V1 route is copied Return Value: TRUE if route matches criteria, and FALSE if it does not --*/ { RTM_ENTITY_INFO EntityInfo; PRTM_ROUTE_INFO V2RouteInfo; RTM_VIEW_SET BestInViews; ULONG Protocol; BOOL Match; DWORD Status; // // Get the route's information from RTMv2 // V2RouteInfo = ALLOC_ROUTE_INFO(V1Regn->Rtmv2Profile.MaxNextHopsInRoute, 1); Status = RtmGetRouteInfo(V1Regn->Rtmv2RegHandle, V2RouteHandle, V2RouteInfo, NULL); if (Status != NO_ERROR) { return FALSE; } // // If we have no criteria, we match every route // if (!ARGUMENT_PRESENT(V1Enum)) { Match = TRUE; } else { Match = FALSE; do { if (V1Enum->EnumFlags & RTM_INCLUDE_DISABLED_ROUTES) { // Is route anything but a unicast or disabled one ? if (V2RouteInfo->BelongsToViews & ~RTM_VIEW_MASK_UCAST) { break; } } // // Does it match the criteria route's protocol ? // if (V1Enum->EnumFlags & RTM_ONLY_THIS_PROTOCOL) { // // Get the protocol type for this route // Status = RtmGetEntityInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo->RouteOwner, &EntityInfo); if (Status != NO_ERROR) { break; } Protocol = EntityInfo.EntityId.EntityProtocolId; Status = RtmReleaseEntityInfo(V1Regn->Rtmv2RegHandle, &EntityInfo); ASSERT(Status == NO_ERROR); // Is this the routing protocol we need ? if (V1Enum->CriteriaRoute.XxRoute.RR_RoutingProtocol != Protocol) { break; } } // // And does it match other criteria in enum ? // if (V1Enum->EnumFlags & RTM_ONLY_BEST_ROUTES) { Status = RtmIsBestRoute(V1Regn->Rtmv2RegHandle, V2RouteHandle, &BestInViews); if ((BestInViews & RTM_VIEW_MASK_UCAST) == 0) { break; } } #if DBG if (V1Enum->EnumFlags & RTM_ONLY_THIS_INTERFACE) { RTM_NEXTHOP_INFO NextHopInfo; ULONG IfIndex; // // We are checking only the first next hop // as we expect this function to be used // only by V1 protocols on their own routes // ASSERT(V2RouteInfo->NextHopsList.NumNextHops == 1); Status = RtmGetNextHopInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo->NextHopsList.NextHops[0], &NextHopInfo); if (Status != NO_ERROR) { break; } // Get the interface index for this nexthop IfIndex = NextHopInfo.InterfaceIndex; Status = RtmReleaseNextHopInfo(V1Regn->Rtmv2RegHandle, &NextHopInfo); ASSERT(Status == NO_ERROR); // Is this the interface that we are enum'ing if (IfIndex !=V1Enum->CriteriaRoute.XxRoute.RR_InterfaceID) { // We have already done this filtering in RTM v2 ASSERT(FALSE); break; } } #endif Match = TRUE; } while (FALSE); } // // If we have a match, then make a copy of this route // if (Match) { if (ARGUMENT_PRESENT(V1Route)) { Status = MakeV1RouteFromV2Route(V1Regn, V2RouteInfo, V1Route); if (Status != NO_ERROR) { Match = FALSE; } } } Status = RtmReleaseRouteInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo); ASSERT(Status == NO_ERROR); return Match; } BOOL WINAPI RtmIsRoute ( IN DWORD ProtocolFamily, IN PVOID Network, OUT PVOID BestRoute OPTIONAL ) /*++ Routine Description: Checks the route table corr. to a protocol family for the existence of a route to the input network. Arguments: ProtocolFamily - Protocol family of the route table Network - Network address we are trying to reach BestRoute - Best route to the network is filled Return Value: TRUE if a best route exists, FALSE if not, Use GetLastError to check the status code --*/ { PV1_REGN_INFO V1Regn; RTM_ROUTE_HANDLE V2RouteHandle; PRTM_DEST_INFO DestInfo; RTM_NET_ADDRESS NetAddr; BOOL Match; DWORD Status; // // Validate incoming parameters before action // if (ProtocolFamily >= RTM_NUM_OF_PROTOCOL_FAMILIES) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } V1Regn = V1Globals.PfRegInfo[ProtocolFamily]; if (V1Regn == NULL) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } // Allocate this var-size dest-info on the stack DestInfo = ALLOC_DEST_INFO(V1Regn->Rtmv2NumViews, 1); MakeNetAddress(Network, TempUlong, ProtocolFamily, &NetAddr); Status = RtmGetExactMatchDestination(V1Regn->Rtmv2RegHandle, &NetAddr, RTM_BEST_PROTOCOL, RTM_VIEW_MASK_UCAST, DestInfo); if (Status == NO_ERROR) { // // We have a unicast route to the network // if (ARGUMENT_PRESENT(BestRoute)) { V2RouteHandle = DestInfo->ViewInfo[0].Route; ASSERT(V2RouteHandle != NULL); // We have no criteria; so pass NULL for it Match = MatchCriteriaAndCopyRoute(V1Regn, V2RouteHandle, NULL, BestRoute); ASSERT(Match == TRUE); } Status = RtmReleaseDestInfo(V1Regn->Rtmv2RegHandle, DestInfo); ASSERT(Status == NO_ERROR); return TRUE; } return FALSE; } BOOL WINAPI RtmLookupIPDestination( IN DWORD DestAddr, OUT PRTM_IP_ROUTE IPRoute ) /*++ Routine Description: Gets the best non-loopback IP route to the input destination address. Arguments: DestAddr - Network address of the input dest IPRoute - Best non-loopback route is filled Return Value: TRUE if a best route exists, FALSE if not, Use GetLastError to check the status code --*/ { PV1_REGN_INFO V1Regn; RTM_NET_ADDRESS NetAddr; PRTM_DEST_INFO DestInfo1; PRTM_DEST_INFO DestInfo2; DWORD Status; // // Validate incoming parameters before action // V1Regn = V1Globals.PfRegInfo[RTM_PROTOCOL_FAMILY_IP]; if (V1Regn == NULL) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } // Allocate this var-size dest-infos on the stack DestInfo1 = ALLOC_DEST_INFO(V1Regn->Rtmv2NumViews, 1); DestInfo2 = ALLOC_DEST_INFO(V1Regn->Rtmv2NumViews, 1); // Convert the V1 addr to a V2 net address format MakeHostAddress((PUCHAR)&DestAddr, RTM_PROTOCOL_FAMILY_IP, &NetAddr); // // Get the best route to the input dest // Status = RtmGetMostSpecificDestination(V1Regn->Rtmv2RegHandle, &NetAddr, RTM_BEST_PROTOCOL, RTM_VIEW_MASK_UCAST, DestInfo1); while (Status == NO_ERROR) { // // Check if this route is a non-loopback one // if (CopyNonLoopbackIPRoute(V1Regn, DestInfo1, IPRoute)) { break; } // // Backtrack up the tree for next best route // Status = RtmGetLessSpecificDestination(V1Regn->Rtmv2RegHandle, DestInfo1->DestHandle, RTM_BEST_PROTOCOL, RTM_VIEW_MASK_UCAST, DestInfo2); ASSERT(SUCCESS(RtmReleaseDestInfo(V1Regn->Rtmv2RegHandle, DestInfo1))); SWAP_POINTERS(DestInfo1, DestInfo2); } if (Status == NO_ERROR) { ASSERT(SUCCESS(RtmReleaseDestInfo(V1Regn->Rtmv2RegHandle, DestInfo1))); return TRUE; } SetLastError(Status); return FALSE; } BOOL CopyNonLoopbackIPRoute ( IN PV1_REGN_INFO V1Regn, IN PRTM_DEST_INFO V2DestInfo, OUT PVOID V1Route ) /*++ Routine Description: Check if the input destination has a non- loopback best route, and if so copy route to the output buffer after conversion to v1 Arguments: V1Regn - RTMv1 Registration info of caller V2DestInfo - Dest whose route we are checking V1Route - Best route is converted to V1 and filled if it is not-loopack Return Value: TRUE if best route is non-loopback, or FALSE --*/ { RTM_ROUTE_HANDLE V2RouteHandle; PRTM_ROUTE_INFO V2RouteInfo; BOOL Match; ULONG Address; DWORD Status; // // Check if the destination addr is loopback // [ Optimized to avoid getting route info ] // Address = * (ULONG *) V2DestInfo->DestAddress.AddrBits; if ((Address & 0x000000FF) == 0x0000007F) { return FALSE; } V2RouteHandle = V2DestInfo->ViewInfo[0].Route; V2RouteInfo = ALLOC_ROUTE_INFO(V1Regn->Rtmv2Profile.MaxNextHopsInRoute, 1); // Get the route's information from RTMv2 Status = RtmGetRouteInfo(V1Regn->Rtmv2RegHandle, V2RouteHandle, V2RouteInfo, NULL); if (Status != NO_ERROR) { return FALSE; }; // If this is a non-loopback route, copy it Match = !(V2RouteInfo->Flags & RTM_ROUTE_FLAGS_LOOPBACK); if (Match) { Status = MakeV1RouteFromV2Route(V1Regn, V2RouteInfo, V1Route); if (Status != NO_ERROR) { Match = FALSE; } } Status = RtmReleaseRouteInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo); ASSERT(Status == NO_ERROR); return Match; } ULONG WINAPI RtmGetNetworkCount ( IN DWORD ProtocolFamily ) /*++ Routine Description: Get the number of networks (same as RTMv2 destinations) in the route table corr. to the input protocol family Arguments: ProtocolFamily - Protocol family of the route table Return Value: Number of destinations, or 0 (Use GetLastError() here) --*/ { RTM_ADDRESS_FAMILY_INFO AddrFamilyInfo; PV1_REGN_INFO V1Regn; UINT NumEntities; DWORD Status; do { // // Validate incoming parameters before action // if (ProtocolFamily >= RTM_NUM_OF_PROTOCOL_FAMILIES) { Status = ERROR_INVALID_PARAMETER; break; } V1Regn = V1Globals.PfRegInfo[ProtocolFamily]; if (V1Regn == NULL) { Status = ERROR_INVALID_HANDLE; break; } // // Query the appropriate table for reqd info // NumEntities = 0; Status = RtmGetAddressFamilyInfo(0, // v1 maps to default Instance ADDRESS_FAMILY[ProtocolFamily], &AddrFamilyInfo, &NumEntities, NULL); if (Status != NO_ERROR) { break; } return AddrFamilyInfo.NumDests; } while (FALSE); // // Some error occured - clean up and return 0 // SetLastError(Status); return 0; } ULONG WINAPI RtmGetRouteAge ( IN PVOID Route ) /*++ Routine Description: Computes the age of the route from its creation time and the current time in seconds. Assumes that the creation time of the route is correctly filled in, which is not the case as we are currently not keeping a FILETIME in the route to save space. If we do keep time, then this function would work without any changes. Arguments: Route - Route whose age we want. Return Value: Age of the route in seconds, or FFFFFFFF (GetLastError gives error in this case). --*/ { ULONGLONG CurTime; // // This code has been directly copied from RTMv1 // GetSystemTimeAsFileTime((FILETIME *)&CurTime); CurTime -= * (PULONGLONG) &(((PRTM_IP_ROUTE)Route)->RR_TimeStamp); if (((PULARGE_INTEGER)&CurTime)->HighPart<10000000) { return (ULONG)(CurTime/10000000); } else { SetLastError (ERROR_INVALID_PARAMETER); return 0xFFFFFFFF; } } // // Common Helper routines // VOID MakeV2RouteFromV1Route ( IN PV1_REGN_INFO V1Regn, IN PVOID V1Route, IN PRTM_NEXTHOP_HANDLE V2NextHop, OUT PRTM_NET_ADDRESS V2DestAddr OPTIONAL, OUT PRTM_ROUTE_INFO V2RouteInfo OPTIONAL ) /*++ Routine Description: Converts a route in RTM v1 format to a route in the RTM v2 format (at present for IP only). The nexthop for the V2 route should have been computed before and passed in as a parameter. Also see function 'MakeV2NextHopFromV1Route'. The function also returns the destination addr along with the RTMv2 route info, as the route info itself does not contain the dest address. Arguments: V1Regn - RTMv1 Registration info of caller V1Route - RTMv1 route being converted to V2 V2NextHop - The V2 nexthop for the V2 route V2DestAddr - V2 destination addr is filled here V2RouteInfo - V2 route information is filled here Return Value: None --*/ { PRTM_IP_ROUTE IPRoute; ULONG TempUlong; // // Do conversion for IP alone (worry about IPX later) // IPRoute = (PRTM_IP_ROUTE) V1Route; if (ARGUMENT_PRESENT(V2RouteInfo)) { ZeroMemory(V2RouteInfo, sizeof(RTM_ROUTE_INFO)); // Fill up the V2 Route Info with V1 info // Assumes caller is owner of the route V2RouteInfo->RouteOwner = V1Regn->Rtmv2RegHandle; V2RouteInfo->Neighbour = V2NextHop; // Should keep all the V1 flags in the V2 route V2RouteInfo->Flags1 = (UCHAR) IPRoute->RR_FamilySpecificData.FSD_Flags; // The only flag we understand is the valid flag // If route is not valid, we add it to no views #if DBG V2RouteInfo->BelongsToViews = RTM_VIEW_MASK_NONE; #endif if (V2RouteInfo->Flags1 & IP_VALID_ROUTE) { V2RouteInfo->BelongsToViews = RTM_VIEW_MASK_UCAST; } if (IsRouteLoopback(IPRoute)) { V2RouteInfo->Flags = RTM_ROUTE_FLAGS_LOOPBACK; } switch (IPRoute->RR_FamilySpecificData.FSD_Type) { case 3: V2RouteInfo->Flags |= RTM_ROUTE_FLAGS_LOCAL; break; case 4: V2RouteInfo->Flags |= RTM_ROUTE_FLAGS_REMOTE; break; } V2RouteInfo->PrefInfo.Preference = IPRoute->RR_FamilySpecificData.FSD_Priority; V2RouteInfo->PrefInfo.Metric = IPRoute->RR_FamilySpecificData.FSD_Metric; // Only the first DWORD is copied by wrapper V2RouteInfo->EntitySpecificInfo = (PVOID) (ULONG_PTR) IPRoute->RR_ProtocolSpecificData.PSD_Data[0]; V2RouteInfo->NextHopsList.NumNextHops = 1; V2RouteInfo->NextHopsList.NextHops[0] = V2NextHop; } // Fill up the V2 Dest Address with V1 info if (ARGUMENT_PRESENT(V2DestAddr)) { MakeNetAddressForIP(&IPRoute->RR_Network, TempUlong, V2DestAddr); } return; } VOID MakeV2NextHopFromV1Route ( IN PV1_REGN_INFO V1Regn, IN PVOID V1Route, OUT PRTM_NEXTHOP_INFO V2NextHopInfo ) /*++ Routine Description: Computes RTMv2 next hop info using the nexthop address and interface index in the RTMv1 route. Arguments: V1Regn - RTMv1 Registration info of caller V1Route - V1 route that is being considered V2NextHopInfo - V2 Next hop info for input route Return Value: None --*/ { PRTM_IP_ROUTE IPRoute; ULONG TempUlong; ZeroMemory(V2NextHopInfo, sizeof(RTM_NEXTHOP_INFO)); // // Do conversion for IP alone (worry about IPX later) // IPRoute = (PRTM_IP_ROUTE) V1Route; // // We ignore the next hop mask in the conversion // // // MakeNetAddressForIP(&IPRoute->RR_NextHopAddress, // TempUlong, // &V2NextHopInfo->NextHopAddress); // UNREFERENCED_PARAMETER(TempUlong); MakeHostAddressForIP(&IPRoute->RR_NextHopAddress, &V2NextHopInfo->NextHopAddress); V2NextHopInfo->NextHopOwner = V1Regn->Rtmv2RegHandle; V2NextHopInfo->InterfaceIndex = IPRoute->RR_InterfaceID; return; } VOID MakeV1RouteFromV2Dest ( IN PV1_REGN_INFO V1Regn, IN PRTM_DEST_INFO DestInfo, OUT PVOID V1Route ) /*++ Routine Description: Fills a V1 route buffer with the destination addr from the V2 route. Arguments: V1Regn - RTMv1 Registration info of caller DestInfo - Destination info in RTMv2 V1Route - V1 route that is being filled in Return Value: None --*/ { PRTM_IP_ROUTE IPRoute; UINT AddrLen; UNREFERENCED_PARAMETER(V1Regn); // // Do conversion for IP alone (worry about IPX later) // IPRoute = (PRTM_IP_ROUTE) V1Route; ZeroMemory(IPRoute, sizeof(RTM_IP_ROUTE)); // // Get the destination addr for this route // IPRoute->RR_Network.N_NetNumber = * (ULONG *) DestInfo->DestAddress.AddrBits; AddrLen = DestInfo->DestAddress.NumBits; ASSERT(AddrLen <= 32); if (AddrLen != 0) { IPRoute->RR_Network.N_NetMask = RtlUlongByteSwap((~0) << (32 - AddrLen)); } // // Fill in dummy family specific data for route // // We make the route the least preferred - by // minimizing its priority and maximizing its // metric - we also treat the route as being // valid and added to the stack - this will // force router manager to delete the route // to this dest in the stack if all routes // to this destination are deleted from RTM. // IPRoute->RR_FamilySpecificData.FSD_Priority = (ULONG) 0; IPRoute->RR_FamilySpecificData.FSD_Metric = IPRoute->RR_FamilySpecificData.FSD_Metric1 = (ULONG) ~0; IPRoute->RR_FamilySpecificData.FSD_Flags = (ULONG) ~0; return; } DWORD MakeV1RouteFromV2Route ( IN PV1_REGN_INFO V1Regn, IN PRTM_ROUTE_INFO V2RouteInfo, OUT PVOID V1Route ) /*++ Routine Description: Converts a route in the RTMv2 format to a V1 route ( at present for IP only ). Arguments: V1Regn - RTMv1 Registration info of caller V2RouteInfo - V2 route information being converted V1Route - Buffer in which V1 route is filled Return Value: Status of the operation --*/ { RTM_ENTITY_INFO EntityInfo; PRTM_DEST_INFO DestInfo; PRTM_IP_ROUTE IPRoute; RTM_NEXTHOP_INFO NextHopInfo; UINT AddrLen; DWORD Status; // // Do conversion for IP alone (worry about IPX later) // IPRoute = (PRTM_IP_ROUTE) V1Route; ZeroMemory(IPRoute, sizeof(RTM_IP_ROUTE)); // // Get the routing protocol for this route // Status = RtmGetEntityInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo->RouteOwner, &EntityInfo); if (Status != NO_ERROR) { return Status; } IPRoute->RR_RoutingProtocol = EntityInfo.EntityId.EntityProtocolId; Status = RtmReleaseEntityInfo(V1Regn->Rtmv2RegHandle, &EntityInfo); ASSERT(Status == NO_ERROR); // // Get the destination addr for this route // // Allocate this var-size dest-info on the stack DestInfo = ALLOC_DEST_INFO(V1Regn->Rtmv2NumViews, 1); Status = RtmGetDestInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo->DestHandle, RTM_BEST_PROTOCOL, RTM_VIEW_ID_UCAST, DestInfo); if (Status != NO_ERROR) { return Status; } IPRoute->RR_Network.N_NetNumber = * (ULONG *) DestInfo->DestAddress.AddrBits; AddrLen = DestInfo->DestAddress.NumBits; ASSERT(AddrLen <= 32); if (AddrLen != 0) { IPRoute->RR_Network.N_NetMask = RtlUlongByteSwap((~0) << (32 - AddrLen)); } Status = RtmReleaseDestInfo(V1Regn->Rtmv2RegHandle, DestInfo); ASSERT(Status == NO_ERROR); // // Get the next hop address and interface // ASSERT(V2RouteInfo->NextHopsList.NumNextHops > 0); Status = RtmGetNextHopInfo(V1Regn->Rtmv2RegHandle, V2RouteInfo->NextHopsList.NextHops[0], &NextHopInfo); if (Status != NO_ERROR) { return Status; } IPRoute->RR_InterfaceID = NextHopInfo.InterfaceIndex; IPRoute->RR_NextHopAddress.N_NetNumber = * (ULONG *) NextHopInfo.NextHopAddress.AddrBits; AddrLen = NextHopInfo.NextHopAddress.NumBits; ASSERT(AddrLen <= 32); if (AddrLen != 0) { IPRoute->RR_NextHopAddress.N_NetMask = RtlUlongByteSwap((~0) >> (32 - AddrLen)); } Status = RtmReleaseNextHopInfo(V1Regn->Rtmv2RegHandle, &NextHopInfo); ASSERT(Status == NO_ERROR); // // Get the family specific data for route // IPRoute->RR_FamilySpecificData.FSD_Priority = V2RouteInfo->PrefInfo.Preference; IPRoute->RR_FamilySpecificData.FSD_Metric = IPRoute->RR_FamilySpecificData.FSD_Metric1 = V2RouteInfo->PrefInfo.Metric; IPRoute->RR_FamilySpecificData.FSD_Flags = V2RouteInfo->Flags1; if (V2RouteInfo->Flags & RTM_ROUTE_FLAGS_LOCAL) { IPRoute->RR_FamilySpecificData.FSD_Type = 3; } else if (V2RouteInfo->Flags & RTM_ROUTE_FLAGS_REMOTE) { IPRoute->RR_FamilySpecificData.FSD_Type = 4; } // // Get the protocol specific data for route // IPRoute->RR_ProtocolSpecificData.PSD_Data[0] = PtrToUlong(V2RouteInfo->EntitySpecificInfo); return NO_ERROR; } #endif // WRAPPER