/*++ Copyright (c) 1997 - 98, Microsoft Corporation Module Name: rtmnhop.c Abstract: Contains routines for managing RTM Next Hops. Author: Chaitanya Kodeboyina (chaitk) 21-Aug-1998 Revision History: --*/ #include "pchrtm.h" #pragma hdrstop DWORD WINAPI RtmAddNextHop ( IN RTM_ENTITY_HANDLE RtmRegHandle, IN PRTM_NEXTHOP_INFO NextHopInfo, IN OUT PRTM_NEXTHOP_HANDLE NextHopHandle OPTIONAL, OUT PRTM_NEXTHOP_CHANGE_FLAGS ChangeFlags ) /*++ Adds or Updates a next hop entry to the entity's next-hop table. If the 'nexthop handle' argument is present, then this next-hop is updated. Otherwise a search is made for the address in the input 'nexthop info', and if a next-hop is found, it is updated. If no matching next-hop is found, the a new next-hop is added. Arguments: RtmRegHandle - RTM registration handle for calling entity, NextHopInfo - Info that corresponds to this next-hop, NextHopHandle - Handle to the next-hop to update is passed in (or NULL), and if a next-hop is created a handle to this new next-hop is returned. ChangeFlags - Flags whether this was a add or an update. Return Value: Status of the operation --*/ { PRTM_NET_ADDRESS NextHopAddress; PENTITY_INFO Entity; PDEST_INFO Dest; PNEXTHOP_LIST NewHopList; PNEXTHOP_INFO NewNextHop; PNEXTHOP_INFO NextHop; LOOKUP_CONTEXT Context; PLIST_ENTRY p; DWORD Status; // // Validate incoming information before attempting an add // VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity); if (NextHopInfo->RemoteNextHop) { VALIDATE_DEST_HANDLE(NextHopInfo->RemoteNextHop, &Dest); } // // If there is a next hop handle, we can avoid a search // NextHop = NULL; if (ARGUMENT_PRESENT(NextHopHandle) && (*NextHopHandle)) { VALIDATE_NEXTHOP_HANDLE(*NextHopHandle, &NextHop); // Make sure that the caller owns this nexthop if (NextHop->NextHopInfo.NextHopOwner != RtmRegHandle) { return ERROR_ACCESS_DENIED; } } #if WRN NewNextHop = NULL; NewHopList = NULL; #endif *ChangeFlags = 0; ACQUIRE_NHOP_TABLE_WRITE_LOCK(Entity); do { // // Search for the next hop if we don't already have one // if (NextHop == NULL) { Status = FindNextHop(Entity, NextHopInfo, &Context, &p); if (SUCCESS(Status)) { // The next hop already exists in the tree NextHop = CONTAINING_RECORD(p, NEXTHOP_INFO, NextHopsLE); } else { // Init new allocations in case we fail in between NewNextHop = NULL; NewHopList = NULL; // // Create a new next hop with the input information // Status = CreateNextHop(Entity, NextHopInfo, &NewNextHop); if (!SUCCESS(Status)) { break; } // // Do we need to create a new list of next hops too ? // if (p == NULL) { NewHopList = AllocNZeroMemory(sizeof(NEXTHOP_LIST)); if (NewHopList == NULL) { break; } InitializeListHead(&NewHopList->NextHopsList); // Insert the next-hop-list into the tree NextHopAddress = &NextHopInfo->NextHopAddress; Status = InsertIntoTable(Entity->NextHopTable, NextHopAddress->NumBits, NextHopAddress->AddrBits, &Context, &NewHopList->LookupLinkage); if (!SUCCESS(Status)) { break; } p = &NewHopList->NextHopsList; } // Insert the next hop in the list and ref it InsertTailList(p, &NewNextHop->NextHopsLE); Entity->NumNextHops++; NextHop = NewNextHop; *ChangeFlags = RTM_NEXTHOP_CHANGE_NEW; } } // // If this is an update, copy necessary information // if (*ChangeFlags != RTM_NEXTHOP_CHANGE_NEW) { CopyToNextHop(Entity, NextHopInfo, NextHop); } // // Return the next hop handle if not passed in // if (ARGUMENT_PRESENT(NextHopHandle)) { if (*NextHopHandle == NULL) { *NextHopHandle = MAKE_HANDLE_FROM_POINTER(NextHop); REFERENCE_NEXTHOP(NextHop, HANDLE_REF); } } Status = NO_ERROR; } while(FALSE); RELEASE_NHOP_TABLE_WRITE_LOCK(Entity); if (!SUCCESS(Status)) { // Some error occured - clean up if (NewHopList) { FreeMemory(NewHopList); } if (NewNextHop) { DEREFERENCE_NEXTHOP(NewNextHop, CREATION_REF); } } return Status; } DWORD WINAPI RtmDeleteNextHop ( IN RTM_ENTITY_HANDLE RtmRegHandle, IN RTM_NEXTHOP_HANDLE NextHopHandle OPTIONAL, IN PRTM_NEXTHOP_INFO NextHopInfo ) /*++ Routine Description: Deletes a next hop from the next-hop table. The next-hop memory remains in use until all reference counts go to 0. Arguments: RtmRegHandle - RTM registration handle for calling entity, NextHopHandle - Handle to the next-hop we want to delete, NextHopInfo - If no NextHopHandle is passed in, this is used to match the next-hop to be deleted. Return Value: Status of the operation --*/ { PRTM_NET_ADDRESS NextHopAddress; PLOOKUP_LINKAGE Linkage; PENTITY_INFO Entity; PNEXTHOP_LIST HopList; PNEXTHOP_INFO NextHop; PLOOKUP_CONTEXT PContext; LOOKUP_CONTEXT Context; PLIST_ENTRY p; DWORD Status; VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity); // // If there is a next hop handle, we can avoid a search // NextHop = NULL; if (ARGUMENT_PRESENT(NextHopHandle)) { VALIDATE_NEXTHOP_HANDLE(NextHopHandle, &NextHop); // Make sure that the caller owns this nexthop if (NextHop->NextHopInfo.NextHopOwner != RtmRegHandle) { return ERROR_ACCESS_DENIED; } } #if WRN Status = ERROR_GEN_FAILURE; #endif ACQUIRE_NHOP_TABLE_WRITE_LOCK(Entity); do { // // Search for the next hop if we don't already have one // if (NextHop == NULL) { Status = FindNextHop(Entity, NextHopInfo, &Context, &p); if (!SUCCESS(Status)) { break; } PContext = &Context; NextHop = CONTAINING_RECORD(p, NEXTHOP_INFO, NextHopsLE); } else { // Make sure that it has not already been deleted if (NextHop->NextHopInfo.State == RTM_NEXTHOP_STATE_DELETED) { break; } PContext = NULL; } // Get a 'possible' list entry that starts the hop list HopList = CONTAINING_RECORD(NextHop->NextHopsLE.Blink, NEXTHOP_LIST, NextHopsList); // Delete this next-hop from the nexthops list NextHop->NextHopInfo.State = RTM_NEXTHOP_STATE_DELETED; RemoveEntryList(&NextHop->NextHopsLE); // Do we have any more next hops on this list if (IsListEmpty(&HopList->NextHopsList)) { // Remove the hop-list from the next hop table NextHopAddress = &NextHop->NextHopInfo.NextHopAddress; Status = DeleteFromTable(Entity->NextHopTable, NextHopAddress->NumBits, NextHopAddress->AddrBits, PContext, &Linkage); ASSERT(SUCCESS(Status) && (&HopList->LookupLinkage == Linkage)); FreeMemory(HopList); } // Dereference the next-hop that was deleted Entity->NumNextHops--; DEREFERENCE_NEXTHOP(NextHop, CREATION_REF); if (ARGUMENT_PRESENT(NextHopHandle)) { DEREFERENCE_NEXTHOP(NextHop, HANDLE_REF); } Status = NO_ERROR; } while (FALSE); RELEASE_NHOP_TABLE_WRITE_LOCK(Entity); return Status; } DWORD WINAPI RtmFindNextHop ( IN RTM_ENTITY_HANDLE RtmRegHandle, IN PRTM_NEXTHOP_INFO NextHopInfo, OUT PRTM_NEXTHOP_HANDLE NextHopHandle, OUT PRTM_NEXTHOP_INFO *NextHopPointer OPTIONAL ) /*++ Routine Description: Finds a next hop, given its info, in entity's next-hop table. Arguments: RtmRegHandle - RTM registration handle for calling entity, NextHopInfo - Info for the next-hop we are searching for ( NextHopOwner, NextHopAddress, IfIndex ), NextHopHandle - Handle to next-hop is returned (if found), NextHopPointer - A pointer to the next-hop is returned for fast direct access by the next-hop's owner. Return Value: Status of the operation --*/ { PENTITY_INFO Entity; PNEXTHOP_INFO NextHop; PLIST_ENTRY p; DWORD Status; DBG_VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity); VALIDATE_ENTITY_HANDLE(NextHopInfo->NextHopOwner, &Entity); if (ARGUMENT_PRESENT(NextHopPointer)) { // Only the nexthop owner gets a direct ptr if (RtmRegHandle != NextHopInfo->NextHopOwner) { return ERROR_ACCESS_DENIED; } } // // Search for the next hop in the next hop table // ACQUIRE_NHOP_TABLE_READ_LOCK(Entity); Status = FindNextHop(Entity, NextHopInfo, NULL, &p); if (SUCCESS(Status)) { NextHop = CONTAINING_RECORD(p, NEXTHOP_INFO, NextHopsLE); *NextHopHandle = MAKE_HANDLE_FROM_POINTER(NextHop); REFERENCE_NEXTHOP(NextHop, HANDLE_REF); if (ARGUMENT_PRESENT(NextHopPointer)) { *NextHopPointer = &NextHop->NextHopInfo; } } RELEASE_NHOP_TABLE_READ_LOCK(Entity); return Status; } DWORD FindNextHop ( IN PENTITY_INFO Entity, IN PRTM_NEXTHOP_INFO NextHopInfo, OUT PLOOKUP_CONTEXT Context OPTIONAL, OUT PLIST_ENTRY *NextHopLE ) /*++ Routine Description: Finds a next hop, given its info, in entity's next-hop table. This is a helper function that is called by public functions that add, delete or find a next hop in the next hop table. Arguments: Entity - Entity whose nexthop table we are searching, NextHopInfo - Info for the next-hop we are searching for ( NextHopOwner, NextHopAddress, IfIndex ), Context - Search context for holding list of nexthops, NextHopLE - List entry for the matching nexthop (if found) (or) list entry before which it'll be inserted. Return Value: Status of the operation --*/ { PRTM_NET_ADDRESS NextHopAddress; PNEXTHOP_LIST NextHopsList; PNEXTHOP_INFO NextHop; ULONG IfIndex; PLOOKUP_LINKAGE Linkage; PLIST_ENTRY NextHops, p; DWORD Status; *NextHopLE = NULL; // // Search for list of next hops, given the address // NextHopAddress = &NextHopInfo->NextHopAddress; Status = SearchInTable(Entity->NextHopTable, NextHopAddress->NumBits, NextHopAddress->AddrBits, Context, &Linkage); if (!SUCCESS(Status)) { return Status; } NextHopsList = CONTAINING_RECORD(Linkage, NEXTHOP_LIST, LookupLinkage); // // Search for the nexthop with the interface idx // IfIndex = NextHopInfo->InterfaceIndex; NextHops = &NextHopsList->NextHopsList; #if WRN NextHop = NULL; #endif for (p = NextHops->Flink; p != NextHops; p = p->Flink) { NextHop = CONTAINING_RECORD(p, NEXTHOP_INFO, NextHopsLE); if (NextHop->NextHopInfo.InterfaceIndex <= IfIndex) { break; } } *NextHopLE = p; if ((p == NextHops) || (NextHop->NextHopInfo.InterfaceIndex != IfIndex)) { return ERROR_NOT_FOUND; } return NO_ERROR; } DWORD WINAPI RtmGetNextHopPointer ( IN RTM_ENTITY_HANDLE RtmRegHandle, IN RTM_NEXTHOP_HANDLE NextHopHandle, OUT PRTM_NEXTHOP_INFO *NextHopPointer ) /*++ Routine Description: Gets a direct pointer to the next-hop for read/write by its owner. Arguments: RtmRegHandle - RTM registration handle for calling entity, NextHopHandle - Handle to the next-hop whose pointer we want, NextHopPointer - A pointer to the next-hop is returned for fast direct access by the caller, only if the caller is the owner of this next-hop. Return Value: Status of the operation --*/ { PENTITY_INFO Entity; PNEXTHOP_INFO NextHop; DBG_VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity); VALIDATE_NEXTHOP_HANDLE(NextHopHandle, &NextHop); // // Return a pointer only if caller owns next-hop // if (NextHop->NextHopInfo.NextHopOwner != RtmRegHandle) { return ERROR_ACCESS_DENIED; } *NextHopPointer = &NextHop->NextHopInfo; return NO_ERROR; } DWORD WINAPI RtmLockNextHop( IN RTM_ENTITY_HANDLE RtmRegHandle, IN RTM_NEXTHOP_HANDLE NextHopHandle, IN BOOL Exclusive, IN BOOL LockNextHop, OUT PRTM_NEXTHOP_INFO *NextHopPointer OPTIONAL ) /*++ Routine Description: Locks or Unlocks a next hop. This function is called by the next-hop's owner to lock the next-hop before making changes directly to the next-hop using a pointer to this next-hop. Arguments: RtmRegHandle - RTM registration handle for calling entity, NextHopHandle - Handle to the next-hop that we want to lock, Exclusive - TRUE to lock in write mode, else read mode, LockNextHop - Lock nexthop if TRUE, Unlock it if FALSE, NextHopPointer - A pointer to the next-hop is returned for fast direct access by the next hop's owner. Return Value: Status of the operation --*/ { PENTITY_INFO Entity; PNEXTHOP_INFO NextHop; VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity); VALIDATE_NEXTHOP_HANDLE(NextHopHandle, &NextHop); // // Lock or unlock only if caller owns next-hop // if (NextHop->NextHopInfo.NextHopOwner != RtmRegHandle) { return ERROR_ACCESS_DENIED; } // Return a direct pointer for use in update if (ARGUMENT_PRESENT(NextHopPointer)) { *NextHopPointer = &NextHop->NextHopInfo; } // Lock or unlock the nexthop as the case may be if (LockNextHop) { if (Exclusive) { ACQUIRE_NHOP_TABLE_WRITE_LOCK(Entity); } else { ACQUIRE_NHOP_TABLE_READ_LOCK(Entity); } } else { if (Exclusive) { RELEASE_NHOP_TABLE_WRITE_LOCK(Entity); } else { RELEASE_NHOP_TABLE_READ_LOCK(Entity); } } return NO_ERROR; }