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