/*++ Copyright (c) 2001-2002 Microsoft Corporation Module Name: isatap.c Abstract: This module contains the ISATAP interface to the IPv6 Helper Service. Author: Mohit Talwar (mohitt) Tue May 07 10:16:49 2002 Environment: User mode only. --*/ #include "precomp.h" #pragma hdrstop #define DEFAULT_ISATAP_STATE ENABLED #define DEFAULT_ISATAP_ROUTER_NAME L"isatap" #define DEFAULT_ISATAP_RESOLUTION_STATE ENABLED #define DEFAULT_ISATAP_RESOLUTION_INTERVAL (24 * HOURS) #define KEY_ISATAP_STATE L"IsatapState" #define KEY_ISATAP_ROUTER_NAME L"IsatapRouterName" #define KEY_ISATAP_RESOLUTION_STATE L"EnableIsatapResolution" #define KEY_ISATAP_RESOLUTION_INTERVAL L"IsatapResolutionInterval" STATE IsatapState; WCHAR IsatapRouterName[NI_MAXHOST]; STATE IsatapResolutionState; ULONG IsatapResolutionInterval; // in minutes HANDLE IsatapTimer; // Periodic timer started for the service. HANDLE IsatapTimerEvent; // Event signalled upon Timer deletion. HANDLE IsatapTimerEventWait; // Wait registered for TimerEvent. IN_ADDR IsatapRouter; IN_ADDR IsatapToken; BOOL IsatapInitialized = FALSE; DWORD GetPreferredSource( IN IN_ADDR Destination, OUT PIN_ADDR Source ) { SOCKADDR_IN DestinationAddress, SourceAddress; int BytesReturned; memset(&DestinationAddress, 0, sizeof(SOCKADDR_IN)); DestinationAddress.sin_family = AF_INET; DestinationAddress.sin_addr = Destination; if (WSAIoctl( g_sIPv4Socket, SIO_ROUTING_INTERFACE_QUERY, &DestinationAddress, sizeof(SOCKADDR_IN), &SourceAddress, sizeof(SOCKADDR_IN), &BytesReturned, NULL, NULL) == SOCKET_ERROR) { return WSAGetLastError(); } *Source = SourceAddress.sin_addr; return NO_ERROR; } VOID IsatapUpdateRouterAddress( VOID ) { DWORD Error = NO_ERROR; ADDRINFOW Hints; PADDRINFOW Addresses; IN_ADDR NewRouter = { INADDR_ANY }, NewToken = { INADDR_ANY }; // // Set the ISATAP router address if ISATAP resolution is enabled. // if (IsatapResolutionState == ENABLED) { // // Resolve IsatapRouterName to an IPv4 address. // ZeroMemory(&Hints, sizeof(Hints)); Hints.ai_family = PF_INET; Error = GetAddrInfoW(IsatapRouterName, NULL, &Hints, &Addresses); if (Error == NO_ERROR) { NewRouter = ((LPSOCKADDR_IN) Addresses->ai_addr)->sin_addr; FreeAddrInfoW(Addresses); // // Determine the preferred source address. // if (GetPreferredSource(NewRouter, &NewToken) != NO_ERROR) { // // What use is the IsatapRouter that cannot be reached? // NewRouter.s_addr = INADDR_ANY; } } else { Trace2(ERR, _T("GetAddrInfoW(%s): %x"), IsatapRouterName, Error); } } // // Update the stack with the new addresses. // IsatapRouter = NewRouter; IsatapToken = NewToken; UpdateRouterLinkAddress(V4_COMPAT_IFINDEX, IsatapToken, IsatapRouter); } VOID IsatapConfigureAddress( IN BOOL Delete, IN IN_ADDR Ipv4 ) /*++ Routine Description: Creates an ISATAP link-scoped address from an IPv4 address. --*/ { SOCKADDR_IN6 IsatapAddress; memset(&IsatapAddress, 0, sizeof(SOCKADDR_IN6)); IsatapAddress.sin6_family = AF_INET6; IsatapAddress.sin6_addr.s6_addr[0] = 0xfe; IsatapAddress.sin6_addr.s6_addr[1] = 0x80; IsatapAddress.sin6_addr.s6_addr[10] = 0x5e; IsatapAddress.sin6_addr.s6_addr[11] = 0xfe; memcpy(&IsatapAddress.sin6_addr.s6_addr[12], &Ipv4, sizeof(IN_ADDR)); (VOID) ConfigureAddressUpdate( V4_COMPAT_IFINDEX, &IsatapAddress, Delete ? 0 : INFINITE_LIFETIME, ADE_UNICAST, PREFIX_CONF_WELLKNOWN, IID_CONF_LL_ADDRESS); } VOID IsatapConfigureAddressList( IN BOOL Delete ) { int i; // // Configure the lifetime of link-local ISATAP addresses. // This will cause them to be either added or deleted. // for (i = 0; i < g_pIpv4AddressList->iAddressCount; i++) { IsatapConfigureAddress( Delete, ((PSOCKADDR_IN) g_pIpv4AddressList->Address[i].lpSockaddr)->sin_addr); } } __inline VOID IsatapRestartTimer( VOID ) { ULONG ResolveInterval = (IsatapResolutionState == ENABLED) ? IsatapResolutionInterval * MINUTES * 1000 // minutes to milliseconds : INFINITE_INTERVAL; (VOID) ChangeTimerQueueTimer(NULL, IsatapTimer, 0, ResolveInterval); } __inline VOID IsatapStart( VOID ) { ASSERT(IsatapState != ENABLED); IsatapState = ENABLED; IsatapConfigureAddressList(FALSE); IsatapRestartTimer(); } __inline VOID IsatapStop( VOID ) { ASSERT(IsatapState == ENABLED); IsatapState = DISABLED; IsatapConfigureAddressList(TRUE); IsatapRestartTimer(); } __inline VOID IsatapRefresh( VOID ) { ASSERT(IsatapState == ENABLED); IsatapRestartTimer(); } VOID CALLBACK IsatapTimerCallback( IN PVOID Parameter, IN BOOLEAN TimerOrWaitFired ) /*++ Routine Description: Callback routine for IsatapTimer expiration. The timer is always active. Arguments: Parameter, TimerOrWaitFired - Ignored. Return Value: None. --*/ { ENTER_API(); TraceEnter("IsatapTimerCallback"); IsatapUpdateRouterAddress(); TraceLeave("IsatapTimerCallback"); LEAVE_API(); } VOID CALLBACK IsatapTimerCleanup( IN PVOID Parameter, IN BOOLEAN TimerOrWaitFired ) /*++ Routine Description: Callback routine for IsatapTimer deletion. Deletion is performed asynchronously since we acquire a lock in the callback function that we hold when deleting the timer. Arguments: Parameter, TimerOrWaitFired - Ignored. Return Value: None. --*/ { UnregisterWait(IsatapTimerEventWait); IsatapTimerEventWait = NULL; CloseHandle(IsatapTimerEvent); IsatapTimerEvent = NULL; IsatapState = IsatapResolutionState = DISABLED; IsatapUpdateRouterAddress(); DecEventCount("IsatapCleanupTimer"); } DWORD IsatapInitializeTimer( VOID ) /*++ Routine Description: Initializes the timer. Arguments: None. Return Value: NO_ERROR or failure code. --*/ { DWORD Error; ULONG ResolveInterval; IsatapTimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (IsatapTimerEvent == NULL) { Error = GetLastError(); return Error; } if (!RegisterWaitForSingleObject( &(IsatapTimerEventWait), IsatapTimerEvent, IsatapTimerCleanup, NULL, INFINITE, 0)) { Error = GetLastError(); CloseHandle(IsatapTimerEvent); return Error; } ResolveInterval = (IsatapResolutionState == ENABLED) ? (IsatapResolutionInterval * MINUTES * 1000) : INFINITE_INTERVAL; if (!CreateTimerQueueTimer( &(IsatapTimer), NULL, IsatapTimerCallback, NULL, 0, ResolveInterval, 0)) { Error = GetLastError(); UnregisterWait(IsatapTimerEventWait); CloseHandle(IsatapTimerEvent); return Error; } IncEventCount("IsatapInitializeTimer"); return NO_ERROR; } VOID IsatapUninitializeTimer( VOID ) /*++ Routine Description: Uninitializes the timer. Typically invoked upon service stop. Arguments: None. Return Value: None. --*/ { DeleteTimerQueueTimer(NULL, IsatapTimer, IsatapTimerEvent); IsatapTimer = NULL; } DWORD IsatapInitialize( VOID ) /*++ Routine Description: Initializes ISATAP and attempts to start it. Arguments: None. Return Value: NO_ERROR or failure code. --*/ { DWORD Error; IsatapState = DEFAULT_ISATAP_STATE; wcscpy(IsatapRouterName, DEFAULT_ISATAP_ROUTER_NAME); IsatapResolutionState = DEFAULT_ISATAP_RESOLUTION_STATE; IsatapResolutionInterval = DEFAULT_ISATAP_RESOLUTION_INTERVAL; IsatapRouter.s_addr = INADDR_ANY; IsatapToken.s_addr = INADDR_ANY; IsatapUpdateRouterAddress(); Error = IsatapInitializeTimer(); if (Error != NO_ERROR) { return Error; } IsatapInitialized = TRUE; return NO_ERROR; } VOID IsatapUninitialize( VOID ) /*++ Routine Description: Uninitializes ISATAP. Arguments: None. Return Value: None. --*/ { if (!IsatapInitialized) { return; } IsatapUninitializeTimer(); IsatapInitialized = FALSE; } VOID IsatapAddressChangeNotification( IN BOOL Delete, IN IN_ADDR Address ) /*++ Routine Description: Process an address deletion or addition request. Arguments: Delete - Supplies a boolean. TRUE if the address was deleted, FALSE o/w. Address - Supplies the IPv4 address that was deleted or added. Return Value: None. Caller LOCK: API. --*/ { IsatapConfigureAddress(Delete, Address); if (IsatapResolutionState == ENABLED) { // // Preferred source address deleted -or- Any address added. // if (Delete ? (IsatapToken.s_addr == Address.s_addr) : (IsatapToken.s_addr == INADDR_ANY)) { Sleep(1000); // Wait a second to ensure DNS is alerted. IsatapUpdateRouterAddress(); } } } VOID IsatapRouteChangeNotification( VOID ) /*++ Routine Description: Process a route change notification. Arguments: None. Return Value: None. Caller LOCK: API. --*/ { if (IsatapResolutionState == ENABLED) { IsatapRefresh(); } } VOID IsatapConfigurationChangeNotification( VOID ) /*++ Routine Description: Process an configuration change request. Arguments: None. Return Value: None. Caller LOCK: API. --*/ { HKEY Key = INVALID_HANDLE_VALUE; STATE State; (VOID) RegOpenKeyExW( HKEY_LOCAL_MACHINE, KEY_GLOBAL, 0, KEY_QUERY_VALUE, &Key); // // Continue despite errors, reverting to default values. // State = GetInteger( Key, KEY_ISATAP_STATE, DEFAULT_ISATAP_STATE); IsatapResolutionState = GetInteger( Key, KEY_ISATAP_RESOLUTION_STATE, DEFAULT_ISATAP_RESOLUTION_STATE); IsatapResolutionInterval= GetInteger( Key, KEY_ISATAP_RESOLUTION_INTERVAL, DEFAULT_ISATAP_RESOLUTION_INTERVAL); GetString( Key, KEY_ISATAP_ROUTER_NAME, IsatapRouterName, NI_MAXHOST, DEFAULT_ISATAP_ROUTER_NAME); if (Key != INVALID_HANDLE_VALUE) { RegCloseKey(Key); } if (State == DISABLED) { IsatapResolutionState = DISABLED; } // // Start / Reconfigure / Stop. // if (State == ENABLED) { if (IsatapState == ENABLED) { IsatapRefresh(); } else { IsatapStart(); } } else { if (IsatapState == ENABLED) { IsatapStop(); } } }