//============================================================================ // Copyright (c) 1995, Microsoft Corporation // // File: work.c // // History: // Abolade Gbadegesin Aug-8-1995 Created. // // worker function implementations //============================================================================ #include "pchrip.h" #pragma hdrstop #define STRSAFE_NO_DEPRECATE #include VOID ProcessSocket( DWORD dwAddrIndex, PIF_TABLE_ENTRY pite, PIF_TABLE pTable ); VOID EnqueueStartFullUpdate( PIF_TABLE_ENTRY pite, LARGE_INTEGER qwLastFullUpdateTime ); DWORD EnqueueDemandUpdateCheck( PUPDATE_CONTEXT pwc ); VOID EnqueueDemandUpdateMessage( DWORD dwInterfaceIndex, DWORD dwError ); DWORD CountInterfaceRoutes( DWORD dwInterfaceIndex ); BOOL ProcessResponseEntry( PIF_TABLE_ENTRY pITE, DWORD dwAddrIndex, DWORD dwSource, PIPRIP_ENTRY pIE, PIPRIP_PEER_STATS pPS ); DWORD SendRouteOnIfList( UPDATE_BUFFER pBufList[], DWORD dwBufCount, DWORD dwSendMode, PROUTE_TABLE pSummaryTable, PRIP_IP_ROUTE pRoute ); //---------------------------------------------------------------------------- // Macro: RTM_ROUTE_FROM_IPRIP_ENTRY // Macro: IPRIP_ENTRY_FROM_RTM_ROUTE // // These two macros are used to transfer data from an RTM route struct // to an IPRIPv2 packet route entry, and vice versa. // The first two bytes of an RTM route's ProtocolSpecificData array are used // to store the route tag contained in the IPRIP packet route-entry //---------------------------------------------------------------------------- #define RTM_ROUTE_FROM_IPRIP_ENTRY(r,i) \ (r)->RR_RoutingProtocol = PROTO_IP_RIP; \ SETROUTEMETRIC((r), ntohl((i)->IE_Metric)); \ (r)->RR_Network.N_NetNumber = (i)->IE_Destination; \ (r)->RR_Network.N_NetMask = (i)->IE_SubnetMask; \ (r)->RR_NextHopAddress.N_NetNumber = (i)->IE_Nexthop; \ (r)->RR_NextHopAddress.N_NetMask = (i)->IE_SubnetMask; \ SETROUTETAG((r), ntohs((i)->IE_RouteTag)) #define IPRIP_ENTRY_FROM_RTM_ROUTE(i,r) \ (i)->IE_AddrFamily = htons(AF_INET); \ (i)->IE_Metric = htonl(GETROUTEMETRIC(r)); \ (i)->IE_Destination = (r)->RR_Network.N_NetNumber; \ (i)->IE_SubnetMask = (r)->RR_Network.N_NetMask; \ (i)->IE_Nexthop = (r)->RR_NextHopAddress.N_NetNumber //---------------------------------------------------------------------------- // Macro: IS_ROUTE_IN_ACCEPT_FILTER // Macro: IS_ROUTE_IN_ANNOUNCE_FILTER // // The following three macros are used to search for a route // in the accept filters and announce filters configured for an interface // The last two macros invoke the first macro which executes the inner loop, // since the inner loop is identical in both cases. //---------------------------------------------------------------------------- #define IS_ROUTE_IN_FILTER(route,ret) \ (ret) = 0; \ for ( ; _pfilt < _pfiltend; _pfilt++) { \ _filt = _pfilt->RF_LoAddress; \ if (INET_CMP(route, _filt, _cmp) == 0) { (ret) = 1; break; } \ else if (_cmp > 0) { \ _filt = _pfilt->RF_HiAddress; \ if (INET_CMP(route, _filt, _cmp) <= 0) { (ret) = 1; break; }\ } \ } #define IS_ROUTE_IN_ACCEPT_FILTER(ic,route,ret) { \ INT _cmp; \ DWORD _filt; \ PIPRIP_ROUTE_FILTER _pfilt, _pfiltend; \ _pfilt = IPRIP_IF_ACCEPT_FILTER_TABLE(ic); \ _pfiltend = _pfilt + (ic)->IC_AcceptFilterCount; \ IS_ROUTE_IN_FILTER(route,ret); \ } #define IS_ROUTE_IN_ANNOUNCE_FILTER(ic,route,ret) { \ INT _cmp; \ DWORD _filt; \ PIPRIP_ROUTE_FILTER _pfilt, _pfiltend; \ _pfilt = IPRIP_IF_ANNOUNCE_FILTER_TABLE(ic); \ _pfiltend = _pfilt + (ic)->IC_AnnounceFilterCount; \ IS_ROUTE_IN_FILTER(route,ret); \ } //---------------------------------------------------------------------------- // Macro: IS_PEER_IN_FILTER // // macro used to search the peer filters //---------------------------------------------------------------------------- #define IS_PEER_IN_FILTER(gc,peer,ret) { \ PDWORD _pdwPeer, _pdwPeerEnd; \ (ret) = 0; \ _pdwPeer = IPRIP_GLOBAL_PEER_FILTER_TABLE(gc); \ _pdwPeerEnd = _pdwPeer + (gc)->GC_PeerFilterCount; \ for ( ; _pdwPeer < _pdwPeerEnd; _pdwPeer++) { \ if (*_pdwPeer == (peer)) { (ret) = 1; break; } \ } \ } //---------------------------------------------------------------------------- // UPDATE BUFFER MANAGEMENT // // The following types and functions are used to simplify // the transmission of routes. The system consists of the struct // UPDATE_BUFFER, which includes a function table and a byte buffer, // and a number of three-function update buffer routine sets. // The sets each contain a routine to start an update buffer, // to add a route to an update buffer, and to finish an update buffer. // // There are separate versions for RIPv1 mode and RIPv2 mode. The function // InitializeUpdateBuffer sets up the function table in an update buffer // depending on the configuration for the interface with which the buffer // is associated. This set-up eliminates the need to check the interface // configuration every time an entry must be added; instead, the config // is checked a single time to set up the function table, and afterward // the function generating the update merely calls the functions in the table. // // The setup also depends on the mode in which the information is being sent. // The address to which the information is being sent is stored in the // update buffer, since this will be required every time a route is added. // However, when a full-update is being generated on an interface operating // in RIPv2 mode, the destination address stored is 224.0.0.9, but the // actual destination network is the network of the out-going interface. // Therefore, this address is also stored since it will be needed for // split-horizon/poison-reverse/subnet-summary processing //---------------------------------------------------------------------------- // // these are the modes in which routes may be transmitted // #define SENDMODE_FULL_UPDATE 0 #define SENDMODE_TRIGGERED_UPDATE 1 #define SENDMODE_SHUTDOWN_UPDATE 2 #define SENDMODE_GENERAL_REQUEST 3 #define SENDMODE_GENERAL_RESPONSE1 4 #define SENDMODE_GENERAL_RESPONSE2 5 #define SENDMODE_SPECIFIC_RESPONSE1 6 #define SENDMODE_SPECIFIC_RESPONSE2 7 // // this function set is for interfaces with announcements disabled // DWORD StartBufferNull( PUPDATE_BUFFER pUB ) { return NO_ERROR; } DWORD AddEntryNull( PUPDATE_BUFFER pUB, PRIP_IP_ROUTE pRIR ) { return NO_ERROR; } DWORD FinishBufferNull( PUPDATE_BUFFER pUB ) { return NO_ERROR; } // // this function-set is for RIPv1 interfaces // DWORD StartBufferVersion1( PUPDATE_BUFFER pUB ); DWORD AddEntryVersion1( PUPDATE_BUFFER pUB, PRIP_IP_ROUTE pRIR ); DWORD FinishBufferVersion1( PUPDATE_BUFFER pUB ); // // this function-set is for RIPv2 interfaces // DWORD StartBufferVersion2( PUPDATE_BUFFER pUB ); DWORD AddEntryVersion2( PUPDATE_BUFFER pUB, PRIP_IP_ROUTE pRIR ); DWORD FinishBufferVersion2( PUPDATE_BUFFER pUB ); //---------------------------------------------------------------------------- // Function: InitializeUpdateBuffer // // this function sets up the update-buffer, writing in the functions to use // for restarting the buffer, adding entries, and finishing the buffer. // It also stores the destination address to which the packet is being sent, // as well as the network and netmask for the destination // This assumes the binding table is locked. //---------------------------------------------------------------------------- DWORD InitializeUpdateBuffer( PIF_TABLE_ENTRY pITE, DWORD dwAddrIndex, PUPDATE_BUFFER pUB, DWORD dwSendMode, DWORD dwDestination, DWORD dwCommand ) { DWORD dwAnnounceMode; PIPRIP_IP_ADDRESS paddr; pUB->UB_Length = 0; // // save the pointer to the interface // pUB->UB_ITE = pITE; pUB->UB_AddrIndex = dwAddrIndex; paddr = IPRIP_IF_ADDRESS_TABLE(pITE->ITE_Binding) + dwAddrIndex; pUB->UB_Socket = pITE->ITE_Sockets[dwAddrIndex]; pUB->UB_Address = paddr->IA_Address; pUB->UB_Netmask = paddr->IA_Netmask; // // save the command // pUB->UB_Command = dwCommand; // // store the absolute address to which this packet is destined, // which may differ from the address passed to sendto() // e.g. RIPv2 packets are destined for the interface's network, // but the address passed to sendto() is 224.0.0.9 // if the destination passed in is 0, use the broadcast address // on the outgoing interface as the destination // if (dwDestination == 0) { if(paddr->IA_Netmask == 0xffffffff) { TRACE0(SEND,"MASK ALL ONES"); pUB->UB_DestAddress = (paddr->IA_Address | ~(NETCLASS_MASK(paddr->IA_Address))); } else { pUB->UB_DestAddress = (paddr->IA_Address | ~paddr->IA_Netmask); } pUB->UB_DestNetmask = paddr->IA_Netmask; } else { pUB->UB_DestAddress = dwDestination; pUB->UB_DestNetmask = GuessSubnetMask(pUB->UB_DestAddress, NULL); } // // decide on the announce mode; // if the mode is DISABLED, we still send responses to SPECIFIC requests // on the interface, so set the mode to RIPv1/v2 if sending a specific // response on a disabled interface // dwAnnounceMode = pITE->ITE_Config->IC_AnnounceMode; if (dwAnnounceMode == IPRIP_ANNOUNCE_DISABLED) { if (dwSendMode == SENDMODE_SPECIFIC_RESPONSE1) { dwAnnounceMode = IPRIP_ANNOUNCE_RIP1; } else if (dwSendMode == SENDMODE_SPECIFIC_RESPONSE2) { dwAnnounceMode = IPRIP_ANNOUNCE_RIP2; } } // // set up the function table and destination address, which // depend on the announce-mode of the interface and on the sort // of information being transmitted // switch (dwAnnounceMode) { // // in RIP1 mode, packets are RIP1, broadcast // case IPRIP_ANNOUNCE_RIP1: pUB->UB_AddRoutine = AddEntryVersion1; pUB->UB_StartRoutine = StartBufferVersion1; pUB->UB_FinishRoutine = FinishBufferVersion1; pUB->UB_Destination.sin_port = htons(IPRIP_PORT); pUB->UB_Destination.sin_family = AF_INET; pUB->UB_Destination.sin_addr.s_addr = pUB->UB_DestAddress; break; // // in RIP1-compatible mode, packets are RIP2, broadcast, // except in the case of a general response to a RIP1 router, // in which case the packets are RIP1, unicast // case IPRIP_ANNOUNCE_RIP1_COMPAT: if (dwSendMode == SENDMODE_GENERAL_RESPONSE1) { pUB->UB_AddRoutine = AddEntryVersion1; pUB->UB_StartRoutine = StartBufferVersion1; pUB->UB_FinishRoutine = FinishBufferVersion1; } else { pUB->UB_AddRoutine = AddEntryVersion2; pUB->UB_StartRoutine = StartBufferVersion2; pUB->UB_FinishRoutine = FinishBufferVersion2; } pUB->UB_Destination.sin_port = htons(IPRIP_PORT); pUB->UB_Destination.sin_family = AF_INET; pUB->UB_Destination.sin_addr.s_addr = pUB->UB_DestAddress; break; // // in RIP2 mode, packets are RIP2, multicast, except in the case // of a general/specific responses, in which cases messages are unicast; // note that a RIP2-only router never sends a general response to // a request from a RIP1 router. // case IPRIP_ANNOUNCE_RIP2: pUB->UB_AddRoutine = AddEntryVersion2; pUB->UB_StartRoutine = StartBufferVersion2; pUB->UB_FinishRoutine = FinishBufferVersion2; pUB->UB_Destination.sin_port = htons(IPRIP_PORT); pUB->UB_Destination.sin_family = AF_INET; // // if sending to a specific destination, as a reponse // to a request or as a full update to a unicast peer, // set the IP address of the destination. // Else send to multicast address. // if ( dwDestination != 0 ) { pUB->UB_Destination.sin_addr.s_addr = pUB->UB_DestAddress; } else { pUB->UB_Destination.sin_addr.s_addr = IPRIP_MULTIADDR; } break; default: TRACE2( IF, "invalid announce mode on interface %d (%s)", pITE->ITE_Index, INET_NTOA(paddr->IA_Address) ); pUB->UB_AddRoutine = AddEntryNull; pUB->UB_StartRoutine = StartBufferNull; pUB->UB_FinishRoutine = FinishBufferNull; return ERROR_INVALID_PARAMETER; } return NO_ERROR; } //---------------------------------------------------------------------------- // Function: SendUpdateBuffer // // This function is invoked by the add-entry and finsih-buffer functions // to send the contents of an update-buffer. //---------------------------------------------------------------------------- DWORD SendUpdateBuffer( PUPDATE_BUFFER pbuf ) { INT iLength; DWORD dwErr; TRACE1(SEND,"SENDING TO %s",INET_NTOA(pbuf->UB_Destination.sin_addr.s_addr)); iLength = sendto( pbuf->UB_Socket, pbuf->UB_Buffer, pbuf->UB_Length, 0, (PSOCKADDR)&pbuf->UB_Destination, sizeof(SOCKADDR_IN) ); if (iLength == SOCKET_ERROR || (DWORD)iLength < pbuf->UB_Length) { // // an error occurred // CHAR szDest[20], *lpszAddr; dwErr = WSAGetLastError(); lstrcpy(szDest, INET_NTOA(pbuf->UB_Destination.sin_addr)); lpszAddr = INET_NTOA(pbuf->UB_Address); TRACE4( SEND, "error %d sending update to %s on interface %d (%s)", dwErr, szDest, pbuf->UB_ITE->ITE_Index, lpszAddr ); LOGWARN2(SENDTO_FAILED, lpszAddr, szDest, dwErr); InterlockedIncrement(&pbuf->UB_ITE->ITE_Stats.IS_SendFailures); } else { if (pbuf->UB_Command == IPRIP_REQUEST) { InterlockedIncrement(&pbuf->UB_ITE->ITE_Stats.IS_RequestsSent); } else { InterlockedIncrement(&pbuf->UB_ITE->ITE_Stats.IS_ResponsesSent); } dwErr = NO_ERROR; } return dwErr; } //---------------------------------------------------------------------------- // Function: StartBufferVersion1 // // This starts a RIPv1 update-buffer, zeroing reserved fields, // setting the version, and setting the command field //---------------------------------------------------------------------------- DWORD StartBufferVersion1( PUPDATE_BUFFER pUB ) { PIPRIP_HEADER pHdr; // // set up the header // pHdr = (PIPRIP_HEADER)pUB->UB_Buffer; pHdr->IH_Version = 1; pHdr->IH_Command = (BYTE)pUB->UB_Command; pHdr->IH_Reserved = 0; pUB->UB_Length = sizeof(IPRIP_HEADER); return NO_ERROR; } //---------------------------------------------------------------------------- // Function: AddEntryVersion1 // // This adds an entry to a RIPv1 buffer, first sending the buffer if it is full //---------------------------------------------------------------------------- DWORD AddEntryVersion1( PUPDATE_BUFFER pUB, PRIP_IP_ROUTE pRIR ) { PIPRIP_ENTRY pie; // // if the buffer is full, transmit its contents and restart it // if ((pUB->UB_Length + sizeof(IPRIP_ENTRY)) > MAX_PACKET_SIZE) { SendUpdateBuffer(pUB); StartBufferVersion1(pUB); } // // point to the end of the buffer // pie = (PIPRIP_ENTRY)(pUB->UB_Buffer + pUB->UB_Length); IPRIP_ENTRY_FROM_RTM_ROUTE(pie, pRIR); // // zero out fields which are reserved in RIP1 // pie->IE_SubnetMask = 0; pie->IE_RouteTag = 0; pie->IE_Nexthop = 0; pUB->UB_Length += sizeof(IPRIP_ENTRY); return NO_ERROR; } //---------------------------------------------------------------------------- // Function: FinishBufferVersion1 // // this sends the contents of a RIPv1 buffer, if any //---------------------------------------------------------------------------- DWORD FinishBufferVersion1( PUPDATE_BUFFER pUB ) { // // send the buffer if it contains any entries // if (pUB->UB_Length > sizeof(IPRIP_HEADER)) { SendUpdateBuffer(pUB); } return NO_ERROR; } //---------------------------------------------------------------------------- // Function: StartBufferVersion2 // // this starts a RIPv2 buffer //---------------------------------------------------------------------------- DWORD StartBufferVersion2( PUPDATE_BUFFER pUB ) { PIPRIP_HEADER pHdr; PIPRIP_IF_CONFIG pic; PIPRIP_AUTHENT_ENTRY pae; // // setup header // pHdr = (PIPRIP_HEADER)pUB->UB_Buffer; pHdr->IH_Version = 2; pHdr->IH_Command = (BYTE)pUB->UB_Command; pHdr->IH_Reserved = 0; pUB->UB_Length = sizeof(IPRIP_HEADER); // // see if we need to set up the authentication entry // pic = pUB->UB_ITE->ITE_Config; if (pic->IC_AuthenticationType == IPRIP_AUTHTYPE_SIMPLE_PASSWORD) { pae = (PIPRIP_AUTHENT_ENTRY)(pUB->UB_Buffer + sizeof(IPRIP_HEADER)); pae->IAE_AddrFamily = htons(ADDRFAMILY_AUTHENT); pae->IAE_AuthType = htons((WORD)pic->IC_AuthenticationType); CopyMemory( pae->IAE_AuthKey, pic->IC_AuthenticationKey, IPRIP_MAX_AUTHKEY_SIZE ); pUB->UB_Length += sizeof(IPRIP_AUTHENT_ENTRY); } return NO_ERROR; } //---------------------------------------------------------------------------- // Function: AddEntryVersion2 // // this adds an entry to RIPv2 buffer, first sending the buffer if it is full //---------------------------------------------------------------------------- DWORD AddEntryVersion2( PUPDATE_BUFFER pUB, PRIP_IP_ROUTE pRIR ) { PIPRIP_ENTRY pie; // // send the contents if the buffer is full // if (pUB->UB_Length + sizeof(IPRIP_ENTRY) > MAX_PACKET_SIZE) { SendUpdateBuffer(pUB); StartBufferVersion2(pUB); } pie = (PIPRIP_ENTRY)(pUB->UB_Buffer + pUB->UB_Length); IPRIP_ENTRY_FROM_RTM_ROUTE(pie, pRIR); // // for RIP routes, we assume that the route tag will be set // in the RTM route struct already; // for non-RIP routes, we write the route tag // for the outgoing interface in the packet entry // if (pRIR->RR_RoutingProtocol == PROTO_IP_RIP) { pie->IE_RouteTag = htons(GETROUTETAG(pRIR)); } else { pie->IE_RouteTag = htons(pUB->UB_ITE->ITE_Config->IC_RouteTag); } pUB->UB_Length += sizeof(IPRIP_ENTRY); return NO_ERROR; } //---------------------------------------------------------------------------- // Function: FinishBufferVersion2 // // this sends the contents of a RIPv2 buffer, if any //---------------------------------------------------------------------------- DWORD FinishBufferVersion2( PUPDATE_BUFFER pUB ) { // // the size above which we send depends on whether or not there // is an authentication entry // if (pUB->UB_ITE->ITE_Config->IC_AuthenticationType == IPRIP_AUTHTYPE_NONE) { if (pUB->UB_Length > sizeof(IPRIP_HEADER)) { SendUpdateBuffer(pUB); } } else { // // there is an authentication entry, so unless there // is also a route entry, we will not send this last buffer // if (pUB->UB_Length > (sizeof(IPRIP_HEADER) + sizeof(IPRIP_AUTHENT_ENTRY))) { SendUpdateBuffer(pUB); } } return NO_ERROR; } //---------------------------------------------------------------------------- // ROUTE ENUMERATION ROUTINES // // The following definitions simplify the enumeration of routes // when routing information is being sent from a single source on multiple // interfaces, for instance when a triggered update is going out on all // interfaces, or when a full-update is being sent, or when a number // of interfaces are being shutdown. the function InitializeGetRoute looks at // the mode in which it is supposed to send routes, and based on that // builds a table of functions which will be used to enumerate the routes. // In the case of a full-update, the enumeration functions would // go to RTM to get the information; in the case of a triggered-update, they // would dequeue routes from the send-queue. //---------------------------------------------------------------------------- // the following are the type definitions of the functions // in each get-route function group typedef DWORD (*PGETROUTE_START)(PVOID *); typedef DWORD (*PGETROUTE_NEXT)(PVOID *, PRIP_IP_ROUTE); typedef DWORD (*PGETROUTE_FINISH)(PVOID *); // The following three functions handle RTM route enumeration DWORD RtmGetRouteStart( PRTM_ENUM_HANDLE phEnumHandle ); DWORD RtmGetRouteNext( RTM_ENUM_HANDLE hEnumHandle, PRIP_IP_ROUTE pRoute ); DWORD RtmGetRouteFinish( RTM_ENUM_HANDLE hEnumHandle ); // The following three functions handle full-update route enumeration // (a full-update enumerates routes from RTM) #define FullUpdateGetRouteStart RtmGetRouteStart #define FullUpdateGetRouteNext RtmGetRouteNext #define FullUpdateGetRouteFinish RtmGetRouteFinish // The following three functions handle triggered-update route enumeration // (a triggered-update enumerates routes from the send-queue) DWORD TriggeredUpdateGetRouteStart( PRTM_ENUM_HANDLE phEnumHandle ); DWORD TriggeredUpdateGetRouteNext( RTM_ENUM_HANDLE hEnumHandle, PRIP_IP_ROUTE pRoute ); DWORD TriggeredUpdateGetRouteFinish( RTM_ENUM_HANDLE hEnumHandle ); // The following three functions handle shutdown-update route enumeration. // On shutdown, routes are enumerated from RTM, but their metrics // are set to IPRIP_INFINITE-1 before being returned #define ShutdownUpdateGetRouteStart RtmGetRouteStart DWORD ShutdownUpdateGetRouteNext(RTM_ENUM_HANDLE hEnumHandle, PRIP_IP_ROUTE pRoute); #define ShutdownUpdateGetRouteFinish RtmGetRouteFinish // The following three functions handle general-response route enumeration // a general response enumerates routes from RTM #define GeneralResponseGetRouteStart RtmGetRouteStart #define GeneralResponseGetRouteNext RtmGetRouteNext #define GeneralResponseGetRouteFinish RtmGetRouteFinish //---------------------------------------------------------------------------- // Function: InitializeGetRoute // // This functions sets up a get-route function group given the send-mode //---------------------------------------------------------------------------- DWORD InitializeGetRoute( DWORD dwSendMode, PGETROUTE_START *ppGS, PGETROUTE_NEXT *ppGN, PGETROUTE_FINISH *ppGF ) { switch (dwSendMode) { case SENDMODE_FULL_UPDATE: *ppGS = FullUpdateGetRouteStart; *ppGN = FullUpdateGetRouteNext; *ppGF = FullUpdateGetRouteFinish; break; case SENDMODE_TRIGGERED_UPDATE: *ppGS = TriggeredUpdateGetRouteStart; *ppGN = TriggeredUpdateGetRouteNext; *ppGF = TriggeredUpdateGetRouteFinish; break; case SENDMODE_SHUTDOWN_UPDATE: *ppGS = ShutdownUpdateGetRouteStart; *ppGN = ShutdownUpdateGetRouteNext; *ppGF = ShutdownUpdateGetRouteFinish; break; case SENDMODE_GENERAL_RESPONSE1: case SENDMODE_GENERAL_RESPONSE2: *ppGS = GeneralResponseGetRouteStart; *ppGN = GeneralResponseGetRouteNext; *ppGF = GeneralResponseGetRouteFinish; break; default: return ERROR_INVALID_PARAMETER; break; } return NO_ERROR; } //---------------------------------------------------------------------------- // Function: RtmGetRouteStart // // starts an enumeration of RTM routes; includes only and all best routes // the enumeration handle is written into ppEnumerator //---------------------------------------------------------------------------- DWORD RtmGetRouteStart( PRTM_ENUM_HANDLE phEnumHandle ) { DWORD dwErr; RTM_NET_ADDRESS rna; RTM_IPV4_MAKE_NET_ADDRESS( &rna, 0 , 0 ); dwErr = RtmCreateDestEnum( ig.IG_RtmHandle, RTM_VIEW_MASK_ANY, RTM_ENUM_START | RTM_ENUM_ALL_DESTS, &rna, RTM_BEST_PROTOCOL, phEnumHandle ); if (dwErr != NO_ERROR) { TRACE1( ROUTE, "error %d when creating enumeration handle", dwErr ); } return dwErr; } //---------------------------------------------------------------------------- // Function: RtmGetRouteNext // // continues an enumeration of RTM routes //---------------------------------------------------------------------------- DWORD RtmGetRouteNext( RTM_ENUM_HANDLE hEnumHandle, PRIP_IP_ROUTE pRoute ) { BOOL bRelDest = FALSE, bRelUcast = FALSE; DWORD dwErr, dwNumDests = 1; RTM_DEST_INFO rdi, rdiTemp; char szNetwork[20], szNextHop[20]; do { // // Get next route // do { dwErr = RtmGetEnumDests( ig.IG_RtmHandle, hEnumHandle, &dwNumDests, &rdiTemp ); if (dwErr == ERROR_NO_MORE_ITEMS) { if (dwNumDests < 1) { break; } dwErr = NO_ERROR; } else if (dwErr != NO_ERROR) { TRACE1(ANY, "error %d enumeratings dests", dwErr); break; } bRelDest = TRUE; // // Get route info for unicast view only // dwErr = RtmGetDestInfo( ig.IG_RtmHandle, rdiTemp.DestHandle, RTM_BEST_PROTOCOL, RTM_VIEW_MASK_UCAST, &rdi ); if (dwErr != NO_ERROR) { TRACE1(ANY, "error %d getting ucast info dests", dwErr); break; } bRelUcast = TRUE; // // Check if any route info is present in the UCAST view // if ( ( rdi.ViewInfo[0].HoldRoute == NULL ) && ( rdi.ViewInfo[0].Route == NULL ) ) { // // This destination has no info in the UCAST view // Release all handles and get next route // dwErr = RtmReleaseDests(ig.IG_RtmHandle, 1, &rdi); if (dwErr != NO_ERROR) { TRACE3( ANY, "error %d releasing UCAST dest %s/%d", dwErr, szNetwork, rdi.DestAddress.NumBits ); } dwErr = RtmReleaseDests(ig.IG_RtmHandle, 1, &rdiTemp); if (dwErr != NO_ERROR) { TRACE3( ANY, "error %d releasing dest %s/%d", dwErr, szNetwork, rdi.DestAddress.NumBits ); } bRelDest = bRelUcast = FALSE; continue; } // // convert to RIP internal representation, if hold down route present // use it as opposed to the best route. // dwErr = GetRouteInfo( rdi.ViewInfo[0].HoldRoute ? rdi.ViewInfo[0].HoldRoute : rdi.ViewInfo[0].Route, NULL, &rdi, pRoute ); } while (FALSE); if (dwErr != NO_ERROR) { break; } lstrcpy(szNetwork, INET_NTOA(pRoute->RR_Network.N_NetNumber)); lstrcpy(szNextHop, INET_NTOA(pRoute->RR_NextHopAddress.N_NetNumber)); // // set metrics as appropriate // if ( rdi.ViewInfo[0].HoldRoute != NULL ) { // // help down routes are always advertized with // metric 16 // #if ROUTE_DBG TRACE2( ROUTE, "Holddown route %s/%d", szNetwork, rdi.DestAddress.NumBits ); #endif SETROUTEMETRIC(pRoute, IPRIP_INFINITE); } else if (pRoute-> RR_RoutingProtocol != PROTO_IP_RIP) { // // non-RIP routes are advertised with metric 2 // TBD: This will need to be re-evaluated if/when we // have a route redistribution policy // SETROUTEMETRIC(pRoute, 2); } } while ( FALSE ); // // release handles as appropriate // if (bRelUcast) { DWORD dwErrTemp; dwErrTemp = RtmReleaseDests(ig.IG_RtmHandle, 1, &rdi); if (dwErrTemp != NO_ERROR) { TRACE3( ANY, "error %d releasing UCAST dest %s/%d", dwErrTemp, szNetwork, rdi.DestAddress.NumBits ); } } if (bRelDest) { DWORD dwErrTemp; dwErrTemp = RtmReleaseDests(ig.IG_RtmHandle, 1, &rdiTemp); if (dwErrTemp != NO_ERROR) { TRACE3( ANY, "error %d releasing dest %s/%d", dwErrTemp, szNetwork, rdi.DestAddress.NumBits ); } } #if ROUTE_DBG if (dwErr == NO_ERROR) { TRACE4( ROUTE, "Enumerated route %s/%d via %s with metric %d", szNetwork, rdi.DestAddress.NumBits, szNextHop, GETROUTEMETRIC(pRoute) ); } #endif return dwErr; } //---------------------------------------------------------------------------- // Function: RtmGetRouteFinish // // terminates an enumeration of RTM routes //---------------------------------------------------------------------------- DWORD RtmGetRouteFinish( RTM_ENUM_HANDLE EnumHandle ) { DWORD dwErr; dwErr = RtmDeleteEnumHandle( ig.IG_RtmHandle, EnumHandle ); if (dwErr != NO_ERROR) { TRACE1( ANY, "error %d closing enumeration handle", dwErr ); } return dwErr; } //---------------------------------------------------------------------------- // Function: ShutdownUpdateGetRouteNext // // continues an enumeration of RTM routes for a shutdown-update. // same as RtmGetRouteNext, except that metrics are set to IPRIP_INFINITE - 1 //---------------------------------------------------------------------------- DWORD ShutdownUpdateGetRouteNext( RTM_ENUM_HANDLE hEnumHandle, PRIP_IP_ROUTE pRoute ) { DWORD dwErr; // // during a shutdown, all non-infinite metrics are set to 15 // dwErr = RtmGetRouteNext(hEnumHandle, pRoute); if (dwErr == NO_ERROR && GETROUTEMETRIC(pRoute) != IPRIP_INFINITE) { SETROUTEMETRIC(pRoute, IPRIP_INFINITE - 1); } return dwErr; } //---------------------------------------------------------------------------- // Function: TriggeredUpdateGetRouteStart // // starts an enumeration of routes from the send queue // for a triggered update. nothing to do, since the caller // of SendRoutes should have locked the send queue already //---------------------------------------------------------------------------- DWORD TriggeredUpdateGetRouteStart( PRTM_ENUM_HANDLE pEnumHandle ) { return NO_ERROR; } //---------------------------------------------------------------------------- // Function: TriggeredUpdateGetRouteNext // // continues an enumeration of routes from the send-queue //---------------------------------------------------------------------------- DWORD TriggeredUpdateGetRouteNext( RTM_ENUM_HANDLE EnumHandle, PRIP_IP_ROUTE pRoute ) { DWORD dwErr; dwErr = DequeueSendEntry(ig.IG_SendQueue, pRoute); if (dwErr == NO_ERROR && pRoute->RR_RoutingProtocol != PROTO_IP_RIP) { // // non-RIP routes are advertised with metric 2 // TBD: This will need to be re-evaluated if/when we // have a route redistribution policy // SETROUTEMETRIC(pRoute, 2); } return dwErr; } //---------------------------------------------------------------------------- // Function: TriggeredUpdateGetRouteFinish // // terminates an enumeration of routes from the send-queue //---------------------------------------------------------------------------- DWORD TriggeredUpdateGetRouteFinish( RTM_ENUM_HANDLE EnumHandle ) { return NO_ERROR; } //---------------------------------------------------------------------------- // Function: SendRoutes // // This function sends triggered updates, full-updates, shutdown-updates, and // responses to general requests; the processing for all such output is the // same. The source of routing information is different, however, and this // difference is abstracted away using the route enumeration function groups // described above. // In the case of sending a response to a general or specific request, // the response should be sent on a single interface using a single IP address, // using a particular type of RIP packet; the caller can specify which // IP address to use by setting the argument dwAddrIndex to the index of the // desired address in the interface's IP address table, and the caller can // specify the type of packet to use by setting the argument dwAnnounceMode // to the corresponding IPRIP_ANNOUNCE_* constant. These arguments are only // used for responses to requests. // // assumes the interface table is locked //---------------------------------------------------------------------------- DWORD SendRoutes( PIF_TABLE_ENTRY pIfList[], DWORD dwIfCount, DWORD dwSendMode, DWORD dwDestination, DWORD dwAddrIndex ) { RTM_ENUM_HANDLE Enumerator; RIP_IP_ROUTE route; PIPRIP_IF_CONFIG pic; PIPRIP_IF_BINDING pib; PIPRIP_IP_ADDRESS paddr; DWORD i, dwErr, dwBufCount; PDWORD pdwPeer, pdwPeerEnd; PIF_TABLE_ENTRY *ppite, *ppitend = NULL; PUPDATE_BUFFER pbuf, pbufend, pBufList; PROUTE_TABLE_ENTRY prte; ROUTE_TABLE summaryTable; PGETROUTE_START pfnGetRouteStart; PGETROUTE_NEXT pfnGetRouteNext; PGETROUTE_FINISH pfnGetRouteFinish; PLIST_ENTRY plstart, plend, phead, ple; // // if no interfaces, go no further // if (dwIfCount == 0) { return ERROR_NO_DATA; } // // initialize the route enumeration function table // dwErr = InitializeGetRoute( dwSendMode, &pfnGetRouteStart, &pfnGetRouteNext, &pfnGetRouteFinish ); if (dwErr != NO_ERROR) { return ERROR_INVALID_PARAMETER; } dwErr = NO_ERROR; Enumerator = NULL; // // create table for summary routes // dwErr = CreateRouteTable(&summaryTable); if (dwErr != 0) { TRACE1(SEND, "error %d initializing summary table", dwErr); return dwErr; } dwErr = NO_ERROR; pBufList = NULL; do { // breakout loop // // the following discussion does not apply when sending routes // to specific destinations: // since unicast peers may be configured on some interfaces, // we need to allocate update buffers for those peers as well. // // also, we will not allocate update buffers for RIPv1 interfaces // on which broadcast is disabled (such interfaces should have // at least one unicast peer configured instead.) // // Thus, the number of update buffers may not be equal to // the number of interfaces, and in the worst case (i.e. where // all interfaces are RIPv1 and have broadcast disabled and have // no unicast peers configured) there may be no update buffers at all. // if (dwDestination != 0) { // // sending to a specific destination; this only happens when // there is a single interface in the list, for instance when // sending a response to a general request // dwBufCount = dwIfCount; } else { // // we are sending a full-update, triggered-update, or // a shutdown-update, and thus routes may be sent by // broadcast/multicast as well as to unicast peers // dwBufCount = 0; ppitend = pIfList + dwIfCount; for (ppite = pIfList; ppite < ppitend; ppite++) { pic = (*ppite)->ITE_Config; pib = (*ppite)->ITE_Binding; if (pic->IC_UnicastPeerMode != IPRIP_PEER_ONLY) { dwBufCount += pib->IB_AddrCount; } if (pic->IC_UnicastPeerMode != IPRIP_PEER_DISABLED) { dwBufCount += pic->IC_UnicastPeerCount; } } } if (dwBufCount == 0) { break; } // // allocate the update buffers for all interfaces // pBufList = RIP_ALLOC(dwBufCount * sizeof(UPDATE_BUFFER)); if (pBufList == NULL) { dwErr = GetLastError(); TRACE2( SEND, "error %d allocating %d bytes for update buffers", dwErr, dwBufCount * sizeof(UPDATE_BUFFER) ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); break; } // // initialize the update buffers allocated; in the case of // sending to a specific destination, initialize a buffer // for each interface; in the case of sending updates, also // initialize buffers for unicast peers. // pbuf = pBufList; pbufend = pBufList + dwBufCount; ppitend = pIfList + dwIfCount; ACQUIRE_BINDING_LOCK_SHARED(); for (ppite = pIfList; ppite < ppitend; ppite++) { if (dwDestination != 0) { // // sending to a specific destination // InitializeUpdateBuffer( *ppite, dwAddrIndex, pbuf, dwSendMode, dwDestination, IPRIP_RESPONSE ); pbuf->UB_StartRoutine(pbuf); ++pbuf; } else { // // sending updates on multiple interfaces // pic = (*ppite)->ITE_Config; pib = (*ppite)->ITE_Binding; // // if broadcast or multicast is enabled on the interface, // and it is not configured to send only to listed peers, // initialize the broadcast/multicast update buffer // if (pic->IC_UnicastPeerMode != IPRIP_PEER_ONLY) { for (i = 0; i < pib->IB_AddrCount; i++) { InitializeUpdateBuffer( *ppite, i, pbuf, dwSendMode, dwDestination, IPRIP_RESPONSE ); pbuf->UB_StartRoutine(pbuf); ++pbuf; } } if (pic->IC_UnicastPeerMode != IPRIP_PEER_DISABLED) { // // initialize update buffers for unicast peers, if any // pdwPeer = IPRIP_IF_UNICAST_PEER_TABLE(pic); pdwPeerEnd = pdwPeer + pic->IC_UnicastPeerCount; for ( ; pdwPeer < pdwPeerEnd; pdwPeer++) { // // Note: forcing peers to be on first address // InitializeUpdateBuffer( *ppite, 0, pbuf, dwSendMode, *pdwPeer, IPRIP_RESPONSE ); pbuf->UB_StartRoutine(pbuf); ++pbuf; } } } } RELEASE_BINDING_LOCK_SHARED(); // // start the route enumeration // if ( pfnGetRouteStart(&Enumerator) == NO_ERROR ) { // // enumerate and transmit the routes // while (pfnGetRouteNext(Enumerator, &route) == NO_ERROR) { // // for each route, send it on each update buffer, // subject to split-horizon/poison-reverse/subnet-summary // pass in the summary table pointer to store summarized routes // dwErr = SendRouteOnIfList( pBufList, dwBufCount, dwSendMode, &summaryTable, &route ); } // // terminate the route enumeration // pfnGetRouteFinish(Enumerator); // // now send all routes which were summarized // plstart = summaryTable.RT_HashTableByNetwork; plend = plstart + ROUTE_HASHTABLE_SIZE; for (phead = plstart; phead < plend; phead++) { for (ple = phead->Flink; ple != phead; ple = ple->Flink) { prte = CONTAINING_RECORD(ple, ROUTE_TABLE_ENTRY, RTE_Link); // // shouldn't summarize when sending summary table contents // so we pass NULL instead of a summary table pointer // SendRouteOnIfList( pBufList, dwBufCount, dwSendMode, NULL, &prte->RTE_Route ); } } // // finally, write the summarized routes to RTM // WriteSummaryRoutes(&summaryTable, ig.IG_RtmHandle); } } while(FALSE); // // free the allocated update buffers, if any // if (pBufList != NULL) { pbufend = pBufList + dwBufCount; for (pbuf = pBufList; pbuf < pbufend; pbuf++) { // // send whatever might remain in the update buffer // pbuf->UB_FinishRoutine(pbuf); } RIP_FREE(pBufList); } // // delete the summary table // DeleteRouteTable(&summaryTable); return dwErr; } //---------------------------------------------------------------------------- // Function: SendRouteOnIfList // // this function sends a single route on all interfaces in the given // interface list, using the update buffers in the given update buffer list //---------------------------------------------------------------------------- DWORD SendRouteOnIfList( UPDATE_BUFFER pBufList[], DWORD dwBufCount, DWORD dwSendMode, PROUTE_TABLE pSummaryTable, PRIP_IP_ROUTE pRoute ) { RIP_IP_ROUTE route; DWORD dwFound, dwTTL; PIF_TABLE_ENTRY pite; PIPRIP_IF_CONFIG pic; PUPDATE_BUFFER pbuf, pbufend; DWORD dwDestNetwork, dwNexthopNetwork; DWORD dwDestNetclassAddr, dwDestNetclassMask; DWORD dwRouteNetclassAddr, dwRouteNetclassMask; DWORD dwRouteNetwork, dwRouteNetmask, dwRouteProtocol; #if ROUTE_DBG CHAR szDest[32]; CHAR szDestMask[32]; CHAR szNexthop[32]; CHAR szNexthopMask[32]; CHAR szRoute[32]; CHAR szRouteMask[32]; // // set up variables used for error and information messages // lstrcpy(szRoute, INET_NTOA(pRoute->RR_Network.N_NetNumber)); lstrcpy(szRouteMask, INET_NTOA(pRoute->RR_Network.N_NetMask)); lstrcpy(szNexthop, INET_NTOA(pRoute->RR_NextHopAddress.N_NetNumber)); lstrcpy(szNexthopMask, INET_NTOA(pRoute->RR_NextHopAddress.N_NetMask)); #endif // // we never send summary routes if they are read from RTM; // we only send them if they are generated in the process // of advertising actual routes on this iteration; // we can tell the difference by checking whether we are still // generating summary routes (i.e. if pSummaryTable is non-NULL); // if we aren't it is time to start sending summary routes // if (pSummaryTable != NULL && pRoute->RR_RoutingProtocol == PROTO_IP_RIP && GETROUTEFLAG(pRoute) == ROUTEFLAG_SUMMARY) { return NO_ERROR; } // // get the route's network and netmask, and compute // the route's network class address and the network class mask; // to support supernetting, we double-check the class mask // and use the supernet mask if necessary // dwRouteProtocol = pRoute->RR_RoutingProtocol; dwRouteNetwork = pRoute->RR_Network.N_NetNumber; dwRouteNetmask = pRoute->RR_Network.N_NetMask; dwRouteNetclassMask = NETCLASS_MASK(dwRouteNetwork); if (dwRouteNetclassMask > dwRouteNetmask) { dwRouteNetclassMask = dwRouteNetmask; } dwRouteNetclassAddr = (dwRouteNetwork & dwRouteNetclassMask); // // go through each update buffer // pbufend = pBufList + dwBufCount; for (pbuf = pBufList; pbuf < pbufend; pbuf++) { pite = pbuf->UB_ITE; pic = pite->ITE_Config; // // if this is a broadcast route entry, skip it // The first condition uses the netmask information for this route, // stored in route table, to determine if it is a broadcast route // The second condition uses the netmask which is computed based // on the address class // The third condition checks if it is an all 1's broadcast // if ( IS_DIRECTED_BROADCAST_ADDR(dwRouteNetwork, dwRouteNetmask) || IS_DIRECTED_BROADCAST_ADDR(dwRouteNetwork, dwRouteNetclassMask) || IS_LOCAL_BROADCAST_ADDR(dwRouteNetwork) ) { continue; } // // if this is the multicast route entry, skip it // if ( CLASSD_ADDR( dwRouteNetwork ) || CLASSE_ADDR( dwRouteNetwork ) ) { continue; } // // If this is a loopback route, skip it. // if ( IS_LOOPBACK_ADDR( dwRouteNetwork ) ) { continue; } // // if this is the rotue to the outgoing interface's network, // (e.g. the route to 10.1.1.0 on interface 10.1.1.1/255.255.255.0) // don't include it in the update // (clearly, we shouldn't AND the default-route's netmask (0) // with anything and expect this to work // if (dwRouteNetmask && dwRouteNetwork == (pbuf->UB_Address & dwRouteNetmask)) { continue; } // // if announcing host routes is disabled on the interface // and this is a host route, skip it // if (dwRouteNetmask == HOSTADDR_MASK && IPRIP_FLAG_IS_DISABLED(pic, ANNOUNCE_HOST_ROUTES)) { continue; } // // if announcing default routes is disabled // and this is a default route, skip it // if (dwRouteNetwork == 0 && IPRIP_FLAG_IS_DISABLED(pic, ANNOUNCE_DEFAULT_ROUTES)) { continue; } // // now put the route through the announce filters // if (pic->IC_AnnounceFilterMode != IPRIP_FILTER_DISABLED) { // // discard if we are including all routes and this route is listed // as an exception, or if we are excluding all routes and // this route is not listed as an exception // IS_ROUTE_IN_ANNOUNCE_FILTER(pic, dwRouteNetwork, dwFound); if ((pic->IC_AnnounceFilterMode == IPRIP_FILTER_INCLUDE && !dwFound) || (pic->IC_AnnounceFilterMode == IPRIP_FILTER_EXCLUDE && dwFound)) { continue; } } // // SUBNET-SUMMARY PROCESSING: // // if the route is not on the network we are sending this to or // if the route's mask is longer than that of the network we are // sending to, or if the route is a network route, add it to the // summary table instead of sending it immediately. // default routes are excepted from summarization // route = *pRoute; if (pSummaryTable != NULL && dwRouteNetwork != 0) { // // get the destination address to which the update is being // sent for this interface; double-check the netclass mask // to accomodate supernets // dwDestNetclassAddr = pbuf->UB_DestAddress; dwDestNetclassMask = NETCLASS_MASK(dwDestNetclassAddr); if (dwDestNetclassMask > pbuf->UB_DestNetmask) { dwDestNetclassMask = pbuf->UB_DestNetmask; } dwDestNetclassAddr = (dwDestNetclassAddr & dwDestNetclassMask); if ((dwRouteNetwork == dwRouteNetclassAddr && dwRouteNetmask == dwRouteNetclassMask) || dwDestNetclassAddr != dwRouteNetclassAddr) { if ((pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP1) || !IPRIP_FLAG_IS_ENABLED(pic, NO_SUBNET_SUMMARY)) { // // either the route is a network route, // or the update is going to a network different // from that of the route // // // create an entry in the summary table instead of sending; // route.RR_Network.N_NetNumber = dwRouteNetclassAddr; route.RR_Network.N_NetMask = dwRouteNetclassMask; if ((dwRouteNetwork != dwRouteNetclassAddr) || (dwRouteNetmask != dwRouteNetclassMask)) { route.RR_RoutingProtocol = PROTO_IP_RIP; SETROUTEFLAG(&route, ROUTEFLAG_SUMMARY); SETROUTETAG(&route, pic->IC_RouteTag); } CreateRouteEntry( pSummaryTable, &route, pic->IC_RouteExpirationInterval, pic->IC_RouteRemovalInterval ); continue; } } else if (pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP1 && dwRouteNetmask != HOSTADDR_MASK && pbuf->UB_Netmask < dwRouteNetmask) { // // this is neither a host route nor a default route, // and the subnet-mask on the outgoing interface is shorter // than that of the route, so the route's network must be // truncated lest it be considered a host route by the routers // who will receive this update // only do this in RIP1 mode, since in RIP2 mode // we can include the netmask in the route entry // route.RR_Network.N_NetNumber &= pbuf->UB_Netmask; route.RR_Network.N_NetMask = pbuf->UB_Netmask; route.RR_RoutingProtocol = PROTO_IP_RIP; SETROUTEFLAG(&route, ROUTEFLAG_SUMMARY); SETROUTETAG(&route, pic->IC_RouteTag); CreateRouteEntry( pSummaryTable, &route, pic->IC_RouteExpirationInterval, pic->IC_RouteRemovalInterval ); continue; } } // // Summary route checks // // Summary routes are to sent only on those interfaces that // require them i.e. Interfaces on which the annouce mode is // RIP1 or on which summarization has been explicity turned on // if (pSummaryTable == NULL && ((GETROUTEFLAG(&route) & ROUTEFLAG_SUMMARY) == ROUTEFLAG_SUMMARY) && pic->IC_AnnounceMode != IPRIP_ANNOUNCE_RIP1 && IPRIP_FLAG_IS_ENABLED(pic, NO_SUBNET_SUMMARY)) { // // This is a summary route, and the interface over which it is // to be sent does not require summary routes to be sent on it // continue; } // // SPLIT-HORIZON/POISON-REVERSE PROCESSING: // // // note that we only do split-horizon/poison-reverse on RIP routes // // // Modification : Split-horizon/poison-reverse done for all routes // // if (dwRouteProtocol != PROTO_IP_RIP || // IPRIP_FLAG_IS_DISABLED(pic, SPLIT_HORIZON)) if (IPRIP_FLAG_IS_DISABLED(pic, SPLIT_HORIZON)) { // // add the entry as-is: // sender should use us as the nexthop to this destination // route.RR_NextHopAddress.N_NetNumber = 0; route.RR_NextHopAddress.N_NetMask = 0; pbuf->UB_AddRoutine(pbuf, &route); } else if (IPRIP_FLAG_IS_DISABLED(pic, POISON_REVERSE)) { // // if the route is being sent to the network from which // the route was learnt, exclude the route altogether // dwDestNetwork = (pbuf->UB_DestAddress & pbuf->UB_DestNetmask); dwNexthopNetwork = (route.RR_NextHopAddress.N_NetNumber & route.RR_NextHopAddress.N_NetMask); // // Check if the route next hop is on the same network as the // socket from which this RIP response is being sent. // If so, do not include this route in the update. // Otherwise, we may still need to do poison-reverse // since the next-hop may be the other end of a point-to-point link // (endpoints of such links can be on different networks) // in which case the first test would succeed (different networks) // but we'd still be required to perform split-horizon. // Therefore if the outgoing interface is the one from which // the route was learnt, we do not include this route in the update. // if (dwNexthopNetwork == dwDestNetwork || (pbuf->UB_ITE->ITE_Type == DEMAND_DIAL && route.RR_InterfaceID == pbuf->UB_ITE->ITE_Index)) { continue; } else { // // sending to a different network, so sender should use // us as the nexthop to this destination // route.RR_NextHopAddress.N_NetNumber = 0; route.RR_NextHopAddress.N_NetMask = 0; } pbuf->UB_AddRoutine(pbuf, &route); } else { // // if the route is being sent to the network from which // the route was learnt, include the route with infinite metric // dwDestNetwork = (pbuf->UB_DestAddress & pbuf->UB_DestNetmask); dwNexthopNetwork = (route.RR_NextHopAddress.N_NetNumber & route.RR_NextHopAddress.N_NetMask); if (dwNexthopNetwork == dwDestNetwork || (pbuf->UB_ITE->ITE_Type == DEMAND_DIAL && route.RR_InterfaceID == pbuf->UB_ITE->ITE_Index)) { // // if a route is advertised with infinite metric due to // poison-reverse and it would still be advertised with // infinite metric in a triggered update, save bandwidth // by excluding the route // if (dwSendMode == SENDMODE_TRIGGERED_UPDATE) { continue; } else { SETROUTEMETRIC(&route, IPRIP_INFINITE); } } else { // // sending to a different network, so sender should use // us as the nexthop to this destination // route.RR_NextHopAddress.N_NetNumber = 0; route.RR_NextHopAddress.N_NetMask = 0; } pbuf->UB_AddRoutine(pbuf, &route); } // // hold advertized destinations // if ((dwSendMode == SENDMODE_FULL_UPDATE) || (dwSendMode == SENDMODE_GENERAL_RESPONSE1) || (dwSendMode == SENDMODE_GENERAL_RESPONSE2)) { // // use the hold interval from the interface over which the // route is over. // if (pite->ITE_Index == route.RR_InterfaceID) { DWORD dwErr; dwErr = RtmHoldDestination( ig.IG_RtmHandle, route.hDest, RTM_VIEW_MASK_UCAST, pic-> IC_RouteRemovalInterval * 1000 ); if (dwErr != NO_ERROR) { TRACE1(ANY, "error %d holding dest", dwErr); } } } } return NO_ERROR; } //---------------------------------------------------------------------------- // Function: SendGeneralRequest // // This function transmits RIP requests on interface to all neighbors in // the interfaces neighbor list. A request is also sent via broadcast or // multicast is the neighbor list is not used exclusively. //---------------------------------------------------------------------------- DWORD SendGeneralRequest( PIF_TABLE_ENTRY pite ) { DWORD i, dwErr; PIPRIP_ENTRY pie; PIPRIP_IF_CONFIG pic; PIPRIP_IF_BINDING pib; PIPRIP_IP_ADDRESS paddr; PDWORD pdwPeer, pdwPeerEnd; pic = pite->ITE_Config; pib = pite->ITE_Binding; paddr = IPRIP_IF_ADDRESS_TABLE(pib); ACQUIRE_BINDING_LOCK_SHARED(); do { // error breakout loop // // broadcast/multicast a request if not using neighbor-list only // if (pic->IC_UnicastPeerMode != IPRIP_PEER_ONLY) { for (i = 0; i < pib->IB_AddrCount; i++, paddr++) { UPDATE_BUFFER ub; // // initialize the update buffer // dwErr = InitializeUpdateBuffer( pite, i, &ub, SENDMODE_GENERAL_REQUEST, 0, IPRIP_REQUEST ); ub.UB_StartRoutine(&ub); // // set up the general request entry // pie = (PIPRIP_ENTRY)(ub.UB_Buffer + ub.UB_Length); pie->IE_AddrFamily = ADDRFAMILY_REQUEST; pie->IE_RouteTag = 0; pie->IE_Destination = 0; pie->IE_SubnetMask = 0; pie->IE_Nexthop = 0; pie->IE_Metric = htonl(IPRIP_INFINITE); ub.UB_Length += sizeof(IPRIP_ENTRY); // // send the buffer // ub.UB_FinishRoutine(&ub); } } // // if the list of peers is not in use, we are done // if (pic->IC_UnicastPeerMode == IPRIP_PEER_DISABLED) { break; } // // send requests to all the configured peers // pdwPeer = IPRIP_IF_UNICAST_PEER_TABLE(pic); pdwPeerEnd = pdwPeer + pic->IC_UnicastPeerCount; for ( ; pdwPeer < pdwPeerEnd; pdwPeer++) { UPDATE_BUFFER ub; // // initialize the update buffer // Note: we are forcing the peers onto the first address // dwErr = InitializeUpdateBuffer( pite, 0, &ub, SENDMODE_GENERAL_REQUEST, *pdwPeer, IPRIP_REQUEST ); ub.UB_StartRoutine(&ub); // // set up the general request entry // pie = (PIPRIP_ENTRY)(ub.UB_Buffer + ub.UB_Length); pie->IE_AddrFamily = ADDRFAMILY_REQUEST; pie->IE_RouteTag = 0; pie->IE_Destination = 0; pie->IE_SubnetMask = 0; pie->IE_Nexthop = 0; pie->IE_Metric = htonl(IPRIP_INFINITE); ub.UB_Length += sizeof(IPRIP_ENTRY); // // send the buffer // ub.UB_FinishRoutine(&ub); } } while(FALSE); RELEASE_BINDING_LOCK_SHARED(); return NO_ERROR; } //---------------------------------------------------------------------------- // Function: AuthenticatePacket // // Given a RIP packet and an interface configuration block, this function // accepts or rejects the packet based on the authentication settings // of the interface and the authentication content of the packet. //---------------------------------------------------------------------------- DWORD AuthenticatePacket( PBYTE pbuf, PIPRIP_AUTHENT_ENTRY pae, PIPRIP_IF_CONFIG pic, PIPRIP_IF_STATS pis, PIPRIP_PEER_STATS pps, PIPRIP_ENTRY *ppie ) { DWORD dwErr; dwErr = NO_ERROR; if (pic->IC_AuthenticationType == IPRIP_AUTHTYPE_NONE) { // // interface is not configured for authentication, // so discard authenticated packets // if (pae->IAE_AddrFamily == htons(ADDRFAMILY_AUTHENT)) { if (pis != NULL) { InterlockedIncrement(&pis->IS_BadResponsePacketsReceived); } if (pps != NULL) { InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer); } dwErr = ERROR_ACCESS_DENIED; } } else { // // interface is using authentication, // so discard unauthenticated packets // and packets using different authentication schemes // if (pae->IAE_AddrFamily != htons(ADDRFAMILY_AUTHENT) || pae->IAE_AuthType != htons((WORD)pic->IC_AuthenticationType)) { if (pis != NULL) { InterlockedIncrement(&pis->IS_BadResponsePacketsReceived); } if (pps != NULL) { InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer); } dwErr = ERROR_ACCESS_DENIED; } else { // // interface and packet are using the same authentication: // check that the packet passes validation // switch(pic->IC_AuthenticationType) { case IPRIP_AUTHTYPE_SIMPLE_PASSWORD: // // for simple passwords, just compare the keys // dwErr = (DWORD)memcmp( pae->IAE_AuthKey, pic->IC_AuthenticationKey, IPRIP_MAX_AUTHKEY_SIZE ); if (dwErr != 0) { dwErr = ERROR_ACCESS_DENIED; } break; case IPRIP_AUTHTYPE_MD5: // // TBD: unimplemented unless required. // break; } // // advance the "first entry" pointer // if (dwErr == NO_ERROR) { ++(*ppie); } } } return dwErr; } //---------------------------------------------------------------------------- // Function: WorkerFunctionProcessInput // // This function is responsible for processing input. // If any peer filters exist, it applies them to the routes received // and passes the packets on to the processing functions. //---------------------------------------------------------------------------- VOID WorkerFunctionProcessInput( PVOID pContext ) { PINPUT_CONTEXT pwc; DWORD dwErr, dwCommand, dwPktsProcessed; PIPRIP_GLOBAL_CONFIG pigc; BOOL bListEmpty; if (!ENTER_RIP_WORKER()) { return; } TRACE0(ENTER, "entering WorkerFunctionProcessInput"); dwPktsProcessed = 0; do { ACQUIRE_LIST_LOCK(ig.IG_RecvQueue); dwErr = DequeueRecvEntry(ig.IG_RecvQueue, &dwCommand, (PBYTE *)&pwc); RELEASE_LIST_LOCK(ig.IG_RecvQueue); if (dwErr != NO_ERROR) { if ( dwErr != ERROR_NO_MORE_ITEMS ) { TRACE1(RECEIVE, "error %d dequeueing received packet", dwErr); } break; } dwPktsProcessed++; // // call the processing function for this type of packet // if (dwCommand == IPRIP_REQUEST) { ProcessRequest(pwc); } else if (dwCommand == IPRIP_RESPONSE) { DWORD dwSource, dwFound = 0; dwSource = pwc->IC_InputSource.sin_addr.s_addr; // // make sure the packet is from the RIP port // if (pwc->IC_InputSource.sin_port != htons(IPRIP_PORT)) { LPSTR lpszAddr = INET_NTOA(dwSource); TRACE2( RECEIVE, "invalid port in RESPONSE from %s on interface %d", lpszAddr, pwc->IC_InterfaceIndex ); LOGINFO1(INVALID_PORT, lpszAddr, NO_ERROR); RIP_FREE(pwc); continue; } // // put the packet through the peer filters since it is a response // ACQUIRE_GLOBAL_LOCK_SHARED(); pigc = ig.IG_Config; if (dwCommand == IPRIP_RESPONSE && pigc->GC_PeerFilterMode != IPRIP_FILTER_DISABLED) { // // discard if this is not from a trusted peer: // this is so if we are including only listed peers and this peer // is not listed, or if we are excluding all listed peers // and this peer is listed // IS_PEER_IN_FILTER(pigc, dwSource, dwFound); if ((!dwFound && pigc->GC_PeerFilterMode == IPRIP_FILTER_INCLUDE) || (dwFound && pigc->GC_PeerFilterMode == IPRIP_FILTER_EXCLUDE)) { LPSTR lpszAddr = INET_NTOA(dwSource); TRACE2( RECEIVE, "FILTER: dropping RESPONSE from %s on interface %d", lpszAddr, pwc->IC_InterfaceIndex ); LOGINFO1(RESPONSE_FILTERED, lpszAddr, NO_ERROR); RELEASE_GLOBAL_LOCK_SHARED(); RIP_FREE(pwc); continue; } } RELEASE_GLOBAL_LOCK_SHARED(); ProcessResponse(pwc); } } while(TRUE); // // Decrement the total number of workitems currently running. // InterlockedDecrement(&ig.IG_NumProcessInputWorkItems); // // It is possible that ProcessSocket() enqueued new packets for processing // between the time we last checked for more packets and the time we // decremented the number of ProcessInputWorkItems. // So, if this was the last workitem, we should check if there are any // remaining enqueued packets to be processed. If yes, we enqueue a // workitem to process those packets // if ( ig.IG_NumProcessInputWorkItems == 0 ) { ACQUIRE_LIST_LOCK(ig.IG_RecvQueue); bListEmpty = IsListEmpty(&ig.IG_RecvQueue->LL_Head); RELEASE_LIST_LOCK(ig.IG_RecvQueue); if ( !bListEmpty ) { dwErr = QueueRipWorker(WorkerFunctionProcessInput, NULL); if (dwErr != NO_ERROR) { TRACE1( RECEIVE, "WorkerFunctionProcessInput: error %d queueing work-item", dwErr ); LOGERR0(QUEUE_WORKER_FAILED, dwErr); } else { InterlockedIncrement(&ig.IG_NumProcessInputWorkItems); } } } TRACE1( LEAVE, "leaving WorkerFunctionProcessInput. Packets processed: %d", dwPktsProcessed ); LEAVE_RIP_WORKER(); return; } //---------------------------------------------------------------------------- // Function: ProcessRequest // // This function handles the processing of an incoming request packet. //---------------------------------------------------------------------------- VOID ProcessRequest( PVOID pContext ) { PBYTE pbuf; DWORD dwSize; CHAR szSource[20]; PIPRIP_IF_STATS pis; PIPRIP_ENTRY pie; PIPRIP_HEADER pih; PIPRIP_AUTHENT_ENTRY pae; PIF_TABLE pTable; PINPUT_CONTEXT pwc; PIF_TABLE_ENTRY pite; PIPRIP_IF_CONFIG pic; PIPRIP_IP_ADDRESS paddr; PPEER_TABLE_ENTRY ppte; PIPRIP_PEER_STATS pps; TRACE0(ENTER, "entering ProcessRequest"); pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_SHARED(); do { // breakout loop // // retrieve the interface on which the request arrived // pwc = (PINPUT_CONTEXT)pContext; pite = GetIfByIndex(pTable, pwc->IC_InterfaceIndex); if (pite == NULL || IF_IS_INACTIVE(pite)) { TRACE1( REQUEST, "processing request: interface %d not found", pwc->IC_InterfaceIndex ); break; } pic = pite->ITE_Config; paddr = IPRIP_IF_ADDRESS_TABLE(pite->ITE_Binding) + pwc->IC_AddrIndex; pbuf = pwc->IC_InputPacket.IP_Packet; pih = (PIPRIP_HEADER)pbuf; pie = (PIPRIP_ENTRY)(pbuf + sizeof(IPRIP_HEADER)); pae = (PIPRIP_AUTHENT_ENTRY)pie; pis = NULL; pps = NULL; lstrcpy(szSource, INET_NTOA(pwc->IC_InputSource.sin_addr)); // // make sure this is a packet we can respond to; // discard if this is a v1 packet and this interface is v2-only or // if this is a v2-packet and this interface is v1-only // if ((pih->IH_Version != 2 && pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP2)) { CHAR szVersion[10]; LPSTR lpszAddr = INET_NTOA(paddr->IA_Address); TRACE2( REQUEST, "discarding non-v2 request on RIPv2 interface %d (%s)", pite->ITE_Index, lpszAddr ); wsprintf(szVersion, "%d", pih->IH_Version); LOGINFO4( PACKET_VERSION_MISMATCH, szVersion, lpszAddr, szSource, "2", 0 ); break; } else if ((pih->IH_Version != 1 && pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP1)) { CHAR szVersion[10]; LPSTR lpszAddr = INET_NTOA(paddr->IA_Address); TRACE2( REQUEST, "discarding RIPv2 request on RIPv1 interface %d (%s)", pite->ITE_Index, lpszAddr ); wsprintf(szVersion, "%d", pih->IH_Version); LOGINFO4( PACKET_VERSION_MISMATCH, szVersion, lpszAddr, szSource, "1", 0 ); break; } // // version 2 packets call for authentication processing; // if (pih->IH_Version == 2) { DWORD dwErr; dwErr = AuthenticatePacket(pbuf, pae, pic, pis, pps, &pie); if (dwErr == ERROR_ACCESS_DENIED) { LPSTR lpszAddr = INET_NTOA(paddr->IA_Address); TRACE3( REQUEST, "dropping packet from %s on interface %d (%s): authentication failed", szSource, pite->ITE_Index, lpszAddr ); LOGWARN2(AUTHENTICATION_FAILED, lpszAddr, szSource, NO_ERROR); break; } } // // find the total remaining size of the packet // dwSize = (DWORD)(((ULONG_PTR)pbuf + pwc->IC_InputLength) - (ULONG_PTR)pie); // // see which kind of request this is // if (pie->IE_AddrFamily == ADDRFAMILY_REQUEST && pie->IE_Metric == htonl(IPRIP_INFINITE) && dwSize == sizeof(IPRIP_ENTRY)) { // // GENERAL REQUEST: // // send all routes on the interface // if (pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED || pwc->IC_InputSource.sin_port != htons(IPRIP_PORT)) { TRACE3( REQUEST, "responding to GENERAL REQUEST from %s on interface %d (%s)", szSource, pite->ITE_Index, INET_NTOA(paddr->IA_Address) ); // // send version 2 packets in response to version 2 requests // and send version 1 packets in response to all other requests // if (pih->IH_Version != 2) { SendRoutes( &pite, 1, SENDMODE_GENERAL_RESPONSE1, pwc->IC_InputSource.sin_addr.s_addr, pwc->IC_AddrIndex ); } else { SendRoutes( &pite, 1, SENDMODE_GENERAL_RESPONSE2, pwc->IC_InputSource.sin_addr.s_addr, pwc->IC_AddrIndex ); } InterlockedIncrement(&ig.IG_Stats.GS_TotalResponsesSent); } } else if (pic->IC_AnnounceMode == IPRIP_ANNOUNCE_DISABLED && pwc->IC_InputSource.sin_port == htons(IPRIP_PORT)) { // // SPECIFIC REQUEST: // We are in silent mode and the request came from port 520, // so we are not allowed to respond. // TRACE3( REQUEST, "ignoring SPECIFIC REQUEST from %s on interface %d (%s)", szSource, pite->ITE_Index, INET_NTOA(paddr->IA_Address) ); } else { IP_NETWORK net; UPDATE_BUFFER ub; RIP_IP_ROUTE route; PIPRIP_ENTRY piend; RTM_NET_ADDRESS rna; RTM_DEST_INFO rdi; DWORD dwErr; // // SPECIFIC REQUEST: // have to look up each destination in the packet // and fill in our metric for it if it exists in RTM // TRACE3( REQUEST, "responding to SPECIFIC REQUEST from %s on interface %d (%s)", szSource, pite->ITE_Index, INET_NTOA(paddr->IA_Address) ); // // acquire the binding-table lock since InitializeUpdateBuffer // needs to call GuessSubnetMask to generate a broadcast address // to which the response will be sent // ACQUIRE_BINDING_LOCK_SHARED(); if (pih->IH_Version != 2) { InitializeUpdateBuffer( pite, pwc->IC_AddrIndex, &ub, SENDMODE_SPECIFIC_RESPONSE1, pwc->IC_InputSource.sin_addr.s_addr, IPRIP_RESPONSE ); } else { InitializeUpdateBuffer( pite, pwc->IC_AddrIndex, &ub, SENDMODE_SPECIFIC_RESPONSE2, pwc->IC_InputSource.sin_addr.s_addr, IPRIP_RESPONSE ); } // // we must reply to the port from which the message was sent // ub.UB_Destination = pwc->IC_InputSource; // // start the update buffer // ub.UB_StartRoutine(&ub); // // query RTM for each route entry in packet // piend = (PIPRIP_ENTRY)(pbuf + pwc->IC_InputLength); for ( ; pie < piend; pie++) { // // ignore unrecognized address families // if (pie->IE_AddrFamily != htons(AF_INET)) { continue; } net.N_NetNumber = pie->IE_Destination; if (pih->IH_Version == 2 && pie->IE_SubnetMask != 0) { net.N_NetMask = pie->IE_SubnetMask; } else { net.N_NetMask = GuessSubnetMask(net.N_NetNumber, NULL); } // // lookup best route to the requested destination // and get the metric // RTM_IPV4_SET_ADDR_AND_MASK( &rna, net.N_NetNumber, net.N_NetMask ); dwErr = RtmGetExactMatchDestination( ig.IG_RtmHandle, &rna, RTM_BEST_PROTOCOL, RTM_VIEW_MASK_UCAST, &rdi ); if (dwErr != NO_ERROR) { pie->IE_Metric = htonl(IPRIP_INFINITE); } else { // // if there is no best route to this destination // metric is INFINITE // if (rdi.ViewInfo[0].Route == NULL) { pie->IE_Metric = htonl(IPRIP_INFINITE); } else { dwErr = GetRouteInfo( rdi.ViewInfo[0].Route, NULL, &rdi, &route ); if (dwErr != NO_ERROR) { pie->IE_Metric = htonl(IPRIP_INFINITE); } else { // // non-RIP routes are advertised with metric 2 // pie->IE_Metric = (route.RR_RoutingProtocol == PROTO_IP_RIP ? htonl(GETROUTEMETRIC(&route)) : htonl(2)); } } // // release the dest info // dwErr = RtmReleaseDestInfo(ig.IG_RtmHandle, &rdi); if (dwErr != NO_ERROR) { char szNet[20], szMask[20]; lstrcpy(szNet, INET_NTOA(route.RR_Network.N_NetNumber)); lstrcpy(szMask, INET_NTOA(route.RR_Network.N_NetMask)); TRACE3( ROUTE, "error %d releasing dest %s:%s", dwErr, szNet, szMask ); } } RTM_ROUTE_FROM_IPRIP_ENTRY(&route, pie); ub.UB_AddRoutine(&ub, &route); } RELEASE_BINDING_LOCK_SHARED(); // // send the buffer now // ub.UB_FinishRoutine(&ub); InterlockedIncrement(&ig.IG_Stats.GS_TotalResponsesSent); } } while(FALSE); RELEASE_IF_LOCK_SHARED(); RIP_FREE(pContext); TRACE0(LEAVE, "leaving ProcessRequest"); } //---------------------------------------------------------------------------- // Function: ProcessResponse // // this function process an incoming IPRIP response packet //---------------------------------------------------------------------------- VOID ProcessResponse( PVOID pContext ) { DWORD dwSource; PBYTE pPacket; PIPRIP_IF_STATS pis; PIF_TABLE pTable; PIPRIP_HEADER pih; BOOL bTriggerUpdate; PIPRIP_ENTRY pie, piend; PIPRIP_AUTHENT_ENTRY pae; PIF_TABLE_ENTRY pite; PIPRIP_IF_CONFIG pic; PIPRIP_IP_ADDRESS paddr; PINPUT_CONTEXT pwc; PPEER_TABLE pPeers; PPEER_TABLE_ENTRY ppte; PIPRIP_PEER_STATS pps; CHAR szSource[20], szNetwork[20]; LPSTR lpszTemp = NULL; TRACE0(ENTER, "entering ProcessResponse"); bTriggerUpdate = FALSE; pTable = ig.IG_IfTable; pPeers = ig.IG_PeerTable; ACQUIRE_IF_LOCK_SHARED(); do { // breakout loop pwc = (PINPUT_CONTEXT)pContext; // // get pointer to receiving interface // pite = GetIfByIndex(pTable, pwc->IC_InterfaceIndex); if (pite == NULL || IF_IS_INACTIVE(pite)) { TRACE1( RESPONSE, "processing response: interface %d not found", pwc->IC_InterfaceIndex ); break; } ZeroMemory(szSource, sizeof(szSource)); ZeroMemory(szNetwork, sizeof(szNetwork)); dwSource = pwc->IC_InputSource.sin_addr.s_addr; lpszTemp = INET_NTOA(dwSource); if (lpszTemp != NULL) { lstrcpy(szSource, lpszTemp); } else { ZeroMemory(szSource, sizeof(szSource)); } // // get pointer to peer struct for sender // ACQUIRE_PEER_LOCK_SHARED(); ppte = GetPeerByAddress(pPeers, dwSource, GETMODE_EXACT, NULL); if (ppte != NULL) { pps = &ppte->PTE_Stats; } else { pps = NULL; } RELEASE_PEER_LOCK_SHARED(); pis = &pite->ITE_Stats; pic = pite->ITE_Config; paddr = IPRIP_IF_ADDRESS_TABLE(pite->ITE_Binding) + pwc->IC_AddrIndex; pPacket = pwc->IC_InputPacket.IP_Packet; pih = (PIPRIP_HEADER)pPacket; pie = (PIPRIP_ENTRY)(pPacket + sizeof(IPRIP_HEADER)); pae = (PIPRIP_AUTHENT_ENTRY)pie; // // make sure our configuration allows us to handle this packet // if ((pih->IH_Version != 2 && pic->IC_AcceptMode == IPRIP_ACCEPT_RIP2)) { CHAR szVersion[10]; LPSTR lpszAddr = INET_NTOA(paddr->IA_Address); InterlockedIncrement(&pis->IS_BadResponsePacketsReceived); if (pps != NULL) { InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer); } if (lpszAddr != NULL) { TRACE2( RESPONSE, "dropping non-v2 packet on RIPv2 interface %d (%s)", pite->ITE_Index, lpszAddr ); wsprintf(szVersion, "%d", pih->IH_Version); LOGWARN4( PACKET_VERSION_MISMATCH, szVersion, lpszAddr, szSource, "2", 0 ); } break; } else if ((pih->IH_Version != 1 && pic->IC_AcceptMode == IPRIP_ACCEPT_RIP1)) { CHAR szVersion[10]; LPSTR lpszAddr = INET_NTOA(paddr->IA_Address); InterlockedIncrement(&pis->IS_BadResponsePacketsReceived); if (pps != NULL) { InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer); } if (lpszAddr != NULL) { TRACE2( RESPONSE, "dropping RIPv2 packet on RIPv1 interface %d (%s)", pite->ITE_Index, lpszAddr ); wsprintf(szVersion, "%d", pih->IH_Version); LOGWARN4( PACKET_VERSION_MISMATCH, szVersion, lpszAddr, szSource, "1", 0 ); } break; } // // version 2 packets call for authentication processing; // if (pih->IH_Version == 2) { DWORD dwErr; dwErr = AuthenticatePacket(pPacket, pae, pic, pis, pps, &pie); if (dwErr == ERROR_ACCESS_DENIED) { LPSTR lpszAddr = INET_NTOA(paddr->IA_Address); if (lpszAddr != NULL) { TRACE3( RESPONSE, "dropping packet from %s on interface %d (%s): authentication failed", szSource, pite->ITE_Index, lpszAddr ); LOGWARN2(AUTHENTICATION_FAILED, lpszAddr, szSource, NO_ERROR); } break; } } // // need to lock the binding table since GuessSubnetMask will be called // inside ProcessResponseEntry // need to lock the global config since EnqueueSendEntry will be called // inside ProcessResponseEntry // ACQUIRE_BINDING_LOCK_SHARED(); ACQUIRE_GLOBAL_LOCK_SHARED(); // // process each entry; reserved fields must be checked for non-RIPv2 // piend = (PIPRIP_ENTRY)(pPacket + pwc->IC_InputLength); if (pih->IH_Version == 1) { for ( ; pie < piend; pie++) { // // validate the route entry fields // if (pie->IE_AddrFamily != htons(AF_INET) || pie->IE_RouteTag != 0 || pie->IE_SubnetMask != 0 || pie->IE_Nexthop != 0) { LPSTR lpszAddr; // // update stats on ignored entries // InterlockedIncrement(&pis->IS_BadResponseEntriesReceived); if (pps != NULL) { InterlockedIncrement( &pps->PS_BadResponseEntriesFromPeer ); } lpszAddr = INET_NTOA(pie->IE_Destination); if (lpszAddr != NULL) { lstrcpy(szNetwork, lpszAddr); lpszAddr = INET_NTOA(paddr->IA_Address); if (lpszAddr != NULL) { LOGINFO3( ROUTE_ENTRY_IGNORED, lpszAddr, szNetwork, szSource, 0 ); } } continue; } // // entry is alright, process it // if (ProcessResponseEntry( pite, pwc->IC_AddrIndex, dwSource, pie, pps )) { bTriggerUpdate = TRUE; } } } else if (pih->IH_Version == 2) { // // this is a RIPv2 packet, so the reserved fields in entries // may optionally contain information about the route; // for ( ; pie < piend; pie++) { // // validate the route entry fields // if (pie->IE_AddrFamily != htons(AF_INET)) { LPSTR lpszAddr; // // update stats on ignored entries // InterlockedIncrement(&pis->IS_BadResponseEntriesReceived); if (pps != NULL) { InterlockedIncrement( &pps->PS_BadResponseEntriesFromPeer ); } lpszAddr = INET_NTOA(pie->IE_Destination); if (lpszAddr != NULL) { lstrcpy(szNetwork, lpszAddr); lpszAddr = INET_NTOA(paddr->IA_Address); if (lpszAddr != NULL) { LOGINFO3( ROUTE_ENTRY_IGNORED, lpszAddr, szNetwork, szSource, 0 ); } } continue; } // // entry is alright, process it // if (ProcessResponseEntry( pite, pwc->IC_AddrIndex, dwSource, pie, pps )) { bTriggerUpdate = TRUE; } } } else { // // this packet's version is greater than 2, so we ignore // the contents of the reserved fields // for ( ; pie < piend; pie++) { // // validate the route entry fields // if (pie->IE_AddrFamily != htons(AF_INET)) { LPSTR lpszAddr; // // update stats on ignored entries // InterlockedIncrement(&pis->IS_BadResponseEntriesReceived); if (pps != NULL) { InterlockedIncrement( &pps->PS_BadResponseEntriesFromPeer ); } lpszAddr = INET_NTOA(pie->IE_Destination); if (lpszAddr != NULL) { lstrcpy(szNetwork, lpszAddr); lpszAddr = INET_NTOA(paddr->IA_Address); if (lpszAddr != NULL) { LOGINFO3( ROUTE_ENTRY_IGNORED, lpszAddr, szNetwork, szSource, 0 ); } } continue; } // // entry is alright, clear reserved fields and process // pie->IE_Nexthop = 0; pie->IE_RouteTag = 0; pie->IE_SubnetMask = 0; if (ProcessResponseEntry( pite, pwc->IC_AddrIndex, dwSource, pie, pps )) { bTriggerUpdate = TRUE; } } } RELEASE_GLOBAL_LOCK_SHARED(); RELEASE_BINDING_LOCK_SHARED(); // // generate a triggered update if necessary // if (bTriggerUpdate) { QueueRipWorker(WorkerFunctionStartTriggeredUpdate, NULL); } } while(FALSE); RELEASE_IF_LOCK_SHARED(); RIP_FREE(pContext); TRACE0(LEAVE, "leaving ProcessResponse"); } //---------------------------------------------------------------------------- // Function: ProcessResponseEntry // // this function processes the given response packet entry, received // on the given interface from the given source. // If a triggered update is necessary, this function returns TRUE. //---------------------------------------------------------------------------- BOOL ProcessResponseEntry( PIF_TABLE_ENTRY pITE, DWORD dwAddrIndex, DWORD dwSource, PIPRIP_ENTRY pIE, PIPRIP_PEER_STATS pPS ) { IP_NETWORK in; PIPRIP_IF_STATS pis; PIPRIP_IF_CONFIG pic; PIPRIP_IP_ADDRESS paddr; CHAR szSource[32]; CHAR szNetmask[32]; CHAR szNexthop[32]; CHAR szNetwork[32]; BOOL bRouteExists, bRelRoute = FALSE; RIP_IP_ROUTE route; DWORD dwNetclassMask, dwNexthop, dwRipMetric; DWORD dwErr = NO_ERROR, dwFlags, dwFound, dwNetwork, dwNetmask; LPSTR lpszAddr; RTM_NET_ADDRESS rna; PRTM_ROUTE_INFO prri = NULL; RTM_ROUTE_HANDLE hRtmRoute; // TRACE0(ENTER, "entering ProcessResponseEntry"); pis = &pITE->ITE_Stats; pic = pITE->ITE_Config; paddr = IPRIP_IF_ADDRESS_TABLE(pITE->ITE_Binding) + dwAddrIndex; // // read destination and figure out subnet mask // if mask is not given in the packet // dwNetwork = pIE->IE_Destination; if (pIE->IE_SubnetMask == 0) { dwNetmask = GuessSubnetMask(dwNetwork, &dwNetclassMask); } else { // // double-check the netclass mask, to accomodate supernets // dwNetmask = pIE->IE_SubnetMask; dwNetclassMask = NETCLASS_MASK(dwNetwork); if (dwNetclassMask > dwNetmask) { dwNetclassMask = dwNetmask; } } #if 1 dwNexthop = dwSource; #else // BUG 205349: using the nexthop field results in flapping // when more than two routers are on the same network. // The full fix is to distinguish between the source of the route // and the nexthop of the route. // // read the next-hop field; // if it is zero or it is not on the same subnet // as the receiving interface, ignore it and use the address // of the source as the next-hop. // otherwise, use the address specified in the packet // as the next-hop. // if (!pIE->IE_Nexthop || (pIE->IE_Nexthop & paddr->IA_Netmask) != (paddr->IA_Address & paddr->IA_Netmask)) { dwNexthop = dwSource; } else { dwNexthop = pIE->IE_Nexthop; } #endif // // set up variables used for error and information messages // lpszAddr = INET_NTOA(dwSource); if (lpszAddr != NULL) { lstrcpy(szSource, lpszAddr);} else { ZeroMemory(szSource, sizeof(szSource)); } lpszAddr = INET_NTOA(dwNetwork); if (lpszAddr != NULL) { lstrcpy(szNetwork, lpszAddr);} else { ZeroMemory(szSource, sizeof(szSource)); } lpszAddr = INET_NTOA(dwNetmask); if (lpszAddr != NULL) { lstrcpy(szNetmask, lpszAddr);} else { ZeroMemory(szSource, sizeof(szSource)); } lpszAddr = INET_NTOA(dwNexthop); if (lpszAddr != NULL) { lstrcpy(szNexthop, lpszAddr);} else { ZeroMemory(szSource, sizeof(szSource)); } if (pPS != NULL) { InterlockedExchange( &pPS->PS_LastPeerRouteTag, (DWORD)ntohs(pIE->IE_RouteTag) ); } do { // breakout loop // // make sure metric is in rational range // dwRipMetric = ntohl(pIE->IE_Metric); if (dwRipMetric > IPRIP_INFINITE) { TRACE4( RESPONSE, "metric == %d, ignoring route to %s via %s advertised by %s", dwRipMetric, szNetwork, szNexthop, szSource ); LOGWARN3( ROUTE_METRIC_INVALID,szNetwork, szNexthop, szSource, dwRipMetric ); break; } // // make sure route is to valid address type // if (CLASSD_ADDR(dwNetwork) || CLASSE_ADDR(dwNetwork)) { TRACE3( RESPONSE, "invalid class, ignoring route to %s via %s advertised by %s", szNetwork, szNexthop, szSource ); LOGINFO3( ROUTE_CLASS_INVALID, szNetwork, szNexthop, szSource, NO_ERROR ); break; } // // make sure route is not to loopback address // if (IS_LOOPBACK_ADDR(dwNetwork)) { TRACE3( RESPONSE, "ignoring loopback route to %s via %s advertised by %s", szNetwork, szNexthop, szSource ); LOGWARN3( LOOPBACK_ROUTE_INVALID, szNetwork, szNexthop, szSource, NO_ERROR ); break; } // // make sure route it is not a broadcast route // The first condition uses the netmask information received in the // advertisement // The second condition uses the netmask which is computed based // on the address class // The third condition checks for the all 1's broadcast // if ( IS_DIRECTED_BROADCAST_ADDR(dwNetwork, dwNetmask) || IS_DIRECTED_BROADCAST_ADDR(dwNetwork, dwNetclassMask) || IS_LOCAL_BROADCAST_ADDR(dwNetwork) ) { TRACE3( RESPONSE, "ignoring broadcast route to %s via %s advertised by %s", szNetwork, szNexthop, szSource ); LOGWARN3( BROADCAST_ROUTE_INVALID, szNetwork, szNexthop, szSource, 0 ); break; } // // discard host routes if the receiving interface // is not configured to accept host routes // // // At this stage the broadcast routes have already been weeded out. // So it is safe to assume that // if Network address width is greater than the Netmask address // width, then it is a host route. // Or, // if the dwNetmask is 255.255.255.255 then it is a host route. // if ( ((dwNetwork & ~dwNetmask) != 0) || (dwNetmask == HOSTADDR_MASK) ) { // // This is a host-route; see whether we can accept it. // if (IPRIP_FLAG_IS_ENABLED(pic, ACCEPT_HOST_ROUTES)) { // // The host route can be accepted. // Set the mask to all-ones to ensure that // the route can be added to the stack. // dwNetmask = HOSTADDR_MASK; } else { // // The host-route must be rejected. // TRACE3( RESPONSE, "ignoring host route to %s via %s advertised by %s", szNetwork, szNexthop, szSource ); LOGINFO3( HOST_ROUTE_INVALID, szNetwork, szNexthop, szSource, NO_ERROR ); break; } } // // discard default routes if the receiving interface // is not configured to accept default routes // if (dwNetwork == 0 && IPRIP_FLAG_IS_DISABLED(pic, ACCEPT_DEFAULT_ROUTES)) { TRACE3( RESPONSE, "ignoring default route to %s via %s advertised by %s", szNetwork, szNexthop, szSource ); LOGINFO3( DEFAULT_ROUTE_INVALID, szNetwork, szNexthop, szSource, NO_ERROR ); break; } // // put the route through the accept filters // if (pic->IC_AcceptFilterMode != IPRIP_FILTER_DISABLED) { // // discard the route if the receiving interface is including // all routes but this route is listed as an exception, or if // the receiving interface is excluding all routes and this // route is not listed as an exception // IS_ROUTE_IN_ACCEPT_FILTER(pic, dwNetwork, dwFound); if ((pic->IC_AcceptFilterMode == IPRIP_FILTER_INCLUDE && !dwFound)|| (pic->IC_AcceptFilterMode == IPRIP_FILTER_EXCLUDE && dwFound)) { TRACE3( RESPONSE, "ignoring filtered route to %s via %s advertised by %s", szNetwork, szNexthop, szSource ); LOGINFO3( ROUTE_FILTERED, szNetwork, szNexthop, szSource, NO_ERROR ); break; } } // // see if the route already exists in RTM's table // in.N_NetNumber = dwNetwork; in.N_NetMask = dwNetmask; RTM_IPV4_SET_ADDR_AND_MASK( &rna, dwNetwork, dwNetmask ); prri = RIP_ALLOC( RTM_SIZE_OF_ROUTE_INFO( ig.IG_RtmProfile.MaxNextHopsInRoute ) ); if ( prri == NULL ) { dwErr = GetLastError(); TRACE2( ANY, "ProcessResponseEntry: error %d while allocating %d bytes", dwErr, RTM_SIZE_OF_ROUTE_INFO(ig.IG_RtmProfile.MaxNextHopsInRoute) ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); break; } prri-> RouteOwner = ig.IG_RtmHandle; dwErr = RtmGetExactMatchRoute( ig.IG_RtmHandle, &rna, RTM_MATCH_OWNER, prri, 0, RTM_VIEW_MASK_ANY, &hRtmRoute ); if ((dwErr != NO_ERROR) || (hRtmRoute == NULL)) { bRouteExists = FALSE; } else{ bRelRoute = TRUE; dwErr = GetRouteInfo( hRtmRoute, prri, NULL, &route ); if (dwErr != NO_ERROR) { bRouteExists = FALSE; break; } else { bRouteExists = TRUE; } } // // add the cost of this interface to the metric // dwRipMetric = min(IPRIP_INFINITE, dwRipMetric + pic->IC_Metric); if (dwRipMetric >= IPRIP_INFINITE && !bRouteExists) { TRACE4( RESPONSE, "metric==%d, ignoring route to %s via %s advertised by %s", IPRIP_INFINITE, szNetwork, szNexthop, szSource ); break; } // // ROUTE ADDITION/UPDATE/REMOVAL: // if (!bRouteExists) { // // NEW ROUTE: // // set up struct to pass to RTM // ZeroMemory(&route, sizeof(route)); route.RR_RoutingProtocol = PROTO_IP_RIP; route.RR_Network = in; SETROUTEMETRIC(&route, dwRipMetric); route.RR_InterfaceID = pITE->ITE_Index; route.RR_NextHopAddress.N_NetNumber = dwNexthop; route.RR_NextHopAddress.N_NetMask = paddr->IA_Netmask; SETROUTETAG(&route, ntohs(pIE->IE_RouteTag)); // // add route to RTM // COMPUTE_ROUTE_METRIC(&route); #if ROUTE_DBG TRACE3( RESPONSE, "Adding route to %s via %s advertised by %s", szNetwork, szNexthop, szSource ); #endif dwErr = AddRtmRoute( ig.IG_RtmHandle, &route, NULL, pic->IC_RouteExpirationInterval, pic->IC_RouteRemovalInterval, TRUE ); if (dwErr != NO_ERROR) { TRACE4( RESPONSE, "error %d adding route to %s via %s advertised by %s", dwErr, szNetwork, szNexthop, szSource ); LOGINFO3( ADD_ROUTE_FAILED_2, szNetwork, szNexthop, szSource, dwErr ); break; } InterlockedIncrement(&ig.IG_Stats.GS_SystemRouteChanges); LOGINFO3( NEW_ROUTE_LEARNT_1, szNetwork, szNexthop, szSource, NO_ERROR ); } else { DWORD dwTimer = 0, dwChangeFlags = 0; BOOL bTriggerUpdate = FALSE, bActive = TRUE; // // EXISTING ROUTE: // // reset time-to-live, and mark route as expiring, // if this advertisement is from the same source // as the existing route, and the existing route's metric // is not already INFINITE; thus, if a route has been // advertised as unreachable, we don't reset its time-to-live // just because we hear an advertisement for the route // if (dwNexthop == route.RR_NextHopAddress.N_NetNumber && GETROUTEMETRIC(&route) != IPRIP_INFINITE) { dwTimer = pic->IC_RouteExpirationInterval; // // if existing route was a summary route, make sure // set the validity flag before you mark it as a // non summary route. Fix for bug #81544 // if ( GETROUTEFLAG( &route ) == ROUTEFLAG_SUMMARY ) { CHAR szRouteNetwork[20], szRouteNetmask[20]; LPSTR lpszAddrTemp = INET_NTOA(route.RR_Network.N_NetNumber); if (lpszAddrTemp != NULL) { lstrcpy(szRouteNetwork, lpszAddrTemp); lpszAddrTemp = INET_NTOA(route.RR_Network.N_NetMask); if (lpszAddrTemp != NULL) { lstrcpy(szRouteNetmask, lpszAddrTemp); TRACE2( RESPONSE, "%s %s summary route to valid route", szRouteNetwork, szRouteNetmask ); } } SETROUTEFLAG( &route, ~ROUTEFLAG_SUMMARY ); } } // // we only need to do further processing if // (a) the advertised route is from the same source as // the existing route and the metrics are different, or // (b) the advertised route has a better metric // if ((dwNexthop == route.RR_NextHopAddress.N_NetNumber && dwRipMetric != GETROUTEMETRIC(&route)) || (dwRipMetric < GETROUTEMETRIC(&route))) { // // if the next-hop's differ, adopt the new next-hop // if (dwNexthop != route.RR_NextHopAddress.N_NetNumber) { route.RR_NextHopAddress.N_NetNumber = dwNexthop; route.RR_NextHopAddress.N_NetMask = paddr->IA_Netmask; InterlockedIncrement(&ig.IG_Stats.GS_SystemRouteChanges); LOGINFO2( ROUTE_NEXTHOP_CHANGED, szNetwork, szNexthop, NO_ERROR ); } else { CHAR szMetric[12]; wsprintf(szMetric, "%d", dwRipMetric); LOGINFO3( ROUTE_METRIC_CHANGED, szNetwork, szNexthop, szMetric, 0 ); } // // check the metric to decide the new time-to-live // if (dwRipMetric == IPRIP_INFINITE) { // // Delete the route // #if ROUTE_DBG TRACE2( ROUTE, "Deleting route to %s:%s", szNetwork, szNetmask ); #endif dwTimer = 0; dwErr = RtmReferenceHandles( ig.IG_RtmHandle, 1, &hRtmRoute ); if (dwErr != NO_ERROR) { TRACE3( ANY, "error %d referencing route to %s:%s", dwErr, szNetwork, szNetmask ); break; } dwErr = RtmDeleteRouteToDest( ig.IG_RtmHandle, hRtmRoute, &dwChangeFlags ); if (dwErr != NO_ERROR) { TRACE3( ANY, "error %d deleting route to %s:%s", dwErr, szNetwork, szNetmask ); } break; } else { // // set the expiration flag and use the expiration TTL // dwTimer = pic->IC_RouteExpirationInterval; } // // use the advertised metric, and set the interface ID, // adapter index, and route tag // SETROUTEMETRIC(&route, dwRipMetric); route.RR_InterfaceID = pITE->ITE_Index; // route.RR_FamilySpecificData.FSD_AdapterIndex = // pITE->ITE_Binding.AdapterIndex; SETROUTETAG(&route, ntohs(pIE->IE_RouteTag)); // // always require a triggered update if we reach here // bTriggerUpdate = TRUE; } if (dwTimer != 0) { COMPUTE_ROUTE_METRIC(&route); #if ROUTE_DBG TRACE4( RESPONSE, "Editing route to %s via %s advertised by %s, metric %d", szNetwork, szNexthop, szSource, dwRipMetric ); #endif dwErr = AddRtmRoute( ig.IG_RtmHandle, &route, NULL, dwTimer, pic-> IC_RouteRemovalInterval, TRUE ); if (dwErr != NO_ERROR) { TRACE4( RESPONSE, "error %d adding route to %s via %s advertised by %s", dwErr, szNetwork, szNexthop, szSource ); LOGINFO3( ADD_ROUTE_FAILED_2,szNetwork,szNexthop,szSource, dwErr ); } } } } while(FALSE); // // if some sort of error occured, increment stats appropriately // if (dwErr != NO_ERROR ) { InterlockedIncrement(&pis->IS_BadResponseEntriesReceived); if (pPS != NULL) { InterlockedIncrement(&pPS->PS_BadResponseEntriesFromPeer); } } // // Release the dest info structure // if (bRelRoute) { dwErr = RtmReleaseRoutes(ig.IG_RtmHandle, 1, &hRtmRoute); if (dwErr != NO_ERROR) { TRACE2( ANY, "error %d releasing route for dest %s", dwErr, szNetwork ); } dwErr = RtmReleaseRouteInfo(ig.IG_RtmHandle, prri); if (dwErr != NO_ERROR) { TRACE2( ANY, "error %d releasing dest %s", dwErr, szNetwork ); } } if ( prri ) { RIP_FREE(prri); } // // always return FALSE. This way no RIP route add/delete/operations // will set of the triggered update mechanism. This mech. is set of // by route change notifications recevied from RTMv2 // return FALSE; } //---------------------------------------------------------------------------- // Function: WorkerFunctionStartFullUpdate // // this function initiates a full-update. It checks to see if a full-update // is already pending, and if not, it sets the full-update-pending flag and // schedules the full-update work item. Then it sets a flag on its interface // indicating a full-update should be generated on the interface. //---------------------------------------------------------------------------- VOID WorkerFunctionStartFullUpdate( PVOID pContext, BOOLEAN bNotUsed ) { DWORD dwIndex; PIF_TABLE pTable; PIF_TABLE_ENTRY pite; PIPRIP_IF_CONFIG pic; if (!ENTER_RIP_API()) { return; } TRACE0(ENTER, "entering WorkerFunctionStartFullUpdate"); pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_SHARED(); EnterCriticalSection(&pTable->IT_CS); do { // breakout loop // // retrieve the interface on which the full-update will be sent // dwIndex = PtrToUlong(pContext); pite = GetIfByIndex(pTable, dwIndex); if (pite == NULL) { TRACE1( SEND, "starting full-update: interface %d not found", dwIndex ); break; } // // if the interface is no longer active, do nothing // if (IF_IS_INACTIVE(pite)) { pite->ITE_Flags &= ~ITEFLAG_FULL_UPDATE_INQUEUE; break; } // // do nothing if a full-update is already pending // if (IF_FULL_UPDATE_PENDING(pite)) { break; } // // only do full-updates on periodic-update interfaces // and don't do full-updates on interfaces configured to be silent; // if (pite->ITE_Config->IC_UpdateMode != IPRIP_UPDATE_PERIODIC || pite->ITE_Config->IC_AnnounceMode == IPRIP_ANNOUNCE_DISABLED) { pite->ITE_Flags &= ~ITEFLAG_FULL_UPDATE_INQUEUE; break; } // // set the full update flags on the interface; // pite->ITE_Flags |= ITEFLAG_FULL_UPDATE_PENDING; // // if there is no full-update pending, // queue the full-update finishing function // if (!IPRIP_FULL_UPDATE_PENDING(pTable)) { DWORD dwRand; // // set the global full-update-pending flag // pTable->IT_Flags |= IPRIP_FLAG_FULL_UPDATE_PENDING; // // we need a random interval between 1 and 5 seconds // dwRand = GetTickCount(); dwRand = RtlRandom(&dwRand); dwRand = 1000 + (DWORD)((double)dwRand / MAXLONG * (4.0 * 1000)); // // Schedule a full update // if (!ChangeTimerQueueTimer( ig.IG_TimerQueueHandle, pTable->IT_FinishFullUpdateTimer, dwRand, 10000000)) { TRACE1( SEND, "error %d setting finish full update timer", GetLastError() ); } } } while(FALSE); LeaveCriticalSection(&pTable->IT_CS); RELEASE_IF_LOCK_SHARED(); TRACE0(LEAVE, "leaving WorkerFunctionStartFullUpdate"); LEAVE_RIP_API(); } //---------------------------------------------------------------------------- // Function: EnqueueStartFullUpdate // // This function is called to enqueue the next start-full-update event // for the given interface. The interface's state is updated as necessary. // It assumes that the following locks have been acquired: // IT_RWL - shared // IT_CS - exclusive // TimerQueue lock - exclusive //---------------------------------------------------------------------------- VOID EnqueueStartFullUpdate( PIF_TABLE_ENTRY pite, LARGE_INTEGER qwLastFullUpdateTime ) { // // set last-full-update time // if (!ChangeTimerQueueTimer( ig.IG_TimerQueueHandle, pite->ITE_FullOrDemandUpdateTimer, RipSecsToMilliSecs(pite->ITE_Config->IC_FullUpdateInterval), 10000000 )) { TRACE1( SEND, "error %d updating start full update timer", GetLastError() ); pite->ITE_Flags &= ~ITEFLAG_FULL_UPDATE_INQUEUE; } } //---------------------------------------------------------------------------- // Function: WorkerFunctionFinishFullUpdate // // This function sends a full-update on every interface which has the // full-update pending flag set, and schedules the next full-update on each // interface. //---------------------------------------------------------------------------- VOID WorkerFunctionFinishFullUpdate( PVOID pContext, BOOLEAN bNotUsed ) { PIF_TABLE pTable; PIPRIP_IF_CONFIG pic; PLIST_ENTRY ple, phead; DWORD dwErr, dwIndex, dwIfCount; LARGE_INTEGER qwCurrentTime; PIF_TABLE_ENTRY pite, *ppite, *ppitend, *pIfList; if (!ENTER_RIP_API()) { return; } TRACE0(ENTER, "entering WorkerFunctionFinishFullUpdate"); pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_SHARED(); EnterCriticalSection(&pTable->IT_CS); pIfList = NULL; ppite = NULL; do { // // first count how many there are // dwIfCount = 0; phead = &pTable->IT_ListByAddress; for (ple = phead->Flink; ple != phead; ple = ple->Flink) { pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); if (IF_IS_ACTIVE(pite) && IF_FULL_UPDATE_PENDING(pite)) { pic = pite->ITE_Config; if (pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC && pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) { ++dwIfCount; } } } if (dwIfCount == 0) { TRACE0(SEND, "finishing full-update: no interfaces"); break; } // // then make memory for the interface pointers // pIfList = RIP_ALLOC(dwIfCount * sizeof(PIF_TABLE_ENTRY)); if (pIfList == NULL) { dwErr = GetLastError(); TRACE2( SEND, "error code %d allocating %d bytes for interface list", dwErr, dwIfCount * sizeof(PIF_TABLE_ENTRY) ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); // // enqueue the next full-update for each interface // RipQuerySystemTime(&qwCurrentTime); pTable->IT_LastUpdateTime = qwCurrentTime; for (ple = phead->Flink; ple != phead; ple = ple->Flink) { pite=CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); if (IF_IS_ACTIVE(pite) && IF_FULL_UPDATE_PENDING(pite)) { pic = pite->ITE_Config; if (pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC && pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) { EnqueueStartFullUpdate(pite, qwCurrentTime); } } } break; } // // and copy the interface pointers to the memory allocated // ppitend = pIfList + dwIfCount; for (ple = phead->Flink, ppite = pIfList; ple != phead && ppite < ppitend; ple = ple->Flink) { pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); if (IF_IS_ACTIVE(pite) && IF_FULL_UPDATE_PENDING(pite)) { pic = pite->ITE_Config; if (pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC && pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) { *ppite++ = pite; } } } // // send the updates // TRACE1(SEND, "sending full-updates on %d interfaces", dwIfCount); SendRoutes(pIfList, dwIfCount, SENDMODE_FULL_UPDATE, 0, 0); // // enqueue the next full-update for each interface // RipQuerySystemTime(&qwCurrentTime); pTable->IT_LastUpdateTime = qwCurrentTime; for (ppite = pIfList; ppite < ppitend; ppite++) { EnqueueStartFullUpdate(*ppite, qwCurrentTime); } // // free the memory allocated for the interface pointers // RIP_FREE(pIfList); } while(FALSE); // // clear the full-update pending flags // phead = &pTable->IT_ListByAddress; for (ple = phead->Flink; ple != phead; ple = ple->Flink) { pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); pite->ITE_Flags &= ~ITEFLAG_FULL_UPDATE_PENDING; } pTable->IT_Flags &= ~IPRIP_FLAG_FULL_UPDATE_PENDING; LeaveCriticalSection(&pTable->IT_CS); RELEASE_IF_LOCK_SHARED(); TRACE0(LEAVE, "leaving WorkerFunctionFinishFullUpdate"); LEAVE_RIP_API(); } //---------------------------------------------------------------------------- // Function: FinishTriggeredUpdate // // This function is responsible for sending out a triggered update // on all interfaces which have triggered updates enabled. // No triggered updates are sent on interfaces which already have // a full-update pending. // Assumes interface table is locked for reading or writing, // and update-lock (IT_CS) is held. //---------------------------------------------------------------------------- VOID FinishTriggeredUpdate( ) { PIF_TABLE pTable; PIPRIP_IF_STATS pis; DWORD dwErr, dwIfCount; PIPRIP_IF_CONFIG pic = NULL; PLIST_ENTRY ple, phead; LARGE_INTEGER qwCurrentTime; PIF_TABLE_ENTRY pite, *ppite, *ppitend, *pIfList; pTable = ig.IG_IfTable; // // we lock the send queue now so that no routes are added // until the existing ones are transmitted // ACQUIRE_LIST_LOCK(ig.IG_SendQueue); do { // breakout loop // // count the interfaces on which the triggered update will be sent // dwIfCount = 0; phead = &pTable->IT_ListByAddress; for (ple = phead->Flink; ple != phead; ple = ple->Flink) { pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); pic = pite->ITE_Config; if (IF_IS_ACTIVE(pite) && !IF_FULL_UPDATE_PENDING(pite) && pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC && pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED && IPRIP_FLAG_IS_ENABLED(pic, TRIGGERED_UPDATES)) { ++dwIfCount; } } if (dwIfCount == 0) { TRACE0(SEND, "finishing triggered-update: no interfaces"); break; } // // allocate memory to hold the interface pointers // pIfList = RIP_ALLOC(dwIfCount * sizeof(PIF_TABLE_ENTRY)); if (pIfList == NULL) { dwErr = GetLastError(); TRACE2( SEND, "error code %d allocating %d bytes for interface list", dwErr, dwIfCount * sizeof(PIF_TABLE_ENTRY) ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); break; } // // copy the interface pointers to the allocated memory // ppitend = pIfList + dwIfCount; for (ple = phead->Flink, ppite = pIfList; ple != phead && ppite < ppitend; ple = ple->Flink) { pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); pic = pite->ITE_Config; if (IF_IS_ACTIVE(pite) && !IF_FULL_UPDATE_PENDING(pite) && pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC && pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED && IPRIP_FLAG_IS_ENABLED(pic, TRIGGERED_UPDATES)) { *ppite++ = pite; } } // // send the triggered-update routes // TRACE1(SEND, "sending triggered-updates on %d interfaces", dwIfCount); SendRoutes(pIfList, dwIfCount, SENDMODE_TRIGGERED_UPDATE, 0, 0); // // update the statistics for each interface // for (ppite = pIfList; ppite < ppitend; ppite++) { pis = &(*ppite)->ITE_Stats; InterlockedIncrement(&pis->IS_TriggeredUpdatesSent); } // // update the last time at which an update was sent // RipQuerySystemTime(&pTable->IT_LastUpdateTime); // // free the memory allocated for the interfaces // RIP_FREE(pIfList); } while (FALSE); // // make sure send-queue is empty // FlushSendQueue(ig.IG_SendQueue); RELEASE_LIST_LOCK(ig.IG_SendQueue); pTable->IT_Flags &= ~IPRIP_FLAG_TRIGGERED_UPDATE_PENDING; return; } //---------------------------------------------------------------------------- // Function: WorkerFunctionStartTriggeredUpdate // // This function checks to see if the minimum interval between triggered // updates has elapsed, and if so, sends a triggered update. Otherwise, // it schedules the triggered update to be sent, and sets flags to indicate // that a triggered update is pending //---------------------------------------------------------------------------- VOID WorkerFunctionStartTriggeredUpdate( PVOID pContext ) { PIF_TABLE pTable; LARGE_INTEGER qwCurrentTime, qwSoonestTriggerTime; if (!ENTER_RIP_WORKER()) { return; } TRACE0(ENTER, "entering WorkerFunctionStartTriggeredUpdate"); pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_SHARED(); EnterCriticalSection(&pTable->IT_CS); // // if triggered update is not pending, queue a triggered update // if (!IPRIP_TRIGGERED_UPDATE_PENDING(pTable)) { // // figure out when is the soonest time a triggered update // can be sent, based on the configured minimum interval // between triggered updates (in seconds) and the last time // a triggered update was generated (in 100-nanosecond units) // ACQUIRE_GLOBAL_LOCK_SHARED(); qwSoonestTriggerTime.HighPart = 0; qwSoonestTriggerTime.LowPart = ig.IG_Config->GC_MinTriggeredUpdateInterval; RipSecsToSystemTime(&qwSoonestTriggerTime); RELEASE_GLOBAL_LOCK_SHARED(); qwSoonestTriggerTime = RtlLargeIntegerAdd( qwSoonestTriggerTime, pTable->IT_LastUpdateTime ); RipQuerySystemTime(&qwCurrentTime); // // figure out if clock has been set backward, by comparing // the current time against the last update time // if (RtlLargeIntegerLessThan( qwCurrentTime, pTable->IT_LastUpdateTime )) { // // Send triggered update anyway, since there is no way // to figure out the if minimum time between updates has // elapsed // FinishTriggeredUpdate(); } else if (RtlLargeIntegerLessThan(qwCurrentTime, qwSoonestTriggerTime)) { // // must defer the triggered update // qwSoonestTriggerTime = RtlLargeIntegerSubtract( qwSoonestTriggerTime, qwCurrentTime ); RipSystemTimeToMillisecs(&qwSoonestTriggerTime); if (!ChangeTimerQueueTimer( ig.IG_TimerQueueHandle, pTable->IT_FinishTriggeredUpdateTimer, qwSoonestTriggerTime.LowPart, 10000000 )) { TRACE1( SEND, "error %d updating finish update timer", GetLastError() ); } else { pTable->IT_Flags |= IPRIP_FLAG_TRIGGERED_UPDATE_PENDING; } } else { // // the minimum time between triggered updates has elapsed, // so send the triggered update now // FinishTriggeredUpdate(); } } LeaveCriticalSection(&pTable->IT_CS); RELEASE_IF_LOCK_SHARED(); TRACE0(LEAVE, "leaving WorkerFunctionStartTriggeredUpdate"); LEAVE_RIP_WORKER(); } //---------------------------------------------------------------------------- // Function: WorkerFunctionFinishTriggeredUpdate // // This function generates a triggered update on all interfaces which // do not have triggered updates disabled. //---------------------------------------------------------------------------- VOID WorkerFunctionFinishTriggeredUpdate( PVOID pContext, BOOLEAN bNotUsed ) { PIF_TABLE pTable; if (!ENTER_RIP_API()) { return; } TRACE0(ENTER, "entering WorkerFunctionFinishTriggeredUpdate"); pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_SHARED(); EnterCriticalSection(&pTable->IT_CS); FinishTriggeredUpdate(); LeaveCriticalSection(&pTable->IT_CS); RELEASE_IF_LOCK_SHARED(); TRACE0(LEAVE, "leaving WorkerFunctionFinishTriggeredUpdate"); LEAVE_RIP_API(); return; } //---------------------------------------------------------------------------- // Function: WorkerFunctionStartDemandUpdate // // This function initiates a demand-update on the speficied interface, // sending a general request on the interface. It then schedules a work-item // to report back to Router Manager when the update is done //---------------------------------------------------------------------------- VOID WorkerFunctionStartDemandUpdate( PVOID pContext ) { PIF_TABLE pTable; RIP_IP_ROUTE route; PUPDATE_CONTEXT pwc; PIF_TABLE_ENTRY pite; DWORD dwErr, dwIndex; if (!ENTER_RIP_WORKER()) { return; } TRACE0(ENTER, "entering WorkerFunctionStartDemandUpdate"); pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_SHARED(); do { // breakout loop // // retrieve the interface on which to perform the demand update // dwIndex = PtrToUlong(pContext); pite = GetIfByIndex(pTable, dwIndex); if (pite == NULL) { TRACE1(SEND, "demand-update: interface %d not found", dwIndex); break; } // // make sure interface is active and has demand-updates enabled // if (IF_IS_INACTIVE(pite)) { TRACE1(SEND, "demand-update: interface %d not active", dwIndex); EnqueueDemandUpdateMessage(dwIndex, ERROR_CAN_NOT_COMPLETE); break; } else if (pite->ITE_Config->IC_UpdateMode != IPRIP_UPDATE_DEMAND) { TRACE1(SEND, "demand-updates disabled on interface %d ", dwIndex); EnqueueDemandUpdateMessage(dwIndex, ERROR_CAN_NOT_COMPLETE); break; } // // setup the update context // pwc = RIP_ALLOC(sizeof(UPDATE_CONTEXT)); if (pwc == NULL) { dwErr = GetLastError(); TRACE2( SEND, "error %d allocating %d bytes", dwErr, sizeof(UPDATE_CONTEXT) ); EnqueueDemandUpdateMessage(dwIndex, dwErr); LOGERR0(HEAP_ALLOC_FAILED, dwErr); break; } pwc->UC_InterfaceIndex = dwIndex; pwc->UC_RetryCount = 1; pwc->UC_RouteCount = 0; // // Create a timer for the demand update checks // if (!CreateTimerQueueTimer( &pite->ITE_FullOrDemandUpdateTimer, ig.IG_TimerQueueHandle, WorkerFunctionFinishDemandUpdate, (PVOID)pwc, 5000, 5000, 0 )) { EnqueueDemandUpdateMessage(dwIndex, GetLastError()); } // // request routing tables from neighbors // SendGeneralRequest(pite); } while (FALSE); RELEASE_IF_LOCK_SHARED(); TRACE0(LEAVE, "leaving WorkerFunctionStartDemandUpdate"); LEAVE_RIP_WORKER(); } //---------------------------------------------------------------------------- // Function: WorkerFunctionFinishDemandUpdate // // This function queues a message informing the Router Manager that // the demand-update requested is complete //---------------------------------------------------------------------------- VOID WorkerFunctionFinishDemandUpdate( PVOID pContext, BOOLEAN bNotUsed ) { PIF_TABLE pTable; PUPDATE_CONTEXT pwc; PIF_TABLE_ENTRY pite; DWORD dwErr, dwIndex, dwRouteCount; if (pContext == NULL) { return; } if (!ENTER_RIP_API()) { return; } TRACE0(ENTER, "entering WorkerFunctionFinishDemandUpdate"); // // get the update context // pwc = (PUPDATE_CONTEXT)pContext; dwIndex = pwc->UC_InterfaceIndex; pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_SHARED(); do { // // retrieve the interface being updated // pite = GetIfByIndex(pTable, dwIndex); if (pite == NULL) { EnqueueDemandUpdateMessage(dwIndex, ERROR_CAN_NOT_COMPLETE); break; } // // report failure if the interface is no longer active // if (!IF_IS_ACTIVE(pite)) { EnqueueDemandUpdateMessage(dwIndex, ERROR_CAN_NOT_COMPLETE); break; } // // get a count of the routes now on the interface // dwRouteCount = CountInterfaceRoutes(dwIndex); // // if there are still no routes, send another request // unless we have sent the maximum number of requests // if (dwRouteCount == 0 && ++pwc->UC_RetryCount < MAX_UPDATE_REQUESTS) { SendGeneralRequest(pite); break; } // // if the number of routes has not changed in the last 5 seconds, // tell the router manager that the update is complete; // otherwise, update the route count and enqueue another check // if (pwc->UC_RouteCount == dwRouteCount) { EnqueueDemandUpdateMessage(dwIndex, NO_ERROR); RIP_FREE(pwc); if (!DeleteTimerQueueTimer( ig.IG_TimerQueueHandle, pite->ITE_FullOrDemandUpdateTimer, NULL)) { TRACE1( SEND, "error %d deleting demand update timer", GetLastError() ); } pite->ITE_FullOrDemandUpdateTimer = NULL; } else { pwc->UC_RouteCount = dwRouteCount; } } while(FALSE); RELEASE_IF_LOCK_SHARED(); TRACE0(LEAVE, "leaving WorkerFunctionFinishDemandUpdate"); LEAVE_RIP_API(); } //---------------------------------------------------------------------------- // Function: CountInterfaceRoutes // // Returns a count of the RIP routes associated with the specified interface //---------------------------------------------------------------------------- DWORD CountInterfaceRoutes( DWORD dwInterfaceIndex ) { HANDLE hRouteEnum; PHANDLE phRoutes = NULL; DWORD dwHandles, dwFlags, i, dwErr, dwCount = 0; dwErr = RtmCreateRouteEnum( ig.IG_RtmHandle, NULL, RTM_VIEW_MASK_UCAST, RTM_ENUM_OWN_ROUTES, NULL, RTM_MATCH_INTERFACE, NULL, dwInterfaceIndex, &hRouteEnum ); if (dwErr != NO_ERROR) { TRACE1( ANY, "CountInterfaceRoutes : error %d creating enum handle", dwErr ); return 0; } // // allocate handle array large enough to hold max handles in an // enum // phRoutes = RIP_ALLOC(ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE)); if ( phRoutes == NULL ) { dwErr = GetLastError(); TRACE2( ANY, "CountInterfaceRoutes: error %d while allocating %d bytes" " to hold max handles in an enum", dwErr, ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE) ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); return 0; } do { dwHandles = ig.IG_RtmProfile.MaxHandlesInEnum; dwErr = RtmGetEnumRoutes( ig.IG_RtmHandle, hRouteEnum, &dwHandles, phRoutes ); for ( i = 0; i < dwHandles; i++ ) { // // Release all route handles // dwErr = RtmReleaseRoutes(ig.IG_RtmHandle, 1, &phRoutes[i]); if (dwErr != NO_ERROR) { TRACE1( ANY, "CountInterfaceRoutes : error %d releasing routes", dwErr ); } } dwCount += dwHandles; } while (dwErr == NO_ERROR); // // close enum handle // dwErr = RtmDeleteEnumHandle(ig.IG_RtmHandle, hRouteEnum); if (dwErr != NO_ERROR) { TRACE1( ANY, "CountInterfaceRoutes : error %d closing enum handle", dwErr ); } if ( phRoutes ) { RIP_FREE(phRoutes); } return dwCount; } //---------------------------------------------------------------------------- // Function: EnqueueDemandUpdateMessage // // This function posts a message to IPRIP's Router Manager event queue // indicating the status of a demand update request. //---------------------------------------------------------------------------- VOID EnqueueDemandUpdateMessage( DWORD dwInterfaceIndex, DWORD dwError ) { MESSAGE msg; PUPDATE_COMPLETE_MESSAGE pmsg; // // set up an UPDATE_COMPLETE message // pmsg = &msg.UpdateCompleteMessage; pmsg->UpdateType = RF_DEMAND_UPDATE_ROUTES; pmsg->UpdateStatus = dwError; pmsg->InterfaceIndex = dwInterfaceIndex; ACQUIRE_LIST_LOCK(ig.IG_EventQueue); EnqueueEvent(ig.IG_EventQueue, UPDATE_COMPLETE, msg); SetEvent(ig.IG_EventEvent); RELEASE_LIST_LOCK(ig.IG_EventQueue); } //---------------------------------------------------------------------------- // Function: WorkerFunctionProcessRtmMessage // // This function handles messages from RTM about new or expired routes. //---------------------------------------------------------------------------- VOID WorkerFunctionProcessRtmMessage( PVOID pContext ) { DWORD dwErr, dwFlags, dwNumDests, dwSize; PIF_TABLE pTable; BOOL bTriggerUpdate, bDone = FALSE; PIF_TABLE_ENTRY pite; PIPRIP_IF_CONFIG pic; RIP_IP_ROUTE route; PRTM_DEST_INFO prdi; CHAR szNetwork[32], szNexthop[32]; if (!ENTER_RIP_WORKER()) { return; } TRACE0(ENTER, "entering WorkerFunctionProcessRtmMessage"); pTable = ig.IG_IfTable; // // allocate a buffer for retrieving the dest info // dwSize = RTM_SIZE_OF_DEST_INFO( ig.IG_RtmProfile.NumberOfViews ); prdi = (PRTM_DEST_INFO) RIP_ALLOC( dwSize ); if ( prdi == NULL ) { dwErr = GetLastError(); TRACE2( ROUTE, "error %d allocating %d bytes for dest info buffers", dwErr, dwSize ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); LEAVE_RIP_WORKER(); return; } // // Acquire locks // ACQUIRE_IF_LOCK_SHARED(); ACQUIRE_GLOBAL_LOCK_SHARED(); ACQUIRE_LIST_LOCK(ig.IG_SendQueue); bTriggerUpdate = FALSE; // // loop dequeueing messages until RTM says there are no more left // while (!bDone) { // // Retrieve route changes // dwNumDests = 1; dwErr = RtmGetChangedDests( ig.IG_RtmHandle, ig.IG_RtmNotifHandle, &dwNumDests, prdi ); if ((dwErr != NO_ERROR) && (dwErr != ERROR_NO_MORE_ITEMS)) { TRACE1(ROUTE, "error %d retrieving changed dests", dwErr); break; } // // check if there are any more changed dests // if (dwErr == ERROR_NO_MORE_ITEMS) { bDone = TRUE; } if (dwNumDests < 1) { break; } if ((prdi-> ViewInfo[0].HoldRoute != NULL) || (prdi-> ViewInfo[0].Route != NULL)) { ZeroMemory(&route, sizeof(RIP_IP_ROUTE)); // // For each route change check if you have a held down route. // if so get the info for the held down route since that is // the one to be advertized. // // N.B. RIP summary routes are not advertized via the route // change processing mechanism. // dwErr = GetRouteInfo( (prdi-> ViewInfo[0].HoldRoute != NULL) ? prdi-> ViewInfo[0].HoldRoute : prdi-> ViewInfo[0].Route, NULL, prdi, &route ); if (dwErr == NO_ERROR) { // // do not advertize RIP summary routes // if ((route.RR_RoutingProtocol != PROTO_IP_RIP) || (GETROUTEFLAG(&route) & ROUTEFLAG_SUMMARY) != ROUTEFLAG_SUMMARY) { // // held down routes are advertized with INFINITE metric // if (prdi-> ViewInfo[0].HoldRoute != NULL) { SETROUTEMETRIC(&route, IPRIP_INFINITE); } EnqueueSendEntry( ig.IG_SendQueue, &route ); bTriggerUpdate = TRUE; } #if ROUTE_DBG else if (route.RR_RoutingProtocol == PROTO_IP_RIP) { TRACE0(ROUTE, "Ignoring route change caused by RIP summary route"); } #endif } } // // release the destination info // dwErr = RtmReleaseChangedDests( ig.IG_RtmHandle, ig.IG_RtmNotifHandle, 1, prdi ); if (dwErr != NO_ERROR) { TRACE1(ANY, "error %d releasing changed dests", dwErr); } } if (prdi) { RIP_FREE(prdi); } // // queue a triggered update now if necessary // if (bTriggerUpdate) { QueueRipWorker(WorkerFunctionStartTriggeredUpdate, NULL); } RELEASE_LIST_LOCK(ig.IG_SendQueue); RELEASE_GLOBAL_LOCK_SHARED(); RELEASE_IF_LOCK_SHARED(); TRACE0(LEAVE, "leaving WorkerFunctionProcessRtmMessage"); LEAVE_RIP_WORKER(); } //---------------------------------------------------------------------------- // Function: WorkerFunctionActivateInterface // // This function sends out the initial general request on an interface. //---------------------------------------------------------------------------- VOID WorkerFunctionActivateInterface( PVOID pContext ) { PIF_TABLE pTable; UPDATE_BUFFER ub; PIPRIP_ENTRY pEntry; PIF_TABLE_ENTRY pite; PIPRIP_IF_CONFIG pic; PIPRIP_IF_BINDING pib; PIPRIP_IP_ADDRESS paddr; SOCKADDR_IN sinDest; DWORD i, dwErr, dwIndex; LARGE_INTEGER qwCurrentTime; if (!ENTER_RIP_WORKER()) { return; } TRACE0(ENTER, "entering WorkerFunctionActivateInterface"); pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_SHARED(); do { // breakout loop // // retrieve the interface to be activated // dwIndex = PtrToUlong(pContext); pite = GetIfByIndex(pTable, dwIndex); if (pite == NULL) { TRACE1(IF, "activating interface: interface %d not found", dwIndex); break; } pic = pite->ITE_Config; pib = pite->ITE_Binding; // // If binding is NULL, assume that interface has been // deativated. This check has been introduced as a consequence // of WorkerFunctionDeactivateInterface being made synchronous. // As a result, by the time this function is invoked an interface // that was in the process of being activated could have been // deactivated. // // The change to synchronous deactivate was made // to accomadate demand dial interfaces that could get connected // and disconnected immeditately, causing the above behaviour // if ( pib == NULL ) { TRACE1( IF, "activating interface %d: Binding not found", dwIndex ); break; } paddr = IPRIP_IF_ADDRESS_TABLE(pib); // // request input notification on the interface's sockets // if (pic->IC_AcceptMode != IPRIP_ACCEPT_DISABLED) { for (i = 0; i < pib->IB_AddrCount; i++) { dwErr = WSAEventSelect( pite->ITE_Sockets[i], ig.IG_IpripInputEvent, FD_READ ); if (dwErr != NO_ERROR) { LPSTR lpszAddr = INET_NTOA(paddr[i].IA_Address); if (lpszAddr != NULL) { TRACE3( IF, "WSAEventSelect returned %d for interface %d (%s)", dwErr, dwIndex, lpszAddr ); LOGERR1(EVENTSELECT_FAILED, lpszAddr, 0); } } } } // // if interface is silent or interface does demand-udpates, // no initial request is sent on it // if (pic->IC_UpdateMode != IPRIP_UPDATE_PERIODIC || pic->IC_AnnounceMode == IPRIP_ANNOUNCE_DISABLED) { // // configured to be silent, do nothing // break; } // // send general request to neighboring routers // SendGeneralRequest(pite); // // create timer for periodic updates, if required. // EnterCriticalSection(&pTable->IT_CS); if (pite->ITE_FullOrDemandUpdateTimer == NULL) { if (!CreateTimerQueueTimer( &pite->ITE_FullOrDemandUpdateTimer, ig.IG_TimerQueueHandle, WorkerFunctionStartFullUpdate, pContext, RipSecsToMilliSecs(pic->IC_FullUpdateInterval), 10000000, 0 )) { dwErr = GetLastError(); TRACE1(IF, "error %d returned by CreateTimerQueueTimer", dwErr); break; } else { pite->ITE_Flags |= ITEFLAG_FULL_UPDATE_INQUEUE; } } else { RipQuerySystemTime(&qwCurrentTime); EnqueueStartFullUpdate(pite, qwCurrentTime); } LeaveCriticalSection(&pTable->IT_CS); } while(FALSE); RELEASE_IF_LOCK_SHARED(); TRACE0(LEAVE, "leaving WorkerFunctionActivateInterface"); LEAVE_RIP_WORKER(); } //---------------------------------------------------------------------------- // Function: WorkerFunctionDeactivateInterface // // This function generates shutdown update on the given interface, and // removes from RTM all RIP-learnt routes associated with the interface. // Assumes the interface table has already been exclusively locked //---------------------------------------------------------------------------- VOID WorkerFunctionDeactivateInterface( PVOID pContext ) { UPDATE_BUFFER ub; PIF_TABLE pTable; RIP_IP_ROUTE route; HANDLE hEnumerator; PHANDLE phRoutes = NULL; BOOL bTriggerUpdate; PIF_TABLE_ENTRY pite; PIPRIP_IF_CONFIG pic; PIPRIP_IF_BINDING pib; PIPRIP_IP_ADDRESS paddr; DWORD i, dwErr, dwFlags, dwIndex, dwHandles; TRACE0(ENTER, "entering WorkerFunctionDeactivateInterface"); dwIndex = PtrToUlong(pContext); bTriggerUpdate = FALSE; pTable = ig.IG_IfTable; do { // breakout loop // // find the interface to be deactivated // pite = GetIfByIndex(pTable, dwIndex); if (pite == NULL) { TRACE1( IF, "de-activating interface: interface %d not found", dwIndex ); break; } pib = pite->ITE_Binding; paddr = IPRIP_IF_ADDRESS_TABLE(pib); // // if graceful shutdown is on and demand-update is off, // send the graceful-shutdown update // if (pite->ITE_Config->IC_UpdateMode == IPRIP_UPDATE_PERIODIC && IPRIP_FLAG_IS_ENABLED(pite->ITE_Config, GRACEFUL_SHUTDOWN)) { // // transmit all RTM routes with non-infinite metrics set to 15 // if (pite->ITE_Config->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) { SendRoutes(&pite, 1, SENDMODE_SHUTDOWN_UPDATE, 0, 0); } } // // this function is called either because an interface // that was active (bound and enabled) is either no longer enabled // or is no longer bound. We complete the deactivation differently // depending on which of these is the case // if (!IF_IS_BOUND(pite) ) { // // the interface was bound, but isn't anymore. // close the socket for the interface // DeleteIfSocket(pite); ACQUIRE_BINDING_LOCK_EXCLUSIVE(); dwErr = DeleteBindingEntry(ig.IG_BindingTable, pite->ITE_Binding); RELEASE_BINDING_LOCK_EXCLUSIVE(); RIP_FREE(pite->ITE_Binding); pite->ITE_Binding = NULL; } else { // // the interface was enabled, but isn't anymore. // tell WinSock to stop notifying us of input // for (i = 0; i < pib->IB_AddrCount; i++) { WSAEventSelect(pite->ITE_Sockets[i], ig.IG_IpripInputEvent, 0); } } // // if full updates pending/queued on this interface, cancel them. // pite-> ITE_Flags &= ~ITEFLAG_FULL_UPDATE_PENDING; pite-> ITE_Flags &= ~ITEFLAG_FULL_UPDATE_INQUEUE; // // if we're announcing routes over this interface, // delete the periodic announcement timer // if (pite->ITE_Config->IC_UpdateMode == IPRIP_UPDATE_PERIODIC && pite->ITE_Config->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) { if (!DeleteTimerQueueTimer( ig.IG_TimerQueueHandle, pite->ITE_FullOrDemandUpdateTimer, NULL)) { TRACE1( ANY, "error %d deleting update timer", GetLastError() ); } pite->ITE_FullOrDemandUpdateTimer = NULL; } // // we're done if graceful shutdown is disabled // or if this is a demand-update interface // if (pite->ITE_Config->IC_UpdateMode != IPRIP_UPDATE_PERIODIC || IPRIP_FLAG_IS_DISABLED(pite->ITE_Config, GRACEFUL_SHUTDOWN)) { break; } // // move the routes learnt on this interface to the send-queue // and set their metrics to 16 // dwErr = RtmCreateRouteEnum( ig.IG_RtmHandle, NULL, RTM_VIEW_MASK_ANY, RTM_ENUM_OWN_ROUTES, NULL, RTM_MATCH_INTERFACE, NULL, pite->ITE_Index, &hEnumerator ); if (dwErr != NO_ERROR) { TRACE1( ANY, "WorkerFunctionDeactivateInterface: error %d creating" " enum handle", dwErr ); break; } // // allocate handle array large enough to hold max handles in an // enum // phRoutes = RIP_ALLOC(ig.IG_RtmProfile.MaxHandlesInEnum*sizeof(HANDLE)); if ( phRoutes == NULL ) { dwErr = GetLastError(); TRACE2( ANY, "WorkerFunctionDeactivateInterface: error %d " "while allocating %d bytes to hold max handles in an enum", dwErr, ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE) ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); break; } // // find all RIP routes learnt on this interface // ACQUIRE_GLOBAL_LOCK_SHARED(); ACQUIRE_LIST_LOCK(ig.IG_SendQueue); do { dwHandles = ig.IG_RtmProfile.MaxHandlesInEnum; dwErr = RtmGetEnumRoutes( ig.IG_RtmHandle, hEnumerator, &dwHandles, phRoutes ); for ( i = 0; i < dwHandles; i++ ) { if (GetRouteInfo( phRoutes[i], NULL, NULL, &route ) == NO_ERROR) { // // set the route's metric to infinite // SETROUTEMETRIC(&route, IPRIP_INFINITE); // // add the route to the send-queue // EnqueueSendEntry(ig.IG_SendQueue, &route); bTriggerUpdate = TRUE; } if (RtmDeleteRouteToDest( ig.IG_RtmHandle, phRoutes[i], &dwFlags ) != NO_ERROR) { // // If delete is successful, this is automatic // if (RtmReleaseRoutes( ig.IG_RtmHandle, 1, &phRoutes[i] ) != NO_ERROR) { TRACE1( ANY, "WorkerFunctionDeactivateInterface: " "error %d releasing route handles", dwErr ); } } } } while ( dwErr == NO_ERROR ); // // close the enm handle // dwErr = RtmDeleteEnumHandle(ig.IG_RtmHandle, hEnumerator); if (dwErr != NO_ERROR) { TRACE1( ANY, "WorkerFunctionDeactivateInterface: error %d " "closing enum handle", dwErr ); } RELEASE_LIST_LOCK(ig.IG_SendQueue); RELEASE_GLOBAL_LOCK_SHARED(); // // queue a triggered-update work-item for the other active interfaces // if (bTriggerUpdate) { dwErr = QueueRipWorker(WorkerFunctionStartTriggeredUpdate, NULL); if (dwErr != NO_ERROR) { TRACE1( IF, "error %d queueing triggered update work-item", dwErr ); LOGERR0(QUEUE_WORKER_FAILED, dwErr); } } } while(FALSE); if ( phRoutes ) { RIP_FREE(phRoutes); } TRACE0(LEAVE, "leaving WorkerFunctionDeactivateInterface"); } //---------------------------------------------------------------------------- // Function: FreeLibraryThread // // This thread is spawned by WorkerFunctionFinishStopProtocol to FreeLibrary // iprip2. This call to FreeLibrary should bring the ref on iprip2.dll to 0 and // thus unload it. The FreeLibraryAndExitThread cannot be called from a worker // thread, so this seperate thread is started to make the call. //---------------------------------------------------------------------------- DWORD FreeLibraryThread( PVOID pContext ) { // // Give time to the WorkerFunctionFinishStopProtocol function to complete // Sleep(10); if (ig.IG_DllHandle) { FreeLibraryAndExitThread(ig.IG_DllHandle, 0); } return 0; } //---------------------------------------------------------------------------- // Function: WorkerFunctionFinishStopProtocol // // This function is called when IPRIP is stopping; it sends out shutdown // updates on all interfaces and removes all RIP routes from RTM //---------------------------------------------------------------------------- VOID WorkerFunctionFinishStopProtocol( PVOID pContext ) { MESSAGE msg = {0, 0, 0}; LONG lThreadCount; PIF_TABLE pTable; PIPRIP_IF_CONFIG pic; PLIST_ENTRY ple, phead; DWORD dwErr, dwIfCount; PIF_TABLE_ENTRY pite, *ppite, *ppitend, *pIfList; HANDLE WaitHandle; TRACE0(ENTER, "entering WorkerFunctionFinishStopProtocol"); // // NOTE: since this is called while the router is stopping, // there is no need for it to use ENTER_RIP_WORKER()/LEAVE_RIP_WORKER() // lThreadCount = PtrToUlong(pContext); // // waits for input thread and timer thread to stop, // and also waits for API callers and worker functions to finish // while (lThreadCount-- > 0) { WaitForSingleObject(ig.IG_ActivitySemaphore, INFINITE); } // // deregister the events set with NtdllWait thread and delete the // timer queue registered with NtdllTimer thread. // These calls should not be inside IG_CS lock and must be done // after all the threads have stopped. // WaitHandle = InterlockedExchangePointer(&ig.IG_IpripInputEventHandle, NULL) ; if (WaitHandle) { UnregisterWaitEx( WaitHandle, INVALID_HANDLE_VALUE ) ; } if (ig.IG_TimerQueueHandle) { DeleteTimerQueueEx(ig.IG_TimerQueueHandle, INVALID_HANDLE_VALUE); } // // we enter the critical section and leave, just to be sure that // all threads have quit their calls to LeaveRipWorker() // EnterCriticalSection(&ig.IG_CS); LeaveCriticalSection(&ig.IG_CS); TRACE0(STOP, "all threads stopped, now performing graceful shutdown"); pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_EXCLUSIVE(); // // send out graceful shutdown updates on all active interfaces // do { phead = &pTable->IT_ListByAddress; // // first count the interfaces on which graceful shutdown is enabled // dwIfCount = 0; for (ple = phead->Flink; ple != phead; ple = ple->Flink) { pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); pic = pite->ITE_Config; if (IF_IS_ACTIVE(pite) && pite->ITE_Binding && pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC && pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED && IPRIP_FLAG_IS_ENABLED(pic, GRACEFUL_SHUTDOWN)) { ++dwIfCount; } } if (dwIfCount == 0) { break; } // // allocate space for the interface pointers // pIfList = RIP_ALLOC(dwIfCount * sizeof(PIF_TABLE_ENTRY)); if (pIfList == NULL) { dwErr = GetLastError(); TRACE2( STOP, "shutdown: error %d allocating %d bytes for interfaces", dwErr, dwIfCount * sizeof(PIF_TABLE_ENTRY) ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); break; } // // copy the interface pointers into the space allocated // ppitend = pIfList + dwIfCount; for (ple = phead->Flink, ppite = pIfList; ple != phead && ppite < ppitend; ple = ple->Flink) { pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); pic = pite->ITE_Config; if (IF_IS_ACTIVE(pite) && pite->ITE_Binding && pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC && pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED && IPRIP_FLAG_IS_ENABLED(pic, GRACEFUL_SHUTDOWN)) { *ppite++ = pite; } } // // pass the array of interfaces to SendRoutes // TRACE1(STOP, "sending shutdown updates on %d interfaces", dwIfCount); SendRoutes(pIfList, dwIfCount, SENDMODE_SHUTDOWN_UPDATE, 0, 0); // // free the array of interfaces // RIP_FREE(pIfList); } while(FALSE); RELEASE_IF_LOCK_EXCLUSIVE(); // // delete all IPRIP routes from RTM // for (ple = phead->Flink; ple != phead; ple = ple->Flink) { pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); BlockDeleteRoutesOnInterface( ig.IG_RtmHandle, pite-> ITE_Index ); } // // cleanup the global structures // TRACE0(STOP, "IPRIP is cleaning up resources"); ProtocolCleanup(TRUE); LOGINFO0(IPRIP_STOPPED, NO_ERROR); // // let the Router Manager know that we are done // ACQUIRE_LIST_LOCK(ig.IG_EventQueue); EnqueueEvent(ig.IG_EventQueue, ROUTER_STOPPED, msg); SetEvent(ig.IG_EventEvent); RELEASE_LIST_LOCK(ig.IG_EventQueue); if (ig.IG_DllHandle) { HANDLE hThread; hThread = CreateThread(0,0,FreeLibraryThread, NULL, 0, NULL); if (hThread != NULL) CloseHandle(hThread); } return; } VOID PrintGlobalStats( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ); VOID PrintGlobalConfig( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ); VOID PrintIfStats( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ); VOID PrintIfConfig( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ); VOID PrintIfBinding( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ); VOID PrintPeerStats( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ); #define ClearScreen(h) { \ DWORD _dwin,_dwout; \ COORD _c = {0, 0}; \ CONSOLE_SCREEN_BUFFER_INFO _csbi; \ GetConsoleScreenBufferInfo(h,&_csbi); \ _dwin = _csbi.dwSize.X * _csbi.dwSize.Y; \ FillConsoleOutputCharacter(h,' ',_dwin,_c,&_dwout); \ } VOID WorkerFunctionMibDisplay( PVOID pContext, BOOLEAN bNotUsed ) { COORD c; HANDLE hConsole = NULL; DWORD dwErr, dwTraceID; DWORD dwExactSize, dwInSize, dwOutSize; IPRIP_MIB_GET_INPUT_DATA imgid; PIPRIP_MIB_GET_OUTPUT_DATA pimgod; LARGE_INTEGER qwNextDisplay, qwCurrentTime; if (!ENTER_RIP_API()) { return; } TraceGetConsole(ig.IG_MibTraceID, &hConsole); if (hConsole == NULL) { LEAVE_RIP_WORKER(); return; } ClearScreen(hConsole); c.X = c.Y = 0; dwInSize = sizeof(imgid); imgid.IMGID_TypeID = IPRIP_GLOBAL_STATS_ID; pimgod = NULL; // // get size of the first entry in the first table // dwErr = MibGetFirst(dwInSize, &imgid, &dwOutSize, pimgod); if (dwErr == ERROR_INSUFFICIENT_BUFFER) { // // allocate a buffer, and set its size // pimgod = RIP_ALLOC(dwOutSize); // // perform the query again // dwErr = MibGetFirst(dwInSize, &imgid, &dwOutSize, pimgod); } // // now that we have the first element in the first table, // we can enumerate the elements in the remaining tables using GetNext // while (dwErr == NO_ERROR) { // // print the current element and set up the query // for the next element (the display functions change imgid // so that it can be used to query the next element) // switch(pimgod->IMGOD_TypeID) { case IPRIP_GLOBAL_STATS_ID: PrintGlobalStats(hConsole, &c, &imgid, pimgod); break; case IPRIP_GLOBAL_CONFIG_ID: PrintGlobalConfig(hConsole,&c, &imgid, pimgod); break; case IPRIP_IF_CONFIG_ID: PrintIfConfig(hConsole, &c, &imgid, pimgod); break; case IPRIP_IF_BINDING_ID: PrintIfBinding(hConsole, &c, &imgid, pimgod); break; case IPRIP_IF_STATS_ID: PrintIfStats(hConsole, &c, &imgid, pimgod); break; case IPRIP_PEER_STATS_ID: PrintPeerStats(hConsole, &c, &imgid, pimgod); break; default: break; } RIP_FREE(pimgod); pimgod = NULL; dwOutSize = 0; // // move to the next line on the console // ++c.Y; // // query the next MIB element // dwErr = MibGetNext(dwInSize, &imgid, &dwOutSize, pimgod); if (dwErr == ERROR_INSUFFICIENT_BUFFER) { // // allocate a new buffer, and set its size // pimgod = RIP_ALLOC(dwOutSize); // // perform the query again // dwErr = MibGetNext(dwInSize, &imgid, &dwOutSize, pimgod); } } // // if memory was allocated, free it now // if (pimgod != NULL) { RIP_FREE(pimgod); } LEAVE_RIP_API(); } #define WriteLine(h,c,fmt,arg) { \ DWORD _dw; \ CHAR _sz[200]; \ _dw = StringCchPrintf(_sz, 200, fmt, arg); \ if ( SUCCEEDED(_dw) ) { \ WriteConsoleOutputCharacter(h,_sz,lstrlen(_sz),c,&_dw); \ ++(c).Y; \ } \ } VOID PrintGlobalStats( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ) { PIPRIP_GLOBAL_STATS pgs; pgs = (PIPRIP_GLOBAL_STATS)pimgod->IMGOD_Buffer; WriteLine( hConsole, *pc, "System Route Changes: %d", pgs->GS_SystemRouteChanges ); WriteLine( hConsole, *pc, "Total Responses Sent: %d", pgs->GS_TotalResponsesSent ); pimgid->IMGID_TypeID = IPRIP_GLOBAL_STATS_ID; } VOID PrintGlobalConfig( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ) { PIPRIP_GLOBAL_CONFIG pgc; PDWORD pdwPeer, pdwPeerEnd; CHAR szFilter[32]; LPSTR lpszAddr = NULL; pgc = (PIPRIP_GLOBAL_CONFIG)pimgod->IMGOD_Buffer; switch (pgc->GC_PeerFilterMode) { case IPRIP_FILTER_DISABLED: lstrcpy(szFilter, "disabled"); break; case IPRIP_FILTER_INCLUDE: lstrcpy(szFilter, "include all"); break; case IPRIP_FILTER_EXCLUDE: lstrcpy(szFilter, "exclude all"); break; default: lstrcpy(szFilter, "invalid"); break; } WriteLine( hConsole, *pc, "Logging Level: %d", pgc->GC_LoggingLevel ); WriteLine( hConsole, *pc, "Max Receive Queue Size: %d bytes", pgc->GC_MaxRecvQueueSize ); WriteLine( hConsole, *pc, "Max Send Queue Size: %d bytes", pgc->GC_MaxSendQueueSize ); WriteLine( hConsole, *pc, "Min Triggered Update interval: %d seconds", pgc->GC_MinTriggeredUpdateInterval ); WriteLine( hConsole, *pc, "Peer Filter Mode: %s", szFilter ); WriteLine( hConsole, *pc, "Peer Filter Count: %d", pgc->GC_PeerFilterCount ); pdwPeer = IPRIP_GLOBAL_PEER_FILTER_TABLE(pgc); pdwPeerEnd = pdwPeer + pgc->GC_PeerFilterCount; for ( ; pdwPeer < pdwPeerEnd; pdwPeer++) { lpszAddr = INET_NTOA(*pdwPeer); if (lpszAddr != NULL) { WriteLine( hConsole, *pc, " %s", lpszAddr ); } } pimgid->IMGID_TypeID = IPRIP_GLOBAL_CONFIG_ID; } VOID PrintIfStats( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ) { PIPRIP_IF_STATS pis; pis = (PIPRIP_IF_STATS)pimgod->IMGOD_Buffer; WriteLine( hConsole, *pc, "Interface Index: %d", pimgod->IMGOD_IfIndex ); WriteLine( hConsole, *pc, "Send Failures: %d", pis->IS_SendFailures ); WriteLine( hConsole, *pc, "Receive Failures: %d", pis->IS_ReceiveFailures ); WriteLine( hConsole, *pc, "Requests Sent: %d", pis->IS_RequestsSent ); WriteLine( hConsole, *pc, "Requests Received: %d", pis->IS_RequestsReceived ); WriteLine( hConsole, *pc, "Responses Sent: %d", pis->IS_ResponsesSent ); WriteLine( hConsole, *pc, "Responses Received: %d", pis->IS_ResponsesReceived ); WriteLine( hConsole, *pc, "Bad Response Packets Received: %d", pis->IS_BadResponsePacketsReceived ); WriteLine( hConsole, *pc, "Bad Response Entries Received: %d", pis->IS_BadResponseEntriesReceived ); WriteLine( hConsole, *pc, "Triggered Updates Sent: %d", pis->IS_TriggeredUpdatesSent ); pimgid->IMGID_TypeID = IPRIP_IF_STATS_ID; pimgid->IMGID_IfIndex = pimgod->IMGOD_IfIndex; } VOID PrintIfConfig( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ) { PIPRIP_IF_CONFIG pic; PDWORD pdwPeer, pdwPeerEnd; PIPRIP_ROUTE_FILTER pfilt, pfiltend; CHAR szAuthType[24], szAuthKey[64]; CHAR szPeer[20], szAccept[20], szAnnounce[20], szFilter[64]; CHAR szUpdateMode[24], szAcceptMode[24], szAnnounceMode[24]; LPSTR lpszAddr = NULL; pic = (PIPRIP_IF_CONFIG)pimgod->IMGOD_Buffer; switch (pic->IC_UpdateMode) { case IPRIP_UPDATE_PERIODIC: lstrcpy(szUpdateMode, "periodic"); break; case IPRIP_UPDATE_DEMAND: lstrcpy(szUpdateMode, "demand"); break; default: lstrcpy(szUpdateMode, "invalid"); break; } switch (pic->IC_AcceptMode) { case IPRIP_ACCEPT_DISABLED: lstrcpy(szAcceptMode, "disabled"); break; case IPRIP_ACCEPT_RIP1: lstrcpy(szAcceptMode, "RIP1"); break; case IPRIP_ACCEPT_RIP1_COMPAT: lstrcpy(szAcceptMode, "RIP1 compatible"); break; case IPRIP_ACCEPT_RIP2: lstrcpy(szAcceptMode, "RIP2"); break; default: lstrcpy(szAcceptMode, "invalid"); break; } switch(pic->IC_AnnounceMode) { case IPRIP_ANNOUNCE_DISABLED: lstrcpy(szAnnounceMode, "disabled"); break; case IPRIP_ANNOUNCE_RIP1: lstrcpy(szAnnounceMode, "RIP1"); break; case IPRIP_ANNOUNCE_RIP1_COMPAT: lstrcpy(szAnnounceMode, "RIP1 compatible"); break; case IPRIP_ANNOUNCE_RIP2: lstrcpy(szAnnounceMode, "RIP2"); break; default: lstrcpy(szAnnounceMode, "invalid"); break; } switch (pic->IC_AuthenticationType) { case IPRIP_AUTHTYPE_NONE: lstrcpy(szAuthType, "none"); break; case IPRIP_AUTHTYPE_SIMPLE_PASSWORD: lstrcpy(szAuthType, "simple password"); break; case IPRIP_AUTHTYPE_MD5: lstrcpy(szAuthType, "MD5"); break; default: lstrcpy(szAuthType, "invalid"); break; } { PSTR psz; CHAR szDigits[] = "0123456789ABCDEF"; PBYTE pb, pbend; psz = szAuthKey; pbend = pic->IC_AuthenticationKey + IPRIP_MAX_AUTHKEY_SIZE; for (pb = pic->IC_AuthenticationKey; pb < pbend; pb++) { *psz++ = szDigits[*pb / 16]; *psz++ = szDigits[*pb % 16]; *psz++ = '-'; } *(--psz) = '\0'; } switch (pic->IC_UnicastPeerMode) { case IPRIP_PEER_DISABLED: lstrcpy(szPeer, "disabled"); break; case IPRIP_PEER_ALSO: lstrcpy(szPeer, "also"); break; case IPRIP_PEER_ONLY: lstrcpy(szPeer, "only"); break; default: lstrcpy(szPeer, "invalid"); break; } switch (pic->IC_AcceptFilterMode) { case IPRIP_FILTER_DISABLED: lstrcpy(szAccept, "disabled"); break; case IPRIP_FILTER_INCLUDE: lstrcpy(szAccept, "include all"); break; case IPRIP_FILTER_EXCLUDE: lstrcpy(szAccept, "exclude all"); break; default: lstrcpy(szAccept, "invalid"); break; } switch (pic->IC_AnnounceFilterMode) { case IPRIP_FILTER_DISABLED: lstrcpy(szAnnounce, "disabled"); break; case IPRIP_FILTER_INCLUDE: lstrcpy(szAnnounce, "include all"); break; case IPRIP_FILTER_EXCLUDE: lstrcpy(szAnnounce, "exclude all"); break; default: lstrcpy(szAnnounce, "invalid"); break; } WriteLine( hConsole, *pc, "Interface Index: %d", pimgod->IMGOD_IfIndex ); WriteLine( hConsole, *pc, "Metric: %d", pic->IC_Metric ); WriteLine( hConsole, *pc, "Update Mode: %s", szUpdateMode ); WriteLine( hConsole, *pc, "Accept Mode: %s", szAcceptMode ); WriteLine( hConsole, *pc, "Announce Mode: %s", szAnnounceMode ); WriteLine( hConsole, *pc, "Accept Host Routes: %s", (IPRIP_FLAG_IS_ENABLED(pic, ACCEPT_HOST_ROUTES) ? "enabled" : "disabled") ); WriteLine( hConsole, *pc, "Announce Host Routes: %s", (IPRIP_FLAG_IS_ENABLED(pic, ANNOUNCE_HOST_ROUTES) ? "enabled" : "disabled") ); WriteLine( hConsole, *pc, "Accept Default Routes: %s", (IPRIP_FLAG_IS_ENABLED(pic, ACCEPT_DEFAULT_ROUTES) ? "enabled" : "disabled") ); WriteLine( hConsole, *pc, "Announce Default Routes: %s", (IPRIP_FLAG_IS_ENABLED(pic, ANNOUNCE_DEFAULT_ROUTES) ? "enabled" : "disabled") ); WriteLine( hConsole, *pc, "Split Horizon: %s", (IPRIP_FLAG_IS_ENABLED(pic, SPLIT_HORIZON) ? "enabled" : "disabled") ); WriteLine( hConsole, *pc, "Poison Reverse: %s", (IPRIP_FLAG_IS_ENABLED(pic, POISON_REVERSE) ? "enabled" : "disabled") ); WriteLine( hConsole, *pc, "Graceful Shutdown: %s", (IPRIP_FLAG_IS_ENABLED(pic, GRACEFUL_SHUTDOWN) ? "enabled" : "disabled") ); WriteLine( hConsole, *pc, "Triggered Updates: %s", (IPRIP_FLAG_IS_ENABLED(pic, TRIGGERED_UPDATES) ? "enabled" : "disabled") ); WriteLine( hConsole, *pc, "Overwrite Static Routes: %s", (IPRIP_FLAG_IS_ENABLED(pic, OVERWRITE_STATIC_ROUTES) ? "enabled" : "disabled") ); WriteLine( hConsole, *pc, "Route Expiration Interval: %d seconds", pic->IC_RouteExpirationInterval ); WriteLine( hConsole, *pc, "Route Removal Interval: %d seconds", pic->IC_RouteRemovalInterval ); WriteLine( hConsole, *pc, "Full Update Interval: %d seconds", pic->IC_FullUpdateInterval ); WriteLine( hConsole, *pc, "Authentication Type: %s", szAuthType ); WriteLine( hConsole, *pc, "Authentication Key: %s", szAuthKey ); WriteLine( hConsole, *pc, "Route Tag: %d", pic->IC_RouteTag ); WriteLine( hConsole, *pc, "Unicast Peer Mode: %s", szPeer ); WriteLine( hConsole, *pc, "Accept Filter Mode: %s", szAccept ); WriteLine( hConsole, *pc, "Announce Filter Mode: %s", szAnnounce ); WriteLine( hConsole, *pc, "Unicast Peer Count: %d", pic->IC_UnicastPeerCount ); pdwPeer = IPRIP_IF_UNICAST_PEER_TABLE(pic); pdwPeerEnd = pdwPeer + pic->IC_UnicastPeerCount; for ( ; pdwPeer < pdwPeerEnd; pdwPeer++) { lpszAddr = INET_NTOA(*pdwPeer); if (lpszAddr != NULL) { WriteLine( hConsole, *pc, " %s", lpszAddr ); } } WriteLine( hConsole, *pc, "Accept Filter Count: %d", pic->IC_AcceptFilterCount ); pfilt = IPRIP_IF_ACCEPT_FILTER_TABLE(pic); pfiltend = pfilt + pic->IC_AcceptFilterCount; for ( ; pfilt < pfiltend; pfilt++) { lpszAddr = INET_NTOA(pfilt->RF_LoAddress); if (lpszAddr != NULL) { lstrcpy(szFilter, lpszAddr); strcat(szFilter, " - "); lpszAddr = INET_NTOA(pfilt->RF_HiAddress); if (lpszAddr != NULL) { strcat(szFilter, INET_NTOA(pfilt->RF_HiAddress)); WriteLine( hConsole, *pc, " %s", szFilter ); } } } WriteLine( hConsole, *pc, "Announce Filter Count: %d", pic->IC_AnnounceFilterCount ); pfilt = IPRIP_IF_ANNOUNCE_FILTER_TABLE(pic); pfiltend = pfilt + pic->IC_AnnounceFilterCount; for ( ; pfilt < pfiltend; pfilt++) { lpszAddr = INET_NTOA(pfilt->RF_LoAddress); if (lpszAddr != NULL) { lstrcpy(szFilter, lpszAddr); strcat(szFilter, " - "); lpszAddr = INET_NTOA(pfilt->RF_HiAddress); if (lpszAddr != NULL) { strcat(szFilter, INET_NTOA(pfilt->RF_HiAddress)); WriteLine( hConsole, *pc, " %s", szFilter ); } } } pimgid->IMGID_TypeID = IPRIP_IF_CONFIG_ID; pimgid->IMGID_IfIndex = pimgod->IMGOD_IfIndex; } VOID PrintIfBinding( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ) { DWORD i; CHAR szAddr[64]; PIPRIP_IF_BINDING pib; PIPRIP_IP_ADDRESS paddr; LPSTR lpszAddr = NULL; pib = (PIPRIP_IF_BINDING) pimgod->IMGOD_Buffer; paddr = IPRIP_IF_ADDRESS_TABLE(pib); WriteLine( hConsole, *pc, "Interface Index: %d", pimgod->IMGOD_IfIndex ); WriteLine( hConsole, *pc, "Address Count: %d", pib->IB_AddrCount ); for (i = 0; i < pib->IB_AddrCount; i++, paddr++) { lpszAddr = INET_NTOA(paddr->IA_Address); if (lpszAddr != NULL) { lstrcpy(szAddr, lpszAddr); lstrcat(szAddr, " - "); lpszAddr = INET_NTOA(paddr->IA_Netmask); if (lpszAddr != NULL) { lstrcat(szAddr, lpszAddr); WriteLine( hConsole, *pc, "Address Entry: %s", szAddr ); } } } pimgid->IMGID_TypeID = IPRIP_IF_BINDING_ID; pimgid->IMGID_IfIndex = pimgod->IMGOD_IfIndex; } VOID PrintPeerStats( HANDLE hConsole, PCOORD pc, PIPRIP_MIB_GET_INPUT_DATA pimgid, PIPRIP_MIB_GET_OUTPUT_DATA pimgod ) { PIPRIP_PEER_STATS pps; LPSTR lpszAddr = INET_NTOA(pimgod->IMGOD_PeerAddress); pps = (PIPRIP_PEER_STATS)pimgod->IMGOD_Buffer; if (lpszAddr != NULL) { WriteLine( hConsole, *pc, "Peer Address: %s", lpszAddr ); } else { WriteLine( hConsole, *pc, "Peer Address: Failed inet_ntoa conv %s", "" ); } WriteLine( hConsole, *pc, "Last Peer Route Tag: %d", pps->PS_LastPeerRouteTag ); WriteLine( hConsole, *pc, "Last Peer Update Tick-Count %d ticks", pps->PS_LastPeerUpdateTickCount ); WriteLine( hConsole, *pc, "Bad Response Packets From Peer: %d", pps->PS_BadResponsePacketsFromPeer ); WriteLine( hConsole, *pc, "Bad Response Entries From Peer: %d", pps->PS_BadResponseEntriesFromPeer ); pimgid->IMGID_TypeID = IPRIP_PEER_STATS_ID; pimgid->IMGID_PeerAddress = pimgod->IMGOD_PeerAddress; } //---------------------------------------------------------------------------- // Function: CallbackFunctionNetworkEvents // // This function queues a worker function to process the input packets. // It registers a ntdll wait event at the end so that only one thread can // be processing the input packets. //---------------------------------------------------------------------------- VOID CallbackFunctionNetworkEvents ( PVOID pContext, BOOLEAN NotUsed ) { HANDLE WaitHandle; // // enter/leaveRipApi should be called to make sure that rip dll is around // if (!ENTER_RIP_API()) { return; } // // set the pointer to NULL, so that Unregister wont be called // WaitHandle = InterlockedExchangePointer(&ig.IG_IpripInputEventHandle, NULL); if (WaitHandle) UnregisterWaitEx( WaitHandle, NULL ) ; QueueRipWorker(WorkerFunctionNetworkEvents,pContext); LEAVE_RIP_API(); } //---------------------------------------------------------------------------- // Function: ProcessNetworkEvents // // This function enumerates the input events on each interface // and processes any incoming input packets //---------------------------------------------------------------------------- VOID WorkerFunctionNetworkEvents ( PVOID pContext ) { DWORD i, dwErr; PIF_TABLE pTable; PIPRIP_IF_CONFIG pic; PIPRIP_IF_BINDING pib; PIF_TABLE_ENTRY pite; PLIST_ENTRY ple, phead; WSANETWORKEVENTS wsane; PIPRIP_IP_ADDRESS paddr; LPSTR lpszAddr = NULL; if (!ENTER_RIP_WORKER()) { return; } pTable = ig.IG_IfTable; ACQUIRE_IF_LOCK_SHARED(); // // go through the list of active interfaces // processing sockets which are read-ready // phead = &pTable->IT_ListByAddress; for (ple = phead->Flink; ple != phead; ple = ple->Flink) { pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress); pic = pite->ITE_Config; if (pic->IC_AcceptMode == IPRIP_ACCEPT_DISABLED) { continue; } pib = pite->ITE_Binding; paddr = IPRIP_IF_ADDRESS_TABLE(pib); for (i = 0; i < pib->IB_AddrCount; i++, paddr++) { if (pite->ITE_Sockets[i] == INVALID_SOCKET) { continue; } // // enumerate network events to see whether // any packets have arrived on this interface // dwErr = WSAEnumNetworkEvents(pite->ITE_Sockets[i], NULL, &wsane); if (dwErr != NO_ERROR) { lpszAddr = INET_NTOA(paddr->IA_Address); if (lpszAddr != NULL) { TRACE3( RECEIVE, "error %d checking for input on interface %d (%s)", dwErr, pite->ITE_Index, lpszAddr ); LOGWARN1(ENUM_NETWORK_EVENTS_FAILED, lpszAddr, dwErr); } continue; } // // see if the input bit is set // if (!(wsane.lNetworkEvents & FD_READ)) { continue; } // // the input flag is set, now see if there was an error // if (wsane.iErrorCode[FD_READ_BIT] != NO_ERROR) { lpszAddr = INET_NTOA(paddr->IA_Address); if (lpszAddr != NULL) { TRACE3( RECEIVE, "error %d in input record for interface %d (%s)", wsane.iErrorCode[FD_READ_BIT], pite->ITE_Index, lpszAddr ); LOGWARN1(INPUT_RECORD_ERROR, lpszAddr, dwErr); } continue; } // // there is no error, so process the socket // ProcessSocket(i, pite, pTable); } } RELEASE_IF_LOCK_SHARED(); // // if dll is not stopping, register input event with NtdllWait thread again // if (ig.IG_Status != IPRIP_STATUS_STOPPING) { if (! RegisterWaitForSingleObject( &ig.IG_IpripInputEventHandle, ig.IG_IpripInputEvent, CallbackFunctionNetworkEvents, NULL, INFINITE, (WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE) )) { dwErr = GetLastError(); TRACE1(START, "error %d registering input event with NtdllWait thread", dwErr); LOGERR0(REGISTER_WAIT_FAILED, dwErr); } } LEAVE_RIP_WORKER(); } //---------------------------------------------------------------------------- // Function: ProcessSocket // // This function receives the message on the given socket and queues it // for processing if the configuration on the receiving interface allows it. //---------------------------------------------------------------------------- VOID ProcessSocket( DWORD dwAddrIndex, PIF_TABLE_ENTRY pite, PIF_TABLE pTable ) { SOCKET sock; PPEER_TABLE pPeers; IPRIP_PACKET pkt; PBYTE pInputPacket; CHAR szSrcAddr[20]; CHAR szLocalAddr[20]; LPSTR lpszTempAddr = NULL; PIPRIP_HEADER pih; PINPUT_CONTEXT pwc; PIPRIP_IF_STATS pis; PIPRIP_IF_CONFIG pic; PIPRIP_IF_BINDING pib; PIPRIP_IP_ADDRESS paddr; PIPRIP_PEER_STATS pps; PPEER_TABLE_ENTRY ppte; DWORD dwErr, dwSrcaddr; SOCKADDR_IN sinInputSource; INT i, iInputLength, iAddrLength; DWORD dwPacketsEnqueued = 0, dwWorkItemsEnqueued = 0, dwRetries = 0; pis = &pite->ITE_Stats; pic = pite->ITE_Config; sock = pite->ITE_Sockets[dwAddrIndex]; pib = pite->ITE_Binding; paddr = IPRIP_IF_ADDRESS_TABLE(pib) + dwAddrIndex; pPeers = ig.IG_PeerTable; iAddrLength = sizeof(SOCKADDR_IN); do { pwc = NULL; pInputPacket = pkt.IP_Packet; // // read the incoming packet // iInputLength = recvfrom( sock, pInputPacket, MAX_PACKET_SIZE, 0, (PSOCKADDR)&sinInputSource, &iAddrLength ); if (iInputLength == 0 || iInputLength == SOCKET_ERROR) { dwErr = WSAGetLastError(); // // If there is not more data to be received we should // break out of the loop as there is no more data to be read. // This should not increment IS_ReceiveFailures // // All other errors should be looged, and IS_ReceiveFailures // should be incremented. Then break out of the loop // if ( iInputLength == SOCKET_ERROR && dwErr == WSAEWOULDBLOCK ) { // // Allow time for more packets to come in // Sleep(0); if ( dwRetries < 3 ) { dwRetries++; continue; } } else { lpszTempAddr = INET_NTOA(paddr->IA_Address); if ( lpszTempAddr ) { lstrcpyn(szLocalAddr, lpszTempAddr, sizeof(szLocalAddr)); } else { ZeroMemory(szLocalAddr, sizeof(szLocalAddr)); } lpszTempAddr = INET_NTOA(sinInputSource.sin_addr.s_addr); if ( lpszTempAddr ) { lstrcpyn(szSrcAddr, lpszTempAddr, sizeof(szSrcAddr)); } else { ZeroMemory(szSrcAddr, sizeof(szSrcAddr)); } if ( dwErr != WSAECONNRESET ) { TRACE3( RECEIVE, "error %d receiving packet on interface %d (%s)", dwErr, pite->ITE_Index, szLocalAddr ); LOGERR1(RECVFROM_FAILED, szLocalAddr, dwErr); InterlockedIncrement(&pis->IS_ReceiveFailures); } else { TRACE3( RECEIVE, "A previous RIP message sent to peer %s from " "interface %d (%s) generated an ICMP Port " "Unreachable error", szSrcAddr, pite->ITE_Index, szLocalAddr ); LOGWARN2(PREVIOUS_SENDTO_FAILED, szSrcAddr, szLocalAddr, dwErr); } } break; } // // After successfully receiving a packet, reset the dwRetries to 0 // dwRetries = 0; dwSrcaddr = sinInputSource.sin_addr.s_addr; // // Set the local and remote address strings // lpszTempAddr = INET_NTOA(paddr->IA_Address); if ( lpszTempAddr ) { lstrcpyn(szLocalAddr, lpszTempAddr, sizeof(szLocalAddr)); } else { ZeroMemory(szLocalAddr, sizeof(szLocalAddr)); } lpszTempAddr = INET_NTOA(dwSrcaddr); if ( lpszTempAddr ) { lstrcpyn(szSrcAddr, lpszTempAddr, sizeof(szSrcAddr)); } else { ZeroMemory(szSrcAddr, sizeof(szSrcAddr)); } // // ignore the packet if it is from a local address // if (GetIfByAddress(pTable, dwSrcaddr, GETMODE_EXACT, NULL) != NULL) { continue; } #if DBG TRACE4( RECEIVE, "received %d-byte packet from %s on interface %d (%s)", iInputLength, szSrcAddr, pite->ITE_Index, szLocalAddr ); #endif // // the packet must contain at least one entry // if (iInputLength < MIN_PACKET_SIZE) { TRACE4( RECEIVE, "%d-byte packet from %s on interface %d (%s) is too small", iInputLength, szSrcAddr, pite->ITE_Index, szLocalAddr ); LOGWARN2(PACKET_TOO_SMALL, szLocalAddr, szSrcAddr, NO_ERROR); continue; } // // find out which peer sent this, or create a new peer // ACQUIRE_PEER_LOCK_EXCLUSIVE(); dwErr = CreatePeerEntry(pPeers, dwSrcaddr, &ppte); if (dwErr == NO_ERROR) { pps = &ppte->PTE_Stats; } else { pps = NULL; // // not a serious error, so go on // TRACE2( RECEIVE, "error %d creating peer statistics entry for %s", dwErr, szSrcAddr ); } RELEASE_PEER_LOCK_EXCLUSIVE(); ACQUIRE_PEER_LOCK_SHARED(); // // place a template over the packet // pih = (PIPRIP_HEADER)pInputPacket; // // update the peer statistics // if (pps != NULL) { InterlockedExchange( &pps->PS_LastPeerUpdateTickCount, GetTickCount() ); InterlockedExchange( &pps->PS_LastPeerUpdateVersion, (DWORD)pih->IH_Version ); } // // discard if the version is invalid, or if the packet is // a RIPv1 packet and the reserved field in the header is non-zero // if (pih->IH_Version == 0) { TRACE3( RECEIVE, "invalid version packet from %s on interface %d (%s)", szSrcAddr, pite->ITE_Index, szLocalAddr ); LOGWARNDATA2( PACKET_VERSION_INVALID, szLocalAddr, szSrcAddr, iInputLength, pInputPacket ); if (pps != NULL) { InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer); } RELEASE_PEER_LOCK_SHARED(); continue; } else if (pih->IH_Version == 1 && pih->IH_Reserved != 0) { TRACE3( RECEIVE, "invalid packet header from %s on interface %d (%s)", szSrcAddr, pite->ITE_Index, szLocalAddr ); LOGWARNDATA2( PACKET_HEADER_CORRUPT, szLocalAddr, szSrcAddr, iInputLength, pInputPacket ); if (pps != NULL) { InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer); } RELEASE_PEER_LOCK_SHARED(); continue; } RELEASE_PEER_LOCK_SHARED(); // // make sure command field is valid, and // update statistics on received packets // Discard the packet if command field is // invalid // if (pih->IH_Command == IPRIP_REQUEST) { InterlockedIncrement(&pis->IS_RequestsReceived); } else if (pih->IH_Command == IPRIP_RESPONSE) { InterlockedIncrement(&pis->IS_ResponsesReceived); } else { continue; } // // allocate and initialize a work-context to be queued // and update the receive queue size // pwc = RIP_ALLOC(sizeof(INPUT_CONTEXT)); if (pwc == NULL) { TRACE4( RECEIVE, "error %d allocating %d bytes for packet on interface %d (%s)", GetLastError(), sizeof(INPUT_CONTEXT), pite->ITE_Index, szLocalAddr ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); // // If we weren't able to allocate memory, we might as well // break out of the loop, instead of receiving more packets // and then again running into out of memory condition. // Hope that by the time ProcessSocket is called next, some // resources will be available // break; } pwc->IC_InterfaceIndex = pite->ITE_Index; pwc->IC_AddrIndex = dwAddrIndex; pwc->IC_InputSource = sinInputSource; pwc->IC_InputLength = iInputLength; pwc->IC_InputPacket = pkt; // // enqueue the packet and source-address as a recv-queue entry // ACQUIRE_GLOBAL_LOCK_SHARED(); ACQUIRE_LIST_LOCK(ig.IG_RecvQueue); dwErr = EnqueueRecvEntry( ig.IG_RecvQueue, pih->IH_Command, (PBYTE)pwc ); RELEASE_LIST_LOCK(ig.IG_RecvQueue); RELEASE_GLOBAL_LOCK_SHARED(); if (dwErr != NO_ERROR) { TRACE4( RECEIVE, "error %d queueing data for packet from %s on interface %d (%s)", dwErr, szSrcAddr, pite->ITE_Index, szLocalAddr ); // // If we weren't able to enqueue the recv entry, we might as well // break out of the loop, instead of receiving more packets. // break; } dwPacketsEnqueued++; // // enqueue the work-item to process the packet // We enqueue a new workitem only if the number of currently enqueued // workitems goes below the number of processors. This is to avoid // enqueueing one workitem for each packet and thus having a large amount // of queued workitems. These large number of queued workitems can // block other workitems, like the ones that are supposed to receive // RIP packets. // if ( ig.IG_NumProcessInputWorkItems < (LONG)ig.IG_MaxProcessInputWorkItems ) { dwErr = QueueRipWorker(WorkerFunctionProcessInput, NULL); if (dwErr != NO_ERROR) { PLIST_ENTRY phead; TRACE4( RECEIVE, "error %d queueing work-item for packet from %s on interface %d (%s)", dwErr, szSrcAddr, pite->ITE_Index, szLocalAddr ); LOGERR0(QUEUE_WORKER_FAILED, dwErr); // // remove the data that was queued for processing // ACQUIRE_LIST_LOCK(ig.IG_RecvQueue); phead = &ig.IG_RecvQueue->LL_Head; RemoveTailList(phead); ig.IG_RecvQueueSize -= sizeof(RECV_QUEUE_ENTRY); RELEASE_LIST_LOCK(ig.IG_RecvQueue); // // If we weren't able to enqueue the work item, we might as well // break out of the loop, instead of receiving more packets. // break; } else { InterlockedIncrement(&ig.IG_NumProcessInputWorkItems); dwWorkItemsEnqueued++; } } } while(TRUE); TRACE2( RECEIVE, "Packets Queued: %d. WorkItems Queued: %d", dwPacketsEnqueued, dwWorkItemsEnqueued ); // // some cleanup is required if an error brought us here // if (pwc != NULL) { RIP_FREE(pwc); } return; } DWORD ProcessRtmNotification( RTM_ENTITY_HANDLE hRtmHandle, // not used RTM_EVENT_TYPE retEventType, PVOID pvContext1, // not used PVOID pvContext2 // not used ) { DWORD dwErr; TRACE1(ROUTE, "ENTERED ProcessRtmNotification, event %d", retEventType ); if (!ENTER_RIP_API()) { return ERROR_CAN_NOT_COMPLETE; } // // only route change notifications are processed // if (retEventType == RTM_CHANGE_NOTIFICATION) { QueueRipWorker(WorkerFunctionProcessRtmMessage, (PVOID)retEventType); dwErr = NO_ERROR; } else { dwErr = ERROR_NOT_SUPPORTED; } LEAVE_RIP_API(); TRACE1(ROUTE, "LEAVING ProcessRtmNotification %d", dwErr); return dwErr; } DWORD BlockDeleteRoutesOnInterface ( IN HANDLE hRtmHandle, IN DWORD dwIfIndex ) /*++ Routine Description : This routine deletes all the routes learnt by the protocol over the specified interface. Parameters : hRtmHandle - Entity registration handle dwIfIndex - Interface over which routes are to be deleted Return Value : --*/ { HANDLE hRtmEnum; PHANDLE phRoutes = NULL; DWORD dwHandles, dwFlags, i, dwErr; dwErr = RtmCreateRouteEnum( hRtmHandle, NULL, RTM_VIEW_MASK_ANY, RTM_ENUM_OWN_ROUTES, NULL, RTM_MATCH_INTERFACE, NULL, dwIfIndex, &hRtmEnum ); if ( dwErr != NO_ERROR ) { TRACE1( ANY, "BlockDeleteRoutesOnInterface: Error %d creating handle", dwErr ); return dwErr; } // // allocate handle array large enough to hold max handles in an // enum // phRoutes = RIP_ALLOC(ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE)); if ( phRoutes == NULL ) { dwErr = GetLastError(); TRACE2( ANY, "BlockDeleteRoutesOnInterface: error %d while " "allocating %d bytes to hold max handles in an enum", dwErr, ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE) ); LOGERR0(HEAP_ALLOC_FAILED, dwErr); return dwErr; } do { dwHandles = ig.IG_RtmProfile.MaxHandlesInEnum; dwErr = RtmGetEnumRoutes( hRtmHandle, hRtmEnum, &dwHandles, phRoutes ); for ( i = 0; i < dwHandles; i++ ) { if ( RtmDeleteRouteToDest( hRtmHandle, phRoutes[i], &dwFlags ) != NO_ERROR ) { // // If delete is successful, this is automatic // TRACE2( ANY, "BlockDeleteRoutesOnInterface : error %d deleting" " routes on interface %d", dwErr, dwIfIndex ); dwErr = RtmReleaseRoutes(hRtmHandle, 1, &phRoutes[i]); if (dwErr != NO_ERROR) { TRACE1(ANY, "error %d releasing route", dwErr); } } } } while (dwErr == NO_ERROR); // // close enum handles // dwErr = RtmDeleteEnumHandle(hRtmHandle, hRtmEnum); if (dwErr != NO_ERROR) { TRACE1( ANY, "BlockDeleteRoutesOnInterface : error %d closing enum handle", dwErr ); } if ( phRoutes ) { RIP_FREE(phRoutes); } return NO_ERROR; }