|
|
/*++
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. Arguments:
None. Return Value:
TRUE if present, FALSE if not.
--*/ { DWORD Error; ULONG Bytes; PIP_ADAPTER_ADDRESSES Adapters, Next; PIP_ADAPTER_UNICAST_ADDRESS Address; WCHAR Guid[MAX_ADAPTER_NAME_LENGTH]; 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) { //
// Disregard non-Teredo interfaces.
//
ConvertOemToUnicode(Next->AdapterName, Guid, MAX_ADAPTER_NAME_LENGTH); if (_wcsicmp(TeredoClient.Io.TunnelInterface, Guid) != 0) { continue; } ASSERT(Next->IfType == IF_TYPE_TUNNEL);
//
// Bail if the interface is disconnected.
//
if (Next->OperStatus != IfOperStatusUp) { break; } 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))) { 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_src)) || IN6_IS_ADDR_LINKLOCAL(&(Ipv6->ip6_dest))) { //
// This should be a valid router advertisement. Note that only router
// advertisement packets are accepted from/to link-local addresses.
//
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(); }
|