//**************************************************************************** // // Microsoft Windows NT RIP // // Copyright 1995-96 // // // Revision History // // // 3/12/95 Gurdeep Singh Pall Created // // // Description: General utility functions: // //**************************************************************************** #include "pchrip.h" #pragma hdrstop DWORD LoadAddressSockets(); DWORD OpenTcp(); DWORD TCPSetInformationEx(LPVOID lpvInBuffer, LPDWORD lpdwInSize, LPVOID lpvOutBuffer, LPDWORD lpdwOutSize); DWORD TCPQueryInformationEx(LPVOID lpvInBuffer, LPDWORD lpdwInSize, LPVOID lpvOutBuffer, LPDWORD lpdwOutSize); //------------------------------------------------------------------- // Function: LogEntry // Parameters: // WORD wEventType type of event (ERROR, WARNING, etc) // DWORD dwMsgID ID of message string // WORD wNumStrings number of strings in lplpStrings // LPSTR *lplpStrings array of strings // DWORD dwErr error code //------------------------------------------------------------------- void LogEntry(WORD wEventType, DWORD dwMsgID, WORD wNumStrings, LPSTR *lplpStrings, DWORD dwErr) { DWORD dwSize; LPVOID lpvData; HANDLE hLog; PSID pSidUser = NULL; hLog = RegisterEventSource(NULL, RIP_SERVICE); dwSize = (dwErr == NO_ERROR) ? 0 : sizeof(dwErr); lpvData = (dwErr == NO_ERROR) ? NULL : (LPVOID)&dwErr; ReportEvent(hLog, wEventType, 0, dwMsgID, pSidUser, wNumStrings, dwSize, lplpStrings, lpvData); DeregisterEventSource(hLog); } //------------------------------------------------------------------- // Function: RipLogError // Parameters: // see LogEntry for parameter description //------------------------------------------------------------------- void RipLogError(DWORD dwMsgID, WORD wNumStrings, LPSTR *lplpStrings, DWORD dwErr) { DWORD dwLevel; dwLevel = g_params.dwLoggingLevel; if (dwLevel < LOGLEVEL_ERROR) { return; } LogEntry(EVENTLOG_ERROR_TYPE, dwMsgID, wNumStrings, lplpStrings, dwErr); } //------------------------------------------------------------------- // Function: LogWarning // Parameters: // see LogEntry for parameter description //------------------------------------------------------------------- void RipLogWarning(DWORD dwMsgID, WORD wNumStrings, LPSTR *lplpStrings, DWORD dwErr) { DWORD dwLevel; dwLevel = g_params.dwLoggingLevel; if (dwLevel < LOGLEVEL_WARNING) { return; } LogEntry(EVENTLOG_WARNING_TYPE, dwMsgID, wNumStrings, lplpStrings, dwErr); } //------------------------------------------------------------------- // Function: LogInformation // Parameters: // see LogEntry for parameter description //------------------------------------------------------------------- void RipLogInformation(DWORD dwMsgID, WORD wNumStrings, LPSTR *lplpStrings, DWORD dwErr) { DWORD dwLevel; dwLevel = g_params.dwLoggingLevel; if (dwLevel < LOGLEVEL_INFORMATION) { return; } LogEntry(EVENTLOG_INFORMATION_TYPE, dwMsgID, wNumStrings, lplpStrings, dwErr); } //------------------------------------------------------------------- // Function: Audit //------------------------------------------------------------------- VOID Audit(IN WORD wEventType, IN DWORD dwMessageId, IN WORD cNumberOfSubStrings, IN LPSTR *plpwsSubStrings) { HANDLE hLog; PSID pSidUser = NULL; // Audit enabled hLog = RegisterEventSourceA(NULL, RIP_SERVICE); ReportEventA(hLog, wEventType, 0, dwMessageId, pSidUser, cNumberOfSubStrings, 0, plpwsSubStrings, (PVOID)NULL); DeregisterEventSource( hLog ); } //------------------------------------------------------------------- // Function: dbgprintf //------------------------------------------------------------------- VOID dbgprintf(LPSTR lpszFormat, ...) { va_list arglist; va_start(arglist, lpszFormat); TraceVprintf(g_dwTraceID, lpszFormat, arglist); va_end(arglist); } //------------------------------------------------------------------- // Function: InitializeAddressTable // // Assumes the address table is locked. //------------------------------------------------------------------- DWORD InitializeAddressTable(BOOL bFirstTime) { LPRIP_ADDRESS lpaddr, lpaddrend; LPRIP_ADDRESS_STATISTICS lpstats; DWORD dwErr, dwCount, *lpdw, *lpdwend; PMIB_IPADDRROW lpTable, lpiae, lpiaeend; // first close old sockets, if necessary if (!bFirstTime) { lpaddrend = g_ripcfg.lpAddrTable + g_ripcfg.dwAddrCount; for (lpaddr = g_ripcfg.lpAddrTable; lpaddr < lpaddrend; lpaddr++) { if (lpaddr->sock != INVALID_SOCKET) { closesocket(lpaddr->sock); lpaddr->sock = INVALID_SOCKET; } } } dwErr = GetIPAddressTable(&lpTable, &dwCount); if (dwErr != 0) { return dwErr; } if (dwCount > MAX_ADDRESS_COUNT) { dwCount = MAX_ADDRESS_COUNT; } lpaddr = g_ripcfg.lpAddrTable; lpstats = g_ripcfg.lpStatsTable->lpAddrStats; lpiaeend = lpTable + dwCount; g_ripcfg.dwAddrCount = dwCount; for (lpiae = lpTable; lpiae < lpiaeend; lpiae++) { if (!lpiae->dwAddr || IP_LOOPBACK_ADDR(lpiae->dwAddr)) { --g_ripcfg.dwAddrCount; continue; } lpaddr->dwIndex = lpiae->dwIndex; lpaddr->dwAddress = lpiae->dwAddr; lpaddr->dwNetmask = lpiae->dwMask; lpaddr->dwFlag = 0; lpdwend = (LPDWORD)(lpstats + 1); for (lpdw = (LPDWORD)lpstats; lpdw < lpdwend; lpdw++) { InterlockedExchange(lpdw, 0); } InterlockedExchange(&lpstats->dwAddress, lpaddr->dwAddress); lpaddr->lpstats = lpstats; lpstats++; lpaddr++; } FreeIPAddressTable(lpTable); // also update the stats address count InterlockedExchange(&g_ripcfg.lpStatsTable->dwAddrCount, g_ripcfg.dwAddrCount); // if no addresses, bail out now if (g_ripcfg.dwAddrCount == 0) { dbgprintf("no IP addresses available for routing"); return NO_ERROR; } // open sockets for each interface we have, and set options on the sockets dwErr = LoadAddressSockets(); return dwErr; } //------------------------------------------------------------------- // Function: InitializeStatsTable // // Creates a mapping of our statistics table in shareable memory // so interested processes can examine RIP's behavior. //------------------------------------------------------------------- DWORD InitializeStatsTable() { DWORD dwErr; g_ripcfg.lpStatsTable = NULL; // set up a pointer to the memory g_ripcfg.lpStatsTable = HeapAlloc(GetProcessHeap(), 0, sizeof(RIP_STATISTICS)); if (g_ripcfg.lpStatsTable == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; dbgprintf( "InitializeStatsTable failed with error %x\n", dwErr ); RipLogError( RIPLOG_ADDR_ALLOC_FAILED, 0, NULL, dwErr ); return dwErr; } ZeroMemory(g_ripcfg.lpStatsTable, sizeof(RIP_STATISTICS)); return 0; } VOID CleanupStatsTable() { if (g_ripcfg.lpStatsTable != NULL) { InterlockedExchange(&g_ripcfg.lpStatsTable->dwAddrCount, 0); HeapFree(GetProcessHeap(), 0, g_ripcfg.lpStatsTable); g_ripcfg.lpStatsTable = NULL; } } //-------------------------------------------------------------------------- // Function: LoadAddressSockets // // Opens, configures, and binds sockets for each address in the table //-------------------------------------------------------------------------- DWORD LoadAddressSockets() { IN_ADDR addr; CHAR szAddress[24] = {0}; CHAR *ppszArgs[] = { szAddress }; CHAR *pszTemp; SOCKADDR_IN sinsock; DWORD dwOption, dwErr; LPRIP_ADDRESS lpaddr, lpend; struct ip_mreq imOption; lpend = g_ripcfg.lpAddrTable + g_ripcfg.dwAddrCount; for (lpaddr = g_ripcfg.lpAddrTable; lpaddr < lpend; lpaddr++) { if ((lpaddr->dwFlag & ADDRFLAG_DISABLED) != 0) { continue; } addr.s_addr = lpaddr->dwAddress; pszTemp = inet_ntoa(addr); if (pszTemp != NULL) { strcpy(szAddress, pszTemp); } lpaddr->sock = socket(AF_INET, SOCK_DGRAM, 0); if (lpaddr->sock == INVALID_SOCKET) { dwErr = WSAGetLastError(); dbgprintf("error %d creating socket for address %s", dwErr, szAddress); RipLogError(RIPLOG_CREATESOCK_FAILED, 1, ppszArgs, dwErr); continue; } dwOption = 1; dwErr = setsockopt(lpaddr->sock, SOL_SOCKET, SO_BROADCAST, (LPBYTE)&dwOption, sizeof(dwOption)); if (dwErr == SOCKET_ERROR) { dwErr = WSAGetLastError(); dbgprintf("error %d enabling broadcast for address %s", dwErr, szAddress); RipLogError(RIPLOG_SET_BCAST_FAILED, 1, ppszArgs, dwErr); // this socket is useless if we can't broadcast on it closesocket(lpaddr->sock); lpaddr->sock = INVALID_SOCKET; continue; } dwOption = 1; dwErr = setsockopt(lpaddr->sock, SOL_SOCKET, SO_REUSEADDR, (LPBYTE)&dwOption, sizeof(dwOption)); if (dwErr == SOCKET_ERROR) { dwErr = WSAGetLastError(); dbgprintf("error %d enabling reuse of address %s", dwErr, szAddress); RipLogError(RIPLOG_SET_REUSE_FAILED, 1, ppszArgs, dwErr); } sinsock.sin_family = AF_INET; sinsock.sin_port = htons(RIP_PORT); sinsock.sin_addr.s_addr = lpaddr->dwAddress; dwErr = bind(lpaddr->sock, (LPSOCKADDR)&sinsock, sizeof(SOCKADDR_IN)); if (dwErr == SOCKET_ERROR) { dwErr = WSAGetLastError(); dbgprintf("error %d binding address %s to RIP port", dwErr, szAddress); RipLogError(RIPLOG_BINDSOCK_FAILED, 1, ppszArgs, dwErr); closesocket(lpaddr->sock); lpaddr->sock = INVALID_SOCKET; continue; } #if DBG dbgprintf( "socket %d bound to %s\n\n", lpaddr-> sock, inet_ntoa( *( (struct in_addr *) &(lpaddr-> dwAddress) ) ) ); #endif // // enable multicasting also // sinsock.sin_addr.s_addr = lpaddr->dwAddress; dwErr = setsockopt(lpaddr->sock, IPPROTO_IP, IP_MULTICAST_IF, (PBYTE)&sinsock.sin_addr, sizeof(IN_ADDR)); if (dwErr == SOCKET_ERROR) { dwErr = WSAGetLastError(); dbgprintf("error %d setting interface %d (%s) as multicast", dwErr, lpaddr->dwIndex, szAddress); RipLogError(RIPLOG_SET_MCAST_IF_FAILED, 1, ppszArgs, dwErr); closesocket(lpaddr->sock); lpaddr->sock = INVALID_SOCKET; continue; } // // join the IPRIP multicast group // imOption.imr_multiaddr.s_addr = RIP_MULTIADDR; imOption.imr_interface.s_addr = lpaddr->dwAddress; dwErr = setsockopt(lpaddr->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (PBYTE)&imOption, sizeof(imOption)); if (dwErr == SOCKET_ERROR) { dwErr = WSAGetLastError(); dbgprintf("error %d enabling multicast on interface %d (%s)", dwErr, lpaddr->dwIndex, szAddress); RipLogError(RIPLOG_JOIN_GROUP_FAILED, 1, ppszArgs, dwErr); closesocket(lpaddr->sock); lpaddr->sock = INVALID_SOCKET; continue; } dwErr = WSAEventSelect( lpaddr->sock, g_netEvent, FD_READ); if (dwErr == SOCKET_ERROR) { dwErr = WSAGetLastError(); dbgprintf("error %d doing WSAEventSelect on interface %d (%s)", dwErr, lpaddr->dwIndex, szAddress); RipLogError(RIPLOG_WSAEVENTSELECT_FAILED, 1, ppszArgs, dwErr); closesocket(lpaddr->sock); lpaddr->sock = INVALID_SOCKET; continue; } } return 0; } //-------------------------------------------------------------------------- // Function: LoadRouteTable // // Get the transports routing table. This is called with firsttime set // to TRUE when RIP loads. After that it is called with firsttime set // to FALSE. Assumes the route table is locked. //-------------------------------------------------------------------------- int LoadRouteTable(BOOL bFirstTime) { IN_ADDR addr; LPHASH_TABLE_ENTRY rt_entry; CHAR szDest[32] = {0}; CHAR szNexthop[32] = {0}; CHAR *pszTemp; DWORD dwRouteTimeout, dwErr, dwRouteCount; LPIPROUTE_ENTRY lpRouteEntryTable, lpentry, lpentend; dwErr = GetRouteTable(&lpRouteEntryTable, &dwRouteCount); if (dwErr != 0) { return dwErr; } dwRouteTimeout = g_params.dwRouteTimeout; // now prune unwanted entries, and add the others to our hash table. // we only load RIP, static, and SNMP routes, and for non-RIP routes // we set the timeout to 90 seconds, // lpentend = lpRouteEntryTable + dwRouteCount; for (lpentry = lpRouteEntryTable; lpentry < lpentend; lpentry++) { if (lpentry->ire_metric1 < METRIC_INFINITE && (lpentry->ire_proto == IRE_PROTO_RIP || lpentry->ire_proto == IRE_PROTO_LOCAL || lpentry->ire_proto == IRE_PROTO_NETMGMT) && !IP_LOOPBACK_ADDR(lpentry->ire_dest) && !IP_LOOPBACK_ADDR(lpentry->ire_nexthop) && !CLASSD_ADDR(lpentry->ire_dest) && !CLASSE_ADDR(lpentry->ire_dest) && !IsBroadcastAddress(lpentry->ire_dest) && !IsDisabledLocalAddress(lpentry->ire_nexthop)) { rt_entry = GetRouteTableEntry(lpentry->ire_index, lpentry->ire_dest, lpentry->ire_mask); // If we hit low memory conditions, get out of the loop. // if (rt_entry == NULL) { dwErr = ERROR_OUTOFMEMORY; break; } // only update the route with information from // the system table if it is a route that was learnt // from the system table; this is so if it is a new route // or if it is an old static or SNMP-added route // if ((rt_entry->dwFlag & NEW_ENTRY) || rt_entry->dwProtocol == IRE_PROTO_LOCAL || rt_entry->dwProtocol == IRE_PROTO_NETMGMT) { // if the route is new and this isn't the first time // we have loaded the system routing table, set change flag // if (!bFirstTime && (rt_entry->dwFlag & NEW_ENTRY)) { rt_entry->dwFlag |= ROUTE_CHANGE; addr.s_addr = lpentry->ire_dest; pszTemp = inet_ntoa(addr); if (pszTemp != NULL) { strcpy(szDest, pszTemp); } addr.s_addr = lpentry->ire_nexthop; pszTemp = inet_ntoa(addr); if (pszTemp != NULL) { strcpy(szNexthop, pszTemp); } dbgprintf("new entry: dest=%s, nexthop=%s, " "metric=%d, protocol=%d", szDest, szNexthop, lpentry->ire_metric1, lpentry->ire_proto); } rt_entry->dwFlag &= ~NEW_ENTRY; // we need to reset all these parameters // because any of them may have changed since // the last time the system route table was loaded. // rt_entry->dwIndex = lpentry->ire_index; rt_entry->dwProtocol = lpentry->ire_proto; rt_entry->dwDestaddr = lpentry->ire_dest; rt_entry->dwNetmask = lpentry->ire_mask; rt_entry->dwNexthop = lpentry->ire_nexthop; rt_entry->dwMetric = lpentry->ire_metric1; if (rt_entry->dwProtocol == IRE_PROTO_RIP) { rt_entry->lTimeout = (LONG)dwRouteTimeout; } else { rt_entry->lTimeout = DEF_LOCALROUTETIMEOUT; } rt_entry->dwFlag &= ~GARBAGE_TIMER; rt_entry->dwFlag |= TIMEOUT_TIMER; // if our estimate is that this is a host route // and its mask tells us it is a host route // then we mark it as being a host route // if (IsHostAddress(rt_entry->dwDestaddr) && rt_entry->dwNetmask == HOSTADDR_MASK) { rt_entry->dwFlag |= ROUTE_HOST; } } } } FreeRouteTable(lpRouteEntryTable); return dwErr; } //-------------------------------------------------------------------------- // Function: UpdateSystemRouteTable // // Parameters: // LPHASH_TABLE_ENTRY rt_entry the entry to update // BOOL bAdd if true, the entry is added // otherwise, the entry is deleted // // Returns: DWORD: // // // Add a new route to the route table. Note: due to MIB's use of // the destination address as an instance number, and also due to // TCP/IP stack allowing multiple entries for a single destination, // an ambiguity can exist. If there is already an entry for this // destination. This will have the effect of changing the existing // entry, rather than creating a new one. // This function assumes the address table is locked. //-------------------------------------------------------------------------- DWORD UpdateSystemRouteTable(LPHASH_TABLE_ENTRY rt_entry, BOOL bAdd) { IN_ADDR addr; DWORD dwErr, dwRouteType; // never delete or update a route which was not created by RIP if (rt_entry->dwProtocol != IRE_PROTO_RIP) { return 0; } if (bAdd) { dwRouteType = (IsLocalAddr(rt_entry->dwNexthop) ? IRE_TYPE_DIRECT : IRE_TYPE_INDIRECT); #if 0 DbgPrintf( "AddRoute : Protocol %x, Index %x, dest addr %x, dest mask %x\n", rt_entry->dwProtocol, rt_entry->dwIndex, rt_entry->dwDestaddr, rt_entry->dwNetmask ); DbgPrintf( "Next Hop %x, Metric %x\n\n", rt_entry->dwNexthop, rt_entry->dwMetric ); #endif dwErr = AddRoute(rt_entry->dwProtocol, dwRouteType, rt_entry->dwIndex, rt_entry->dwDestaddr, rt_entry->dwNetmask, rt_entry->dwNexthop, rt_entry->dwMetric); } else { dwErr = DeleteRoute(rt_entry->dwIndex, rt_entry->dwDestaddr, rt_entry->dwNetmask, rt_entry->dwNexthop); } if (dwErr == STATUS_SUCCESS) { if (bAdd) { InterlockedIncrement( &g_ripcfg.lpStatsTable->dwRoutesAddedToSystemTable); } else { InterlockedIncrement( &g_ripcfg.lpStatsTable->dwRoutesDeletedFromSystemTable); } } else { if (bAdd) { dbgprintf("error %X adding route to system table", dwErr); RipLogError(RIPLOG_ADD_ROUTE_FAILED, 0, NULL, dwErr); InterlockedIncrement( &g_ripcfg.lpStatsTable->dwSystemAddRouteFailures); } else { dbgprintf("error %X deleting route from system table", dwErr); RipLogError(RIPLOG_DELETE_ROUTE_FAILED, 0, NULL, dwErr); InterlockedIncrement( &g_ripcfg.lpStatsTable->dwSystemDeleteRouteFailures); } } return dwErr; } #ifndef CHICAGO //------------------------------------------------------------------ // Function: OpenTcp // // Parameters: // none. // // Opens the handle to the Tcpip driver. //------------------------------------------------------------------ DWORD OpenTcp() { NTSTATUS status; UNICODE_STRING nameString; IO_STATUS_BLOCK ioStatusBlock; OBJECT_ATTRIBUTES objectAttributes; // Open the ip stack for setting routes and parps later. // // Open a Handle to the TCP driver. // RtlInitUnicodeString(&nameString, DD_TCP_DEVICE_NAME); InitializeObjectAttributes(&objectAttributes, &nameString, OBJ_CASE_INSENSITIVE, NULL, NULL); status = NtCreateFile(&g_ripcfg.hTCPDriver, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &objectAttributes, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, 0, NULL, 0); return (status == STATUS_SUCCESS ? 0 : ERROR_OPEN_FAILED); } //--------------------------------------------------------------------- // Function: TCPQueryInformationEx // // Parameters: // TDIObjectID *ID The TDI Object ID to query // void *Buffer buffer to contain the query results // LPDWORD *BufferSize pointer to the size of the buffer // filled in with the amount of data. // UCHAR *Context context value for the query. should // be zeroed for a new query. It will be // filled with context information for // linked enumeration queries. // // Returns: // An NTSTATUS value. // // This routine provides the interface to the TDI QueryInformationEx // facility of the TCP/IP stack on NT. //--------------------------------------------------------------------- DWORD TCPQueryInformationEx(LPVOID lpvInBuffer, LPDWORD lpdwInSize, LPVOID lpvOutBuffer, LPDWORD lpdwOutSize) { NTSTATUS status; IO_STATUS_BLOCK isbStatusBlock; if (g_ripcfg.hTCPDriver == NULL) { OpenTcp(); } status = NtDeviceIoControlFile(g_ripcfg.hTCPDriver, // Driver handle NULL, // Event NULL, // APC Routine NULL, // APC context &isbStatusBlock, // Status block IOCTL_TCP_QUERY_INFORMATION_EX, // Control lpvInBuffer, // Input buffer *lpdwInSize, // Input buffer size lpvOutBuffer, // Output buffer *lpdwOutSize); // Output buffer size if (status == STATUS_PENDING) { status = NtWaitForSingleObject(g_ripcfg.hTCPDriver, TRUE, NULL); status = isbStatusBlock.Status; } if (status != STATUS_SUCCESS) { *lpdwOutSize = 0; } else { *lpdwOutSize = (ULONG)isbStatusBlock.Information; } return status; } //--------------------------------------------------------------------------- // Function: TCPSetInformationEx // // Parameters: // // TDIObjectID *ID the TDI Object ID to set // void *lpvBuffer data buffer containing the information // to be set // DWORD dwBufferSize the size of the data buffer. // // This routine provides the interface to the TDI SetInformationEx // facility of the TCP/IP stack on NT. //--------------------------------------------------------------------------- DWORD TCPSetInformationEx(LPVOID lpvInBuffer, LPDWORD lpdwInSize, LPVOID lpvOutBuffer, LPDWORD lpdwOutSize) { NTSTATUS status; IO_STATUS_BLOCK isbStatusBlock; if (g_ripcfg.hTCPDriver == NULL) { OpenTcp(); } status = NtDeviceIoControlFile(g_ripcfg.hTCPDriver, // Driver handle NULL, // Event NULL, // APC Routine NULL, // APC context &isbStatusBlock, // Status block IOCTL_TCP_SET_INFORMATION_EX, // Control lpvInBuffer, // Input buffer *lpdwInSize, // Input buffer size lpvOutBuffer, // Output buffer *lpdwOutSize); // Output buffer size if (status == STATUS_PENDING) { status = NtWaitForSingleObject(g_ripcfg.hTCPDriver, TRUE, NULL); status = isbStatusBlock.Status; } if (status != STATUS_SUCCESS) { *lpdwOutSize = 0; } else { *lpdwOutSize = (ULONG)isbStatusBlock.Information; } return status; } #endif