Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1382 lines
29 KiB

/*++
Copyright (c) 2001-2002 Microsoft Corporation
Module Name:
client.c
Abstract:
This module contains the teredo client implementation.
Author:
Mohit Talwar (mohitt) Mon Oct 22 15:17:20 2001
Environment:
User mode only.
--*/
#include "precomp.h"
#pragma hdrstop
TEREDO_CLIENT_STATE TeredoClient;
TEREDO_PACKET_IO_COMPLETE TeredoClientReadComplete;
TEREDO_PACKET_IO_COMPLETE TeredoClientWriteComplete;
TEREDO_PACKET_IO_COMPLETE TeredoClientBubbleComplete;
TEREDO_PACKET_IO_COMPLETE TeredoClientReceiveComplete;
TEREDO_PACKET_IO_COMPLETE TeredoClientTransmitComplete;
TEREDO_PACKET_IO_COMPLETE TeredoClientMulticastComplete;
VOID TeredoTransmitMulticastBubble( VOID );
BOOL
TeredoAddressPresent(
VOID
)
/*++
Routine Description:
Determine whether an IPv6 tunnel interface has a teredo address. The
address must have been configured from a router advertisement. If the
address is found, we remember the teredo tunnel interface name.
Arguments:
None.
Return Value:
TRUE if present, FALSE if not.
--*/
{
DWORD Error;
ULONG Bytes;
PIP_ADAPTER_ADDRESSES Adapters, Next;
PIP_ADAPTER_UNICAST_ADDRESS Address;
BOOL Found = FALSE;
TraceEnter("TeredoAddressPresent");
//
// 10 Adapters, each with 3 strings and 4 unicast addresses.
// This would usually be more than enough!
//
Bytes = 10 * (
sizeof(IP_ADAPTER_ADDRESSES) +
2 * MAX_ADAPTER_NAME_LENGTH + MAX_ADAPTER_DESCRIPTION_LENGTH +
4 * (sizeof(IP_ADAPTER_UNICAST_ADDRESS) + sizeof(SOCKADDR_IN6)));
Adapters = MALLOC(Bytes);
if (Adapters == NULL) {
return FALSE;
}
do {
Error = GetAdaptersAddresses(
AF_INET6,
GAA_FLAG_SKIP_FRIENDLY_NAME |
GAA_FLAG_SKIP_DNS_SERVER |
GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_ANYCAST,
NULL, Adapters, &Bytes);
if (Error == ERROR_BUFFER_OVERFLOW) {
Next = REALLOC(Adapters, Bytes);
if (Next != NULL) {
Adapters = Next;
} else {
Error = ERROR_OUTOFMEMORY;
}
}
} while (Error == ERROR_BUFFER_OVERFLOW);
if (Error != NO_ERROR) {
goto Bail;
}
for (Next = Adapters; Next != NULL; Next = Next->Next) {
if ((Next->IfType != IF_TYPE_TUNNEL) ||
(Next->OperStatus != IfOperStatusUp)) {
continue;
}
for (Address = Next->FirstUnicastAddress;
Address != NULL;
Address = Address->Next) {
if ((Address->PrefixOrigin != PREFIX_CONF_RA) ||
(Address->DadState != IpDadStatePreferred)) {
continue;
}
if (TeredoEqualPrefix(
&(((PSOCKADDR_IN6) Address->Address.lpSockaddr)->sin6_addr),
&(TeredoClient.Ipv6Prefix))) {
ConvertOemToUnicode(
Next->AdapterName,
TeredoClient.Io.TunnelInterface,
MAX_ADAPTER_NAME_LENGTH);
Found = TRUE;
goto Bail;
}
}
}
Bail:
FREE(Adapters);
return Found;
}
VOID
CALLBACK
TeredoClientIoCompletionCallback(
IN DWORD ErrorCode,
IN DWORD Bytes,
IN LPOVERLAPPED Overlapped
)
/*++
Routine Description:
Callback routine for I/O completion on TUN interface device or UDP socket.
Arguments:
ErrorCode - Supplies the I/O completion status.
Bytes - Supplies the number of bytes transferred.
Overlapped - Supplies the completion context.
Return Value:
None.
--*/
{
static CONST PTEREDO_PACKET_IO_COMPLETE Callback[] =
{
TeredoClientReadComplete,
TeredoClientWriteComplete,
TeredoClientBubbleComplete,
NULL, // No bouncing...
TeredoClientReceiveComplete,
TeredoClientTransmitComplete,
TeredoClientMulticastComplete,
};
PTEREDO_PACKET Packet = Cast(
CONTAINING_RECORD(Overlapped, TEREDO_PACKET, Overlapped),
TEREDO_PACKET);
ASSERT(Packet->Type != TEREDO_PACKET_BOUNCE);
//
// This completion function usually posts the packet for another I/O.
// Since we are called by a non-I/O worker thread, asynchronous I/O
// requests posted here might terminate when this thread does. This
// is rare enough that we don't special case it. Moreover, we only
// make best effort guarantees to the upper layer!
//
(*Callback[Packet->Type])(ErrorCode, Bytes, Packet);
}
VOID
CALLBACK
TeredoClientTimerCallback(
IN PVOID Parameter,
IN BOOLEAN TimerOrWaitFired
)
/*++
Routine Description:
Callback routine for TeredoClient.Timer expiration.
The timer is active in the probe and qualified states.
Arguments:
Parameter, TimerOrWaitFired - Ignored.
Return Value:
None.
--*/
{
ENTER_API();
if (TeredoClient.State == TEREDO_STATE_PROBE) {
if (TeredoClient.RestartQualifiedTimer) {
//
// Probe -> Qualified.
//
if (TeredoAddressPresent()) {
//
// The stack has validated and processed an RA.
//
TeredoQualifyClient();
} else {
//
// The stack has not received any valid RA.
//
TeredoStopClient();
}
} else {
//
// Probe -> Offline.
//
TeredoStopClient();
}
} else {
if (TeredoClient.RestartQualifiedTimer) {
//
// Qualified -> Qualified.
//
TeredoQualifyClient();
} else {
//
// Qualified -> Probe.
//
TeredoProbeClient();
}
}
LEAVE_API();
}
VOID
CALLBACK
TeredoClientTimerCleanup(
IN PVOID Parameter,
IN BOOLEAN TimerOrWaitFired
)
/*++
Routine Description:
Callback routine for TeredoClient.Timer deletion.
Deletion is performed asynchronously since we acquire a lock in
the callback function that we hold when deleting the timer.
Arguments:
Parameter, TimerOrWaitFired - Ignored.
Return Value:
None.
--*/
{
TeredoDereferenceClient();
}
VOID
TeredoClientAddressDeletionNotification(
IN IN_ADDR Address
)
/*++
Routine Description:
Process an address deletion request.
Arguments:
Address - Supplies the address that was deleted.
Return Value:
None.
Caller LOCK: API.
--*/
{
if (!IN4_ADDR_EQUAL(Address, TeredoClient.Io.SourceAddress.sin_addr)) {
return;
}
//
// Refresh the socket state (the socket bound to SourceAddress).
//
if (TeredoRefreshSocket(&(TeredoClient.Io)) != NO_ERROR) {
//
// [Probe | Qualified] -> Offline.
//
TeredoStopClient();
return;
}
if (IN4_ADDR_EQUAL(
TeredoClient.Io.SourceAddress.sin_addr,
TeredoClient.Io.ServerAddress.sin_addr)) {
//
// [Probe | Qualified] -> Offline.
//
TeredoStopClient();
return;
}
//
// [Probe | Qualified] -> Probe.
//
TeredoProbeClient();
}
VOID
TeredoClientRefreshIntervalChangeNotification(
VOID
)
/*++
Routine Description:
Process a refresh interval change request.
Arguments:
None.
Return Value:
None.
Caller LOCK: API.
--*/
{
if (TeredoClient.RefreshInterval == TeredoClientRefreshInterval) {
return;
}
TeredoClient.RefreshInterval = TeredoClientRefreshInterval;
if (TeredoClient.State == TEREDO_STATE_QUALIFIED) {
//
// Refresh interval has been updated.
// Qualified -> Qualified.
//
TeredoQualifyClient();
}
}
VOID
TeredoStartClient(
VOID
)
/*++
Routine Description:
Attempt to start the teredo service at the client.
Events / Transitions
ServiceStart Offline -> Probe.
ServiceEnable Offline -> Probe.
AdapterArrival Offline -> Probe.
AddressAddition Offline -> Probe.
Arguments:
None.
Return Value:
None.
Caller LOCK: API.
--*/
{
TraceEnter("TeredoStartClient");
//
// Can't have both the client and server on the same node.
//
if (TeredoServer.State != TEREDO_STATE_OFFLINE) {
return;
}
//
// Well, the service has already been started!
//
if (TeredoClient.State != TEREDO_STATE_OFFLINE) {
return;
}
TeredoClient.State = TEREDO_STATE_PROBE;
//
// Start I/O processing.
//
if (TeredoStartIo(&(TeredoClient.Io)) != NO_ERROR) {
goto Bail;
}
if (IN4_ADDR_EQUAL(
TeredoClient.Io.SourceAddress.sin_addr,
TeredoClient.Io.ServerAddress.sin_addr)) {
goto Bail;
}
//
// Start a one shot probe timer.
//
if (!CreateTimerQueueTimer(
&(TeredoClient.Timer),
NULL,
TeredoClientTimerCallback,
NULL,
TEREDO_PROBE_INTERVAL * 1000, // in milliseconds.
INFINITE_INTERVAL,
0)) {
goto Bail;
}
//
// Obtain a reference on the teredo client for the running timer.
//
TeredoReferenceClient();
return;
Bail:
TeredoClient.State = TEREDO_STATE_OFFLINE;
TeredoStopIo(&(TeredoClient.Io));
}
VOID
TeredoStopClient(
VOID
)
/*++
Routine Description:
Stop the teredo service at the client.
Events / Transitions
ProbeTimer Probe -> Offline.
ServiceStop [Probe | Qualified] -> Offline.
ServiceDisable [Probe | Qualified] -> Offline.
AdapterRemoval [Probe | Qualified] -> Offline.
AddressDeletion [Probe | Qualified] -> Offline.
Arguments:
None.
Return Value:
None.
Caller LOCK: API.
--*/
{
TraceEnter("TeredoStopClient");
//
// Well, the service was never started!
//
if (TeredoClient.State == TEREDO_STATE_OFFLINE) {
return;
}
TeredoClient.State = TEREDO_STATE_OFFLINE;
TeredoClient.Ipv6Prefix = in6addr_any;
TeredoClient.RestartQualifiedTimer = FALSE;
DeleteTimerQueueTimer(
NULL, TeredoClient.Timer, TeredoClient.TimerEvent);
TeredoClient.Timer = NULL;
TeredoStopIo(&(TeredoClient.Io));
TeredoUninitializePeerSet();
}
VOID
TeredoProbeClient(
VOID
)
/*++
Routine Description:
Probe the teredo service at the client.
Events / Transitions
QualifiedTimer Qualified -> Probe.
AddressDeletion [Probe | Qualified] -> Probe.
Arguments:
None.
Return Value:
None.
Caller LOCK: API.
--*/
{
TraceEnter("TeredoProbeClient");
TeredoClient.State = TEREDO_STATE_PROBE;
//
// Reconnect!
//
if (!ReconnectInterface(TeredoClient.Io.TunnelInterface)) {
//
// [Probe | Qualified] -> Offline.
//
TeredoStopClient();
return;
}
if (!ChangeTimerQueueTimer(
NULL,
TeredoClient.Timer,
TEREDO_PROBE_INTERVAL * 1000, // in milliseconds.
INFINITE_INTERVAL)) {
TeredoStopClient();
return;
}
TeredoClient.RestartQualifiedTimer = FALSE;
}
VOID
TeredoQualifyClient(
VOID
)
/*++
Routine Description:
Qualify the teredo service at the client.
Events / Transitions
RouterAdvertisement Probe -> Qualified.
NatMappingRefresh Qualified -> Qualified.
RefreshIntervalChange Qualified -> Qualified.
Arguments:
None.
Return Value:
None.
Caller LOCK: API.
--*/
{
TraceEnter("TeredoQualifyClient");
TeredoClient.State = TEREDO_STATE_QUALIFIED;
if (!ChangeTimerQueueTimer(
NULL,
TeredoClient.Timer,
TeredoClient.RefreshInterval * 1000, // in milliseconds.
INFINITE_INTERVAL)) {
//
// [Probe | Qualified] -> Offline.
//
TeredoStopClient();
return;
}
TeredoTransmitMulticastBubble();
TeredoClient.RestartQualifiedTimer = FALSE;
}
DWORD
TeredoInitializeClient(
VOID
)
/*++
Routine Description:
Initializes the client.
Arguments:
None.
Return Value:
NO_ERROR or failure code.
--*/
{
DWORD Error;
//
// Obtain a reference on the teredo client for initialization.
//
TeredoClient.ReferenceCount = 1;
TeredoClient.PeerHeap
= TeredoClient.TimerEvent
= TeredoClient.TimerEventWait
= NULL;
TeredoClient.BubbleTicks = 0;
TeredoClient.BubblePosted = FALSE;
TeredoInitializePacket(&(TeredoClient.Packet));
TeredoClient.Packet.Type = TEREDO_PACKET_MULTICAST;
TeredoClient.Packet.Buffer.len = sizeof(IP6_HDR);
ASSERT(TeredoClient.Packet.Buffer.buf ==
(PUCHAR) &(TeredoClient.Bubble));
TeredoClient.Bubble.ip6_flow = 0;
TeredoClient.Bubble.ip6_plen = 0;
TeredoClient.Bubble.ip6_nxt = IPPROTO_NONE;
TeredoClient.Bubble.ip6_hlim = IPV6_HOPLIMIT;
TeredoClient.Bubble.ip6_vfc = IPV6_VERSION;
// Peer->Bubble.ip6_src... Filled in when sending.
TeredoClient.Bubble.ip6_dest = TeredoIpv6MulticastPrefix;
//
// Multicast bubble destination UDP port & IPv4 address.
//
TeredoParseAddress(
&(TeredoClient.Bubble.ip6_dest),
&(TeredoClient.Packet.SocketAddress.sin_addr),
&(TeredoClient.Packet.SocketAddress.sin_port));
Error = TeredoInitializeIo(
&(TeredoClient.Io),
TeredoClient.Packet.SocketAddress.sin_addr,
TeredoReferenceClient,
TeredoDereferenceClient,
TeredoClientIoCompletionCallback);
if (Error != NO_ERROR) {
return Error;
}
TeredoClient.PeerHeap = HeapCreate(0, 0, 0);
if (TeredoClient.PeerHeap == NULL) {
Error = GetLastError();
goto Bail;
}
TeredoClient.TimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (TeredoClient.TimerEvent == NULL) {
Error = GetLastError();
goto Bail;
}
if (!RegisterWaitForSingleObject(
&(TeredoClient.TimerEventWait),
TeredoClient.TimerEvent,
TeredoClientTimerCleanup,
NULL,
INFINITE,
0)) {
Error = GetLastError();
goto Bail;
}
TeredoClient.RestartQualifiedTimer = FALSE;
TeredoClient.Time = TeredoGetTime();
TeredoClient.State = TEREDO_STATE_OFFLINE;
TeredoClient.Ipv6Prefix = in6addr_any;
TeredoClient.RefreshInterval = TeredoClientRefreshInterval;
TeredoClient.Timer = INVALID_HANDLE_VALUE;
Error = TeredoInitializePeerSet();
if (Error != NO_ERROR) {
goto Bail;
}
IncEventCount("TeredoInitializeClient");
return NO_ERROR;
Bail:
TeredoCleanupIo(&(TeredoClient.Io));
if (TeredoClient.PeerHeap != NULL) {
HeapDestroy(TeredoClient.PeerHeap);
TeredoClient.PeerHeap = NULL;
}
if (TeredoClient.TimerEventWait != NULL) {
UnregisterWait(TeredoClient.TimerEventWait);
TeredoClient.TimerEventWait = NULL;
}
if (TeredoClient.TimerEvent != NULL) {
CloseHandle(TeredoClient.TimerEvent);
TeredoClient.TimerEvent = NULL;
}
return Error;
}
VOID
TeredoUninitializeClient(
VOID
)
/*++
Routine Description:
Uninitializes the client. Typically invoked upon service stop.
Arguments:
None.
Return Value:
None.
--*/
{
TeredoStopClient();
TeredoDereferenceClient();
}
VOID
TeredoCleanupClient(
VOID
)
/*++
Routine Description:
Cleans up the client after the last reference to it has been released.
Arguments:
None.
Return Value:
None.
--*/
{
TeredoCleanupPeerSet();
UnregisterWait(TeredoClient.TimerEventWait);
TeredoClient.TimerEventWait = NULL;
CloseHandle(TeredoClient.TimerEvent);
TeredoClient.TimerEvent = NULL;
HeapDestroy(TeredoClient.PeerHeap);
TeredoClient.PeerHeap = NULL;
TeredoCleanupIo(&(TeredoClient.Io));
DecEventCount("TeredoCleanupClient");
}
VOID
TeredoTransmitMulticastBubble(
VOID
)
/*++
Routine Description:
Transmit a teredo multicast bubble on the native link.
Arguments:
None.
Return Value:
None.
Caller LOCK: API.
--*/
{
ASSERT(TeredoClient.State == TEREDO_STATE_QUALIFIED);
if (TeredoClient.BubbleTicks == 0) {
//
// No multicast bubbles should be sent.
//
return;
}
if (--TeredoClient.BubbleTicks != 0) {
//
// Our time is not yet up!
//
return;
}
if (TeredoClient.BubblePosted == TRUE) {
//
// At most one outstanding multicast bubble is allowed! Try later.
//
TeredoClient.BubbleTicks = 1;
return;
}
//
// Reset the timer.
//
TeredoClient.BubbleTicks = TEREDO_MULTICAST_BUBBLE_TICKS;
//
// Obtain a reference for the posted multicast bubble.
//
TeredoReferenceClient();
TeredoClient.Bubble.ip6_src = TeredoClient.Ipv6Prefix;
if (TeredoTransmitPacket(
&(TeredoClient.Io), &(TeredoClient.Packet)) != NULL) {
TeredoClientMulticastComplete(
NO_ERROR, sizeof(IP6_HDR), &(TeredoClient.Packet));
}
}
VOID
TeredoTransmitBubble(
IN PTEREDO_PEER Peer
)
/*++
Routine Description:
Transmit a teredo bubble to a peer.
Arguments:
Peer - Supplies the peer of interest.
Return Value:
None.
--*/
{
if (TIME_GREATER(
Peer->LastTransmit,
(TeredoClient.Time - TEREDO_BUBBLE_INTERVAL))) {
//
// Rate limit bubble transmission.
//
return;
}
if (TIME_GREATER(
(TeredoClient.Time - TEREDO_BUBBLE_THRESHHOLD),
Peer->LastReceive) &&
TIME_GREATER(
Peer->LastTransmit,
(TeredoClient.Time - TEREDO_SLOW_BUBBLE_INTERVAL))) {
//
// If the peer refuses to respond, drop rate (to once in 5 minutes).
//
return;
}
if (InterlockedExchange(&(Peer->BubblePosted), TRUE)) {
//
// At most one outstanding bubble is allowed!
//
return;
}
//
// Obtain a reference for the posted bubble.
//
TeredoReferencePeer(Peer);
Peer->LastTransmit = TeredoClient.Time;
Peer->BubbleCount++;
Peer->Bubble.ip6_src = TeredoClient.Ipv6Prefix;
if (TeredoTransmitPacket(
&(TeredoClient.Io), &(Peer->Packet)) != NULL) {
TeredoClientBubbleComplete(
NO_ERROR, sizeof(IP6_HDR), &(Peer->Packet));
}
}
BOOL
TeredoReceiveRouterAdvertisement(
IN PTEREDO_PACKET Packet,
IN ULONG Bytes
)
/*++
Routine Description:
Process the router advertisement packet received on the UDP socket.
Arguments:
Packet - Supplies the packet that was received.
Bytes - Supplies the length of the packet.
Return Value:
TRUE if the packet should be forwarded to the stack, FALSE otherwise.
--*/
{
PUCHAR Buffer = Packet->Buffer.buf;
ICMPv6Header *Icmp6;
UCHAR Type;
ULONG Length;
NDOptionPrefixInformation *Prefix = NULL;
if (!IN4_SOCKADDR_EQUAL(
&(Packet->SocketAddress), &(TeredoClient.Io.ServerAddress))) {
//
// Only the teredo server is allowed to send an RA.
//
return FALSE;
}
//
// Parse up until the ICMPv6 header for the router advertisement.
//
Icmp6 = TeredoParseIpv6Headers(Buffer, Bytes);
if (Icmp6 == NULL) {
return FALSE;
}
if ((Icmp6->Type != ICMPv6_ROUTER_ADVERT) || (Icmp6->Code != 0)) {
return FALSE;
}
Buffer = (PUCHAR) (Icmp6 + 1);
Bytes -= (ULONG) (Buffer - Packet->Buffer.buf);
//
// Parse the rest of the router advertisement header.
//
if (Bytes < sizeof(NDRouterAdvertisement)) {
return FALSE;
}
Buffer += sizeof(NDRouterAdvertisement);
Bytes -= sizeof(NDRouterAdvertisement);
while (Bytes != 0) {
//
// Parse TLV options.
//
if (Bytes < 8) {
return FALSE;
}
Type = Buffer[0];
Length = (Buffer[1] * 8);
if ((Length == 0) || (Bytes < Length)) {
return FALSE;
}
if (Type == ND_OPTION_PREFIX_INFORMATION) {
if (Prefix != NULL) {
//
// There should only be one advertised prefix.
//
return FALSE;
}
if (Length != sizeof(NDOptionPrefixInformation)) {
return FALSE;
}
Prefix = (NDOptionPrefixInformation *) Buffer;
if (!TeredoValidAdvertisedPrefix(
&(Prefix->Prefix), Prefix->PrefixLength)) {
return FALSE;
}
}
Buffer += Length;
Bytes -= Length;
}
//
// We have a valid router advertisement!
// [Probe | Qualified] -> Qualified.
//
if (!IN6_ADDR_EQUAL(&(TeredoClient.Ipv6Prefix), &(Prefix->Prefix))) {
//
// We've either created a new IPv6 address or changed the existing one.
// Transmit a multicast bubble as soon as the client qualifies.
//
TeredoClient.BubbleTicks =
(TEREDO_MULTICAST_BUBBLE_TICKS != 0) ? 1 : 0;
}
TeredoClient.Ipv6Prefix = Prefix->Prefix;
TeredoClient.RestartQualifiedTimer = TRUE;
return TRUE;
}
BOOL
TeredoClientReceiveData(
IN PTEREDO_PACKET Packet
)
/*++
Routine Description:
Process the data packet received on the UDP socket.
Arguments:
Packet - Supplies the packet that was received.
Return Value:
TRUE if the packet should be forwarded to the stack, FALSE otherwise.
--*/
{
PIP6_HDR Ipv6;
IN_ADDR Address;
USHORT Port;
PTEREDO_PEER Peer;
if (IN6_IS_ADDR_UNSPECIFIED(&(TeredoClient.Ipv6Prefix))) {
//
// The client hasn't been qualified ever!
//
return FALSE;
}
if (IN4_SOCKADDR_EQUAL(
&(Packet->SocketAddress), &(TeredoClient.Io.ServerAddress))) {
//
// The client received the packet from the teredo server.
//
if (TeredoClient.State == TEREDO_STATE_QUALIFIED) {
//
// The NAT mapping has been refreshed.
// NOTE: Since we don't acquire the API lock here, there is a small
// chance that we have now transitioned to PROBE state. If so,
// setting the flag to TRUE below will mistakenly cause us to
// re-enter the qualified state. However that's quite harmless.
//
TeredoClient.RestartQualifiedTimer = TRUE;
}
return TRUE;
}
Ipv6 = (PIP6_HDR) Packet->Buffer.buf;
if (!TeredoServicePrefix(&(Ipv6->ip6_src))) {
//
// The IPv6 source address should be a valid teredo address.
//
return FALSE;
}
TeredoParseAddress(&(Ipv6->ip6_src), &Address, &Port);
if (!TeredoIpv4GlobalAddress((PUCHAR) &Address)) {
//
// The IPv4 source address should be global scope.
//
return FALSE;
}
if (!IN4_ADDR_EQUAL(Packet->SocketAddress.sin_addr, Address) ||
(Packet->SocketAddress.sin_port != Port)) {
//
// Should have been constructed by the *right* teredo peer.
//
return FALSE;
}
Peer = TeredoFindOrCreatePeer(&(Ipv6->ip6_src));
if (Peer != NULL) {
Peer->LastReceive = TeredoClient.Time;
TeredoTransmitBubble(Peer);
TeredoDereferencePeer(Peer);
}
return TRUE;
}
VOID
TeredoClientReadComplete(
IN DWORD Error,
IN ULONG Bytes,
IN PTEREDO_PACKET Packet
)
/*++
Routine Description:
Process a read completion on the TUN device.
--*/
{
PIP6_HDR Ipv6;
IN_ADDR Address;
USHORT Port;
PTEREDO_PEER Peer;
if ((Error != NO_ERROR) || (Bytes < sizeof(IP6_HDR))) {
//
// Attempt to post the read again.
// If we are going offline, the packet is destroyed in the attempt.
//
TeredoPostRead(&(TeredoClient.Io), Packet);
return;
}
TraceEnter("TeredoClientReadComplete");
TeredoClient.Time = TeredoGetTime();
Ipv6 = (PIP6_HDR) Packet->Buffer.buf;
//
// Default to tunneling the packet to the teredo server.
//
Packet->SocketAddress = TeredoClient.Io.ServerAddress;
if (TeredoServicePrefix(&(Ipv6->ip6_dest))) {
//
// If the IPv6 destination address is a teredo address,
// the IPv4 destination address should be global scope.
//
TeredoParseAddress(&(Ipv6->ip6_dest), &Address, &Port);
if (!TeredoIpv4GlobalAddress((PUCHAR) &Address)) {
goto Bail;
}
Peer = TeredoFindOrCreatePeer(&(Ipv6->ip6_dest));
if (Peer != NULL) {
if (TIME_GREATER(
Peer->LastReceive,
(TeredoClient.Time - TEREDO_REFRESH_INTERVAL))) {
//
// Tunnel the packet directly to the peer.
//
Packet->SocketAddress.sin_addr = Address;
Packet->SocketAddress.sin_port = Port;
Peer->LastTransmit = TeredoClient.Time;
} else {
TeredoTransmitBubble(Peer);
}
TeredoDereferencePeer(Peer);
}
}
Packet->Type = TEREDO_PACKET_TRANSMIT;
Packet->Buffer.len = Bytes;
if (TeredoTransmitPacket(&(TeredoClient.Io), Packet) == NULL) {
return;
}
Bail:
//
// We are done processing this packet.
//
TeredoClientTransmitComplete(NO_ERROR, Bytes, Packet);
}
VOID
TeredoClientWriteComplete(
IN DWORD Error,
IN ULONG Bytes,
IN PTEREDO_PACKET Packet
)
/*++
Routine Description:
Process a write completion on the TUN device.
--*/
{
TraceEnter("TeredoClientWriteComplete");
//
// Attempt to post the receive again.
// If we are going offline, the packet is destroyed in the attempt.
//
Packet->Type = TEREDO_PACKET_RECEIVE;
Packet->Buffer.len = IPV6_TEREDOMTU;
TeredoPostReceives(&(TeredoClient.Io), Packet);
}
VOID
TeredoClientBubbleComplete(
IN DWORD Error,
IN ULONG Bytes,
IN PTEREDO_PACKET Packet
)
/*++
Routine Description:
Process a bubble transmit completion on the UDP socket.
--*/
{
PTEREDO_PEER Peer = Cast(
CONTAINING_RECORD(Packet, TEREDO_PEER, Packet), TEREDO_PEER);
TraceEnter("TeredoClientBubbleComplete");
Peer->BubblePosted = FALSE;
TeredoDereferencePeer(Peer);
}
VOID
TeredoClientReceiveComplete(
IN DWORD Error,
IN ULONG Bytes,
IN PTEREDO_PACKET Packet
)
/*++
Routine Description:
Process a receive completion on the UDP socket.
--*/
{
PIP6_HDR Ipv6;
BOOL Forward = FALSE;
InterlockedDecrement(&(TeredoClient.Io.PostedReceives));
if ((Error != NO_ERROR) || (Bytes < sizeof(IP6_HDR))) {
//
// Attempt to post the receive again.
// If we are going offline, the packet is destroyed in the attempt.
//
TeredoPostReceives(&(TeredoClient.Io), Packet);
return;
}
TraceEnter("TeredoClientReceiveComplete");
TeredoClient.Time = TeredoGetTime();
Ipv6 = (PIP6_HDR) Packet->Buffer.buf;
if (IN6_IS_ADDR_LINKLOCAL(&(Ipv6->ip6_dest))) {
//
// This should be a valid router advertisement. Note that only router
// advertisement packets are accepted on our link-local address.
//
Forward = TeredoReceiveRouterAdvertisement(Packet, Bytes);
} else {
//
// This may be a packet of any other kind. Note that the IPv6 stack
// drops router advertisements with a non link-local source address.
//
Forward = TeredoClientReceiveData(Packet);
}
if (Forward) {
Packet->Type = TEREDO_PACKET_WRITE;
Packet->Buffer.len = Bytes;
if (TeredoWritePacket(&(TeredoClient.Io), Packet) == NULL) {
return;
}
}
//
// We are done processing this packet.
//
TeredoClientWriteComplete(NO_ERROR, Bytes, Packet);
}
VOID
TeredoClientTransmitComplete(
IN DWORD Error,
IN ULONG Bytes,
IN PTEREDO_PACKET Packet
)
/*++
Routine Description:
Process a transmit completion on the UDP socket.
--*/
{
TraceEnter("TeredoClientTransmitComplete");
//
// Attempt to post the read again.
// If we are going offline, the packet is destroyed in the attempt.
//
Packet->Type = TEREDO_PACKET_READ;
Packet->Buffer.len = IPV6_TEREDOMTU;
TeredoPostRead(&(TeredoClient.Io), Packet);
}
VOID
TeredoClientMulticastComplete(
IN DWORD Error,
IN ULONG Bytes,
IN PTEREDO_PACKET Packet
)
/*++
Routine Description:
Process a multicast bubble transmit completion on the UDP socket.
--*/
{
ASSERT(Packet == &(TeredoClient.Packet));
TraceEnter("TeredoClientMulticastComplete");
TeredoClient.BubblePosted = FALSE;
TeredoDereferenceClient();
}