|
|
/*++
Copyright (c) 2001-2002 Microsoft Corporation
Module Name:
common.c
Abstract:
This module contains the teredo interface to the IPv6 Helper Service.
Author:
Mohit Talwar (mohitt) Wed Nov 07 11:27:01 2001
Environment:
User mode only.
--*/
#include "precomp.h"
#pragma hdrstop
CONST IN6_ADDR TeredoIpv6ServicePrefix = TEREDO_SERVICE_PREFIX; CONST IN6_ADDR TeredoIpv6MulticastPrefix = TEREDO_MULTICAST_PREFIX;
#define DEVICE_PREFIX L"\\Device\\"
LPGUID TeredoWmiEvent[] = { (LPGUID) &GUID_NDIS_NOTIFY_ADAPTER_ARRIVAL, (LPGUID) &GUID_NDIS_NOTIFY_ADAPTER_REMOVAL, (LPGUID) &GUID_NDIS_NOTIFY_DEVICE_POWER_ON, (LPGUID) &GUID_NDIS_NOTIFY_DEVICE_POWER_OFF, };
HANDLE TeredoTimer; // Periodic timer started for the service.
HANDLE TeredoTimerEvent; // Event signalled upon Timer deletion.
HANDLE TeredoTimerEventWait; // Wait registered for TimerEvent.
ULONG TeredoResolveInterval = TEREDO_RESOLVE_INTERVAL;
ULONG TeredoClientRefreshInterval = TEREDO_REFRESH_INTERVAL; BOOL TeredoClientEnabled = (TEREDO_DEFAULT_TYPE == TEREDO_CLIENT); BOOL TeredoServerEnabled = (TEREDO_DEFAULT_TYPE == TEREDO_SERVER); WCHAR TeredoServerName[NI_MAXHOST] = TEREDO_SERVER_NAME;
BOOL TeredoInitialized = FALSE;
ICMPv6Header * TeredoParseIpv6Headers ( IN PUCHAR Buffer, IN ULONG Bytes ) { UCHAR NextHeader = IP_PROTOCOL_V6; ULONG Length;
//
// Parse up until the ICMPv6 header.
//
for (;;) { switch (NextHeader) { case IP_PROTOCOL_V6: if (Bytes < sizeof(IP6_HDR)) { return NULL; } NextHeader = ((PIP6_HDR) Buffer)->ip6_nxt; Length = sizeof(IP6_HDR); break; case IP_PROTOCOL_HOP_BY_HOP: case IP_PROTOCOL_DEST_OPTS: case IP_PROTOCOL_ROUTING: if (Bytes < sizeof(ExtensionHeader)) { return NULL; } NextHeader = ((ExtensionHeader *) Buffer)->NextHeader; Length = ((ExtensionHeader *) Buffer)->HeaderExtLength * 8 + 8; break;
case IP_PROTOCOL_FRAGMENT: if (Bytes < sizeof(FragmentHeader)) { return NULL; } NextHeader = ((FragmentHeader *) Buffer)->NextHeader; Length = sizeof(FragmentHeader); break; case IP_PROTOCOL_AH: if (Bytes < sizeof(AHHeader)) { return NULL; } NextHeader = ((AHHeader *) Buffer)->NextHeader; Length = sizeof(AHHeader) + ((AHHeader *) Buffer)->PayloadLen * 4 + 8; break;
case IP_PROTOCOL_ICMPv6: if (Bytes < sizeof(ICMPv6Header)) { return NULL; } return (ICMPv6Header *) Buffer; default: return NULL; } if (Bytes < Length) { return NULL; } Buffer += Length; Bytes -= Length; } }
__inline VOID TeredoStart( VOID ) { //
// Both client and server should not be enabled on the same node.
//
ASSERT(!TeredoClientEnabled || !TeredoServerEnabled);
if (TeredoClientEnabled) { //
// The service might already be running, but that's alright.
//
TeredoStartClient(); }
if (TeredoServerEnabled) { //
// The service might already be running, but that's alright.
//
TeredoStartServer(); } }
__inline VOID TeredoStop( VOID ) { //
// Both client and server should not be enabled on the same node.
//
ASSERT(!TeredoClientEnabled || !TeredoServerEnabled);
if (TeredoClientEnabled) { //
// The service might not be running, but that's all right.
//
TeredoStopClient(); }
if (TeredoServerEnabled) { //
// The service might not be running, but that's all right.
//
TeredoStopServer(); } }
DWORD __inline TeredoEnableWmiEvent( IN LPGUID EventGuid, IN BOOLEAN Enable ) { return WmiNotificationRegistrationW( EventGuid, // Event Type.
Enable, // Enable or Disable.
TeredoWmiEventNotification, // Callback.
0, // Context.
NOTIFICATION_CALLBACK_DIRECT); // Notification Flags.
}
VOID __inline TeredoDeregisterWmiEventNotification( VOID ) { int i; for (i = 0; i < (sizeof(TeredoWmiEvent) / sizeof(LPGUID)); i++) { (VOID) TeredoEnableWmiEvent(TeredoWmiEvent[i], FALSE); } }
DWORD __inline TeredoRegisterWmiEventNotification( VOID ) { DWORD Error; int i; for (i = 0; i < (sizeof(TeredoWmiEvent) / sizeof(LPGUID)); i++) { Error = TeredoEnableWmiEvent(TeredoWmiEvent[i], TRUE); if (Error != NO_ERROR) { Trace2(ANY, L"TeredoEnableWmiEvent(%u): Error(%x)", i, Error); goto Bail; } }
return NO_ERROR;
Bail: TeredoDeregisterWmiEventNotification(); return Error; }
VOID CALLBACK TeredoTimerCallback( IN PVOID Parameter, IN BOOLEAN TimerOrWaitFired ) /*++
Routine Description:
Callback routine for TeredoTimer expiration. The timer is always active.
Arguments:
Parameter, TimerOrWaitFired - Ignored.
Return Value:
None.
--*/ { ENTER_API(); TeredoStart(); LEAVE_API(); }
VOID CALLBACK TeredoTimerCleanup( IN PVOID Parameter, IN BOOLEAN TimerOrWaitFired ) /*++
Routine Description:
Callback routine for TeredoTimer 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(TeredoTimerEventWait); CloseHandle(TeredoTimerEvent); DecEventCount("TeredoCleanupTimer"); }
DWORD TeredoInitializeTimer( VOID ) /*++
Routine Description:
Initializes the timer.
Arguments:
None.
Return Value:
NO_ERROR or failure code.
--*/ { DWORD Error; ULONG ResolveInterval; TeredoTimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (TeredoTimerEvent == NULL) { Error = GetLastError(); return Error; }
if (!RegisterWaitForSingleObject( &(TeredoTimerEventWait), TeredoTimerEvent, TeredoTimerCleanup, NULL, INFINITE, 0)) { Error = GetLastError(); CloseHandle(TeredoTimerEvent); return Error; }
//
// If the service is enabled, we attempt to start it every
// TeredoResolveInterval seconds. Else we disable its timer.
//
ResolveInterval = (TEREDO_DEFAULT_TYPE == TEREDO_DISABLE) ? INFINITE_INTERVAL : (TeredoResolveInterval * 1000); if (!CreateTimerQueueTimer( &(TeredoTimer), NULL, TeredoTimerCallback, NULL, ResolveInterval, ResolveInterval, 0)) { Error = GetLastError(); UnregisterWait(TeredoTimerEventWait); CloseHandle(TeredoTimerEvent); return Error; }
IncEventCount("TeredoInitializeTimer"); return NO_ERROR; }
VOID TeredoUninitializeTimer( VOID ) /*++
Routine Description:
Uninitializes the timer. Typically invoked upon service stop.
Arguments:
None.
Return Value:
None.
--*/ { DeleteTimerQueueTimer(NULL, TeredoTimer, TeredoTimerEvent); TeredoTimer = NULL; }
DWORD TeredoInitializeGlobals( VOID ) /*++
Routine Description:
Initializes the teredo client and server and attempts to start them.
Arguments:
None.
Return Value:
NO_ERROR or failure code.
--*/ { DWORD Error; BOOL ClientInitialized = FALSE; BOOL ServerInitialized = FALSE; BOOL TimerInitialized = FALSE; Error = TeredoInitializeClient(); if (Error != NO_ERROR) { goto Bail; } ClientInitialized = TRUE; Error = TeredoInitializeServer(); if (Error != NO_ERROR) { goto Bail; } ServerInitialized = TRUE;
Error = TeredoInitializeTimer(); if (Error != NO_ERROR) { goto Bail; } TimerInitialized = TRUE;
Error = TeredoRegisterWmiEventNotification(); if (Error != NO_ERROR) { goto Bail; } TeredoStart();
TeredoInitialized = TRUE; return NO_ERROR;
Bail: //
// This can always be safely invoked!
//
TeredoDeregisterWmiEventNotification(); if (TimerInitialized) { TeredoUninitializeTimer(); }
if (ServerInitialized) { TeredoUninitializeServer(); }
if (ClientInitialized) { TeredoUninitializeClient(); }
return Error; }
VOID TeredoUninitializeGlobals( VOID ) /*++
Routine Description:
Uninitializes the teredo client and server.
Arguments:
None.
Return Value:
None. --*/ { if (!TeredoInitialized) { return; }
TeredoDeregisterWmiEventNotification(); TeredoUninitializeTimer(); TeredoUninitializeServer(); TeredoUninitializeClient();
TeredoInitialized = FALSE; }
VOID TeredoAddressChangeNotification( 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.
--*/ { if (Delete) { //
// Both client and server should not be running on the same node.
//
ASSERT((TeredoClient.State == TEREDO_STATE_OFFLINE) || (TeredoServer.State == TEREDO_STATE_OFFLINE));
if (TeredoClient.State != TEREDO_STATE_OFFLINE) { TeredoClientAddressDeletionNotification(Address); } if (TeredoServer.State != TEREDO_STATE_OFFLINE) { TeredoServerAddressDeletionNotification(Address); }
return; }
//
// Address addition.
// Attempt to start the service (if it is not already running).
//
TeredoStart(); }
VOID TeredoConfigurationChangeNotification( VOID ) /*++
Routine Description:
Process an configuration change request.
Arguments:
None. Return Value:
None. Caller LOCK: API.
--*/ { HKEY Key = INVALID_HANDLE_VALUE; TEREDO_TYPE Type; BOOL EnableClient, EnableServer; ULONG RefreshInterval, ResolveInterval; WCHAR OldServerName[NI_MAXHOST]; BOOL IoStateChange = FALSE; (VOID) RegOpenKeyExW( HKEY_LOCAL_MACHINE, KEY_TEREDO, 0, KEY_QUERY_VALUE, &Key); //
// Continue despite errors, reverting to default values.
//
//
// Get the new configuration parameters.
//
RefreshInterval = GetInteger( Key, KEY_TEREDO_REFRESH_INTERVAL, TEREDO_REFRESH_INTERVAL); if (RefreshInterval == 0) { //
// Invalid value. Revert to default.
//
RefreshInterval = TEREDO_REFRESH_INTERVAL; } TeredoClientRefreshInterval = RefreshInterval; TeredoClientRefreshIntervalChangeNotification(); Type = GetInteger(Key, KEY_TEREDO_TYPE, TEREDO_DEFAULT_TYPE); if ((Type == TEREDO_DEFAULT) || (Type >= TEREDO_MAXIMUM)) { //
// Invalid value. Revert to default.
//
Type = TEREDO_DEFAULT_TYPE; } EnableClient = (Type == TEREDO_CLIENT); EnableServer = (Type == TEREDO_SERVER);
wcscpy(OldServerName, TeredoServerName); GetString( Key, KEY_TEREDO_SERVER_NAME, TeredoServerName, NI_MAXHOST, TEREDO_SERVER_NAME); if (_wcsicmp(TeredoServerName, OldServerName) != 0) { IoStateChange = TRUE; }
RegCloseKey(Key); //
// Both client and server should not be enabled on the same node.
//
ASSERT(!TeredoClientEnabled || !TeredoServerEnabled);
//
// Stop / Start / Reconfigure.
//
if (!EnableClient && TeredoClientEnabled) { TeredoClientEnabled = FALSE; TeredoStopClient(); } if (!EnableServer && TeredoServerEnabled) { TeredoServerEnabled = FALSE; TeredoStopServer(); }
if (EnableClient) { if (TeredoClient.State != TEREDO_STATE_OFFLINE) { if (IoStateChange) { //
// Refresh I/O state.
//
TeredoClientAddressDeletionNotification( TeredoClient.Io.SourceAddress.sin_addr); } } else { TeredoClientEnabled = TRUE; TeredoStartClient(); } } if (EnableServer) { if (TeredoServer.State != TEREDO_STATE_OFFLINE) { if (IoStateChange) { //
// Refresh I/O state.
//
TeredoServerAddressDeletionNotification( TeredoServer.Io.SourceAddress.sin_addr); } } else { TeredoServerEnabled = TRUE; TeredoStartServer(); } }
//
// If the service is enabled, we attempt to start it every
// TeredoResolveInterval seconds. Else we disable its timer.
//
ResolveInterval = (Type == TEREDO_DISABLE) ? INFINITE_INTERVAL : (TeredoResolveInterval * 1000); (VOID) ChangeTimerQueueTimer( NULL, TeredoTimer, ResolveInterval, ResolveInterval); }
VOID WINAPI TeredoWmiEventNotification( IN PWNODE_HEADER Event, IN UINT_PTR Context ) /*++
Routine Description:
Process a WMI event (specifically adapter arrival or removal). Arguments:
Event - Supplies event specific information.
Context - Supplies the context registered. Return Value:
None. --*/ { PWNODE_SINGLE_INSTANCE Instance = (PWNODE_SINGLE_INSTANCE) Event; USHORT AdapterNameLength; WCHAR AdapterName[MAX_ADAPTER_NAME_LENGTH], *AdapterGuid; PTEREDO_IO Io = NULL;
if (Instance == NULL) { return; } ENTER_API(); TraceEnter("TeredoWmiEventNotification"); //
// WNODE_SINGLE_INSTANCE is organized thus...
// +-----------------------------------------------------------+
// |<--- DataBlockOffset --->| AdapterNameLength | AdapterName |
// +-----------------------------------------------------------+
//
// AdapterName is defined as "\DEVICE\"AdapterGuid
//
AdapterNameLength = *((PUSHORT) (((PUCHAR) Instance) + Instance->DataBlockOffset)); RtlCopyMemory( AdapterName, ((PUCHAR) Instance) + Instance->DataBlockOffset + sizeof(USHORT), AdapterNameLength); AdapterName[AdapterNameLength / sizeof(WCHAR)] = L'\0'; AdapterGuid = AdapterName + wcslen(DEVICE_PREFIX); Trace1(ANY, L"TeredoAdapter: %s", AdapterGuid);
if ((memcmp( &(Event->Guid), &GUID_NDIS_NOTIFY_ADAPTER_ARRIVAL, sizeof(GUID)) == 0) || (memcmp( &(Event->Guid), &GUID_NDIS_NOTIFY_DEVICE_POWER_ON, sizeof(GUID)) == 0)) { //
// Adapter arrival (perhaps TUN).
// Attempt to start the service (if it is not already running).
//
Trace0(ANY, L"Adapter Arrival"); TeredoStart(); }
if ((memcmp( &(Event->Guid), &GUID_NDIS_NOTIFY_ADAPTER_REMOVAL, sizeof(GUID)) == 0) || (memcmp( &(Event->Guid), &GUID_NDIS_NOTIFY_DEVICE_POWER_OFF, sizeof(GUID)) == 0)) { if (TeredoClient.State != TEREDO_STATE_OFFLINE) { Io = &(TeredoClient.Io); }
if (TeredoServer.State != TEREDO_STATE_OFFLINE) { Io = &(TeredoServer.Io); }
if ((Io != NULL) && (_wcsicmp(Io->TunnelInterface, AdapterGuid) == 0)) { //
// TUN adapter removal.
// Stop the service if it is running.
//
Trace0(ANY, L"Adapter Removal"); TeredoStop(); } }
LEAVE_API(); }
|