You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3415 lines
96 KiB
3415 lines
96 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Abstract:
|
|
|
|
Functions implementing the 6to4 service, to provide IPv6 connectivity
|
|
over an IPv4 network.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
extern DWORD
|
|
APIENTRY
|
|
RasQuerySharedPrivateLan(
|
|
OUT GUID* LanGuid );
|
|
|
|
STATE g_stService = DISABLED;
|
|
ULONG g_ulEventCount = 0;
|
|
|
|
//
|
|
// Worst metric for which we can add a route
|
|
//
|
|
#define UNREACHABLE 0x7fffffff
|
|
|
|
const IN6_ADDR SixToFourPrefix = { 0x20, 0x02, 0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
|
|
#define SIXTOFOUR_METRIC 1000
|
|
|
|
// Metric of subnet/sitelocal route on a router
|
|
#define SUBNET_ROUTE_METRIC 1
|
|
#define SITELOCAL_ROUTE_METRIC 1
|
|
|
|
// Information on a 6to4 subnet that we've generated as a router
|
|
typedef struct _SUBNET_CONTEXT {
|
|
IN_ADDR V4Addr;
|
|
int Publish;
|
|
u_int ValidLifetime;
|
|
u_int PreferredLifetime;
|
|
} SUBNET_CONTEXT, *PSUBNET_CONTEXT;
|
|
|
|
//
|
|
// Variables for settings
|
|
//
|
|
|
|
#define DEFAULT_ENABLE_6TO4 AUTOMATIC
|
|
#define DEFAULT_ENABLE_RESOLUTION AUTOMATIC
|
|
#define DEFAULT_ENABLE_ROUTING AUTOMATIC
|
|
#define DEFAULT_RESOLUTION_INTERVAL (24 * HOURS)
|
|
#define DEFAULT_ENABLE_SITELOCALS ENABLED
|
|
#define DEFAULT_ENABLE_6OVER4 DISABLED
|
|
#define DEFAULT_ENABLE_V4COMPAT DISABLED
|
|
#define DEFAULT_RELAY_NAME L"6to4.ipv6.microsoft.com."
|
|
#define DEFAULT_UNDO_ON_STOP ENABLED
|
|
|
|
#define KEY_ENABLE_6TO4 L"Enable6to4"
|
|
#define KEY_ENABLE_RESOLUTION L"EnableResolution"
|
|
#define KEY_ENABLE_ROUTING L"EnableRouting"
|
|
#define KEY_ENABLE_SITELOCALS L"EnableSiteLocals"
|
|
#define KEY_ENABLE_6OVER4 L"Enable6over4"
|
|
#define KEY_ENABLE_V4COMPAT L"EnableV4Compat"
|
|
#define KEY_RESOLUTION_INTERVAL L"ResolutionInterval"
|
|
#define KEY_UNDO_ON_STOP L"UndoOnStop"
|
|
#define KEY_RELAY_NAME L"RelayName"
|
|
|
|
typedef enum {
|
|
IPV4_SCOPE_NODE,
|
|
IPV4_SCOPE_LINK,
|
|
IPV4_SCOPE_SM_SITE,
|
|
IPV4_SCOPE_MD_SITE,
|
|
IPV4_SCOPE_LG_SITE,
|
|
IPV4_SCOPE_GLOBAL,
|
|
NUM_IPV4_SCOPES
|
|
} IPV4_SCOPE;
|
|
|
|
//
|
|
// Global config settings
|
|
//
|
|
|
|
typedef struct {
|
|
STATE stEnable6to4;
|
|
STATE stEnableRouting;
|
|
STATE stEnableResolution;
|
|
STATE stEnableSiteLocals;
|
|
STATE stEnable6over4;
|
|
STATE stEnableV4Compat;
|
|
ULONG ulResolutionInterval; // in minutes
|
|
WCHAR pwszRelayName[NI_MAXHOST];
|
|
STATE stUndoOnStop;
|
|
} GLOBAL_SETTINGS;
|
|
|
|
GLOBAL_SETTINGS g_GlobalSettings;
|
|
|
|
typedef struct {
|
|
STATE st6to4State;
|
|
STATE stRoutingState;
|
|
STATE stResolutionState;
|
|
} GLOBAL_STATE;
|
|
|
|
GLOBAL_STATE g_GlobalState = { DISABLED, DISABLED, DISABLED };
|
|
|
|
const ADDR_LIST EmptyAddressList = {0};
|
|
|
|
// List of public IPv4 addresses used when updating the routing state
|
|
ADDR_LIST *g_pIpv4AddressList = NULL;
|
|
|
|
//
|
|
// Variables for interfaces (addresses and routing)
|
|
//
|
|
|
|
typedef struct _IF_SETTINGS {
|
|
WCHAR pwszAdapterName[MAX_ADAPTER_NAME];
|
|
|
|
STATE stEnableRouting; // be a router on this private iface?
|
|
} IF_SETTINGS, *PIF_SETTINGS;
|
|
|
|
typedef struct _IF_SETTINGS_LIST {
|
|
ULONG ulNumInterfaces;
|
|
IF_SETTINGS arrIf[0];
|
|
} IF_SETTINGS_LIST, *PIF_SETTINGS_LIST;
|
|
|
|
PIF_SETTINGS_LIST g_pInterfaceSettingsList = NULL;
|
|
|
|
typedef struct _IF_INFO {
|
|
WCHAR pwszAdapterName[MAX_ADAPTER_NAME];
|
|
|
|
ULONG ulIPv6IfIndex;
|
|
STATE stRoutingState; // be a router on this private iface?
|
|
ULONG ulNumGlobals;
|
|
ADDR_LIST *pAddressList;
|
|
} IF_INFO, *PIF_INFO;
|
|
|
|
typedef struct _IF_LIST {
|
|
ULONG ulNumInterfaces;
|
|
ULONG ulNumScopedAddrs[NUM_IPV4_SCOPES];
|
|
IF_INFO arrIf[0];
|
|
} IF_LIST, *PIF_LIST;
|
|
|
|
PIF_LIST g_pInterfaceList = NULL;
|
|
|
|
HANDLE g_hAddressChangeEvent = NULL;
|
|
OVERLAPPED g_hAddressChangeOverlapped;
|
|
HANDLE g_hAddressChangeWaitHandle = NULL;
|
|
|
|
HANDLE g_hRouteChangeEvent = NULL;
|
|
OVERLAPPED g_hRouteChangeOverlapped;
|
|
HANDLE g_hRouteChangeWaitHandle = NULL;
|
|
|
|
// This state tracks whether there are any global IPv4 addresses.
|
|
STATE g_st6to4State = DISABLED;
|
|
|
|
BOOL g_b6to4Required = TRUE;
|
|
|
|
SOCKET g_sIPv4Socket = INVALID_SOCKET;
|
|
|
|
|
|
//////////////////////////
|
|
// Routines for 6to4
|
|
//////////////////////////
|
|
|
|
VOID
|
|
Update6to4State(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
PreDelete6to4Address(
|
|
IN LPSOCKADDR_IN Ipv4Address,
|
|
IN PIF_LIST InterfaceList,
|
|
IN STATE OldRoutingState
|
|
);
|
|
|
|
VOID
|
|
Delete6to4Address(
|
|
IN LPSOCKADDR_IN Ipv4Address,
|
|
IN PIF_LIST InterfaceList,
|
|
IN STATE OldRoutingState
|
|
);
|
|
|
|
VOID
|
|
Add6to4Address(
|
|
IN LPSOCKADDR_IN Ipv4Address,
|
|
IN PIF_LIST InterfaceList,
|
|
IN STATE OldRoutingState
|
|
);
|
|
|
|
VOID
|
|
PreDelete6to4Routes(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
Update6to4Routes(
|
|
VOID
|
|
);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Variables for relays
|
|
//
|
|
|
|
typedef struct _RELAY_INFO {
|
|
SOCKADDR_IN sinAddress; // IPv4 address
|
|
SOCKADDR_IN6 sin6Address; // IPv6 address
|
|
ULONG ulMetric;
|
|
} RELAY_INFO, *PRELAY_INFO;
|
|
|
|
typedef struct _RELAY_LIST {
|
|
ULONG ulNumRelays;
|
|
RELAY_INFO arrRelay[0];
|
|
} RELAY_LIST, *PRELAY_LIST;
|
|
|
|
PRELAY_LIST g_pRelayList = NULL;
|
|
HANDLE g_hTimerQueue = INVALID_HANDLE_VALUE;
|
|
HANDLE g_h6to4ResolutionTimer = INVALID_HANDLE_VALUE;
|
|
HANDLE g_h6to4TimerCancelledEvent = NULL;
|
|
HANDLE g_h6to4TimerCancelledWait = NULL;
|
|
|
|
VOID
|
|
UpdateGlobalResolutionState();
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// GetAddrStr - helper routine to get a string literal for an address
|
|
LPTSTR
|
|
GetAddrStr(
|
|
IN LPSOCKADDR pSockaddr,
|
|
IN ULONG ulSockaddrLen)
|
|
{
|
|
static TCHAR tBuffer[INET6_ADDRSTRLEN];
|
|
INT iRet;
|
|
ULONG ulLen;
|
|
|
|
ulLen = sizeof(tBuffer);
|
|
iRet = WSAAddressToString(pSockaddr, ulSockaddrLen, NULL, tBuffer, &ulLen);
|
|
|
|
if (iRet) {
|
|
swprintf(tBuffer, L"<err %d>", WSAGetLastError());
|
|
}
|
|
|
|
return tBuffer;
|
|
}
|
|
|
|
BOOL
|
|
ConvertOemToUnicode(
|
|
IN LPSTR OemString,
|
|
OUT LPWSTR UnicodeString,
|
|
IN int UnicodeLen)
|
|
{
|
|
return (MultiByteToWideChar(CP_OEMCP, 0, OemString, (int)(strlen(OemString)+1),
|
|
UnicodeString, UnicodeLen) != 0);
|
|
}
|
|
|
|
BOOL
|
|
ConvertUnicodeToOem(
|
|
IN LPWSTR UnicodeString,
|
|
OUT LPSTR OemString,
|
|
IN int OemLen)
|
|
{
|
|
return (WideCharToMultiByte(CP_OEMCP, 0, UnicodeString,
|
|
(int)(wcslen(UnicodeString)+1), OemString, OemLen, NULL, NULL) != 0);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Subroutines for manipulating the list of (usually) public addresses
|
|
// being used for both 6to4 addresses and subnet prefixes.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
MakeEmptyAddressList(
|
|
OUT PADDR_LIST *ppList)
|
|
{
|
|
*ppList = MALLOC(FIELD_OFFSET(ADDR_LIST, Address[0]));
|
|
if (!*ppList) {
|
|
return GetLastError();
|
|
}
|
|
|
|
(*ppList)->iAddressCount = 0;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
FreeAddressList(
|
|
IN PADDR_LIST *ppAddressList)
|
|
{
|
|
ADDR_LIST *pList = *ppAddressList;
|
|
int i;
|
|
|
|
if (pList == NULL) {
|
|
return;
|
|
}
|
|
|
|
// Free all addresses
|
|
for (i=0; i<pList->iAddressCount; i++) {
|
|
FREE(pList->Address[i].lpSockaddr);
|
|
}
|
|
|
|
// Free the list
|
|
FREE(pList);
|
|
*ppAddressList = NULL;
|
|
}
|
|
|
|
DWORD
|
|
AddAddressToList(
|
|
IN LPSOCKADDR_IN pAddress,
|
|
IN ADDR_LIST **ppAddressList,
|
|
IN ULONG ul6over4IfIndex)
|
|
{
|
|
ADDR_LIST *pOldList = *ppAddressList;
|
|
ADDR_LIST *pNewList;
|
|
int n = pOldList->iAddressCount;
|
|
|
|
// Copy existing addresses
|
|
pNewList = MALLOC( FIELD_OFFSET(ADDR_LIST, Address[n+1]) );
|
|
if (!pNewList) {
|
|
return GetLastError();
|
|
}
|
|
CopyMemory(pNewList, pOldList,
|
|
FIELD_OFFSET(ADDR_LIST, Address[n]));
|
|
pNewList->iAddressCount = n+1;
|
|
|
|
// Add new address
|
|
pNewList->Address[n].lpSockaddr = MALLOC(sizeof(SOCKADDR_IN));
|
|
if (!pNewList->Address[n].lpSockaddr) {
|
|
FREE(pNewList);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
CopyMemory(pNewList->Address[n].lpSockaddr, pAddress, sizeof(SOCKADDR_IN));
|
|
pNewList->Address[n].iSockaddrLength = sizeof(SOCKADDR_IN);
|
|
pNewList->Address[n].ul6over4IfIndex = ul6over4IfIndex;
|
|
|
|
// Free the old list without freeing the sockaddrs
|
|
FREE(pOldList);
|
|
|
|
*ppAddressList = pNewList;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
FindAddressInList(
|
|
IN LPSOCKADDR_IN pAddress,
|
|
IN ADDR_LIST *pAddressList,
|
|
OUT ULONG *pulIndex)
|
|
{
|
|
int i;
|
|
|
|
// Find address in list
|
|
for (i=0; i<pAddressList->iAddressCount; i++) {
|
|
if (!memcmp(pAddress, pAddressList->Address[i].lpSockaddr,
|
|
sizeof(SOCKADDR_IN))) {
|
|
*pulIndex = i;
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
|
|
Trace1(ERR, _T("ERROR: FindAddressInList didn't find %d.%d.%d.%d"),
|
|
PRINT_IPADDR(pAddress->sin_addr.s_addr));
|
|
|
|
return ERROR_NOT_FOUND;
|
|
}
|
|
|
|
DWORD
|
|
RemoveAddressFromList(
|
|
IN ULONG ulIndex,
|
|
IN ADDR_LIST *pAddressList)
|
|
{
|
|
// Free old address
|
|
FREE(pAddressList->Address[ulIndex].lpSockaddr);
|
|
|
|
// Move the last entry into its place
|
|
pAddressList->iAddressCount--;
|
|
pAddressList->Address[ulIndex] =
|
|
pAddressList->Address[pAddressList->iAddressCount];
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// GlobalInfo-related subroutines
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
ConfigureRouteTableUpdate(
|
|
IN const IN6_ADDR *Prefix,
|
|
IN u_int PrefixLen,
|
|
IN u_int Interface,
|
|
IN const IN6_ADDR *Neighbor,
|
|
IN int Publish,
|
|
IN int Immortal,
|
|
IN u_int ValidLifetime,
|
|
IN u_int PreferredLifetime,
|
|
IN u_int SitePrefixLen,
|
|
IN u_int Metric)
|
|
{
|
|
IPV6_INFO_ROUTE_TABLE Route;
|
|
SOCKADDR_IN6 saddr;
|
|
DWORD dwErr;
|
|
|
|
ZeroMemory(&saddr, sizeof(saddr));
|
|
saddr.sin6_family = AF_INET6;
|
|
saddr.sin6_addr = *Prefix;
|
|
|
|
Trace7(FSM, _T("Updating route %s/%d iface %d metric %d lifetime %d/%d publish %d"),
|
|
GetAddrStr((LPSOCKADDR)&saddr, sizeof(saddr)),
|
|
PrefixLen,
|
|
Interface,
|
|
Metric,
|
|
PreferredLifetime,
|
|
ValidLifetime,
|
|
Publish);
|
|
|
|
memset(&Route, 0, sizeof Route);
|
|
Route.This.Prefix = *Prefix;
|
|
Route.This.PrefixLength = PrefixLen;
|
|
Route.This.Neighbor.IF.Index = Interface;
|
|
Route.This.Neighbor.Address = *Neighbor;
|
|
Route.ValidLifetime = ValidLifetime;
|
|
Route.PreferredLifetime = PreferredLifetime;
|
|
Route.Publish = Publish;
|
|
Route.Immortal = Immortal;
|
|
Route.SitePrefixLength = SitePrefixLen;
|
|
Route.Preference = Metric;
|
|
Route.Type = RTE_TYPE_MANUAL;
|
|
|
|
dwErr = UpdateRouteTable(&Route)? NO_ERROR : GetLastError();
|
|
|
|
if (dwErr != NO_ERROR) {
|
|
Trace1(ERR, _T("UpdateRouteTable got error %d"), dwErr);
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
InitializeGlobalInfo()
|
|
{
|
|
DWORD dwErr;
|
|
|
|
g_GlobalSettings.stEnable6to4 = DEFAULT_ENABLE_6TO4;
|
|
g_GlobalSettings.stEnableRouting = DEFAULT_ENABLE_ROUTING;
|
|
g_GlobalSettings.stEnableResolution = DEFAULT_ENABLE_RESOLUTION;
|
|
g_GlobalSettings.ulResolutionInterval = DEFAULT_RESOLUTION_INTERVAL;
|
|
g_GlobalSettings.stEnableSiteLocals = DEFAULT_ENABLE_SITELOCALS;
|
|
g_GlobalSettings.stEnable6over4 = DEFAULT_ENABLE_6OVER4;
|
|
g_GlobalSettings.stEnableV4Compat = DEFAULT_ENABLE_V4COMPAT;
|
|
g_GlobalSettings.stUndoOnStop = DEFAULT_UNDO_ON_STOP;
|
|
wcscpy(g_GlobalSettings.pwszRelayName, DEFAULT_RELAY_NAME);
|
|
|
|
g_GlobalState.st6to4State = DISABLED;
|
|
g_GlobalState.stRoutingState = DISABLED;
|
|
g_GlobalState.stResolutionState = DISABLED;
|
|
|
|
g_sIPv4Socket = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (g_sIPv4Socket == INVALID_SOCKET) {
|
|
Trace0(ERR, _T("socket failed\n"));
|
|
return WSAGetLastError();
|
|
}
|
|
|
|
dwErr = MakeEmptyAddressList(&g_pIpv4AddressList);
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
// Called by: Stop6to4
|
|
VOID
|
|
UninitializeGlobalInfo()
|
|
{
|
|
closesocket(g_sIPv4Socket);
|
|
g_sIPv4Socket = INVALID_SOCKET;
|
|
|
|
FreeAddressList(&g_pIpv4AddressList);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// IPv4 and IPv6 Address-related subroutines
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct {
|
|
IPV4_SCOPE Scope;
|
|
DWORD Address;
|
|
DWORD Mask;
|
|
ULONG MaskLen;
|
|
} IPV4_SCOPE_PREFIX;
|
|
|
|
IPV4_SCOPE_PREFIX
|
|
Ipv4ScopePrefix[] = {
|
|
{ IPV4_SCOPE_NODE, 0x0100007f, 0xffffffff, 32 }, // 127.0.0.1/32
|
|
{ IPV4_SCOPE_LINK, 0x0000fea9, 0x0000ffff, 16 }, // 169.254/16
|
|
{ IPV4_SCOPE_SM_SITE, 0x0000a8c0, 0x0000ffff, 16 }, // 192.168/16
|
|
{ IPV4_SCOPE_MD_SITE, 0x000010ac, 0x0000f0ff, 12 }, // 172.16/12
|
|
{ IPV4_SCOPE_LG_SITE, 0x0000000a, 0x000000ff, 8 }, // 10/8
|
|
{ IPV4_SCOPE_GLOBAL, 0x00000000, 0x00000000, 0 }, // 0/0
|
|
};
|
|
|
|
IPV4_SCOPE
|
|
GetIPv4Scope(
|
|
IN DWORD Addr)
|
|
{
|
|
int i;
|
|
for (i=0; ; i++) {
|
|
if ((Addr & Ipv4ScopePrefix[i].Mask) == Ipv4ScopePrefix[i].Address) {
|
|
return Ipv4ScopePrefix[i].Scope;
|
|
}
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
MakeAddressList(
|
|
IN PIP_ADDR_STRING pIpAddrList,
|
|
OUT ADDR_LIST **ppAddressList,
|
|
OUT PULONG pulGlobals,
|
|
IN OUT PULONG pulCumulNumScopedAddrs)
|
|
{
|
|
ULONG ulGlobals = 0, ulAddresses = 0;
|
|
INT iLength;
|
|
DWORD dwErr = NO_ERROR;
|
|
ADDR_LIST *pList = NULL;
|
|
PIP_ADDR_STRING pIpAddr;
|
|
SOCKADDR_IN *pSin;
|
|
IPV4_SCOPE scope;
|
|
|
|
// Count addresses
|
|
for (pIpAddr=pIpAddrList; pIpAddr; pIpAddr=pIpAddr->Next) {
|
|
ulAddresses++;
|
|
}
|
|
|
|
*ppAddressList = NULL;
|
|
*pulGlobals = 0;
|
|
|
|
pList = MALLOC( FIELD_OFFSET(ADDR_LIST, Address[ulAddresses] ));
|
|
if (pList == NULL) {
|
|
return GetLastError();
|
|
}
|
|
|
|
ulAddresses = 0;
|
|
for (pIpAddr=pIpAddrList; pIpAddr; pIpAddr=pIpAddr->Next) {
|
|
|
|
Trace1(FSM, _T("Adding address %hs"), pIpAddr->IpAddress.String);
|
|
|
|
iLength = sizeof(SOCKADDR_IN);
|
|
pSin = MALLOC( iLength );
|
|
if (pSin == NULL) {
|
|
continue;
|
|
}
|
|
|
|
dwErr = WSAStringToAddressA(pIpAddr->IpAddress.String,
|
|
AF_INET,
|
|
NULL,
|
|
(LPSOCKADDR)pSin,
|
|
&iLength);
|
|
if (dwErr == SOCKET_ERROR) {
|
|
FREE(pSin);
|
|
pSin = NULL;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Don't allow 0.0.0.0 as an address. On an interface with no
|
|
// addresses, the IPv4 stack will report 1 address of 0.0.0.0.
|
|
//
|
|
if (pSin->sin_addr.s_addr == INADDR_ANY) {
|
|
FREE(pSin);
|
|
pSin = NULL;
|
|
continue;
|
|
}
|
|
|
|
if ((pSin->sin_addr.s_addr & 0x000000FF) == 0) {
|
|
//
|
|
// An address in 0/8 isn't a real IP address, it's a fake one that
|
|
// the IPv4 stack sticks on a receive-only adapter.
|
|
//
|
|
FREE(pSin);
|
|
pSin = NULL;
|
|
continue;
|
|
}
|
|
|
|
scope = GetIPv4Scope(pSin->sin_addr.s_addr);
|
|
pulCumulNumScopedAddrs[scope]++;
|
|
|
|
if (scope == IPV4_SCOPE_GLOBAL) {
|
|
ulGlobals++;
|
|
}
|
|
|
|
pList->Address[ulAddresses].iSockaddrLength = iLength;
|
|
pList->Address[ulAddresses].lpSockaddr = (LPSOCKADDR)pSin;
|
|
ulAddresses++;
|
|
}
|
|
|
|
pList->iAddressCount = ulAddresses;
|
|
*ppAddressList = pList;
|
|
*pulGlobals = ulGlobals;
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
//
|
|
// Create a 6to4 unicast address for this machine.
|
|
//
|
|
VOID
|
|
Make6to4Address(
|
|
OUT LPSOCKADDR_IN6 pIPv6Address,
|
|
IN LPSOCKADDR_IN pIPv4Address)
|
|
{
|
|
IN_ADDR *pIPv4 = &pIPv4Address->sin_addr;
|
|
|
|
memset(pIPv6Address, 0, sizeof (SOCKADDR_IN6));
|
|
pIPv6Address->sin6_family = AF_INET6;
|
|
|
|
pIPv6Address->sin6_addr.s6_addr[0] = 0x20;
|
|
pIPv6Address->sin6_addr.s6_addr[1] = 0x02;
|
|
memcpy(&pIPv6Address->sin6_addr.s6_addr[2], pIPv4, sizeof(IN_ADDR));
|
|
memcpy(&pIPv6Address->sin6_addr.s6_addr[12], pIPv4, sizeof(IN_ADDR));
|
|
}
|
|
|
|
|
|
//
|
|
// Create a 6to4 anycast address from a local IPv4 address.
|
|
//
|
|
VOID
|
|
Make6to4AnycastAddress(
|
|
OUT LPSOCKADDR_IN6 pIPv6Address,
|
|
IN LPSOCKADDR_IN pIPv4Address)
|
|
{
|
|
IN_ADDR *pIPv4 = &pIPv4Address->sin_addr;
|
|
|
|
memset(pIPv6Address, 0, sizeof(SOCKADDR_IN6));
|
|
pIPv6Address->sin6_family = AF_INET6;
|
|
pIPv6Address->sin6_addr.s6_addr[0] = 0x20;
|
|
pIPv6Address->sin6_addr.s6_addr[1] = 0x02;
|
|
memcpy(&pIPv6Address->sin6_addr.s6_addr[2], pIPv4, sizeof(IN_ADDR));
|
|
}
|
|
|
|
//
|
|
// Create a v4-compatible address from an IPv4 address.
|
|
//
|
|
VOID
|
|
MakeV4CompatibleAddress(
|
|
OUT LPSOCKADDR_IN6 pIPv6Address,
|
|
IN LPSOCKADDR_IN pIPv4Address)
|
|
{
|
|
IN_ADDR *pIPv4 = &pIPv4Address->sin_addr;
|
|
|
|
memset(pIPv6Address, 0, sizeof(SOCKADDR_IN6));
|
|
pIPv6Address->sin6_family = AF_INET6;
|
|
memcpy(&pIPv6Address->sin6_addr.s6_addr[12], pIPv4, sizeof(IN_ADDR));
|
|
}
|
|
|
|
DWORD
|
|
ConfigureAddressUpdate(
|
|
IN u_int Interface,
|
|
IN SOCKADDR_IN6 *Sockaddr,
|
|
IN u_int Lifetime,
|
|
IN int Type,
|
|
IN u_int PrefixConf,
|
|
IN u_int SuffixConf)
|
|
{
|
|
IPV6_UPDATE_ADDRESS Address;
|
|
DWORD dwErr = NO_ERROR;
|
|
IN6_ADDR *Addr = &Sockaddr->sin6_addr;
|
|
|
|
Trace6(FSM,
|
|
_T("ConfigureAddressUpdate: if %u addr %s life %u type %d conf %u/%u"),
|
|
Interface,
|
|
GetAddrStr((LPSOCKADDR)Sockaddr, sizeof(SOCKADDR_IN6)),
|
|
Lifetime,
|
|
Type,
|
|
PrefixConf,
|
|
SuffixConf);
|
|
|
|
memset(&Address, 0, sizeof Address);
|
|
Address.This.IF.Index = Interface;
|
|
Address.This.Address = *Addr;
|
|
Address.ValidLifetime = Address.PreferredLifetime = Lifetime;
|
|
Address.Type = Type;
|
|
Address.PrefixConf = PrefixConf;
|
|
Address.InterfaceIdConf = SuffixConf;
|
|
|
|
if (!UpdateAddress(&Address)) {
|
|
dwErr = GetLastError();
|
|
Trace1(ERR, _T("ERROR: UpdateAddress got error %d"), dwErr);
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
void
|
|
Configure6to4Subnets(
|
|
IN ULONG ulIfIndex,
|
|
IN PSUBNET_CONTEXT pSubnet);
|
|
|
|
void
|
|
Unconfigure6to4Subnets(
|
|
IN ULONG ulIfIndex,
|
|
IN PSUBNET_CONTEXT pSubnet);
|
|
|
|
// Called by: OnChangeInterfaceInfo
|
|
DWORD
|
|
AddAddress(
|
|
IN LPSOCKADDR_IN pIPv4Address, // public address
|
|
IN PIF_LIST pInterfaceList, // interface list
|
|
IN STATE stOldRoutingState) // routing state
|
|
{
|
|
SOCKADDR_IN6 OurAddress;
|
|
DWORD dwErr;
|
|
ULONG ul6over4IfIndex;
|
|
|
|
Trace2(ENTER, _T("AddAddress %d.%d.%d.%d, isrouter=%d"),
|
|
PRINT_IPADDR(pIPv4Address->sin_addr.s_addr),
|
|
stOldRoutingState);
|
|
|
|
// Add 6over4 interface (if enabled)
|
|
if (g_GlobalSettings.stEnable6over4 == ENABLED) {
|
|
ul6over4IfIndex = Create6over4Interface(pIPv4Address->sin_addr);
|
|
} else {
|
|
ul6over4IfIndex = 0;
|
|
}
|
|
|
|
Trace1(ERR, _T("6over4 ifindex=%d"), ul6over4IfIndex);
|
|
|
|
// Put the IPv4 address on our "public" list
|
|
dwErr = AddAddressToList(pIPv4Address, &g_pIpv4AddressList,
|
|
ul6over4IfIndex);
|
|
if (dwErr != NO_ERROR) {
|
|
return dwErr;
|
|
}
|
|
|
|
if (GetIPv4Scope(pIPv4Address->sin_addr.s_addr) == IPV4_SCOPE_GLOBAL) {
|
|
// Add v4-compatible address (if enabled)
|
|
if (g_GlobalSettings.stEnableV4Compat == ENABLED) {
|
|
MakeV4CompatibleAddress(&OurAddress, pIPv4Address);
|
|
dwErr = ConfigureAddressUpdate(
|
|
V4_COMPAT_IFINDEX, &OurAddress, INFINITE_LIFETIME,
|
|
ADE_UNICAST, PREFIX_CONF_WELLKNOWN,
|
|
IID_CONF_LL_ADDRESS);
|
|
if (dwErr != NO_ERROR) {
|
|
return dwErr;
|
|
}
|
|
}
|
|
}
|
|
|
|
IsatapAddressChangeNotification(FALSE, pIPv4Address->sin_addr);
|
|
|
|
#ifdef TEREDO
|
|
TeredoAddressChangeNotification(FALSE, pIPv4Address->sin_addr);
|
|
#endif // TEREDO
|
|
|
|
Add6to4Address(pIPv4Address, pInterfaceList, stOldRoutingState);
|
|
|
|
TraceLeave("AddAddress");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// Delete the 6to4 address from the global state, and prepare to
|
|
// delete it from the stack.
|
|
//
|
|
// Called by: UninitializeInterfaces
|
|
VOID
|
|
PreDeleteAddress(
|
|
IN LPSOCKADDR_IN pIPv4Address,
|
|
IN PIF_LIST pInterfaceList,
|
|
IN STATE stOldRoutingState)
|
|
{
|
|
Trace2(ENTER, _T("PreDeleteAddress %d.%d.%d.%d, wasrouter=%d"),
|
|
PRINT_IPADDR(pIPv4Address->sin_addr.s_addr),
|
|
stOldRoutingState);
|
|
|
|
PreDelete6to4Address(pIPv4Address, pInterfaceList, stOldRoutingState);
|
|
|
|
TraceLeave("PreDeleteAddress");
|
|
}
|
|
|
|
// Delete 6to4 address information from the stack.
|
|
//
|
|
// Called by: OnChangeInterfaceInfo, UninitializeInterfaces
|
|
VOID
|
|
DeleteAddress(
|
|
IN LPSOCKADDR_IN pIPv4Address,
|
|
IN PIF_LIST pInterfaceList,
|
|
IN STATE stOldRoutingState)
|
|
{
|
|
SOCKADDR_IN6 OurAddress;
|
|
DWORD dwErr;
|
|
ULONG i;
|
|
|
|
Trace2(ENTER, _T("DeleteAddress %d.%d.%d.%d wasrouter=%d"),
|
|
PRINT_IPADDR(pIPv4Address->sin_addr.s_addr),
|
|
stOldRoutingState);
|
|
|
|
if (GetIPv4Scope(pIPv4Address->sin_addr.s_addr) == IPV4_SCOPE_GLOBAL) {
|
|
|
|
// Delete the v4-compatible address from the stack (if enabled)
|
|
if (g_GlobalSettings.stEnableV4Compat == ENABLED) {
|
|
MakeV4CompatibleAddress(&OurAddress, pIPv4Address);
|
|
ConfigureAddressUpdate(
|
|
V4_COMPAT_IFINDEX, &OurAddress, 0, ADE_UNICAST,
|
|
PREFIX_CONF_WELLKNOWN, IID_CONF_LL_ADDRESS);
|
|
}
|
|
}
|
|
|
|
IsatapAddressChangeNotification(TRUE, pIPv4Address->sin_addr);
|
|
|
|
#ifdef TEREDO
|
|
TeredoAddressChangeNotification(TRUE, pIPv4Address->sin_addr);
|
|
#endif // TEREDO
|
|
|
|
Delete6to4Address(pIPv4Address, pInterfaceList, stOldRoutingState);
|
|
|
|
//
|
|
// We're now completely done with the IPv4 address, so
|
|
// remove it from the public address list.
|
|
//
|
|
dwErr = FindAddressInList(pIPv4Address, g_pIpv4AddressList, &i);
|
|
if (dwErr == NO_ERROR) {
|
|
// Delete 6over4 interface (if enabled)
|
|
if (g_GlobalSettings.stEnable6over4 == ENABLED) {
|
|
DeleteInterface(g_pIpv4AddressList->Address[i].ul6over4IfIndex);
|
|
}
|
|
|
|
RemoveAddressFromList(i, g_pIpv4AddressList);
|
|
}
|
|
|
|
TraceLeave("DeleteAddress");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Relay-related subroutines
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Given a relay, make sure a default route to it exists with the right metric
|
|
//
|
|
VOID
|
|
AddOrUpdate6to4Relay(
|
|
IN PRELAY_INFO pRelay)
|
|
{
|
|
Trace1(ENTER, _T("AddOrUpdate6to4Relay %d.%d.%d.%d"),
|
|
PRINT_IPADDR(pRelay->sinAddress.sin_addr.s_addr));
|
|
|
|
//
|
|
// Create the default route.
|
|
//
|
|
ConfigureRouteTableUpdate(&in6addr_any, 0,
|
|
SIX_TO_FOUR_IFINDEX,
|
|
&pRelay->sin6Address.sin6_addr,
|
|
TRUE, // Publish.
|
|
TRUE, // Immortal.
|
|
2 * HOURS, // Valid lifetime.
|
|
30 * MINUTES, // Preferred lifetime.
|
|
0,
|
|
pRelay->ulMetric);
|
|
}
|
|
|
|
VOID
|
|
FreeRelayList(
|
|
IN PRELAY_LIST *ppList)
|
|
{
|
|
if (*ppList) {
|
|
FREE(*ppList);
|
|
*ppList = NULL;
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
InitializeRelays()
|
|
{
|
|
g_pRelayList = NULL;
|
|
|
|
g_hTimerQueue = CreateTimerQueue();
|
|
if (g_hTimerQueue == INVALID_HANDLE_VALUE) {
|
|
return GetLastError();
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
IncEventCount(
|
|
IN PCHAR pszWhere)
|
|
{
|
|
ULONG ulCount = InterlockedIncrement(&g_ulEventCount);
|
|
Trace2(FSM, _T("++%u event count (%hs)"), ulCount, pszWhere);
|
|
}
|
|
|
|
VOID
|
|
DecEventCount(
|
|
IN PCHAR pszWhere)
|
|
{
|
|
|
|
ULONG ulCount = InterlockedDecrement(&g_ulEventCount);
|
|
Trace2(FSM, _T("--%u event count (%hs)"), ulCount, pszWhere);
|
|
|
|
if ((ulCount == 0) && (g_stService == DISABLED)) {
|
|
SetHelperServiceStatus(SERVICE_STOPPED, NO_ERROR);
|
|
}
|
|
}
|
|
|
|
// This routine is invoked when a resolution timer has been cancelled
|
|
// and all outstanding timer routines have completed. It is responsible
|
|
// for releasing the event count for the periodic timer.
|
|
//
|
|
VOID CALLBACK
|
|
OnResolutionTimerCancelled(
|
|
IN PVOID lpParameter,
|
|
IN BOOLEAN TimerOrWaitFired)
|
|
{
|
|
TraceEnter("OnResolutionTimerCancelled");
|
|
|
|
DecEventCount("RT:CancelResolutionTimer");
|
|
|
|
TraceLeave("OnResolutionTimerCancelled");
|
|
}
|
|
|
|
DWORD
|
|
InitEvents()
|
|
{
|
|
ASSERT(g_h6to4TimerCancelledEvent == NULL);
|
|
g_h6to4TimerCancelledEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (g_h6to4TimerCancelledEvent == NULL)
|
|
return GetLastError();
|
|
|
|
//
|
|
// Schedule OnResolutionTimerCancelled() to be called whenever
|
|
// g_h6to4TimerCancelledEvent is signalled.
|
|
//
|
|
if (! RegisterWaitForSingleObject(&g_h6to4TimerCancelledWait,
|
|
g_h6to4TimerCancelledEvent,
|
|
OnResolutionTimerCancelled,
|
|
NULL,
|
|
INFINITE,
|
|
WT_EXECUTEDEFAULT)) {
|
|
return GetLastError();
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
CleanupHelperService()
|
|
{
|
|
if (g_h6to4TimerCancelledWait != NULL) {
|
|
UnregisterWait(g_h6to4TimerCancelledWait);
|
|
g_h6to4TimerCancelledWait = NULL;
|
|
}
|
|
|
|
if (g_h6to4TimerCancelledEvent != NULL) {
|
|
CloseHandle(g_h6to4TimerCancelledEvent);
|
|
g_h6to4TimerCancelledEvent = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CancelResolutionTimer(
|
|
IN OUT HANDLE *phResolutionTimer,
|
|
IN HANDLE hEvent)
|
|
{
|
|
Trace0(FSM, _T("Cancelling RT"));
|
|
|
|
// Stop the resolution timer
|
|
if (*phResolutionTimer != INVALID_HANDLE_VALUE) {
|
|
|
|
// Must be done non-blocking since we're holding the lock
|
|
// the resolution timeout needs. Ask for notification
|
|
// when the cancel completes so we can release the event count.
|
|
DeleteTimerQueueTimer(g_hTimerQueue, *phResolutionTimer, hEvent);
|
|
|
|
*phResolutionTimer = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delete all stack state related to a given relay
|
|
//
|
|
void
|
|
Delete6to4Relay(
|
|
IN PRELAY_INFO pRelay)
|
|
{
|
|
Trace1(ENTER, _T("Delete6to4Relay %d.%d.%d.%d"),
|
|
PRINT_IPADDR(pRelay->sinAddress.sin_addr.s_addr));
|
|
|
|
ConfigureRouteTableUpdate(&in6addr_any, 0,
|
|
SIX_TO_FOUR_IFINDEX,
|
|
&pRelay->sin6Address.sin6_addr,
|
|
FALSE, // Publish.
|
|
FALSE, // Immortal.
|
|
0, // Valid lifetime.
|
|
0, // Preferred lifetime.
|
|
0,
|
|
pRelay->ulMetric);
|
|
}
|
|
|
|
VOID
|
|
UninitializeRelays()
|
|
{
|
|
ULONG i;
|
|
|
|
TraceEnter("UninitializeRelays");
|
|
|
|
CancelResolutionTimer(&g_h6to4ResolutionTimer,
|
|
g_h6to4TimerCancelledEvent);
|
|
|
|
// Delete the timer queue
|
|
if (g_hTimerQueue != INVALID_HANDLE_VALUE) {
|
|
DeleteTimerQueue(g_hTimerQueue);
|
|
g_hTimerQueue = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (g_GlobalSettings.stUndoOnStop == ENABLED) {
|
|
// Delete existing relay tunnels
|
|
for (i=0; g_pRelayList && (i<g_pRelayList->ulNumRelays); i++) {
|
|
Delete6to4Relay(&g_pRelayList->arrRelay[i]);
|
|
}
|
|
}
|
|
|
|
// Free the "old list"
|
|
FreeRelayList(&g_pRelayList);
|
|
|
|
TraceLeave("UninitializeRelays");
|
|
}
|
|
|
|
//
|
|
// Start or update the resolution timer to expire in <ulMinutes> minutes
|
|
//
|
|
DWORD
|
|
RestartResolutionTimer(
|
|
IN ULONG ulDelayMinutes,
|
|
IN ULONG ulPeriodMinutes,
|
|
IN HANDLE *phResolutionTimer,
|
|
IN WAITORTIMERCALLBACK OnTimeout)
|
|
{
|
|
ULONG DelayTime = ulDelayMinutes * MINUTES * 1000; // convert mins to ms
|
|
ULONG PeriodTime = ulPeriodMinutes * MINUTES * 1000; // convert mins to ms
|
|
BOOL bRet;
|
|
DWORD dwErr;
|
|
|
|
if (*phResolutionTimer != INVALID_HANDLE_VALUE) {
|
|
bRet = ChangeTimerQueueTimer(g_hTimerQueue, *phResolutionTimer,
|
|
DelayTime, PeriodTime);
|
|
} else {
|
|
bRet = CreateTimerQueueTimer(phResolutionTimer,
|
|
g_hTimerQueue,
|
|
OnTimeout,
|
|
NULL,
|
|
DelayTime,
|
|
PeriodTime,
|
|
0);
|
|
if (bRet) {
|
|
IncEventCount("RT:RestartResolutionTimer");
|
|
}
|
|
}
|
|
|
|
dwErr = (bRet)? NO_ERROR : GetLastError();
|
|
|
|
Trace3(TIMER,
|
|
_T("RestartResolutionTimer: DueTime %d minutes, Period %d minutes, ReturnCode %d"),
|
|
ulDelayMinutes, ulPeriodMinutes, dwErr);
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
//
|
|
// Convert an addrinfo list into a relay list with appropriate metrics
|
|
//
|
|
DWORD
|
|
MakeRelayList(
|
|
IN struct addrinfo *addrs)
|
|
{
|
|
struct addrinfo *ai;
|
|
ULONG ulNumRelays = 0;
|
|
ULONG ulLatency;
|
|
|
|
for (ai=addrs; ai; ai=ai->ai_next) {
|
|
ulNumRelays++;
|
|
}
|
|
|
|
g_pRelayList = MALLOC( FIELD_OFFSET(RELAY_LIST, arrRelay[ulNumRelays]));
|
|
if (g_pRelayList == NULL) {
|
|
return GetLastError();
|
|
}
|
|
|
|
g_pRelayList->ulNumRelays = ulNumRelays;
|
|
|
|
ulNumRelays = 0;
|
|
for (ai=addrs; ai; ai=ai->ai_next) {
|
|
CopyMemory(&g_pRelayList->arrRelay[ulNumRelays].sinAddress, ai->ai_addr,
|
|
ai->ai_addrlen);
|
|
|
|
//
|
|
// Check connectivity using a possible 6to4 address for the relay
|
|
// router. However, we'll actually set TTL=1 and accept a
|
|
// hop count exceeded message, so we don't have to guess right.
|
|
//
|
|
Make6to4Address(&g_pRelayList->arrRelay[ulNumRelays].sin6Address,
|
|
&g_pRelayList->arrRelay[ulNumRelays].sinAddress);
|
|
|
|
// ping it to compute a metric
|
|
ulLatency = ConfirmIPv6Reachability(&g_pRelayList->arrRelay[ulNumRelays].sin6Address, 1000/*ms*/);
|
|
if (ulLatency != 0) {
|
|
g_pRelayList->arrRelay[ulNumRelays].ulMetric = 1000 + ulLatency;
|
|
} else {
|
|
g_pRelayList->arrRelay[ulNumRelays].ulMetric = UNREACHABLE;
|
|
}
|
|
|
|
ulNumRelays++;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// When the name-resolution timer expires, it's time to re-resolve the
|
|
// relay name to a list of relays.
|
|
//
|
|
DWORD
|
|
WINAPI
|
|
OnResolutionTimeout(
|
|
IN PVOID lpData,
|
|
IN BOOLEAN Reason)
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
ADDRINFOW hints;
|
|
PADDRINFOW addrs;
|
|
PRELAY_LIST pOldRelayList;
|
|
ULONG i, j;
|
|
|
|
ENTER_API();
|
|
TraceEnter("OnResolutionTimeout");
|
|
|
|
if (g_stService == DISABLED) {
|
|
TraceLeave("OnResolutionTimeout (disabled)");
|
|
LEAVE_API();
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
pOldRelayList = g_pRelayList;
|
|
g_pRelayList = NULL;
|
|
|
|
// If any 6to4 addresses are configured,
|
|
// Resolve the relay name to a set of IPv4 addresses
|
|
// Else
|
|
// Make the new set empty
|
|
if (g_GlobalState.stResolutionState == ENABLED) {
|
|
// Resolve the relay name to a set of IPv4 addresses
|
|
ZeroMemory(&hints, sizeof(hints));
|
|
hints.ai_family = PF_INET;
|
|
dwErr = GetAddrInfoW(g_GlobalSettings.pwszRelayName, NULL, &hints, &addrs);
|
|
|
|
if (dwErr == NO_ERROR) {
|
|
dwErr = MakeRelayList((PADDRINFOA)addrs);
|
|
FreeAddrInfoW(addrs);
|
|
addrs = NULL;
|
|
} else {
|
|
Trace2(ERR, _T("GetAddrInfoW(%s) returned error %d"),
|
|
g_GlobalSettings.pwszRelayName, dwErr);
|
|
}
|
|
}
|
|
|
|
// Compare the new set to the old set
|
|
// For each address in the new set, ping it to compute a metric
|
|
// For each new address, add a route
|
|
// For each old address not in the new list, delete the route
|
|
// For each address in both, update the route if the metric has changed
|
|
//
|
|
for (i=0; g_pRelayList && (i<g_pRelayList->ulNumRelays); i++) {
|
|
for (j=0; pOldRelayList && (j<pOldRelayList->ulNumRelays); j++) {
|
|
if (g_pRelayList->arrRelay[i].sinAddress.sin_addr.s_addr
|
|
== pOldRelayList->arrRelay[j].sinAddress.sin_addr.s_addr) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pOldRelayList && (j<pOldRelayList->ulNumRelays)) {
|
|
// update the route if the metric has changed
|
|
if (g_pRelayList->arrRelay[i].ulMetric
|
|
!= pOldRelayList->arrRelay[j].ulMetric) {
|
|
AddOrUpdate6to4Relay(&g_pRelayList->arrRelay[i]);
|
|
}
|
|
|
|
g_pRelayList->arrRelay[i].sin6Address = pOldRelayList->arrRelay[j].sin6Address;
|
|
} else {
|
|
// add a relay
|
|
AddOrUpdate6to4Relay(&g_pRelayList->arrRelay[i]);
|
|
}
|
|
}
|
|
for (j=0; pOldRelayList && (j<pOldRelayList->ulNumRelays); j++) {
|
|
for (i=0; g_pRelayList && (i<g_pRelayList->ulNumRelays); i++) {
|
|
if (g_pRelayList->arrRelay[i].sinAddress.sin_addr.s_addr ==
|
|
pOldRelayList->arrRelay[j].sinAddress.sin_addr.s_addr) {
|
|
break;
|
|
}
|
|
}
|
|
if (!g_pRelayList || (i == g_pRelayList->ulNumRelays)) {
|
|
// delete a relay
|
|
Delete6to4Relay(&pOldRelayList->arrRelay[j]);
|
|
}
|
|
}
|
|
|
|
FreeRelayList(&pOldRelayList);
|
|
|
|
TraceLeave("OnResolutionTimeout");
|
|
LEAVE_API();
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Routing-related subroutines
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
PIF_SETTINGS
|
|
FindInterfaceSettings(
|
|
IN WCHAR *pwszAdapterName,
|
|
IN IF_SETTINGS_LIST *pList);
|
|
|
|
STATE
|
|
Get6to4State(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// Decide whether 6to4 should be enabled or not.
|
|
//
|
|
if (g_GlobalSettings.stEnable6to4 == AUTOMATIC) {
|
|
return (g_b6to4Required ? ENABLED : DISABLED);
|
|
} else {
|
|
return g_GlobalSettings.stEnable6to4;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Decide whether routing will be enabled at all
|
|
//
|
|
STATE
|
|
GetGlobalRoutingState(
|
|
VOID
|
|
)
|
|
{
|
|
PIF_LIST pIfList = g_pInterfaceList;
|
|
DWORD dwErr;
|
|
GUID guid;
|
|
|
|
if (Get6to4State() == DISABLED) {
|
|
return DISABLED;
|
|
}
|
|
|
|
// If routing is explicitly enabled or disabled, use that
|
|
if (g_GlobalSettings.stEnableRouting != AUTOMATIC) {
|
|
return g_GlobalSettings.stEnableRouting;
|
|
}
|
|
|
|
// Disable routing if there is no private interface used by ICS
|
|
dwErr = RasQuerySharedPrivateLan(&guid);
|
|
if (dwErr != NO_ERROR) {
|
|
return DISABLED;
|
|
}
|
|
|
|
// Disable routing if there are no global IPv4 addresses
|
|
if (!pIfList || !pIfList->ulNumScopedAddrs[IPV4_SCOPE_GLOBAL]) {
|
|
return DISABLED;
|
|
}
|
|
|
|
return ENABLED;
|
|
}
|
|
|
|
//
|
|
// Decide whether a given interface is one we should treat as
|
|
// a private link to be a router on.
|
|
//
|
|
// Called by: UpdateInterfaceRoutingState, MakeInterfaceList
|
|
STATE
|
|
GetInterfaceRoutingState(
|
|
IN PIF_INFO pIf) // potential private interface
|
|
{
|
|
PIF_SETTINGS pIfSettings;
|
|
STATE stEnableRouting = AUTOMATIC;
|
|
DWORD dwErr;
|
|
GUID guid;
|
|
UNICODE_STRING usGuid;
|
|
WCHAR buffer[MAX_INTERFACE_NAME_LEN];
|
|
|
|
if (GetGlobalRoutingState() == DISABLED) {
|
|
return DISABLED;
|
|
}
|
|
|
|
pIfSettings = FindInterfaceSettings(pIf->pwszAdapterName,
|
|
g_pInterfaceSettingsList);
|
|
if (pIfSettings) {
|
|
stEnableRouting = pIfSettings->stEnableRouting;
|
|
}
|
|
|
|
if (stEnableRouting != AUTOMATIC) {
|
|
return stEnableRouting;
|
|
}
|
|
|
|
//
|
|
// Enable routing if this is the private interface used by ICS
|
|
//
|
|
dwErr = RasQuerySharedPrivateLan(&guid);
|
|
if (dwErr != NO_ERROR) {
|
|
// no private interface
|
|
return DISABLED;
|
|
}
|
|
|
|
usGuid.Buffer = buffer;
|
|
usGuid.MaximumLength = MAX_INTERFACE_NAME_LEN;
|
|
dwErr = RtlStringFromGUID(&guid, &usGuid);
|
|
if (dwErr != NO_ERROR) {
|
|
// no private interface
|
|
return DISABLED;
|
|
}
|
|
|
|
Trace1(ERR, _T("ICS private interface: %ls"), usGuid.Buffer);
|
|
|
|
//
|
|
// Compare guid to pIf->pwszAdapterName
|
|
//
|
|
// This must be done using a case-insensitive comparison since
|
|
// GetAdaptersInfo() returns GUID strings with upper-case letters
|
|
// while RtlGetStringFromGUID uses lower-case letters.
|
|
//
|
|
if (!_wcsicmp(pIf->pwszAdapterName, usGuid.Buffer)) {
|
|
return ENABLED;
|
|
}
|
|
|
|
return DISABLED;
|
|
}
|
|
|
|
// Called by: Configure6to4Subnets, Unconfigure6to4Subnets
|
|
VOID
|
|
Create6to4Prefixes(
|
|
OUT IN6_ADDR *pSubnetPrefix,
|
|
OUT IN6_ADDR *pSiteLocalPrefix,
|
|
IN IN_ADDR *ipOurAddr, // public address
|
|
IN ULONG ulIfIndex) // private interface
|
|
{
|
|
//
|
|
// Create a subnet prefix for the interface,
|
|
// using the interface index as the subnet number.
|
|
//
|
|
memset(pSubnetPrefix, 0, sizeof(IN6_ADDR));
|
|
pSubnetPrefix->s6_addr[0] = 0x20;
|
|
pSubnetPrefix->s6_addr[1] = 0x02;
|
|
memcpy(&pSubnetPrefix->s6_addr[2], ipOurAddr, sizeof(IN_ADDR));
|
|
pSubnetPrefix->s6_addr[6] = HIBYTE(ulIfIndex);
|
|
pSubnetPrefix->s6_addr[7] = LOBYTE(ulIfIndex);
|
|
|
|
//
|
|
// Create a site-local prefix for the interface,
|
|
// using the interface index as the subnet number.
|
|
//
|
|
memset(pSiteLocalPrefix, 0, sizeof(IN6_ADDR));
|
|
pSiteLocalPrefix->s6_addr[0] = 0xfe;
|
|
pSiteLocalPrefix->s6_addr[1] = 0xc0;
|
|
pSiteLocalPrefix->s6_addr[6] = HIBYTE(ulIfIndex);
|
|
pSiteLocalPrefix->s6_addr[7] = LOBYTE(ulIfIndex);
|
|
}
|
|
|
|
// Called by: EnableInterfaceRouting, AddAddress
|
|
void
|
|
Configure6to4Subnets(
|
|
IN ULONG ulIfIndex, // private interface
|
|
IN PSUBNET_CONTEXT pSubnet) // subnet info, incl. public address
|
|
{
|
|
IN6_ADDR SubnetPrefix;
|
|
IN6_ADDR SiteLocalPrefix;
|
|
|
|
if ((GetIPv4Scope(pSubnet->V4Addr.s_addr) != IPV4_SCOPE_GLOBAL)) {
|
|
return;
|
|
}
|
|
|
|
Create6to4Prefixes(&SubnetPrefix, &SiteLocalPrefix, &pSubnet->V4Addr,
|
|
ulIfIndex);
|
|
|
|
//
|
|
// Configure the subnet route.
|
|
//
|
|
ConfigureRouteTableUpdate(&SubnetPrefix, 64,
|
|
ulIfIndex, &in6addr_any,
|
|
pSubnet->Publish,
|
|
pSubnet->Publish,
|
|
pSubnet->ValidLifetime,
|
|
pSubnet->PreferredLifetime,
|
|
((g_GlobalSettings.stEnableSiteLocals == ENABLED) ? 48 : 0),
|
|
SUBNET_ROUTE_METRIC);
|
|
|
|
if (g_GlobalSettings.stEnableSiteLocals == ENABLED) {
|
|
ConfigureRouteTableUpdate(&SiteLocalPrefix, 64,
|
|
ulIfIndex, &in6addr_any,
|
|
pSubnet->Publish,
|
|
pSubnet->Publish,
|
|
pSubnet->ValidLifetime,
|
|
pSubnet->PreferredLifetime,
|
|
0,
|
|
SITELOCAL_ROUTE_METRIC);
|
|
}
|
|
}
|
|
|
|
// Called by: DisableInterfaceRouting, DeleteAddress
|
|
void
|
|
Unconfigure6to4Subnets(
|
|
IN ULONG ulIfIndex, // private interface
|
|
IN PSUBNET_CONTEXT pSubnet) // subnet info, inc. public address
|
|
{
|
|
IN6_ADDR SubnetPrefix;
|
|
IN6_ADDR SiteLocalPrefix;
|
|
|
|
if ((GetIPv4Scope(pSubnet->V4Addr.s_addr) != IPV4_SCOPE_GLOBAL)) {
|
|
return;
|
|
}
|
|
|
|
Create6to4Prefixes(&SubnetPrefix, &SiteLocalPrefix, &pSubnet->V4Addr,
|
|
ulIfIndex);
|
|
|
|
//
|
|
// Give the 6to4 route a zero lifetime, making it invalid.
|
|
// If we are a router, continue to publish the 6to4 route
|
|
// until we have disabled routing. This will allow
|
|
// the last Router Advertisements to go out with the prefix.
|
|
//
|
|
ConfigureRouteTableUpdate(&SubnetPrefix, 64,
|
|
ulIfIndex, &in6addr_any,
|
|
pSubnet->Publish, // Publish.
|
|
pSubnet->Publish, // Immortal.
|
|
pSubnet->ValidLifetime,
|
|
pSubnet->PreferredLifetime,
|
|
0, 0);
|
|
|
|
if (g_GlobalSettings.stEnableSiteLocals == ENABLED) {
|
|
ConfigureRouteTableUpdate(&SiteLocalPrefix, 64,
|
|
ulIfIndex, &in6addr_any,
|
|
pSubnet->Publish, // Publish.
|
|
pSubnet->Publish, // Immortal.
|
|
pSubnet->ValidLifetime,
|
|
pSubnet->PreferredLifetime,
|
|
0, 0);
|
|
}
|
|
}
|
|
|
|
#define PUBLIC_ZONE_ID 1
|
|
#define PRIVATE_ZONE_ID 2
|
|
|
|
// Called by: EnableRouting, DisableRouting, EnableInterfaceRouting,
|
|
// DisableInterfaceRouting
|
|
DWORD
|
|
ConfigureInterfaceUpdate(
|
|
IN u_int Interface,
|
|
IN int Advertises,
|
|
IN int Forwards)
|
|
{
|
|
IPV6_INFO_INTERFACE Update;
|
|
DWORD Result;
|
|
|
|
IPV6_INIT_INFO_INTERFACE(&Update);
|
|
|
|
Update.This.Index = Interface;
|
|
Update.Advertises = Advertises;
|
|
Update.Forwards = Forwards;
|
|
|
|
if (Advertises == TRUE) {
|
|
Update.ZoneIndices[ADE_SITE_LOCAL] = PRIVATE_ZONE_ID;
|
|
Update.ZoneIndices[ADE_ADMIN_LOCAL] = PRIVATE_ZONE_ID;
|
|
Update.ZoneIndices[ADE_SUBNET_LOCAL] = PRIVATE_ZONE_ID;
|
|
} else if (Advertises == FALSE) {
|
|
Update.ZoneIndices[ADE_SITE_LOCAL] = PUBLIC_ZONE_ID;
|
|
Update.ZoneIndices[ADE_ADMIN_LOCAL] = PUBLIC_ZONE_ID;
|
|
Update.ZoneIndices[ADE_SUBNET_LOCAL] = PUBLIC_ZONE_ID;
|
|
}
|
|
|
|
Result = UpdateInterface(&Update);
|
|
|
|
Trace4(ERR, _T("UpdateInterface if=%d adv=%d fwd=%d result=%d"),
|
|
Interface, Advertises, Forwards, Result);
|
|
|
|
return Result;
|
|
}
|
|
|
|
// Called by: UpdateGlobalRoutingState
|
|
VOID
|
|
EnableRouting()
|
|
{
|
|
SOCKADDR_IN6 AnycastAddress;
|
|
int i;
|
|
LPSOCKADDR_IN pOurAddr;
|
|
|
|
TraceEnter("EnableRouting");
|
|
|
|
//
|
|
// Enable forwarding on the tunnel pseudo-interfaces.
|
|
//
|
|
ConfigureInterfaceUpdate(SIX_TO_FOUR_IFINDEX, -1, TRUE);
|
|
ConfigureInterfaceUpdate(V4_COMPAT_IFINDEX, -1, TRUE);
|
|
|
|
//
|
|
// Add anycast addresses for all 6to4 addresses
|
|
//
|
|
for (i=0; i<g_pIpv4AddressList->iAddressCount; i++) {
|
|
pOurAddr = (LPSOCKADDR_IN)g_pIpv4AddressList->Address[i].lpSockaddr;
|
|
if ((GetIPv4Scope(pOurAddr->sin_addr.s_addr) != IPV4_SCOPE_GLOBAL)) {
|
|
continue;
|
|
}
|
|
|
|
Make6to4AnycastAddress(&AnycastAddress, pOurAddr);
|
|
ConfigureAddressUpdate(
|
|
SIX_TO_FOUR_IFINDEX, &AnycastAddress, INFINITE_LIFETIME,
|
|
ADE_ANYCAST, PREFIX_CONF_WELLKNOWN, IID_CONF_WELLKNOWN);
|
|
}
|
|
|
|
g_GlobalState.stRoutingState = ENABLED;
|
|
|
|
TraceLeave("EnableRouting");
|
|
}
|
|
|
|
// Called by: UpdateGlobalRoutingState
|
|
VOID
|
|
DisableRouting()
|
|
{
|
|
SOCKADDR_IN6 AnycastAddress;
|
|
int i;
|
|
LPSOCKADDR_IN pOurAddr;
|
|
DWORD dwErr;
|
|
|
|
TraceEnter("DisableRouting");
|
|
|
|
//
|
|
// Disable forwarding on the tunnel pseudo-interfaces.
|
|
//
|
|
ConfigureInterfaceUpdate(SIX_TO_FOUR_IFINDEX, -1, FALSE);
|
|
ConfigureInterfaceUpdate(V4_COMPAT_IFINDEX, -1, FALSE);
|
|
|
|
//
|
|
// Remove anycast addresses for all 6to4 addresses
|
|
//
|
|
for (i=0; i<g_pIpv4AddressList->iAddressCount; i++) {
|
|
pOurAddr = (LPSOCKADDR_IN)g_pIpv4AddressList->Address[i].lpSockaddr;
|
|
if ((GetIPv4Scope(pOurAddr->sin_addr.s_addr) != IPV4_SCOPE_GLOBAL)) {
|
|
continue;
|
|
}
|
|
|
|
Make6to4AnycastAddress(&AnycastAddress, pOurAddr);
|
|
dwErr = ConfigureAddressUpdate(
|
|
SIX_TO_FOUR_IFINDEX, &AnycastAddress, 0,
|
|
ADE_ANYCAST, PREFIX_CONF_WELLKNOWN, IID_CONF_WELLKNOWN);
|
|
}
|
|
|
|
g_GlobalState.stRoutingState = DISABLED;
|
|
|
|
TraceLeave("DisableRouting");
|
|
}
|
|
|
|
|
|
// Called by: UpdateInterfaceRoutingState
|
|
VOID
|
|
EnableInterfaceRouting(
|
|
IN PIF_INFO pIf, // private interface
|
|
IN PADDR_LIST pPublicAddressList) // public address list
|
|
{
|
|
int i;
|
|
LPSOCKADDR_IN pOurAddr;
|
|
SUBNET_CONTEXT Subnet;
|
|
|
|
Trace2(ERR, _T("Enabling routing on interface %d: %ls"),
|
|
pIf->ulIPv6IfIndex, pIf->pwszAdapterName);
|
|
|
|
ConfigureInterfaceUpdate(pIf->ulIPv6IfIndex, TRUE, TRUE);
|
|
|
|
// For each public address
|
|
for (i=0; i<pPublicAddressList->iAddressCount; i++) {
|
|
pOurAddr = (LPSOCKADDR_IN)pPublicAddressList->Address[i].lpSockaddr;
|
|
Subnet.V4Addr = pOurAddr->sin_addr;
|
|
Subnet.Publish = TRUE;
|
|
Subnet.ValidLifetime = 2 * DAYS;
|
|
Subnet.PreferredLifetime = 30 * MINUTES;
|
|
Configure6to4Subnets(pIf->ulIPv6IfIndex, &Subnet);
|
|
}
|
|
|
|
pIf->stRoutingState = ENABLED;
|
|
}
|
|
|
|
// Called by: PreUpdateInterfaceRoutingState, UninitializeInterfaces
|
|
BOOL
|
|
PreDisableInterfaceRouting(
|
|
IN PIF_INFO pIf, // private interface
|
|
IN PADDR_LIST pPublicAddressList)
|
|
{
|
|
int i;
|
|
LPSOCKADDR_IN pOurAddr;
|
|
SUBNET_CONTEXT Subnet;
|
|
|
|
Trace1(ERR, _T("Pre-Disabling routing on interface %d"),
|
|
pIf->ulIPv6IfIndex);
|
|
|
|
//
|
|
// For each public address, publish RA saying we're going away
|
|
//
|
|
for (i=0; i<pPublicAddressList->iAddressCount; i++) {
|
|
pOurAddr = (LPSOCKADDR_IN)pPublicAddressList->Address[i].lpSockaddr;
|
|
Subnet.V4Addr = pOurAddr->sin_addr;
|
|
Subnet.Publish = TRUE;
|
|
Subnet.ValidLifetime = Subnet.PreferredLifetime = 0;
|
|
Unconfigure6to4Subnets(pIf->ulIPv6IfIndex, &Subnet);
|
|
}
|
|
|
|
return (pPublicAddressList->iAddressCount > 0);
|
|
}
|
|
|
|
// Called by: UpdateInterfaceRoutingState, UninitializeInterfaces
|
|
VOID
|
|
DisableInterfaceRouting(
|
|
IN PIF_INFO pIf, // private interface
|
|
IN PADDR_LIST pPublicAddressList)
|
|
{
|
|
int i;
|
|
LPSOCKADDR_IN pOurAddr;
|
|
SUBNET_CONTEXT Subnet;
|
|
|
|
Trace1(ERR, _T("Disabling routing on interface %d"), pIf->ulIPv6IfIndex);
|
|
|
|
ConfigureInterfaceUpdate(pIf->ulIPv6IfIndex, FALSE, FALSE);
|
|
|
|
//
|
|
// For each public address, unconfigure 6to4 subnets
|
|
//
|
|
for (i=0; i<pPublicAddressList->iAddressCount; i++) {
|
|
pOurAddr = (LPSOCKADDR_IN)pPublicAddressList->Address[i].lpSockaddr;
|
|
Subnet.V4Addr = pOurAddr->sin_addr;
|
|
Subnet.Publish = FALSE;
|
|
Subnet.ValidLifetime = Subnet.PreferredLifetime = 0;
|
|
Unconfigure6to4Subnets(pIf->ulIPv6IfIndex, &Subnet);
|
|
}
|
|
|
|
pIf->stRoutingState = DISABLED;
|
|
}
|
|
|
|
BOOL // TRUE if need to sleep
|
|
PreUpdateInterfaceRoutingState(
|
|
IN PIF_INFO pIf, // private interface
|
|
IN PADDR_LIST pPublicAddressList)
|
|
{
|
|
STATE stIfRoutingState = GetInterfaceRoutingState(pIf);
|
|
|
|
if (pIf->stRoutingState == stIfRoutingState) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(stIfRoutingState == ENABLED)) {
|
|
return PreDisableInterfaceRouting(pIf, pPublicAddressList);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Update the current state of an interface (i.e. whether or not it's a
|
|
// private interface on which we're serving as a router) according to
|
|
// configuration and whether IPv4 global addresses exist on the interface.
|
|
//
|
|
// Called by: UpdateGlobalRoutingState, OnConfigChange
|
|
VOID
|
|
UpdateInterfaceRoutingState(
|
|
IN PIF_INFO pIf, // private interface
|
|
IN PADDR_LIST pPublicAddressList)
|
|
{
|
|
STATE stIfRoutingState = GetInterfaceRoutingState(pIf);
|
|
|
|
if (pIf->stRoutingState == stIfRoutingState) {
|
|
return;
|
|
}
|
|
|
|
if (stIfRoutingState == ENABLED) {
|
|
EnableInterfaceRouting(pIf, pPublicAddressList);
|
|
} else {
|
|
DisableInterfaceRouting(pIf, pPublicAddressList);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
PreUpdateGlobalRoutingState()
|
|
{
|
|
ULONG i;
|
|
PIF_LIST pList = g_pInterfaceList;
|
|
BOOL bWait = FALSE;
|
|
|
|
if (pList == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < pList->ulNumInterfaces; i++) {
|
|
bWait |= PreUpdateInterfaceRoutingState(&pList->arrIf[i],
|
|
g_pIpv4AddressList);
|
|
}
|
|
|
|
return bWait;
|
|
}
|
|
|
|
// Called by: OnConfigChange, OnChangeInterfaceInfo
|
|
VOID
|
|
UpdateGlobalRoutingState()
|
|
{
|
|
ULONG i;
|
|
PIF_LIST pList = g_pInterfaceList;
|
|
STATE stNewRoutingState;
|
|
|
|
stNewRoutingState = GetGlobalRoutingState();
|
|
|
|
if (g_GlobalState.stRoutingState != stNewRoutingState) {
|
|
if (stNewRoutingState == ENABLED) {
|
|
EnableRouting();
|
|
} else {
|
|
DisableRouting();
|
|
}
|
|
}
|
|
|
|
if (pList == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i=0; i<pList->ulNumInterfaces; i++) {
|
|
UpdateInterfaceRoutingState(&pList->arrIf[i], g_pIpv4AddressList);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Interface-related subroutines
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
PIF_SETTINGS
|
|
FindInterfaceSettings(
|
|
IN WCHAR *pwszAdapterName,
|
|
IN IF_SETTINGS_LIST *pList)
|
|
{
|
|
ULONG i;
|
|
PIF_SETTINGS pIf;
|
|
|
|
if (pList == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
for (i=0; i<pList->ulNumInterfaces; i++) {
|
|
pIf = &pList->arrIf[i];
|
|
if (wcscmp(pIf->pwszAdapterName, pwszAdapterName)) {
|
|
return pIf;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PIF_INFO
|
|
FindInterfaceInfo(
|
|
IN WCHAR *pwszAdapterName,
|
|
IN IF_LIST *pList)
|
|
{
|
|
ULONG i;
|
|
PIF_INFO pIf;
|
|
|
|
if (pList == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
for (i=0; i<pList->ulNumInterfaces; i++) {
|
|
pIf = &pList->arrIf[i];
|
|
if (!wcscmp(pIf->pwszAdapterName, pwszAdapterName)) {
|
|
return pIf;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
DWORD NTAPI
|
|
OnRouteChange(
|
|
IN PVOID Context,
|
|
IN BOOLEAN TimedOut
|
|
);
|
|
|
|
VOID
|
|
StopRouteChangeNotification()
|
|
{
|
|
if (g_hRouteChangeWaitHandle) {
|
|
//
|
|
// Block until we're sure that the route change callback isn't
|
|
// still running.
|
|
//
|
|
LEAVE_API();
|
|
UnregisterWaitEx(g_hRouteChangeWaitHandle, INVALID_HANDLE_VALUE);
|
|
ENTER_API();
|
|
|
|
//
|
|
// Release the event we counted for RegisterWaitForSingleObject
|
|
//
|
|
DecEventCount("AC:StopIpv4RouteChangeNotification");
|
|
g_hRouteChangeWaitHandle = NULL;
|
|
}
|
|
if (g_hRouteChangeEvent) {
|
|
CloseHandle(g_hRouteChangeEvent);
|
|
g_hRouteChangeEvent = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
StartRouteChangeNotification()
|
|
{
|
|
ULONG Error;
|
|
BOOL bOk;
|
|
HANDLE TcpipHandle;
|
|
|
|
TraceEnter("StartRouteChangeNotification");
|
|
|
|
//
|
|
// Create an event on which to receive notifications
|
|
// and register a callback routine to be invoked if the event is signalled.
|
|
// Then request notification of route changes on the event.
|
|
//
|
|
|
|
if (!g_hRouteChangeEvent) {
|
|
g_hRouteChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (g_hRouteChangeEvent == NULL) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Count the following register as an event.
|
|
//
|
|
IncEventCount("AC:StartIpv4RouteChangeNotification");
|
|
|
|
bOk = RegisterWaitForSingleObject(&g_hRouteChangeWaitHandle,
|
|
g_hRouteChangeEvent,
|
|
OnRouteChange,
|
|
NULL,
|
|
INFINITE,
|
|
0);
|
|
if (!bOk) {
|
|
DecEventCount("AC:StartIpv4RouteChangeNotification");
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
ZeroMemory(&g_hRouteChangeOverlapped, sizeof(OVERLAPPED));
|
|
g_hRouteChangeOverlapped.hEvent = g_hRouteChangeEvent;
|
|
|
|
Error = NotifyRouteChange(&TcpipHandle, &g_hRouteChangeOverlapped);
|
|
if (Error != ERROR_IO_PENDING) {
|
|
goto Error;
|
|
}
|
|
|
|
return;
|
|
|
|
Error:
|
|
//
|
|
// A failure has occurred, so cleanup and quit.
|
|
// We proceed in this case without notification of route changes.
|
|
//
|
|
StopRouteChangeNotification();
|
|
|
|
TraceLeave("StartRouteChangeNotification");
|
|
}
|
|
|
|
|
|
// This routine is invoked when a change to the IPv4 route table is signalled.
|
|
//
|
|
DWORD NTAPI
|
|
OnRouteChange(
|
|
IN PVOID Context,
|
|
IN BOOLEAN TimedOut)
|
|
{
|
|
ENTER_API();
|
|
TraceEnter("OnRouteChange");
|
|
|
|
if (g_stService == DISABLED) {
|
|
Trace0(FSM, L"Service disabled");
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// First register for another route change notification.
|
|
// We must do this *before* processing this route change,
|
|
// to avoid missing an route change.
|
|
//
|
|
StartRouteChangeNotification();
|
|
|
|
UpdateGlobalResolutionState();
|
|
IsatapRouteChangeNotification();
|
|
#ifdef TEREDO
|
|
TeredoRouteChangeNotification();
|
|
#endif // TEREDO
|
|
|
|
Done:
|
|
TraceLeave("OnRouteChange");
|
|
LEAVE_API();
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD NTAPI
|
|
OnChangeInterfaceInfo(
|
|
IN PVOID Context,
|
|
IN BOOLEAN TimedOut
|
|
);
|
|
|
|
VOID
|
|
StopAddressChangeNotification()
|
|
{
|
|
if (g_hAddressChangeWaitHandle) {
|
|
//
|
|
// Block until we're sure that the address change callback isn't
|
|
// still running.
|
|
//
|
|
LEAVE_API();
|
|
UnregisterWaitEx(g_hAddressChangeWaitHandle, INVALID_HANDLE_VALUE);
|
|
ENTER_API();
|
|
|
|
//
|
|
// Release the event we counted for RegisterWaitForSingleObject
|
|
//
|
|
DecEventCount("AC:StopIpv4AddressChangeNotification");
|
|
g_hAddressChangeWaitHandle = NULL;
|
|
}
|
|
if (g_hAddressChangeEvent) {
|
|
CloseHandle(g_hAddressChangeEvent);
|
|
g_hAddressChangeEvent = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
StartAddressChangeNotification()
|
|
{
|
|
ULONG Error;
|
|
BOOL bOk;
|
|
HANDLE TcpipHandle;
|
|
|
|
TraceEnter("StartAddressChangeNotification");
|
|
|
|
//
|
|
// Create an event on which to receive notifications
|
|
// and register a callback routine to be invoked if the event is signalled.
|
|
// Then request notification of address changes on the event.
|
|
//
|
|
|
|
if (!g_hAddressChangeEvent) {
|
|
g_hAddressChangeEvent = CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
if (g_hAddressChangeEvent == NULL) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Count the following register as an event.
|
|
//
|
|
IncEventCount("AC:StartIpv4AddressChangeNotification");
|
|
|
|
bOk = RegisterWaitForSingleObject(&g_hAddressChangeWaitHandle,
|
|
g_hAddressChangeEvent,
|
|
OnChangeInterfaceInfo,
|
|
NULL,
|
|
INFINITE,
|
|
0);
|
|
if (!bOk) {
|
|
DecEventCount("AC:StartIpv4AddressChangeNotification");
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
ZeroMemory(&g_hAddressChangeOverlapped, sizeof(OVERLAPPED));
|
|
g_hAddressChangeOverlapped.hEvent = g_hAddressChangeEvent;
|
|
|
|
Error = NotifyAddrChange(&TcpipHandle, &g_hAddressChangeOverlapped);
|
|
if (Error != ERROR_IO_PENDING) {
|
|
goto Error;
|
|
}
|
|
|
|
return;
|
|
|
|
Error:
|
|
|
|
//
|
|
// A failure has occurred, so cleanup and quit.
|
|
// We proceed in this case without notification of address changes.
|
|
//
|
|
|
|
StopAddressChangeNotification();
|
|
|
|
TraceLeave("StartAddressChangeNotification");
|
|
}
|
|
|
|
//
|
|
// Convert an "adapter" list to an "interface" list and store the result in
|
|
// the global g_pInterfaceList.
|
|
//
|
|
DWORD
|
|
MakeInterfaceList(
|
|
IN PIP_ADAPTER_INFO pAdapterInfo)
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
ULONG ulNumInterfaces = 0, ulSize;
|
|
PIP_ADAPTER_INFO pAdapter;
|
|
PIF_INFO pIf;
|
|
IPV6_INFO_INTERFACE *pIfStackInfo;
|
|
|
|
// count adapters
|
|
for (pAdapter=pAdapterInfo; pAdapter; pAdapter=pAdapter->Next) {
|
|
ulNumInterfaces++;
|
|
}
|
|
|
|
// allocate enough space
|
|
ulSize = FIELD_OFFSET(IF_LIST, arrIf[ulNumInterfaces]);
|
|
g_pInterfaceList = MALLOC(ulSize);
|
|
if (g_pInterfaceList == NULL) {
|
|
return GetLastError();
|
|
}
|
|
|
|
// fill in list
|
|
g_pInterfaceList->ulNumInterfaces = ulNumInterfaces;
|
|
ZeroMemory(g_pInterfaceList->ulNumScopedAddrs,
|
|
sizeof(ULONG) * NUM_IPV4_SCOPES);
|
|
ulNumInterfaces = 0;
|
|
for (pAdapter=pAdapterInfo; pAdapter; pAdapter=pAdapter->Next) {
|
|
pIf = &g_pInterfaceList->arrIf[ulNumInterfaces];
|
|
|
|
ConvertOemToUnicode(pAdapter->AdapterName, pIf->pwszAdapterName,
|
|
MAX_ADAPTER_NAME);
|
|
|
|
Trace1(FSM, _T("Adding interface %ls"), pIf->pwszAdapterName);
|
|
|
|
dwErr = MakeAddressList(&pAdapter->IpAddressList,
|
|
&pIf->pAddressList, &pIf->ulNumGlobals,
|
|
g_pInterfaceList->ulNumScopedAddrs);
|
|
|
|
pIfStackInfo = GetInterfaceStackInfo(pIf->pwszAdapterName);
|
|
if (pIfStackInfo) {
|
|
pIf->ulIPv6IfIndex = pIfStackInfo->This.Index;
|
|
} else {
|
|
pIf->ulIPv6IfIndex = 0;
|
|
}
|
|
FREE(pIfStackInfo);
|
|
|
|
pIf->stRoutingState = DISABLED;
|
|
|
|
ulNumInterfaces++;
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
VOID
|
|
FreeInterfaceList(
|
|
IN OUT PIF_LIST *ppList)
|
|
{
|
|
ULONG i;
|
|
|
|
if (*ppList == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i=0; i<(*ppList)->ulNumInterfaces; i++) {
|
|
FreeAddressList( &(*ppList)->arrIf[i].pAddressList );
|
|
}
|
|
|
|
FREE(*ppList);
|
|
*ppList = NULL;
|
|
}
|
|
|
|
DWORD
|
|
InitializeInterfaces()
|
|
{
|
|
g_pInterfaceList = NULL;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
ProcessInterfaceStateChange(
|
|
IN ADDR_LIST CONST *pAddressList,
|
|
IN ADDR_LIST *pOldAddressList,
|
|
IN PIF_LIST pOldInterfaceList,
|
|
IN GLOBAL_STATE *pOldState,
|
|
IN OUT BOOL *pbNeedDelete)
|
|
{
|
|
INT j,k;
|
|
LPSOCKADDR_IN pAddr;
|
|
|
|
// For each new global address not in old list,
|
|
// add a 6to4 address
|
|
for (j=0; j<pAddressList->iAddressCount; j++) {
|
|
pAddr = (LPSOCKADDR_IN)pAddressList->Address[j].lpSockaddr;
|
|
|
|
Trace1(FSM, _T("Checking for new address %d.%d.%d.%d"),
|
|
PRINT_IPADDR(pAddr->sin_addr.s_addr));
|
|
|
|
// See if address is in old list
|
|
for (k=0; k<pOldAddressList->iAddressCount; k++) {
|
|
if (pAddr->sin_addr.s_addr == ((LPSOCKADDR_IN)pOldAddressList->Address[k].lpSockaddr)->sin_addr.s_addr) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If so, continue
|
|
if (k<pOldAddressList->iAddressCount) {
|
|
continue;
|
|
}
|
|
|
|
// Add an address, and use it for routing if enabled
|
|
AddAddress(pAddr, g_pInterfaceList, g_GlobalState.stRoutingState);
|
|
}
|
|
|
|
// For each old global address not in the new list,
|
|
// delete a 6to4 address
|
|
for (k=0; k<pOldAddressList->iAddressCount; k++) {
|
|
pAddr = (LPSOCKADDR_IN)pOldAddressList->Address[k].lpSockaddr;
|
|
|
|
Trace1(FSM, _T("Checking for old address %d.%d.%d.%d"),
|
|
PRINT_IPADDR(pAddr->sin_addr.s_addr));
|
|
|
|
// See if address is in new list
|
|
for (j=0; j<pAddressList->iAddressCount; j++) {
|
|
if (((LPSOCKADDR_IN)pAddressList->Address[j].lpSockaddr)->sin_addr.s_addr
|
|
== pAddr->sin_addr.s_addr) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If so, continue
|
|
if (j<pAddressList->iAddressCount) {
|
|
continue;
|
|
}
|
|
|
|
// Prepare to delete the 6to4 address
|
|
PreDeleteAddress(pAddr, pOldInterfaceList, pOldState->stRoutingState);
|
|
*pbNeedDelete = TRUE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FinishInterfaceStateChange(
|
|
IN ADDR_LIST CONST *pAddressList,
|
|
IN ADDR_LIST *pOldAddressList,
|
|
IN PIF_LIST pOldInterfaceList,
|
|
IN GLOBAL_STATE *pOldState)
|
|
{
|
|
INT j,k;
|
|
LPSOCKADDR_IN pAddr;
|
|
|
|
// For each old global address not in the new list,
|
|
// delete a 6to4 address
|
|
for (k=0; k<pOldAddressList->iAddressCount; k++) {
|
|
pAddr = (LPSOCKADDR_IN)pOldAddressList->Address[k].lpSockaddr;
|
|
|
|
Trace1(FSM, _T("Checking for old address %d.%d.%d.%d"),
|
|
PRINT_IPADDR(pAddr->sin_addr.s_addr));
|
|
|
|
// See if address is in new list
|
|
for (j=0; j<pAddressList->iAddressCount; j++) {
|
|
if (((LPSOCKADDR_IN)pAddressList->Address[j].lpSockaddr)->sin_addr.s_addr
|
|
== pAddr->sin_addr.s_addr) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If so, continue
|
|
if (j<pAddressList->iAddressCount) {
|
|
continue;
|
|
}
|
|
|
|
// Prepare to delete the 6to4 address
|
|
DeleteAddress(pAddr, pOldInterfaceList, pOldState->stRoutingState);
|
|
}
|
|
}
|
|
|
|
// This routine is invoked when a change to the set of local IPv4 addressed
|
|
// is signalled. It is responsible for updating the bindings of the
|
|
// private and public interfaces, and re-requesting change notification.
|
|
//
|
|
DWORD NTAPI
|
|
OnChangeInterfaceInfo(
|
|
IN PVOID Context,
|
|
IN BOOLEAN TimedOut)
|
|
{
|
|
PIF_INFO pIf, pOldIf;
|
|
ULONG i, ulSize = 0;
|
|
PIP_ADAPTER_INFO pAdapterInfo = NULL;
|
|
PIF_LIST pOldInterfaceList;
|
|
DWORD dwErr = NO_ERROR;
|
|
ADDR_LIST *pAddressList, *pOldAddressList;
|
|
GLOBAL_SETTINGS OldSettings;
|
|
GLOBAL_STATE OldState;
|
|
BOOL bNeedDelete = FALSE, bWait = FALSE;
|
|
|
|
ENTER_API();
|
|
TraceEnter("OnChangeInterfaceInfo");
|
|
|
|
if (g_stService == DISABLED) {
|
|
Trace0(FSM, L"Service disabled");
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// First register for another address change notification.
|
|
// We must do this *before* getting the address list,
|
|
// to avoid missing an address change.
|
|
//
|
|
StartAddressChangeNotification();
|
|
|
|
OldSettings = g_GlobalSettings; // struct copy
|
|
OldState = g_GlobalState; // struct copy
|
|
|
|
//
|
|
// Get the new set of IPv4 addresses on interfaces
|
|
//
|
|
|
|
for (;;) {
|
|
dwErr = GetAdaptersInfo(pAdapterInfo, &ulSize);
|
|
if (dwErr == ERROR_SUCCESS) {
|
|
break;
|
|
}
|
|
if (dwErr == ERROR_NO_DATA) {
|
|
dwErr = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (pAdapterInfo) {
|
|
FREE(pAdapterInfo);
|
|
pAdapterInfo = NULL;
|
|
}
|
|
|
|
if (dwErr != ERROR_BUFFER_OVERFLOW) {
|
|
dwErr = GetLastError();
|
|
goto Done;
|
|
}
|
|
|
|
pAdapterInfo = MALLOC(ulSize);
|
|
if (pAdapterInfo == NULL) {
|
|
dwErr = GetLastError();
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
pOldInterfaceList = g_pInterfaceList;
|
|
g_pInterfaceList = NULL;
|
|
|
|
MakeInterfaceList(pAdapterInfo);
|
|
if (pAdapterInfo) {
|
|
FREE(pAdapterInfo);
|
|
pAdapterInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// First update global address list
|
|
//
|
|
|
|
// For each interface in the new list...
|
|
for (i=0; i<g_pInterfaceList->ulNumInterfaces; i++) {
|
|
pIf = &g_pInterfaceList->arrIf[i];
|
|
|
|
pAddressList = pIf->pAddressList;
|
|
|
|
pOldIf = FindInterfaceInfo(pIf->pwszAdapterName,
|
|
pOldInterfaceList);
|
|
|
|
pOldAddressList = (pOldIf)? pOldIf->pAddressList : &EmptyAddressList;
|
|
|
|
if (pOldIf) {
|
|
pIf->stRoutingState = pOldIf->stRoutingState;
|
|
}
|
|
|
|
ProcessInterfaceStateChange(pAddressList, pOldAddressList,
|
|
pOldInterfaceList, &OldState, &bNeedDelete);
|
|
}
|
|
|
|
// For each old interface not in the new list,
|
|
// delete information.
|
|
for (i=0; pOldInterfaceList && (i<pOldInterfaceList->ulNumInterfaces); i++){
|
|
pOldIf = &pOldInterfaceList->arrIf[i];
|
|
pOldAddressList = pOldIf->pAddressList;
|
|
pIf = FindInterfaceInfo(pOldIf->pwszAdapterName, g_pInterfaceList);
|
|
if (pIf) {
|
|
continue;
|
|
}
|
|
ProcessInterfaceStateChange(&EmptyAddressList, pOldAddressList,
|
|
pOldInterfaceList, &OldState, &bNeedDelete);
|
|
}
|
|
|
|
Trace2(FSM, _T("num globals=%d num publics=%d"),
|
|
g_pInterfaceList->ulNumScopedAddrs[IPV4_SCOPE_GLOBAL],
|
|
g_pIpv4AddressList->iAddressCount);
|
|
|
|
if (g_pInterfaceList->ulNumScopedAddrs[IPV4_SCOPE_GLOBAL] == 0) {
|
|
PreDelete6to4Routes();
|
|
}
|
|
|
|
bWait = PreUpdateGlobalRoutingState();
|
|
|
|
//
|
|
// If needed, wait a bit to ensure that Router Advertisements
|
|
// carrying the zero lifetime prefixes get sent.
|
|
//
|
|
if (bWait || (bNeedDelete && (OldState.stRoutingState == ENABLED))) {
|
|
Sleep(2000);
|
|
}
|
|
|
|
g_st6to4State = (g_pInterfaceList->ulNumScopedAddrs[IPV4_SCOPE_GLOBAL] > 0)
|
|
? ENABLED : DISABLED;
|
|
|
|
UpdateGlobalResolutionState();
|
|
|
|
Update6to4Routes();
|
|
|
|
UpdateGlobalRoutingState();
|
|
|
|
//
|
|
// Now finish removing the 6to4 addresses.
|
|
//
|
|
if (bNeedDelete) {
|
|
for (i=0; i<g_pInterfaceList->ulNumInterfaces; i++) {
|
|
pIf = &g_pInterfaceList->arrIf[i];
|
|
|
|
pAddressList = pIf->pAddressList;
|
|
|
|
pOldIf = FindInterfaceInfo(pIf->pwszAdapterName,
|
|
pOldInterfaceList);
|
|
|
|
pOldAddressList = (pOldIf)? pOldIf->pAddressList : &EmptyAddressList;
|
|
|
|
FinishInterfaceStateChange(pAddressList, pOldAddressList,
|
|
pOldInterfaceList, &OldState);
|
|
|
|
}
|
|
for (i=0; pOldInterfaceList && (i<pOldInterfaceList->ulNumInterfaces); i++){
|
|
pOldIf = &pOldInterfaceList->arrIf[i];
|
|
pOldAddressList = pOldIf->pAddressList;
|
|
pIf = FindInterfaceInfo(pOldIf->pwszAdapterName, g_pInterfaceList);
|
|
if (pIf) {
|
|
continue;
|
|
}
|
|
FinishInterfaceStateChange(&EmptyAddressList, pOldAddressList,
|
|
pOldInterfaceList, &OldState);
|
|
}
|
|
}
|
|
|
|
FreeInterfaceList(&pOldInterfaceList);
|
|
|
|
Done:
|
|
TraceLeave("OnChangeInterfaceInfo");
|
|
LEAVE_API();
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
// Note that this function can take over 2 seconds to complete if we're a
|
|
// router. (This is by design).
|
|
//
|
|
// Called by: Stop6to4
|
|
VOID
|
|
UninitializeInterfaces()
|
|
{
|
|
PIF_INFO pIf;
|
|
ULONG i;
|
|
int k;
|
|
ADDR_LIST *pAddressList;
|
|
LPSOCKADDR_IN pAddr;
|
|
|
|
TraceEnter("UninitializeInterfaces");
|
|
|
|
// Cancel the address change notification
|
|
StopIpv6AddressChangeNotification();
|
|
StopAddressChangeNotification();
|
|
StopRouteChangeNotification();
|
|
|
|
// Since this is the first function called when stopping,
|
|
// the "old" global state/settings is in g_GlobalState/Settings.
|
|
|
|
if (g_GlobalSettings.stUndoOnStop == ENABLED) {
|
|
|
|
if (g_GlobalState.stRoutingState == ENABLED) {
|
|
//
|
|
// First announce we're going away
|
|
//
|
|
|
|
PreDelete6to4Routes();
|
|
|
|
//
|
|
// Now do the same for subnets we're advertising
|
|
//
|
|
for (i=0; i<g_pInterfaceList->ulNumInterfaces; i++) {
|
|
pIf = &g_pInterfaceList->arrIf[i];
|
|
|
|
pAddressList = pIf->pAddressList;
|
|
|
|
// For each old global address not in the new list,
|
|
// delete a 6to4 address (see below)
|
|
Trace1(FSM, _T("Checking %d old addresses"),
|
|
pAddressList->iAddressCount);
|
|
for (k=0; k<pAddressList->iAddressCount; k++) {
|
|
pAddr = (LPSOCKADDR_IN)pAddressList->Address[k].lpSockaddr;
|
|
|
|
Trace1(FSM, _T("Checking for old address %d.%d.%d.%d"),
|
|
PRINT_IPADDR(pAddr->sin_addr.s_addr));
|
|
|
|
PreDeleteAddress(pAddr, g_pInterfaceList, ENABLED);
|
|
}
|
|
|
|
if (pIf->stRoutingState == ENABLED) {
|
|
PreDisableInterfaceRouting(pIf, g_pIpv4AddressList);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Wait a bit to ensure that Router Advertisements
|
|
// carrying the zero lifetime prefixes get sent.
|
|
//
|
|
Sleep(2000);
|
|
}
|
|
|
|
g_st6to4State = DISABLED;
|
|
|
|
Update6to4Routes();
|
|
|
|
//
|
|
// Delete 6to4 addresses
|
|
//
|
|
for (i=0; g_pInterfaceList && i<g_pInterfaceList->ulNumInterfaces; i++) {
|
|
pIf = &g_pInterfaceList->arrIf[i];
|
|
|
|
pAddressList = pIf->pAddressList;
|
|
|
|
// For each old global address not in the new list,
|
|
// delete a 6to4 address (see below)
|
|
Trace1(FSM, _T("Checking %d old addresses"),
|
|
pAddressList->iAddressCount);
|
|
for (k=0; k<pAddressList->iAddressCount; k++) {
|
|
pAddr = (LPSOCKADDR_IN)pAddressList->Address[k].lpSockaddr;
|
|
|
|
Trace1(FSM, _T("Checking for old address %d.%d.%d.%d"),
|
|
PRINT_IPADDR(pAddr->sin_addr.s_addr));
|
|
|
|
DeleteAddress(pAddr, g_pInterfaceList,
|
|
g_GlobalState.stRoutingState);
|
|
}
|
|
|
|
// update the IPv6 routing state
|
|
if (pIf->stRoutingState == ENABLED) {
|
|
DisableInterfaceRouting(pIf, g_pIpv4AddressList);
|
|
}
|
|
}
|
|
|
|
if (g_GlobalState.stRoutingState == ENABLED) {
|
|
DisableRouting();
|
|
}
|
|
}
|
|
|
|
// Free the "old list"
|
|
FreeInterfaceList(&g_pInterfaceList);
|
|
|
|
TraceLeave("UninitializeInterfaces");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Event-processing functions
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Get an integer value from the registry
|
|
ULONG
|
|
GetInteger(
|
|
IN HKEY hKey,
|
|
IN LPCTSTR lpName,
|
|
IN ULONG ulDefault)
|
|
{
|
|
DWORD dwErr, dwType;
|
|
ULONG ulSize, ulValue;
|
|
|
|
if (hKey == INVALID_HANDLE_VALUE) {
|
|
return ulDefault;
|
|
}
|
|
|
|
ulSize = sizeof(ulValue);
|
|
dwErr = RegQueryValueEx(hKey, lpName, NULL, &dwType, (PBYTE)&ulValue,
|
|
&ulSize);
|
|
|
|
if (dwErr != ERROR_SUCCESS) {
|
|
return ulDefault;
|
|
}
|
|
|
|
if (dwType != REG_DWORD) {
|
|
return ulDefault;
|
|
}
|
|
|
|
if (ulValue == DEFAULT) {
|
|
return ulDefault;
|
|
}
|
|
|
|
return ulValue;
|
|
}
|
|
|
|
// Get a string value from the registry
|
|
VOID
|
|
GetString(
|
|
IN HKEY hKey,
|
|
IN LPCTSTR lpName,
|
|
IN PWCHAR pBuff,
|
|
IN ULONG ulLength,
|
|
IN PWCHAR pDefault)
|
|
{
|
|
DWORD dwErr, dwType;
|
|
ULONG ulSize;
|
|
|
|
if (hKey == INVALID_HANDLE_VALUE) {
|
|
wcsncpy(pBuff, pDefault, ulLength);
|
|
return;
|
|
}
|
|
|
|
ulSize = ulLength - sizeof(L'\0');
|
|
dwErr = RegQueryValueEx(hKey, lpName, NULL, &dwType, (PBYTE)pBuff,
|
|
&ulSize);
|
|
|
|
if (dwErr != ERROR_SUCCESS) {
|
|
wcsncpy(pBuff, pDefault, ulLength);
|
|
return;
|
|
}
|
|
|
|
if (dwType != REG_SZ) {
|
|
wcsncpy(pBuff, pDefault, ulLength);
|
|
return;
|
|
}
|
|
|
|
if (pBuff[0] == L'\0') {
|
|
wcsncpy(pBuff, pDefault, ulLength);
|
|
return;
|
|
}
|
|
|
|
ASSERT(ulSize < ulLength);
|
|
pBuff[ulSize / sizeof(WCHAR)] = '\0'; // ensure NULL termination.
|
|
}
|
|
|
|
// called when # of 6to4 addresses becomes 0 or non-zero
|
|
// and when stEnableResolution setting changes
|
|
//
|
|
// Called by: OnConfigChange, OnChangeInterfaceInfo, OnChangeRouteInfo
|
|
VOID
|
|
UpdateGlobalResolutionState(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
// Decide whether relay name resolution should be enabled or not
|
|
if (Get6to4State() == DISABLED) {
|
|
g_GlobalState.stResolutionState = DISABLED;
|
|
} else if (g_GlobalSettings.stEnableResolution != AUTOMATIC) {
|
|
g_GlobalState.stResolutionState = g_GlobalSettings.stEnableResolution;
|
|
} else {
|
|
// Enable if we have any 6to4 addresses
|
|
g_GlobalState.stResolutionState = g_st6to4State;
|
|
}
|
|
|
|
if (g_GlobalState.stResolutionState == ENABLED) {
|
|
//
|
|
// Restart the resolution timer, even if it's already running
|
|
// and the name and interval haven't changed. We also get
|
|
// called when we first get an IP address, such as when we
|
|
// dial up to the Internet, and we want to immediately retry
|
|
// resolution at this point.
|
|
//
|
|
(VOID) RestartResolutionTimer(
|
|
0,
|
|
g_GlobalSettings.ulResolutionInterval,
|
|
&g_h6to4ResolutionTimer,
|
|
(WAITORTIMERCALLBACK) OnResolutionTimeout);
|
|
} else {
|
|
if (g_h6to4ResolutionTimer != INVALID_HANDLE_VALUE) {
|
|
//
|
|
// stop it
|
|
//
|
|
CancelResolutionTimer(&g_h6to4ResolutionTimer,
|
|
g_h6to4TimerCancelledEvent);
|
|
}
|
|
|
|
// Delete all existing relays
|
|
if (g_pRelayList) {
|
|
for (i=0; i<g_pRelayList->ulNumRelays; i++) {
|
|
Delete6to4Relay(&g_pRelayList->arrRelay[i]);
|
|
}
|
|
FreeRelayList(&g_pRelayList);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
Update6over4State(
|
|
IN STATE State
|
|
)
|
|
{
|
|
int i;
|
|
|
|
if (g_GlobalSettings.stEnable6over4 == State) {
|
|
return;
|
|
}
|
|
g_GlobalSettings.stEnable6over4 = State;
|
|
|
|
if (g_GlobalSettings.stEnable6over4 == ENABLED) {
|
|
// Create 6over4 interfaces
|
|
for (i=0; i<g_pIpv4AddressList->iAddressCount; i++) {
|
|
if (g_pIpv4AddressList->Address[i].ul6over4IfIndex) {
|
|
continue;
|
|
}
|
|
Trace1(ERR, _T("Creating interface for %d.%d.%d.%d"),
|
|
PRINT_IPADDR(((LPSOCKADDR_IN)g_pIpv4AddressList->Address[i].lpSockaddr)->sin_addr.s_addr));
|
|
|
|
g_pIpv4AddressList->Address[i].ul6over4IfIndex = Create6over4Interface(((LPSOCKADDR_IN)g_pIpv4AddressList->Address[i].lpSockaddr)->sin_addr);
|
|
}
|
|
} else {
|
|
// Delete all 6over4 interfaces
|
|
for (i=0; i<g_pIpv4AddressList->iAddressCount; i++) {
|
|
if (!g_pIpv4AddressList->Address[i].ul6over4IfIndex) {
|
|
continue;
|
|
}
|
|
Trace1(ERR, _T("Deleting interface for %d.%d.%d.%d"),
|
|
PRINT_IPADDR(((LPSOCKADDR_IN)g_pIpv4AddressList->Address[i].lpSockaddr)->sin_addr.s_addr));
|
|
DeleteInterface(g_pIpv4AddressList->Address[i].ul6over4IfIndex);
|
|
g_pIpv4AddressList->Address[i].ul6over4IfIndex = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process a change to the state of whether v4-compatible addresses
|
|
// are enabled.
|
|
VOID
|
|
UpdateV4CompatState(
|
|
IN STATE State
|
|
)
|
|
{
|
|
int i;
|
|
LPSOCKADDR_IN pIPv4Address;
|
|
SOCKADDR_IN6 OurAddress;
|
|
u_int AddressLifetime;
|
|
|
|
if (g_GlobalSettings.stEnableV4Compat == State) {
|
|
return;
|
|
}
|
|
g_GlobalSettings.stEnableV4Compat = State;
|
|
|
|
// Create or delete the route, and figure out the address lifetime.
|
|
if (g_GlobalSettings.stEnableV4Compat == ENABLED) {
|
|
ConfigureRouteTableUpdate(&in6addr_any, 96,
|
|
V4_COMPAT_IFINDEX, &in6addr_any,
|
|
TRUE, // Publish.
|
|
TRUE, // Immortal.
|
|
2 * HOURS, // Valid lifetime.
|
|
30 * MINUTES, // Preferred lifetime.
|
|
0,
|
|
SIXTOFOUR_METRIC);
|
|
|
|
AddressLifetime = INFINITE_LIFETIME;
|
|
} else {
|
|
ConfigureRouteTableUpdate(&in6addr_any, 96,
|
|
V4_COMPAT_IFINDEX, &in6addr_any,
|
|
FALSE, // Publish.
|
|
FALSE, // Immortal.
|
|
0, 0, 0, 0);
|
|
|
|
AddressLifetime = 0;
|
|
}
|
|
|
|
// Now go and update the lifetime of v4-compatible addresses,
|
|
// which will cause them to be added or deleted.
|
|
for (i=0; i<g_pIpv4AddressList->iAddressCount; i++) {
|
|
pIPv4Address = (LPSOCKADDR_IN)g_pIpv4AddressList->
|
|
Address[i].lpSockaddr;
|
|
|
|
if (GetIPv4Scope(pIPv4Address->sin_addr.s_addr) != IPV4_SCOPE_GLOBAL) {
|
|
continue;
|
|
}
|
|
|
|
MakeV4CompatibleAddress(&OurAddress, pIPv4Address);
|
|
|
|
ConfigureAddressUpdate(V4_COMPAT_IFINDEX, &OurAddress,
|
|
AddressLifetime, ADE_UNICAST,
|
|
PREFIX_CONF_WELLKNOWN, IID_CONF_LL_ADDRESS);
|
|
}
|
|
}
|
|
|
|
|
|
// Process a change to something in the registry
|
|
DWORD
|
|
OnConfigChange()
|
|
{
|
|
HKEY hGlobal, hInterfaces, hIf;
|
|
DWORD dwErr, dwSize;
|
|
STATE State6over4, StateV4Compat;
|
|
DWORD i;
|
|
WCHAR pwszAdapterName[MAX_ADAPTER_NAME];
|
|
IF_SETTINGS *pIfSettings;
|
|
|
|
hGlobal = hInterfaces = hIf = INVALID_HANDLE_VALUE;
|
|
|
|
ENTER_API();
|
|
TraceEnter("OnConfigChange");
|
|
|
|
if (g_stService == DISABLED) {
|
|
TraceLeave("OnConfigChange (disabled)");
|
|
LEAVE_API();
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// Read global settings from the registry
|
|
dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KEY_GLOBAL, 0, KEY_QUERY_VALUE,
|
|
&hGlobal);
|
|
|
|
g_GlobalSettings.stEnable6to4 = GetInteger(
|
|
hGlobal, KEY_ENABLE_6TO4, DEFAULT_ENABLE_6TO4);
|
|
g_GlobalSettings.stEnableRouting = GetInteger(
|
|
hGlobal, KEY_ENABLE_ROUTING, DEFAULT_ENABLE_ROUTING);
|
|
g_GlobalSettings.stEnableSiteLocals = GetInteger(
|
|
hGlobal, KEY_ENABLE_SITELOCALS, DEFAULT_ENABLE_SITELOCALS);
|
|
g_GlobalSettings.stEnableResolution = GetInteger(
|
|
hGlobal, KEY_ENABLE_RESOLUTION, DEFAULT_ENABLE_RESOLUTION);
|
|
g_GlobalSettings.ulResolutionInterval = GetInteger(
|
|
hGlobal, KEY_RESOLUTION_INTERVAL, DEFAULT_RESOLUTION_INTERVAL);
|
|
GetString(
|
|
hGlobal, KEY_RELAY_NAME,
|
|
g_GlobalSettings.pwszRelayName, NI_MAXHOST, DEFAULT_RELAY_NAME);
|
|
|
|
if (g_GlobalSettings.stEnable6to4 == DISABLED) {
|
|
g_GlobalSettings.stEnableRouting
|
|
= g_GlobalSettings.stEnableResolution
|
|
= DISABLED;
|
|
}
|
|
|
|
State6over4 = GetInteger(
|
|
hGlobal, KEY_ENABLE_6OVER4, DEFAULT_ENABLE_6OVER4);
|
|
StateV4Compat = GetInteger(
|
|
hGlobal, KEY_ENABLE_V4COMPAT, DEFAULT_ENABLE_V4COMPAT);
|
|
|
|
g_GlobalSettings.stUndoOnStop = GetInteger(
|
|
hGlobal, KEY_UNDO_ON_STOP, DEFAULT_UNDO_ON_STOP);
|
|
|
|
if (hGlobal != INVALID_HANDLE_VALUE) {
|
|
RegCloseKey(hGlobal);
|
|
}
|
|
|
|
// Read interface settings from the registry
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, KEY_INTERFACES, 0, KEY_QUERY_VALUE,
|
|
&hInterfaces) == NO_ERROR) {
|
|
// For each interface in the registry
|
|
for (i=0; ; i++) {
|
|
dwSize = sizeof(pwszAdapterName) / sizeof(WCHAR);
|
|
dwErr = RegEnumKeyEx(hInterfaces, i, pwszAdapterName, &dwSize,
|
|
NULL, NULL, NULL, NULL);
|
|
if (dwErr != NO_ERROR) {
|
|
break;
|
|
}
|
|
|
|
// Find settings
|
|
pIfSettings = FindInterfaceSettings(pwszAdapterName,
|
|
g_pInterfaceSettingsList);
|
|
if (pIfSettings) {
|
|
// Read interface settings
|
|
(VOID) RegOpenKeyEx(
|
|
hInterfaces, pwszAdapterName, 0, KEY_QUERY_VALUE, &hIf);
|
|
|
|
pIfSettings->stEnableRouting = GetInteger(
|
|
hIf, KEY_ENABLE_ROUTING, DEFAULT_ENABLE_ROUTING);
|
|
|
|
if (hIf != INVALID_HANDLE_VALUE) {
|
|
RegCloseKey(hIf);
|
|
}
|
|
}
|
|
}
|
|
RegCloseKey(hInterfaces);
|
|
}
|
|
|
|
Update6to4State();
|
|
|
|
Update6over4State(State6over4);
|
|
|
|
UpdateV4CompatState(StateV4Compat);
|
|
|
|
if (!QueueUpdateGlobalPortState(NULL)) {
|
|
Trace0(SOCKET, L"QueueUpdateGlobalPortState failed");
|
|
}
|
|
|
|
IsatapConfigurationChangeNotification();
|
|
|
|
#ifdef TEREDO
|
|
TeredoConfigurationChangeNotification();
|
|
#endif // TEREDO
|
|
|
|
TraceLeave("OnConfigChange");
|
|
LEAVE_API();
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Startup/Shutdown-related functions
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Start the IPv6 helper service.
|
|
//
|
|
// To prevent the SCM from marking the service as hung, we periodically update
|
|
// our status, indicating that we are making progress but need more time.
|
|
//
|
|
// Called by: OnStartup
|
|
DWORD
|
|
StartHelperService()
|
|
{
|
|
DWORD dwErr;
|
|
WSADATA wsaData;
|
|
|
|
SetHelperServiceStatus(SERVICE_START_PENDING, NO_ERROR);
|
|
|
|
IncEventCount("StartHelperService");
|
|
|
|
g_stService = ENABLED;
|
|
|
|
//
|
|
// Initialize Winsock.
|
|
//
|
|
|
|
if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
|
|
Trace0(ERR, _T("WSAStartup failed\n"));
|
|
return GetLastError();
|
|
}
|
|
|
|
if (!InitIPv6Library()) {
|
|
dwErr = GetLastError();
|
|
Trace1(ERR, _T("InitIPv6Library failed with error %d"), dwErr);
|
|
return dwErr;
|
|
}
|
|
|
|
dwErr = InitEvents();
|
|
if (dwErr) {
|
|
return dwErr;
|
|
}
|
|
|
|
// Initialize the "old set" of config settings to the defaults
|
|
dwErr = InitializeGlobalInfo();
|
|
if (dwErr) {
|
|
return dwErr;
|
|
}
|
|
|
|
// Initialize the "old set" of interfaces (IPv4 addresses) to be empty
|
|
dwErr = InitializeInterfaces();
|
|
if (dwErr) {
|
|
return dwErr;
|
|
}
|
|
|
|
// Initialize the "old set" of relays to be empty
|
|
dwErr = InitializeRelays();
|
|
if (dwErr) {
|
|
return dwErr;
|
|
}
|
|
|
|
// Initialize the TCP proxy port list
|
|
InitializePorts();
|
|
|
|
// Initialize ISATAP
|
|
SetHelperServiceStatus(SERVICE_START_PENDING, NO_ERROR);
|
|
dwErr = IsatapInitialize();
|
|
if (dwErr) {
|
|
return dwErr;
|
|
}
|
|
|
|
#ifdef TEREDO
|
|
// Initialize Teredo
|
|
SetHelperServiceStatus(SERVICE_START_PENDING, NO_ERROR);
|
|
dwErr = TeredoInitializeGlobals();
|
|
if (dwErr) {
|
|
return dwErr;
|
|
}
|
|
#endif // TEREDO
|
|
|
|
// Process a config change event
|
|
SetHelperServiceStatus(SERVICE_START_PENDING, NO_ERROR);
|
|
dwErr = OnConfigChange();
|
|
if (dwErr) {
|
|
return dwErr;
|
|
}
|
|
|
|
// Request IPv4 route change notifications.
|
|
SetHelperServiceStatus(SERVICE_START_PENDING, NO_ERROR);
|
|
StartRouteChangeNotification();
|
|
|
|
// Process an IPv4 address change event.
|
|
// This will also schedule a resolution timer expiration if needed.
|
|
SetHelperServiceStatus(SERVICE_START_PENDING, NO_ERROR);
|
|
dwErr = OnChangeInterfaceInfo(NULL, FALSE);
|
|
if (dwErr) {
|
|
return dwErr;
|
|
}
|
|
|
|
// Request IPv6 address change notifications.
|
|
SetHelperServiceStatus(SERVICE_START_PENDING, NO_ERROR);
|
|
dwErr = StartIpv6AddressChangeNotification();
|
|
if (dwErr) {
|
|
return dwErr;
|
|
}
|
|
|
|
SetHelperServiceStatus(SERVICE_RUNNING, NO_ERROR);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Stop the IPv6 helper service. Since this is called with the global lock,
|
|
// we're guaranteed this won't be called while another 6to4 operation
|
|
// is in progress. However, another thread may be blocked waiting for
|
|
// the lock, so we set the state to stopped and check it in all other
|
|
// places after getting the lock.
|
|
//
|
|
// Called by: OnStop
|
|
VOID
|
|
StopHelperService(
|
|
IN DWORD Error
|
|
)
|
|
{
|
|
SetHelperServiceStatus(SERVICE_STOP_PENDING, Error);
|
|
|
|
g_stService = DISABLED;
|
|
|
|
// We do these in the opposite order from Start6to4
|
|
|
|
#ifdef TEREDO
|
|
// Uninitialize Teredo
|
|
TeredoUninitializeGlobals();
|
|
#endif // TEREDO
|
|
|
|
// Uninitialize ISATAP
|
|
IsatapUninitialize();
|
|
|
|
// Stop proxying
|
|
UninitializePorts();
|
|
|
|
// Stop the resolution timer and free resources
|
|
UninitializeRelays();
|
|
|
|
// Cancel the IPv4 address change request and free resources
|
|
// Also, stop being a router if we are one.
|
|
UninitializeInterfaces();
|
|
|
|
// Free settings resources
|
|
UninitializeGlobalInfo();
|
|
|
|
UninitIPv6Library();
|
|
|
|
DecEventCount("StopHelperService");
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// 6to4 Specific Code
|
|
////////////////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
__inline
|
|
Configure6to4Address(
|
|
IN BOOL Delete,
|
|
IN PSOCKADDR_IN Ipv4Address
|
|
)
|
|
{
|
|
SOCKADDR_IN6 Ipv6Address;
|
|
|
|
if ((GetIPv4Scope(Ipv4Address->sin_addr.s_addr) != IPV4_SCOPE_GLOBAL)) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
Make6to4Address(&Ipv6Address, Ipv4Address);
|
|
return ConfigureAddressUpdate(
|
|
SIX_TO_FOUR_IFINDEX,
|
|
&Ipv6Address,
|
|
Delete ? 0 : INFINITE_LIFETIME,
|
|
ADE_UNICAST, PREFIX_CONF_WELLKNOWN, IID_CONF_LL_ADDRESS);
|
|
}
|
|
|
|
|
|
VOID
|
|
PreDelete6to4Address(
|
|
IN LPSOCKADDR_IN Ipv4Address,
|
|
IN PIF_LIST InterfaceList,
|
|
IN STATE OldRoutingState
|
|
)
|
|
{
|
|
ULONG i;
|
|
SUBNET_CONTEXT Subnet;
|
|
PIF_INFO Interface;
|
|
|
|
if ((g_GlobalState.st6to4State != ENABLED) ||
|
|
(GetIPv4Scope(Ipv4Address->sin_addr.s_addr) != IPV4_SCOPE_GLOBAL)) {
|
|
return;
|
|
}
|
|
|
|
if (OldRoutingState != ENABLED) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Disable the subnet routes on each private interface.
|
|
// This will generate RAs that have a zero lifetime
|
|
// for the subnet prefixes.
|
|
//
|
|
Subnet.V4Addr = Ipv4Address->sin_addr;
|
|
Subnet.Publish = TRUE;
|
|
Subnet.ValidLifetime = Subnet.PreferredLifetime = 0;
|
|
|
|
for (i=0; i<InterfaceList->ulNumInterfaces; i++) {
|
|
Interface = &InterfaceList->arrIf[i];
|
|
if (Interface->stRoutingState != ENABLED) {
|
|
continue;
|
|
}
|
|
|
|
Unconfigure6to4Subnets(Interface->ulIPv6IfIndex, &Subnet);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
Delete6to4Address(
|
|
IN LPSOCKADDR_IN Ipv4Address,
|
|
IN PIF_LIST InterfaceList,
|
|
IN STATE OldRoutingState
|
|
)
|
|
{
|
|
SOCKADDR_IN6 AnycastAddress;
|
|
ULONG i;
|
|
PIF_INFO Interface;
|
|
SUBNET_CONTEXT Subnet;
|
|
|
|
if ((g_GlobalState.st6to4State != ENABLED) ||
|
|
(GetIPv4Scope(Ipv4Address->sin_addr.s_addr) != IPV4_SCOPE_GLOBAL)) {
|
|
return;
|
|
}
|
|
|
|
// Delete the 6to4 address from the stack
|
|
(VOID) Configure6to4Address(TRUE, (PSOCKADDR_IN) Ipv4Address);
|
|
|
|
if (OldRoutingState != ENABLED) {
|
|
return;
|
|
}
|
|
|
|
Make6to4AnycastAddress(&AnycastAddress, Ipv4Address);
|
|
(VOID) ConfigureAddressUpdate(
|
|
SIX_TO_FOUR_IFINDEX, &AnycastAddress, 0,
|
|
ADE_ANYCAST, PREFIX_CONF_WELLKNOWN, IID_CONF_WELLKNOWN);
|
|
|
|
// Remove subnets from all routing interfaces
|
|
Subnet.V4Addr = Ipv4Address->sin_addr;
|
|
Subnet.Publish = FALSE;
|
|
Subnet.ValidLifetime = Subnet.PreferredLifetime = 0;
|
|
|
|
for (i = 0; i < InterfaceList->ulNumInterfaces; i++) {
|
|
Interface = &InterfaceList->arrIf[i];
|
|
if (Interface->stRoutingState != ENABLED) {
|
|
continue;
|
|
}
|
|
|
|
Unconfigure6to4Subnets(Interface->ulIPv6IfIndex, &Subnet);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
Add6to4Address(
|
|
IN LPSOCKADDR_IN Ipv4Address,
|
|
IN PIF_LIST InterfaceList,
|
|
IN STATE OldRoutingState
|
|
)
|
|
{
|
|
DWORD Error;
|
|
SOCKADDR_IN6 AnycastAddress;
|
|
ULONG i;
|
|
PIF_INFO Interface;
|
|
SUBNET_CONTEXT Subnet;
|
|
|
|
if ((g_GlobalState.st6to4State != ENABLED) ||
|
|
(GetIPv4Scope(Ipv4Address->sin_addr.s_addr) != IPV4_SCOPE_GLOBAL)) {
|
|
return;
|
|
}
|
|
|
|
// Add a 6to4 address.
|
|
Error = Configure6to4Address(FALSE, (PSOCKADDR_IN) Ipv4Address);
|
|
if (Error != NO_ERROR) {
|
|
return;
|
|
}
|
|
|
|
|
|
if (OldRoutingState != ENABLED) {
|
|
return;
|
|
}
|
|
|
|
Make6to4AnycastAddress(&AnycastAddress, Ipv4Address);
|
|
Error = ConfigureAddressUpdate(
|
|
SIX_TO_FOUR_IFINDEX, &AnycastAddress, INFINITE_LIFETIME,
|
|
ADE_ANYCAST, PREFIX_CONF_WELLKNOWN, IID_CONF_WELLKNOWN);
|
|
if (Error != NO_ERROR) {
|
|
return;
|
|
}
|
|
|
|
// Add subnets to all routing interfaces
|
|
for (i = 0; i < InterfaceList->ulNumInterfaces; i++) {
|
|
Interface = &InterfaceList->arrIf[i];
|
|
if (Interface->stRoutingState != ENABLED) {
|
|
continue;
|
|
}
|
|
|
|
Subnet.V4Addr = Ipv4Address->sin_addr;
|
|
Subnet.Publish = TRUE;
|
|
Subnet.ValidLifetime = 2 * HOURS;
|
|
Subnet.PreferredLifetime = 30 * MINUTES;
|
|
Configure6to4Subnets(Interface->ulIPv6IfIndex, &Subnet);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
PreDelete6to4Routes(
|
|
VOID
|
|
)
|
|
{
|
|
if ((g_GlobalState.st6to4State != ENABLED) ||
|
|
(g_GlobalState.stRoutingState != ENABLED) ||
|
|
(g_st6to4State != ENABLED)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We were acting as a router and were publishing the 6to4 route, give the
|
|
// route a zero lifetime and continue to publish it until we have disabled
|
|
// routing. This allows the last RA to go out with the prefix.
|
|
//
|
|
(VOID) ConfigureRouteTableUpdate(
|
|
&SixToFourPrefix, 16, SIX_TO_FOUR_IFINDEX, &in6addr_any,
|
|
TRUE, // Publish
|
|
TRUE, // Immortal
|
|
0, 0, 0, 0);
|
|
|
|
//
|
|
// Do the same for the v4-compatible address route (if enabled).
|
|
//
|
|
if (g_GlobalSettings.stEnableV4Compat == ENABLED) {
|
|
(VOID) ConfigureRouteTableUpdate(
|
|
&in6addr_any, 96, V4_COMPAT_IFINDEX, &in6addr_any,
|
|
TRUE, // Publish
|
|
TRUE, // Immortal
|
|
0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
Update6to4Routes(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL Delete;
|
|
|
|
//
|
|
// CAVEAT: We might still end up trying to add a route that exists,
|
|
// or delete one that doesn't. But this should be harmless.
|
|
//
|
|
|
|
//
|
|
// Create/Delete the route for the 6to4 prefix.
|
|
// This route causes packets sent to 6to4 addresses
|
|
// to be encapsulated and sent to the extracted v4 address.
|
|
//
|
|
Delete = (Get6to4State() != ENABLED) || (g_st6to4State != ENABLED);
|
|
|
|
(VOID) ConfigureRouteTableUpdate(
|
|
&SixToFourPrefix, 16, SIX_TO_FOUR_IFINDEX, &in6addr_any,
|
|
!Delete, // Publish
|
|
!Delete, // Immortal
|
|
Delete ? 0 : 2 * HOURS, // Valid lifetime.
|
|
Delete ? 0 : 30 * MINUTES, // Preferred lifetime.
|
|
0, SIXTOFOUR_METRIC);
|
|
|
|
//
|
|
// Create/Delete the v4-compatible address route.
|
|
//
|
|
Delete |= (g_GlobalSettings.stEnableV4Compat != ENABLED);
|
|
|
|
(VOID) ConfigureRouteTableUpdate(
|
|
&in6addr_any, 96, V4_COMPAT_IFINDEX, &in6addr_any,
|
|
!Delete, // Publish
|
|
!Delete, // Immortal
|
|
Delete ? 0 : 2 * HOURS, // Valid lifetime.
|
|
Delete ? 0 : 30 * MINUTES, // Preferred lifetime.
|
|
0, SIXTOFOUR_METRIC);
|
|
}
|
|
|
|
|
|
VOID
|
|
Start6to4(
|
|
VOID
|
|
)
|
|
{
|
|
int i;
|
|
|
|
ASSERT(g_GlobalState.st6to4State == DISABLED);
|
|
|
|
for (i = 0; i < g_pIpv4AddressList->iAddressCount; i++) {
|
|
(VOID) Configure6to4Address(
|
|
FALSE, (PSOCKADDR_IN) g_pIpv4AddressList->Address[i].lpSockaddr);
|
|
}
|
|
|
|
Update6to4Routes();
|
|
|
|
UpdateGlobalRoutingState();
|
|
|
|
UpdateGlobalResolutionState();
|
|
|
|
g_GlobalState.st6to4State = ENABLED;
|
|
}
|
|
|
|
|
|
VOID
|
|
Stop6to4(
|
|
VOID
|
|
)
|
|
{
|
|
int i;
|
|
|
|
ASSERT(g_GlobalState.st6to4State == ENABLED);
|
|
|
|
PreDelete6to4Routes();
|
|
|
|
if (PreUpdateGlobalRoutingState()) {
|
|
Sleep(2000);
|
|
}
|
|
|
|
for (i = 0; i < g_pIpv4AddressList->iAddressCount; i++) {
|
|
(VOID) Configure6to4Address(
|
|
TRUE, (PSOCKADDR_IN) g_pIpv4AddressList->Address[i].lpSockaddr);
|
|
}
|
|
|
|
Update6to4Routes();
|
|
|
|
UpdateGlobalRoutingState();
|
|
|
|
UpdateGlobalResolutionState();
|
|
|
|
g_GlobalState.st6to4State = DISABLED;
|
|
}
|
|
|
|
|
|
VOID
|
|
Refresh6to4(
|
|
VOID
|
|
)
|
|
{
|
|
ASSERT(g_GlobalState.st6to4State == ENABLED);
|
|
|
|
if (PreUpdateGlobalRoutingState()) {
|
|
Sleep(2000);
|
|
}
|
|
UpdateGlobalRoutingState();
|
|
|
|
UpdateGlobalResolutionState();
|
|
}
|
|
|
|
|
|
VOID
|
|
Update6to4State(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// Start / Reconfigure / Stop.
|
|
//
|
|
if (Get6to4State() == ENABLED) {
|
|
if (g_GlobalState.st6to4State == ENABLED) {
|
|
Refresh6to4();
|
|
} else {
|
|
Start6to4();
|
|
}
|
|
} else {
|
|
if (g_GlobalState.st6to4State == ENABLED) {
|
|
Stop6to4();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
RequirementChangeNotification(
|
|
IN BOOL Required
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process a possible requirement change notification.
|
|
|
|
Arguments:
|
|
|
|
Required - Whether the 6to4 service is required for global connectivity.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Caller LOCK: API.
|
|
|
|
--*/
|
|
{
|
|
if (g_b6to4Required != Required) {
|
|
g_b6to4Required = Required;
|
|
Update6to4State();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdateServiceRequirements(
|
|
IN PIP_ADAPTER_ADDRESSES Adapters
|
|
)
|
|
{
|
|
BOOL Require6to4 = TRUE, RequireTeredo = TRUE;
|
|
|
|
GUID PrivateLan;
|
|
BOOL IcsEnabled = (RasQuerySharedPrivateLan(&PrivateLan) == NO_ERROR);
|
|
|
|
PIP_ADAPTER_ADDRESSES Next;
|
|
PIP_ADAPTER_UNICAST_ADDRESS Address;
|
|
WCHAR Guid[MAX_ADAPTER_NAME_LENGTH];
|
|
PSOCKADDR_IN6 Ipv6;
|
|
|
|
|
|
|
|
for (Next = Adapters; Next != NULL; Next = Next->Next) {
|
|
//
|
|
// Disregard disconnected interfaces.
|
|
//
|
|
if (Next->OperStatus != IfOperStatusUp) {
|
|
continue;
|
|
}
|
|
|
|
#ifdef TEREDO
|
|
//
|
|
// Disregard the Teredo interface.
|
|
//
|
|
ConvertOemToUnicode(Next->AdapterName, Guid, MAX_ADAPTER_NAME_LENGTH);
|
|
if (TeredoInterface(Guid)) {
|
|
ASSERT(Next->IfType == IF_TYPE_TUNNEL);
|
|
continue;
|
|
}
|
|
#else
|
|
DBG_UNREFERENCED_LOCAL_VARIABLE(Guid);
|
|
#endif // TEREDO
|
|
|
|
for (Address = Next->FirstUnicastAddress;
|
|
Address != NULL;
|
|
Address = Address->Next) {
|
|
//
|
|
// Consider only preferred global IPv6 addresses.
|
|
//
|
|
if (Address->Address.lpSockaddr->sa_family != AF_INET6) {
|
|
continue;
|
|
}
|
|
|
|
if (Address->DadState != IpDadStatePreferred) {
|
|
continue;
|
|
}
|
|
|
|
Ipv6 = (PSOCKADDR_IN6) Address->Address.lpSockaddr;
|
|
if (TeredoIpv6GlobalAddress(&(Ipv6->sin6_addr))) {
|
|
//
|
|
// Since this is not the Teredo interface, and it has a global
|
|
// IPv6 address, Teredo's not required for global connectivity.
|
|
//
|
|
RequireTeredo = FALSE;
|
|
if (Next->Ipv6IfIndex != SIX_TO_FOUR_IFINDEX) {
|
|
//
|
|
// Since this is not the 6to4 interface either, and it has
|
|
// a global IPv6 address, 6to4's not required for global
|
|
// connectivity.
|
|
//
|
|
Require6to4 = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!Require6to4) {
|
|
ASSERT(!RequireTeredo);
|
|
goto Done;
|
|
}
|
|
}
|
|
}
|
|
|
|
Done:
|
|
//
|
|
// 1. ICS requires 6to4 for advertising a prefix on the private LAN,
|
|
// at least until it implements prefix-delegation or RA proxy.
|
|
//
|
|
// 2. As a result of this advertisement, ICS will configure 6to4 addresses
|
|
// on its private interface as well. If the service should then disable
|
|
// 6to4 because of the presence of these global addresses on the private
|
|
// interface, it would lose these very addresses it was relying upon. The
|
|
// service would notice that it has no global IPv6 addresses and be forced
|
|
// to enable 6to4. Hence it will end up in an infinite loop, cycling 6to4
|
|
// between enabled and disabled states.
|
|
//
|
|
// To circumvent these two issues, we always enable 6to4 on an ICS box.
|
|
//
|
|
RequirementChangeNotification(Require6to4 || IcsEnabled);
|
|
#ifdef TEREDO
|
|
TeredoRequirementChangeNotification(RequireTeredo);
|
|
#endif // TEREDO
|
|
}
|