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