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