831 lines
23 KiB
C
831 lines
23 KiB
C
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
|
|
//
|
|
// Copyright (c) 2000 Microsoft Corporation
|
|
//
|
|
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
|
|
// You should have received a copy of the Microsoft End-User License Agreement
|
|
// for this software along with this release; see the file "license.txt".
|
|
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
|
|
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Source address selection and destination address ordering.
|
|
//
|
|
|
|
#include "oscfg.h"
|
|
#include "ndis.h"
|
|
#include "ip6imp.h"
|
|
#include "ip6def.h"
|
|
#include "route.h"
|
|
#include "select.h"
|
|
|
|
KSPIN_LOCK SelectLock;
|
|
PrefixPolicyEntry *PrefixPolicyTable;
|
|
PrefixPolicyEntry PrefixPolicyNull;
|
|
|
|
//* InitSelect
|
|
//
|
|
// Initialize the address selection module.
|
|
//
|
|
void
|
|
InitSelect(void)
|
|
{
|
|
IPv6Addr Prefix;
|
|
|
|
KeInitializeSpinLock(&SelectLock);
|
|
|
|
//
|
|
// The default prefix policy, when nothing in the table matches.
|
|
// (Normally there will be a ::/0 policy.)
|
|
//
|
|
PrefixPolicyNull.Precedence = (uint) -1;
|
|
PrefixPolicyNull.SrcLabel = (uint) -1;
|
|
PrefixPolicyNull.DstLabel = (uint) -1;
|
|
|
|
//
|
|
// Configure persistent policies from the registry.
|
|
//
|
|
ConfigurePrefixPolicies();
|
|
}
|
|
|
|
|
|
//* UnloadSelect
|
|
//
|
|
// Called when the IPv6 stack is unloading.
|
|
//
|
|
void
|
|
UnloadSelect(void)
|
|
{
|
|
PrefixPolicyReset();
|
|
}
|
|
|
|
|
|
//* PrefixPolicyReset
|
|
//
|
|
// Deletes all prefix policies.
|
|
// Called with no locks held.
|
|
//
|
|
void
|
|
PrefixPolicyReset(void)
|
|
{
|
|
PrefixPolicyEntry *List;
|
|
PrefixPolicyEntry *PPE;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Free the prefix policies.
|
|
//
|
|
KeAcquireSpinLock(&SelectLock, &OldIrql);
|
|
List = PrefixPolicyTable;
|
|
PrefixPolicyTable = NULL;
|
|
KeReleaseSpinLock(&SelectLock, OldIrql);
|
|
|
|
while ((PPE = List) != NULL) {
|
|
List = PPE->Next;
|
|
ExFreePool(PPE);
|
|
}
|
|
}
|
|
|
|
|
|
//* PrefixPolicyUpdate
|
|
//
|
|
// Updates the prefix policy table by creating a new policy entry
|
|
// or updating an existing entry.
|
|
//
|
|
void
|
|
PrefixPolicyUpdate(
|
|
const IPv6Addr *PolicyPrefix,
|
|
uint PrefixLength,
|
|
uint Precedence,
|
|
uint SrcLabel,
|
|
uint DstLabel)
|
|
{
|
|
IPv6Addr Prefix;
|
|
PrefixPolicyEntry *PPE;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT((Precedence != (uint)-1) &&
|
|
(SrcLabel != (uint)-1) &&
|
|
(DstLabel != (uint)-1));
|
|
|
|
//
|
|
// Ensure that the unused prefix bits are zero.
|
|
// This makes the prefix comparisons below safe.
|
|
//
|
|
CopyPrefix(&Prefix, PolicyPrefix, PrefixLength);
|
|
|
|
KeAcquireSpinLock(&SelectLock, &OldIrql);
|
|
|
|
for (PPE = PrefixPolicyTable; ; PPE = PPE->Next) {
|
|
|
|
if (PPE == NULL) {
|
|
//
|
|
// The prefix policy does not exist, so create it.
|
|
//
|
|
PPE = ExAllocatePool(NonPagedPool, sizeof *PPE);
|
|
if (PPE == NULL) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
|
"PrefixPolicyUpdate: out of pool\n"));
|
|
break;
|
|
}
|
|
|
|
PPE->Prefix = Prefix;
|
|
PPE->PrefixLength = PrefixLength;
|
|
PPE->Precedence = Precedence;
|
|
PPE->SrcLabel = SrcLabel;
|
|
PPE->DstLabel = DstLabel;
|
|
|
|
PPE->Next = PrefixPolicyTable;
|
|
PrefixPolicyTable = PPE;
|
|
break;
|
|
}
|
|
|
|
if ((PPE->PrefixLength == PrefixLength) &&
|
|
IP6_ADDR_EQUAL(&PPE->Prefix, &Prefix)) {
|
|
//
|
|
// Update the existing policy.
|
|
//
|
|
PPE->Precedence = Precedence;
|
|
PPE->SrcLabel = SrcLabel;
|
|
PPE->DstLabel = DstLabel;
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&SelectLock, OldIrql);
|
|
}
|
|
|
|
|
|
//* PrefixPolicyDelete
|
|
//
|
|
// Updates the prefix policy table by deleting a policy entry.
|
|
//
|
|
void
|
|
PrefixPolicyDelete(
|
|
const IPv6Addr *PolicyPrefix,
|
|
uint PrefixLength)
|
|
{
|
|
IPv6Addr Prefix;
|
|
PrefixPolicyEntry **PrevPPE;
|
|
PrefixPolicyEntry *PPE;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Ensure that the unused prefix bits are zero.
|
|
// This makes the prefix comparisons below safe.
|
|
//
|
|
CopyPrefix(&Prefix, PolicyPrefix, PrefixLength);
|
|
|
|
KeAcquireSpinLock(&SelectLock, &OldIrql);
|
|
|
|
for (PrevPPE = &PrefixPolicyTable; ; PrevPPE = &PPE->Next) {
|
|
PPE = *PrevPPE;
|
|
|
|
if (PPE == NULL) {
|
|
//
|
|
// The prefix policy does not exist, so do nothing.
|
|
//
|
|
break;
|
|
}
|
|
|
|
if ((PPE->PrefixLength == PrefixLength) &&
|
|
IP6_ADDR_EQUAL(&PPE->Prefix, &Prefix)) {
|
|
//
|
|
// Delete the prefix policy.
|
|
//
|
|
*PrevPPE = PPE->Next;
|
|
ExFreePool(PPE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&SelectLock, OldIrql);
|
|
}
|
|
|
|
void
|
|
PrefixPolicyLookup(
|
|
const IPv6Addr *Addr,
|
|
uint *Precedence,
|
|
uint *SrcLabel,
|
|
uint *DstLabel)
|
|
{
|
|
PrefixPolicyEntry *PPE, *BestPPE = NULL;
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&SelectLock, &OldIrql);
|
|
|
|
for (PPE = PrefixPolicyTable; PPE != NULL; PPE = PPE->Next) {
|
|
|
|
if (HasPrefix(Addr, &PPE->Prefix, PPE->PrefixLength)) {
|
|
|
|
if ((BestPPE == NULL) ||
|
|
(BestPPE->PrefixLength < PPE->PrefixLength)) {
|
|
//
|
|
// So far this is our best match.
|
|
//
|
|
BestPPE = PPE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BestPPE == NULL) {
|
|
//
|
|
// There were no matches, so return default values.
|
|
//
|
|
BestPPE = &PrefixPolicyNull;
|
|
}
|
|
|
|
//
|
|
// Return information from the best matching policy.
|
|
//
|
|
if (Precedence != NULL)
|
|
*Precedence = BestPPE->Precedence;
|
|
if (SrcLabel != NULL)
|
|
*SrcLabel = BestPPE->SrcLabel;
|
|
if (DstLabel != NULL)
|
|
*DstLabel = BestPPE->DstLabel;
|
|
|
|
KeReleaseSpinLock(&SelectLock, OldIrql);
|
|
}
|
|
|
|
//* FindBestSourceAddress
|
|
//
|
|
// Given an outgoing interface and a destination address,
|
|
// finds the best source address (NTE) to use.
|
|
//
|
|
// May be called with the route cache lock held.
|
|
//
|
|
// If found, returns a reference for the NTE.
|
|
//
|
|
NetTableEntry *
|
|
FindBestSourceAddress(
|
|
Interface *IF, // Interface we're sending from.
|
|
const IPv6Addr *Dest) // Destination we're sending to.
|
|
{
|
|
NetTableEntry *BestNTE = NULL;
|
|
ushort DestScope;
|
|
uint Length, BestLength;
|
|
uint DstLabel, SrcLabel, BestSrcLabel;
|
|
AddressEntry *ADE;
|
|
NetTableEntry *NTE;
|
|
KIRQL OldIrql;
|
|
|
|
DestScope = AddressScope(Dest);
|
|
|
|
PrefixPolicyLookup(Dest, NULL, NULL, &DstLabel);
|
|
|
|
KeAcquireSpinLock(&IF->Lock, &OldIrql);
|
|
|
|
for (ADE = IF->ADE; ADE != NULL; ADE = ADE->Next) {
|
|
NTE = (NetTableEntry *)ADE;
|
|
|
|
//
|
|
// Only consider valid (preferred & deprecated) unicast addresses.
|
|
//
|
|
if ((NTE->Type == ADE_UNICAST) && IsValidNTE(NTE)) {
|
|
|
|
Length = CommonPrefixLength(Dest, &NTE->Address);
|
|
if (Length == IPV6_ADDRESS_LENGTH) {
|
|
//
|
|
// Rule 1: Prefer same address.
|
|
// No need to keep looking.
|
|
//
|
|
BestNTE = NTE;
|
|
break;
|
|
}
|
|
|
|
PrefixPolicyLookup(&NTE->Address, NULL, &SrcLabel, NULL);
|
|
|
|
if (BestNTE == NULL) {
|
|
//
|
|
// We don't have a choice yet, so take what we can get.
|
|
//
|
|
FoundAddress:
|
|
BestNTE = NTE;
|
|
BestSrcLabel = SrcLabel;
|
|
BestLength = Length;
|
|
}
|
|
else if (BestNTE->Scope != NTE->Scope) {
|
|
//
|
|
// Rule 2: Prefer appropriate scope.
|
|
// If one is bigger & one smaller than the destination,
|
|
// we should use the address that is bigger.
|
|
// If both are bigger than the destination,
|
|
// we should use the address with smaller scope.
|
|
// If both are smaller than the destination,
|
|
// we should use the address with larger scope.
|
|
//
|
|
if (BestNTE->Scope < NTE->Scope) {
|
|
if (BestNTE->Scope < DestScope)
|
|
goto FoundAddress;
|
|
}
|
|
else {
|
|
if (DestScope <= NTE->Scope)
|
|
goto FoundAddress;
|
|
}
|
|
}
|
|
else if (BestNTE->DADState != NTE->DADState) {
|
|
//
|
|
// Rule 3: Avoid deprecated addresses.
|
|
//
|
|
if (BestNTE->DADState < NTE->DADState)
|
|
goto FoundAddress;
|
|
}
|
|
//
|
|
// Rule 4: Prefer home addresses.
|
|
// Not yet implemented, pending mobility support.
|
|
//
|
|
// Rule 5: Prefer outgoing interface.
|
|
// Not needed, because we only consider addresses
|
|
// assigned to the outgoing interface.
|
|
//
|
|
else if ((BestSrcLabel == DstLabel) != (SrcLabel == DstLabel)) {
|
|
//
|
|
// Rule 6: Prefer matching label.
|
|
// One source address has a label matching
|
|
// the destination, and the other doesn't.
|
|
// Choose the one with the matching label.
|
|
//
|
|
if (SrcLabel == DstLabel)
|
|
goto FoundAddress;
|
|
}
|
|
else if ((BestNTE->AddrConf == ADDR_CONF_ANONYMOUS) !=
|
|
(NTE->AddrConf == ADDR_CONF_ANONYMOUS)) {
|
|
//
|
|
// Rule 7: Prefer anonymous addresses.
|
|
//
|
|
if (NTE->AddrConf == ADDR_CONF_ANONYMOUS)
|
|
goto FoundAddress;
|
|
}
|
|
else {
|
|
//
|
|
// Rule 8: Use longest matching prefix.
|
|
//
|
|
if (BestLength < Length)
|
|
goto FoundAddress;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BestNTE != NULL)
|
|
AddRefNTE(BestNTE);
|
|
|
|
KeReleaseSpinLock(&IF->Lock, OldIrql);
|
|
|
|
return BestNTE;
|
|
}
|
|
|
|
//* ProcessSiteLocalAddresses
|
|
//
|
|
// Examines the input array of addresses
|
|
// and either removes unqualified site-local addresses
|
|
// or qualifies them with the appropriate site scope-id,
|
|
// depending on whether there are any global addresses
|
|
// in the array that match in the site prefix table.
|
|
//
|
|
// Rearranges the key array, not the input address array.
|
|
// Modifies the scope-ids of site-local addresses in the array.
|
|
//
|
|
void
|
|
ProcessSiteLocalAddresses(
|
|
TDI_ADDRESS_IP6 *Addrs,
|
|
uint *Key,
|
|
uint *pNumAddrs)
|
|
{
|
|
uint NumAddrs = *pNumAddrs;
|
|
int SawSiteLocal = FALSE;
|
|
int SawGlobal = FALSE;
|
|
uint i;
|
|
|
|
//
|
|
// First see if there are unqualified site-local addresses
|
|
// and global addresses in the array.
|
|
//
|
|
for (i = 0; i < NumAddrs; i++) {
|
|
TDI_ADDRESS_IP6 *Tdi = &Addrs[Key[i]];
|
|
IPv6Addr *Addr = (IPv6Addr *) &Tdi->sin6_addr;
|
|
|
|
if (IsGlobal(Addr))
|
|
SawGlobal = TRUE;
|
|
else if (IsSiteLocal(Addr)) {
|
|
if (Tdi->sin6_scope_id == 0)
|
|
SawSiteLocal = TRUE;
|
|
}
|
|
}
|
|
|
|
if (SawSiteLocal && SawGlobal) {
|
|
uint ScopeId = 0;
|
|
|
|
//
|
|
// Check the global addresses against the site-prefix table,
|
|
// to determine the appropriate site scope-id.
|
|
// If we don't find a matching global address,
|
|
// we remove the site-local addresses.
|
|
// If we do find matching global addresses
|
|
// (all with the same site scope-id),
|
|
// then we update the site-local addresses' scope-id.
|
|
//
|
|
|
|
for (i = 0; i < NumAddrs; i++) {
|
|
TDI_ADDRESS_IP6 *Tdi = &Addrs[Key[i]];
|
|
IPv6Addr *Addr = (IPv6Addr *) &Tdi->sin6_addr;
|
|
|
|
if (IsGlobal(Addr)) {
|
|
uint ThisScopeId;
|
|
|
|
ThisScopeId = SitePrefixMatch(Addr);
|
|
if (ThisScopeId != 0) {
|
|
//
|
|
// This global address matches a site prefix.
|
|
//
|
|
if (ScopeId == 0) {
|
|
//
|
|
// Save the scope-id, but keep looking.
|
|
//
|
|
ScopeId = ThisScopeId;
|
|
}
|
|
else if (ScopeId != ThisScopeId) {
|
|
//
|
|
// We have found an inconsistency, so remove
|
|
// all unqualified site-local addresses.
|
|
//
|
|
ScopeId = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ScopeId == 0) {
|
|
uint j = 0;
|
|
|
|
//
|
|
// Remove all unqualified site-local addresses.
|
|
//
|
|
for (i = 0; i < NumAddrs; i++) {
|
|
TDI_ADDRESS_IP6 *Tdi = &Addrs[Key[i]];
|
|
IPv6Addr *Addr = (IPv6Addr *) &Tdi->sin6_addr;
|
|
|
|
if (IsSiteLocal(Addr) &&
|
|
(Tdi->sin6_scope_id == 0)) {
|
|
//
|
|
// Exclude this address from the key array.
|
|
//
|
|
;
|
|
}
|
|
else {
|
|
//
|
|
// Include this address in the key array.
|
|
//
|
|
Key[j++] = Key[i];
|
|
}
|
|
}
|
|
*pNumAddrs = j;
|
|
}
|
|
else {
|
|
//
|
|
// Set the scope-id of unqualified site-local addresses.
|
|
//
|
|
for (i = 0; i < NumAddrs; i++) {
|
|
TDI_ADDRESS_IP6 *Tdi = &Addrs[Key[i]];
|
|
IPv6Addr *Addr = (IPv6Addr *) &Tdi->sin6_addr;
|
|
|
|
if (IsSiteLocal(Addr) &&
|
|
(Tdi->sin6_scope_id == 0))
|
|
Tdi->sin6_scope_id = ScopeId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Records some information about a destination address:
|
|
// Its precedence, whether the preferred source address
|
|
// for the destination "matches" the destination,
|
|
// and if it does match, the common prefix length
|
|
// of the two addresses.
|
|
//
|
|
typedef struct SortAddrInfo {
|
|
uint Preference;
|
|
uint Precedence; // -1 indicates no precedence.
|
|
ushort Scope;
|
|
uchar Flags;
|
|
uchar CommonPrefixLen; // Valid if not SAI_FLAG_DONTUSE.
|
|
} SortAddrInfo;
|
|
|
|
#define SAI_FLAG_DONTUSE 0x1
|
|
#define SAI_FLAG_SCOPE_MISMATCH 0x2
|
|
#define SAI_FLAG_DEPRECATED 0x4
|
|
#define SAI_FLAG_LABEL_MISMATCH 0x8
|
|
|
|
//* CompareSortAddrInfo
|
|
//
|
|
// Compares two addresses A & B and returns
|
|
// an indication of their relative desirability
|
|
// as destination addresses:
|
|
// >0 means A is preferred,
|
|
// 0 means no preference,
|
|
// <0 means B is preferred.
|
|
//
|
|
// Instead of looking directly at the addresses,
|
|
// we look at some precomputed information.
|
|
//
|
|
int
|
|
CompareSortAddrInfo(SortAddrInfo *A, SortAddrInfo *B)
|
|
{
|
|
//
|
|
// Rule 1: Avoid unusable destinations.
|
|
//
|
|
if (A->Flags & SAI_FLAG_DONTUSE) {
|
|
if (B->Flags & SAI_FLAG_DONTUSE)
|
|
return 0; // No preference.
|
|
else
|
|
return -1; // Prefer B.
|
|
}
|
|
else {
|
|
if (B->Flags & SAI_FLAG_DONTUSE)
|
|
return 1; // Prefer A.
|
|
else
|
|
; // Fall through to code below.
|
|
}
|
|
|
|
if ((A->Flags & SAI_FLAG_SCOPE_MISMATCH) !=
|
|
(B->Flags & SAI_FLAG_SCOPE_MISMATCH)) {
|
|
//
|
|
// Rule 2: Prefer matching scope.
|
|
//
|
|
if (A->Flags & SAI_FLAG_SCOPE_MISMATCH)
|
|
return -1; // Prefer B.
|
|
else
|
|
return 1; // Prefer A.
|
|
}
|
|
|
|
if ((A->Flags & SAI_FLAG_DEPRECATED) !=
|
|
(B->Flags & SAI_FLAG_DEPRECATED)) {
|
|
//
|
|
// Rule 3: Avoid deprecated addresses.
|
|
//
|
|
if (A->Flags & SAI_FLAG_DEPRECATED)
|
|
return -1; // Prefer B.
|
|
else
|
|
return 1; // Prefer A.
|
|
}
|
|
|
|
//
|
|
// Rule 4: Prefer home addresses.
|
|
// Not yet implemented, pending mobility support.
|
|
//
|
|
|
|
if ((A->Flags & SAI_FLAG_LABEL_MISMATCH) !=
|
|
(B->Flags & SAI_FLAG_LABEL_MISMATCH)) {
|
|
//
|
|
// Rule 5: Prefer matching label.
|
|
//
|
|
if (A->Flags & SAI_FLAG_LABEL_MISMATCH)
|
|
return -1; // Prefer B.
|
|
else
|
|
return 1; // Prefer A.
|
|
}
|
|
|
|
if ((A->Precedence != (uint)-1) &&
|
|
(B->Precedence != (uint)-1) &&
|
|
(A->Precedence != B->Precedence)) {
|
|
//
|
|
// Rule 6: Prefer higher precedence.
|
|
//
|
|
if (A->Precedence > B->Precedence)
|
|
return 1; // Prefer A.
|
|
else
|
|
return -1; // Prefer B.
|
|
}
|
|
|
|
if (A->Preference != B->Preference) {
|
|
//
|
|
// Rule 7: Prefer *lower* preference.
|
|
// For example, this is used to prefer destinations reached via
|
|
// physical (native) interfaces over virtual (tunnel) interfaces.
|
|
//
|
|
if (A->Preference < B->Preference)
|
|
return 1; // Prefer A.
|
|
else
|
|
return -1; // Prefer B.
|
|
}
|
|
|
|
if (A->Scope != B->Scope) {
|
|
//
|
|
// Rule 8: Prefer smaller scope.
|
|
//
|
|
if (A->Scope < B->Scope)
|
|
return 1; // Prefer A.
|
|
else
|
|
return -1; // Prefer B.
|
|
}
|
|
|
|
if (A->CommonPrefixLen != B->CommonPrefixLen) {
|
|
//
|
|
// Rule 9: Use longest matching prefix.
|
|
//
|
|
if (A->CommonPrefixLen > B->CommonPrefixLen)
|
|
return 1; // Prefer A.
|
|
else
|
|
return -1; // Prefer B.
|
|
}
|
|
|
|
//
|
|
// We have no preference.
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
//* SortDestAddresses
|
|
//
|
|
// Sorts the input array of addresses,
|
|
// from most preferred destination to least preferred.
|
|
//
|
|
// The address array is read-only;
|
|
// the Key array of indices is sorted.
|
|
//
|
|
void
|
|
SortDestAddresses(
|
|
const TDI_ADDRESS_IP6 *Addrs,
|
|
uint *Key,
|
|
uint NumAddrs)
|
|
{
|
|
SortAddrInfo *Info;
|
|
uint i, j;
|
|
|
|
Info = ExAllocatePool(NonPagedPool, sizeof *Info * NumAddrs);
|
|
if (Info == NULL) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
|
"SortDestAddresses: no pool\n"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Calculate some information about each destination address.
|
|
// This will be the basis for our sort.
|
|
//
|
|
|
|
for (i = 0; i < NumAddrs; i++) {
|
|
SortAddrInfo *info = &Info[i];
|
|
const TDI_ADDRESS_IP6 *Tdi = &Addrs[Key[i]];
|
|
const IPv6Addr *Addr = (const IPv6Addr *) &Tdi->sin6_addr;
|
|
uint DstLabel, SrcLabel;
|
|
|
|
//
|
|
// Lookup the precedence of this destination address and
|
|
// the desired label for source addresses used
|
|
// with this destination.
|
|
//
|
|
PrefixPolicyLookup(Addr, &info->Precedence, NULL, &DstLabel);
|
|
|
|
if (IsV4Mapped(Addr)) {
|
|
IPAddr V4Dest = ExtractV4Address(Addr);
|
|
IPAddr V4Source;
|
|
|
|
info->Scope = V4AddressScope(V4Dest);
|
|
|
|
if (TunnelGetSourceAddress(V4Dest, &V4Source)) {
|
|
IPv6Addr Source;
|
|
|
|
//
|
|
// Create an IPv4-mapped address.
|
|
//
|
|
CreateV4Mapped(&Source, V4Source);
|
|
|
|
info->Flags = 0;
|
|
info->CommonPrefixLen = (uchar)
|
|
CommonPrefixLength(Addr, &Source);
|
|
|
|
if (V4AddressScope(V4Source) != info->Scope)
|
|
info->Flags |= SAI_FLAG_SCOPE_MISMATCH;
|
|
|
|
//
|
|
// Lookup the label of the preferred source address.
|
|
//
|
|
PrefixPolicyLookup(&Source, NULL, &SrcLabel, NULL);
|
|
|
|
//
|
|
// We do not know interface/route metrics
|
|
// for IPv4, so just use zero.
|
|
//
|
|
info->Preference = 0;
|
|
|
|
if ((DstLabel != (uint)-1) &&
|
|
(SrcLabel != (uint)-1) &&
|
|
(DstLabel != SrcLabel)) {
|
|
//
|
|
// The best source address for this destination
|
|
// does not match the destination.
|
|
//
|
|
info->Flags |= SAI_FLAG_LABEL_MISMATCH;
|
|
}
|
|
}
|
|
else
|
|
info->Flags = SAI_FLAG_DONTUSE;
|
|
}
|
|
else {
|
|
RouteCacheEntry *RCE;
|
|
|
|
info->Scope = AddressScope(Addr);
|
|
|
|
//
|
|
// Find the preferred source address for this destination.
|
|
//
|
|
if (RouteToDestination(Addr, Tdi->sin6_scope_id,
|
|
NULL, 0, &RCE) == IP_SUCCESS) {
|
|
const IPv6Addr *Source = &RCE->NTE->Address;
|
|
Interface *IF = RCE->NCE->IF;
|
|
|
|
info->Flags = 0;
|
|
info->CommonPrefixLen = (uchar)
|
|
CommonPrefixLength(Addr, Source);
|
|
|
|
if (RCE->NTE->Scope != info->Scope)
|
|
info->Flags |= SAI_FLAG_SCOPE_MISMATCH;
|
|
|
|
if (RCE->NTE->DADState != DAD_STATE_PREFERRED)
|
|
info->Flags |= SAI_FLAG_DEPRECATED;
|
|
|
|
//
|
|
// Lookup the label of the preferred source address.
|
|
//
|
|
PrefixPolicyLookup(Source, NULL, &SrcLabel, NULL);
|
|
|
|
//
|
|
// REVIEW - Instead of using interface preference,
|
|
// would it be better to cache interface+route preference
|
|
// in the RCE?
|
|
//
|
|
info->Preference = IF->Preference;
|
|
|
|
//
|
|
// If the next-hop is definitely unreachable,
|
|
// then we don't want to use this destination.
|
|
// NB: No locking here, this is a heuristic check.
|
|
//
|
|
if ((IF->Flags & IF_FLAG_MEDIA_DISCONNECTED) ||
|
|
RCE->NCE->IsUnreachable)
|
|
info->Flags |= SAI_FLAG_DONTUSE;
|
|
|
|
ReleaseRCE(RCE);
|
|
|
|
if ((DstLabel != (uint)-1) &&
|
|
(SrcLabel != (uint)-1) &&
|
|
(DstLabel != SrcLabel)) {
|
|
//
|
|
// The best source address for this destination
|
|
// does not match the destination.
|
|
//
|
|
info->Flags |= SAI_FLAG_LABEL_MISMATCH;
|
|
}
|
|
}
|
|
else
|
|
info->Flags = SAI_FLAG_DONTUSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Perform the actual sort operation.
|
|
// Because we expect NumAddrs to be small,
|
|
// we use a simple quadratic sort.
|
|
//
|
|
ASSERT(NumAddrs > 0);
|
|
for (i = 0; i < NumAddrs - 1; i++) {
|
|
for (j = i + 1; j < NumAddrs; j++) {
|
|
int Compare;
|
|
|
|
//
|
|
// As a tie-breaker, if the comparison function
|
|
// has no preference we look at the original
|
|
// position of the two addresses and prefer
|
|
// the one that came first.
|
|
//
|
|
Compare = CompareSortAddrInfo(&Info[i], &Info[j]);
|
|
if ((Compare < 0) ||
|
|
((Compare == 0) && (Key[j] < Key[i]))) {
|
|
uint TempKey;
|
|
SortAddrInfo TempInfo;
|
|
|
|
//
|
|
// Address j is preferred over address i,
|
|
// so swap addresses i & j to put j first.
|
|
//
|
|
TempKey = Key[i];
|
|
Key[i] = Key[j];
|
|
Key[j] = TempKey;
|
|
|
|
//
|
|
// We also have to swap the address info.
|
|
//
|
|
TempInfo = Info[i];
|
|
Info[i] = Info[j];
|
|
Info[j] = TempInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
ExFreePool(Info);
|
|
}
|