windows-nt/Source/XPSP1/NT/net/tcpip/tpipv6/6to4svc/common.c
2020-09-26 16:20:57 +08:00

773 lines
16 KiB
C

/*++
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();
}