570 lines
10 KiB
C
570 lines
10 KiB
C
/*++
|
|
|
|
Copyright (c) 2001-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
peer.c
|
|
|
|
Abstract:
|
|
|
|
This module contains teredo peer management functions.
|
|
|
|
Author:
|
|
|
|
Mohit Talwar (mohitt) Wed Oct 24 14:05:08 2001
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
__inline
|
|
USHORT
|
|
TeredoHash(
|
|
IN CONST IN6_ADDR *Address
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Hash a peer's teredo IPv6 address. Used by our static hash table
|
|
implementation. Sum of mapped address and port words, mod # buckets.
|
|
|
|
Arguments:
|
|
|
|
Address - Supplies the peer's teredo IPv6 address.
|
|
|
|
Return Value:
|
|
|
|
Hashed Value.
|
|
|
|
--*/
|
|
{
|
|
return ((Address->s6_words[1] + // Teredo mapped IPv4 address.
|
|
Address->s6_words[2] + // Teredo mapped IPv4 address.
|
|
Address->s6_words[3]) // Teredo mapped UDP port.
|
|
% BUCKET_COUNT);
|
|
}
|
|
|
|
|
|
PTEREDO_PEER
|
|
TeredoCreatePeer(
|
|
IN PLIST_ENTRY BucketHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a teredo peer entry.
|
|
|
|
Arguments:
|
|
|
|
BucketHead - Supplies the bucket list head to which the peer belongs.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR or failure code.
|
|
|
|
Caller LOCK: Client::PeerSet.
|
|
|
|
--*/
|
|
{
|
|
PTEREDO_PEER Peer;
|
|
|
|
//
|
|
// Allocate the peer structure from the appropriate heap.
|
|
//
|
|
Peer = (PTEREDO_PEER) HeapAlloc(
|
|
TeredoClient.PeerHeap, 0, sizeof(TEREDO_PEER));
|
|
if (Peer == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize fields that remain unchanged for used neighors.
|
|
//
|
|
|
|
#if DBG
|
|
Peer->Signature = TEREDO_PEER_SIGNATURE;
|
|
#endif // DBG
|
|
|
|
//
|
|
// Insert the peer at the beginning of the LRU list.
|
|
//
|
|
TeredoClient.PeerSet.Size++;
|
|
InsertHeadList(BucketHead, &(Peer->Link));
|
|
|
|
Peer->ReferenceCount = 1;
|
|
Peer->BubblePosted = FALSE;
|
|
|
|
TeredoInitializePacket(&(Peer->Packet));
|
|
Peer->Packet.Type = TEREDO_PACKET_BUBBLE;
|
|
Peer->Packet.Buffer.len = sizeof(IP6_HDR);
|
|
ASSERT(Peer->Packet.Buffer.buf == (PUCHAR) &(Peer->Bubble));
|
|
|
|
//
|
|
// Create the teredo bubble packet.
|
|
//
|
|
Peer->Bubble.ip6_flow = 0;
|
|
Peer->Bubble.ip6_plen = 0;
|
|
Peer->Bubble.ip6_nxt = IPPROTO_NONE;
|
|
Peer->Bubble.ip6_hlim = IPV6_HOPLIMIT;
|
|
Peer->Bubble.ip6_vfc = IPV6_VERSION;
|
|
|
|
//
|
|
// Obtain a reference on the teredo client for the peer.
|
|
//
|
|
TeredoReferenceClient();
|
|
return Peer;
|
|
}
|
|
|
|
|
|
VOID
|
|
TeredoDestroyPeer(
|
|
IN PTEREDO_PEER Peer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys a peer entry.
|
|
|
|
Arguments:
|
|
|
|
Peer - Supplies the peer entry to destroy.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(IsListEmpty(&(Peer->Link)));
|
|
ASSERT(Peer->ReferenceCount == 0);
|
|
ASSERT(Peer->BubblePosted == FALSE);
|
|
|
|
HeapFree(TeredoClient.PeerHeap, 0, (PUCHAR) Peer);
|
|
|
|
//
|
|
// Release the peer's reference on the teredo client.
|
|
// This might cause the client to be cleaned up, hence we do it last.
|
|
//
|
|
TeredoDereferenceClient();
|
|
}
|
|
|
|
|
|
VOID
|
|
TeredoInitializePeer(
|
|
OUT PTEREDO_PEER Peer,
|
|
IN CONST IN6_ADDR *Address
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the state of a peer upon creation or reuse.
|
|
|
|
The peer is already inserted in the appropriate bucket.
|
|
|
|
Arguments:
|
|
|
|
Peer - Returns a peer with its state initialized.
|
|
|
|
Address - Supplies the peer's teredo IPv6 address.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Caller LOCK: Client::PeerSet.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(Peer->ReferenceCount == 1);
|
|
ASSERT(Peer->BubblePosted == FALSE);
|
|
|
|
//
|
|
// Reset fields for both new and used peers...
|
|
//
|
|
Peer->LastReceive = Peer->LastTransmit =
|
|
TeredoClient.Time - TEREDO_REFRESH_INTERVAL;
|
|
Peer->Address = *Address;
|
|
Peer->BubbleCount = 0;
|
|
|
|
//
|
|
// Teredo mapped UDP port & IPv4 address.
|
|
//
|
|
TeredoParseAddress(
|
|
Address,
|
|
&(Peer->Packet.SocketAddress.sin_addr),
|
|
&(Peer->Packet.SocketAddress.sin_port));
|
|
|
|
//
|
|
// Update fields in the teredo bubble packet.
|
|
//
|
|
Peer->Bubble.ip6_dest = Peer->Address;
|
|
// Peer->Bubble.ip6_src... Filled in when sending.
|
|
}
|
|
|
|
|
|
VOID
|
|
TeredoDeletePeer(
|
|
IN OUT PTEREDO_PEER Peer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete a peer from the peer set, thus initiating its destruction.
|
|
|
|
Arguments:
|
|
|
|
Interface - Returns a peer deleted from the peer set.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Caller LOCK: Client::PeerSet.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Unlink the neigbor from the peer set...
|
|
//
|
|
TeredoClient.PeerSet.Size--;
|
|
RemoveEntryList(&(Peer->Link));
|
|
InitializeListHead(&(Peer->Link));
|
|
|
|
//
|
|
// And release the reference obtained for being in it.
|
|
//
|
|
TeredoDereferencePeer(Peer);
|
|
}
|
|
|
|
|
|
BOOL
|
|
__inline
|
|
TeredoCachedPeer(
|
|
IN PTEREDO_PEER Peer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the peer belonging to the PeerSet is cached.
|
|
|
|
Arguments:
|
|
|
|
Peer - Supplies the peer being inspected.
|
|
The peer should still be a member of the peer set.
|
|
|
|
Return Value:
|
|
|
|
TRUE if cached, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// The peer does belong to a peer set. Right?
|
|
//
|
|
ASSERT(!IsListEmpty(&(Peer->Link)));
|
|
return (Peer->ReferenceCount == 1);
|
|
}
|
|
|
|
|
|
PTEREDO_PEER
|
|
TeredoReusePeer(
|
|
IN PLIST_ENTRY BucketHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reuse an existing peer entry from the bucket if one is cached.
|
|
|
|
Arguments:
|
|
|
|
BucketHead - Supplies the bucket list head to which the peer belongs.
|
|
|
|
Return Value:
|
|
|
|
Peer entry to reuse or NULL.
|
|
|
|
Caller LOCK: Client::PeerSet.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY Next;
|
|
PTEREDO_PEER Peer;
|
|
|
|
Next = BucketHead->Blink;
|
|
if (Next == BucketHead) {
|
|
return NULL;
|
|
}
|
|
|
|
Peer = Cast(CONTAINING_RECORD(Next, TEREDO_PEER, Link), TEREDO_PEER);
|
|
if (TeredoCachedPeer(Peer)) {
|
|
//
|
|
// Insert the peer at the beginning of the LRU list.
|
|
//
|
|
RemoveEntryList(Next);
|
|
InsertHeadList(BucketHead, Next);
|
|
|
|
return Peer;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PTEREDO_PEER
|
|
TeredoReuseOrCreatePeer(
|
|
IN PLIST_ENTRY BucketHead,
|
|
IN CONST IN6_ADDR *Address
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reuse or create a teredo peer and (re)initialize its state.
|
|
|
|
Arguments:
|
|
|
|
BucketHead - Supplies the bucket list head to which the peer belongs.
|
|
|
|
Address - Supplies the peer's teredo IPv6 address.
|
|
|
|
Return Value:
|
|
|
|
Peer entry to use or NULL.
|
|
|
|
Caller LOCK: Client::PeerSet.
|
|
|
|
--*/
|
|
{
|
|
PTEREDO_PEER Peer;
|
|
|
|
Peer = TeredoReusePeer(BucketHead);
|
|
if (Peer == NULL) {
|
|
Peer = TeredoCreatePeer(BucketHead);
|
|
}
|
|
|
|
if (Peer == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
TeredoInitializePeer(Peer, Address);
|
|
return Peer;
|
|
}
|
|
|
|
|
|
PTEREDO_PEER
|
|
TeredoFindPeer(
|
|
IN PLIST_ENTRY BucketHead,
|
|
IN CONST IN6_ADDR *Address
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find a peer entry with the given address.
|
|
|
|
Arguments:
|
|
|
|
BucketHead - Supplies the bucket list head to which the peer belongs.
|
|
|
|
Address - Supplies the peer's teredo IPv6 address.
|
|
|
|
Return Value:
|
|
|
|
Peer entry or NULL.
|
|
|
|
Caller LOCK: Client::PeerSet.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY Next;
|
|
PTEREDO_PEER Peer;
|
|
|
|
for (Next = BucketHead->Flink; Next != BucketHead; Next = Next->Flink) {
|
|
Peer = Cast(
|
|
CONTAINING_RECORD(Next, TEREDO_PEER, Link), TEREDO_PEER);
|
|
if (TeredoEqualPrefix(&(Peer->Address), Address)) {
|
|
return Peer; // found!
|
|
}
|
|
}
|
|
|
|
return NULL; // not found!
|
|
}
|
|
|
|
|
|
PTEREDO_PEER
|
|
TeredoFindOrCreatePeer(
|
|
IN CONST IN6_ADDR *Address
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find a peer entry with the given teredo IPv6 address.
|
|
|
|
Create one if the search is unsuccessful.
|
|
|
|
Returns a reference on the found/created peer to the caller.
|
|
|
|
Arguments:
|
|
|
|
Address - Supplies the peer's teredo IPv6 address.
|
|
|
|
Return Value:
|
|
|
|
Peer entry or NULL.
|
|
|
|
--*/
|
|
{
|
|
PTEREDO_PEER Peer = NULL;
|
|
PLIST_ENTRY Head = TeredoClient.PeerSet.Bucket + TeredoHash(Address);
|
|
|
|
ASSERT(Address->s6_words[0] == TeredoIpv6ServicePrefix.s6_words[0]);
|
|
|
|
//
|
|
// Note: Since we typically do only a small amount of work while holding
|
|
// this lock we don't need it to be a multiple-reader-single-writer lock!
|
|
//
|
|
EnterCriticalSection(&(TeredoClient.PeerSet.Lock));
|
|
|
|
if (TeredoClient.State != TEREDO_STATE_OFFLINE) {
|
|
Peer = TeredoFindPeer(Head, Address);
|
|
if (Peer == NULL) {
|
|
Peer = TeredoReuseOrCreatePeer(Head, Address);
|
|
}
|
|
|
|
if (Peer != NULL) {
|
|
TeredoReferencePeer(Peer);
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&(TeredoClient.PeerSet.Lock));
|
|
|
|
return Peer;
|
|
}
|
|
|
|
|
|
DWORD
|
|
TeredoInitializePeerSet(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the peer set, organized as a statically sized hash table.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR or failure code.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
|
|
__try {
|
|
InitializeCriticalSection(&(TeredoClient.PeerSet.Lock));
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetLastError();
|
|
}
|
|
|
|
TeredoClient.PeerSet.Size = 0;
|
|
for (i = 0; i < BUCKET_COUNT; i++) {
|
|
InitializeListHead(TeredoClient.PeerSet.Bucket + i);
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
VOID
|
|
TeredoUninitializePeerSet(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Uninitializes the peer set. Typically invoked when going offline.
|
|
Deletes all existing peers.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PTEREDO_PEER Peer;
|
|
ULONG i;
|
|
|
|
ASSERT(TeredoClient.State == TEREDO_STATE_OFFLINE);
|
|
|
|
EnterCriticalSection(&(TeredoClient.PeerSet.Lock));
|
|
|
|
for (i = 0;
|
|
(i < BUCKET_COUNT) && (TeredoClient.PeerSet.Size != 0);
|
|
i++) {
|
|
Head = TeredoClient.PeerSet.Bucket + i;
|
|
while (!IsListEmpty(Head)) {
|
|
Next = RemoveHeadList(Head);
|
|
Peer = Cast(
|
|
CONTAINING_RECORD(Next, TEREDO_PEER, Link), TEREDO_PEER);
|
|
TeredoDeletePeer(Peer);
|
|
}
|
|
}
|
|
|
|
ASSERT(TeredoClient.PeerSet.Size == 0); // no more, no less
|
|
|
|
LeaveCriticalSection(&(TeredoClient.PeerSet.Lock));
|
|
}
|
|
|
|
|
|
VOID
|
|
TeredoCleanupPeerSet(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up the peer set. Typically invoked upon service stop.
|
|
All peers should have been deleted.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(TeredoClient.PeerSet.Size == 0); // no more, no less
|
|
|
|
DeleteCriticalSection(&(TeredoClient.PeerSet.Lock));
|
|
}
|