📄 mngprfx.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
// mngprfx.c
#include "mngprfx.h"
#include <winsock2.h>
#include <ipexport.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <winreg.h>
#define MAX_DOWNSTREAM_IFS 8
#ifdef DEBUG
// Only change the period for sending unsolicited RAs if running
// debug version of DHCPv6 to faciliate simpler debugging.
#define DEBUG_PREFIX_ADVERT_MIN_MS 4500
#define DEBUG_PREFIX_ADVERT_MAX_MS 5000
#endif
#define MAX_REG_STR 128
#define DHCPV6_REGKEY_BASE TEXT("\\COMM\\DHCPv6L")
#define DHCPV6_REGVAL_DOWNSTREAM_IFS TEXT("DownstreamInterfaces")
#define DHCPV6_REGVAL_ACCEPT_PD TEXT("AcceptPrefixDelegation")
HANDLE g_hIPv6Stack = INVALID_HANDLE_VALUE;
//
// Zero-out bits past the given PrefixLen.
//
void TrimPrefix(IPv6Addr* pPrefix, UINT PrefixLen) {
UINT i;
if (PrefixLen >= 128) return; // Nothing to do
if (PrefixLen % 8 != 0) {
UCHAR mask = 0;
for (i = 1; i <= PrefixLen % 8; i += 1)
mask |= 1<<(8-i);
pPrefix->s6_bytes[PrefixLen/8] &= mask;
}
for (i = (PrefixLen + 7) / 8; i < 16; i += 1)
pPrefix->s6_bytes[i] = 0;
}
//
// Create a network prefix based on InPrefix and SubnetPrefix using method
// suggested in subnetting example provided in RFC 3633, section 1.2.
//
BOOL CreateSubnetPrefix(
IPv6Addr* pInPrefix, UINT InPrefixLen, USHORT SubnetPrefix,
IPv6Addr* pOutPrefix, UINT* pOutPrefixLen
) {
ASSERT(pInPrefix != NULL && pOutPrefix != NULL && pOutPrefixLen != NULL);
// We could handle spliting longer prefixes, but there is no need to do this.
// RFC 3633 (section 1.2), suggests subnetting prefixes based on
// delegated prefix of only 48 bits.
//
if (InPrefixLen > 48) return FALSE;
memcpy(pOutPrefix, pInPrefix, sizeof(IPv6Addr));
TrimPrefix(pOutPrefix, InPrefixLen);
*pOutPrefixLen = InPrefixLen + (InPrefixLen % 16 ? 16 - (InPrefixLen % 16) : 0) + 16;
pOutPrefix->s6_words[*pOutPrefixLen/16 - 1] = ntohs(SubnetPrefix);
return TRUE;
}
IPv6Addr* GetIpFromSockAddr(SOCKET_ADDRESS* pSockAddr) {
return &((PSOCKADDR_IN6)(pSockAddr->lpSockaddr))->sin6_addr;
}
//
// Reconfigures given interface for routing.
//
BOOL UpdateInterface(
UINT IfIdx,
BOOL fAdvertises,
BOOL fForwards
) {
IPV6_INFO_INTERFACE If;
UINT BytesReturned;
IPV6_INIT_INFO_INTERFACE(&If);
If.This.Index = IfIdx;
If.Advertises = fAdvertises;
If.Forwards = fForwards;
#if DEBUG
If.MinRtrAdvInterval = DEBUG_PREFIX_ADVERT_MIN_MS;
If.MaxRtrAdvInterval = DEBUG_PREFIX_ADVERT_MAX_MS;
#endif
// On RETAIL, use defaults (or otherwise configured current values)
// for prefix advertisement period.
return DeviceIoControl(g_hIPv6Stack,
IOCTL_IPV6_UPDATE_INTERFACE,
&If, sizeof If,
NULL, 0, &BytesReturned, NULL);
}
//
// Updates route table entry with given setttings.
//
BOOL UpdateRoute(
UINT IfIdx,
IPv6Addr* pIfAddr,
IPv6Addr* pPrefix,
UINT PrefixLen,
UINT ValidLifetime,
UINT PreferredLifetime,
UINT Preference,
BOOL bPublish
) {
IPV6_INFO_ROUTE_TABLE Route;
UINT BytesReturned;
ASSERT(pIfAddr != NULL);
ASSERT(pPrefix != NULL);
Route.SitePrefixLength = 0;
Route.ValidLifetime = ValidLifetime;
Route.PreferredLifetime = PreferredLifetime;
Route.Preference = Preference;
Route.Type = RTE_TYPE_AUTOCONF;
Route.This.Neighbor.IF.Index = IfIdx;
Route.This.Neighbor.Address = *pIfAddr;
Route.This.Prefix = *pPrefix;
Route.This.PrefixLength = PrefixLen;
Route.Publish = bPublish;
Route.Immortal = FALSE; // Aging route
return DeviceIoControl(g_hIPv6Stack,
IOCTL_IPV6_UPDATE_ROUTE_TABLE,
&Route, sizeof Route,
NULL, 0, &BytesReturned, NULL);
}
//
// Publish existing routes for the given interface (if not already published).
//
BOOL PublishExistingRoutes(
UINT IfIdx,
IPv6Addr* pIfAddr,
IPv6Addr* pPrefix,
UINT PrefixLen
) {
IPV6_QUERY_ROUTE_TABLE Query, NextQuery;
IPV6_INFO_ROUTE_TABLE Route;
UINT BytesReturned;
BOOL fRoutePublished = FALSE;
NextQuery.Neighbor.IF.Index = 0;
for (;;) {
Query = NextQuery;
if (! DeviceIoControl(g_hIPv6Stack,
IOCTL_IPV6_QUERY_ROUTE_TABLE,
&Query, sizeof Query,
&Route, sizeof Route,
&BytesReturned, NULL)) {
return FALSE;
}
NextQuery = Route.Next;
if (Query.Neighbor.IF.Index == IfIdx) {
Route.This = Query;
if (Route.Type != RTE_TYPE_SYSTEM && // Ignore loopback.
IN6_ADDR_EQUAL(&Route.This.Prefix, pPrefix) &&
Route.This.PrefixLength == PrefixLen) {
if (Route.Publish) {
// Route has been already published; good enough.
fRoutePublished = TRUE;
} else if (pIfAddr == NULL) {
fRoutePublished =
UpdateRoute(IfIdx, &Route.This.Neighbor.Address,
pPrefix, PrefixLen,
Route.ValidLifetime, Route.PreferredLifetime,
Route.Preference, TRUE);
if (! fRoutePublished) break; // Error!
// Address not specified, other routes are possible.
} else if (IN6_ADDR_EQUAL(&Route.This.Neighbor.Address, pIfAddr)) {
fRoutePublished =
UpdateRoute(IfIdx, pIfAddr,
pPrefix, PrefixLen,
Route.ValidLifetime, Route.PreferredLifetime,
Route.Preference, TRUE);
// No more than default route with same address is expected
// to be on one interface.
break;
}
}
}
if (NextQuery.Neighbor.IF.Index == 0)
break;
}
return fRoutePublished;
}
//
// Ungraciously stolen from tcpipv6\tcpip6\inc\ip6def.h
//
__inline int
IsLinkLocal(const IPv6Addr *Addr) {
return ((Addr->s6_bytes[0] == 0xfe) &&
((Addr->s6_bytes[1] & 0xc0) == 0x80));
}
__inline int
IsSiteLocal(const IPv6Addr *Addr) {
return ((Addr->s6_bytes[0] == 0xfe) &&
((Addr->s6_bytes[1] & 0xc0) == 0xc0));
}
//
// Set or refresh interface address, prefix and route settings and begin
// routing on the interface.
//
BOOL SetOrRefreshPrefix(
PIP_ADAPTER_ADDRESSES pUpIf, // Upstream interface
PIP_ADAPTER_ADDRESSES pDownIf, // Downstream interface
IPv6Addr* pPrefix, // Prefix to be assigned
UINT PrefixLen, // Length
UINT ValidLifetime, // Valid lifetime; Note: can use INFINITE_LIFETIME
UINT PreferredLifetime // Preferred lifetime; Note: can use INFINITE_LIFETIME
) {
BOOL fRet = FALSE; // Be pessimistic
IPv6Addr NullAddr;
ASSERT(pUpIf != NULL);
ASSERT(pDownIf != NULL);
ASSERT(pPrefix != NULL);
// Setup null addr that will be used to configure default routes, etc.
memset(&NullAddr, 0, sizeof(NullAddr));
//
// Begin forwarding and advertising on downstream interface and
// forwarding on the upstream interface
//
if (! UpdateInterface(pDownIf->Ipv6IfIndex, TRUE, TRUE ) ) goto cleanup;
if (! UpdateInterface(pUpIf->Ipv6IfIndex, FALSE, TRUE) ) goto cleanup;
//
// Configure specific route on downstream interface.
//
if (! UpdateRoute(pDownIf->Ipv6IfIndex, &NullAddr,
pPrefix, PrefixLen,
ValidLifetime, PreferredLifetime,
ROUTE_PREF_HIGHEST, TRUE) )
goto cleanup;
//
// Notes:
// - No need for a default route for downstream interface.
// - No need to assign global address on downstream interface - it
// will be assigned automatically when route is created.
//
// Find a default route for public interface and set it to published (if it is
// not published already). Need to publish default route on upstream interface
// so that router lifetime advertised in RAs is > 0. (This route will not be
// advertised on public network.)
//
fRet = PublishExistingRoutes(pUpIf->Ipv6IfIndex, NULL,
&NullAddr, 0);
cleanup:
ASSERT(fRet);
return fRet;
}
//
// Consult registry settings to determine whether:
// UpIfName upstream interface can accept prefix delegations, and (optionally)
// DownIfName interface belongs to the set of downstream interfaces for
// UpIfName. Can be NULL.
//
// If DownIfName is NULL, then function will only check whether UpIfName
// can accept PD.
//
BOOL IsPDEnabledInterface(PCHAR UpIfName, PCHAR DownIfName) {
BOOL fRet = FALSE; // Be pessimistic
HKEY hKey = INVALID_HANDLE_VALUE;
TCHAR szRegValue[MAX_REG_STR];
TCHAR szDownIfName[MAX_REG_STR];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -