// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) // // Copyright (c) 1985-2000 Microsoft Corporation // // This file is part of the Microsoft Research IPv6 Network Protocol Stack. // You should have received a copy of the Microsoft End-User License Agreement // for this software along with this release; see the file "license.txt". // If not, please see http://www.research.microsoft.com/msripv6/license.htm, // or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399. // // Abstract: // // Internet Protocol Version 6 link-level support for some common // LAN types: Ethernet, Token Ring, etc. // // // This manifest constant causes the NDIS_PROTOCOL_CHARACTERISTICS struct to // use the NDIS 5 format if compiled using the NT 5 ddk. If using the NT4 ddk // this has no effect. // #ifndef NDIS50 #define NDIS50 1 #endif #include "oscfg.h" #include "ndis.h" #include "tunuser.h" #include "ip6imp.h" #include "llip6if.h" #include "lan.h" #include "ntddip6.h" #include "ip6def.h" #ifndef NDIS_API #define NDIS_API #endif uint NdisVersion; // The major NDIS version we actualy register with. static ulong LanLookahead = LOOKAHEAD_SIZE; #define LAN_TUNNEL_DEFAULT_PREFERENCE 1 #define NdisMediumTunnel NdisMediumMax static WCHAR LanName[] = TCPIPV6_NAME; NDIS_HANDLE LanHandle; // Our NDIS protocol handle. typedef struct LanRequest { NDIS_REQUEST Request; KEVENT Event; NDIS_STATUS Status; } LanRequest; //* DoNDISRequest - Submit a request to an NDIS driver. // // This is a utility routine to submit a general request to an NDIS // driver. The caller specifes the request code (OID), a buffer and // a length. This routine allocates a request structure, fills it in, // and submits the request. // NDIS_STATUS DoNDISRequest( LanInterface *Adapter, // Pointer to the LanInterface adapter strucuture. NDIS_REQUEST_TYPE RT, // Type of request to be done (Set or Query). NDIS_OID OID, // Value to be set/queried. void *Info, // Pointer to the buffer to be passed. uint Length, // Length of data in above buffer. uint *Needed) // Location to fill in with bytes needed in buffer. { LanRequest Request; NDIS_STATUS Status; // Now fill it in. Request.Request.RequestType = RT; if (RT == NdisRequestSetInformation) { Request.Request.DATA.SET_INFORMATION.Oid = OID; Request.Request.DATA.SET_INFORMATION.InformationBuffer = Info; Request.Request.DATA.SET_INFORMATION.InformationBufferLength = Length; } else { Request.Request.DATA.QUERY_INFORMATION.Oid = OID; Request.Request.DATA.QUERY_INFORMATION.InformationBuffer = Info; Request.Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length; } // // Note that we can NOT use Adapter->ai_event and ai_status here. // There may be multiple concurrent DoNDISRequest calls. // // Initialize our event. KeInitializeEvent(&Request.Event, SynchronizationEvent, FALSE); if (!Adapter->ai_resetting) { // Submit the request. NdisRequest(&Status, Adapter->ai_handle, &Request.Request); // Wait for it to finish. if (Status == NDIS_STATUS_PENDING) { (void) KeWaitForSingleObject(&Request.Event, UserRequest, KernelMode, FALSE, NULL); Status = Request.Status; } } else Status = NDIS_STATUS_NOT_ACCEPTED; if (Needed != NULL) *Needed = Request.Request.DATA.QUERY_INFORMATION.BytesNeeded; return Status; } //* LanRequestComplete - Lan request complete handler. // // This routine is called by the NDIS driver when a general request // completes. Lan blocks on all requests, so we'll just wake up // whoever's blocked on this request. // void NDIS_API LanRequestComplete( NDIS_HANDLE Handle, // Binding handle (really our LanInterface). PNDIS_REQUEST Context, // Request that completed. NDIS_STATUS Status) // Final status of requested command. { LanRequest *Request = (LanRequest *) Context; UNREFERENCED_PARAMETER(Handle); // // Signal the completion of a generic synchronous request. // See DoNDISRequest. // Request->Status = Status; KeSetEvent(&Request->Event, 0, FALSE); } //* LanTransmitComplete - Lan transmit complete handler. // // This routine is called by the NDIS driver when a send completes. // This is a pretty time critical operation, we need to get through here // quickly. We just take statistics and call the upper layer send // complete handler. // void NDIS_API LanTransmitComplete( NDIS_HANDLE Handle, // Binding handle (really LanInterface we sent on). PNDIS_PACKET Packet, // Packet that was sent. NDIS_STATUS Status) // Final status of send. { LanInterface *Interface = (LanInterface *)Handle; Interface->ai_qlen--; // // Take statistics. // if (Status == NDIS_STATUS_SUCCESS) { UINT TotalLength; NdisQueryPacket(Packet, NULL, NULL, NULL, &TotalLength); Interface->ai_outoctets += TotalLength; } else { if (Status == NDIS_STATUS_RESOURCES) Interface->ai_outdiscards++; else Interface->ai_outerrors++; } UndoAdjustPacketBuffer(Packet); IPv6SendComplete(Interface->ai_context, Packet, ((Status == NDIS_STATUS_SUCCESS) ? IP_SUCCESS : IP_GENERAL_FAILURE)); } //* LanTransmit - Send a frame. // // The main Lan transmit routine, called by the upper layer. // void LanTransmit( void *Context, // A pointer to the LanInterface. PNDIS_PACKET Packet, // Packet to send. uint Offset, // Offset from start of packet to IP header. const void *LinkAddress) // Link-level address of destination. { LanInterface *Interface = (LanInterface *)Context; void *BufAddr; NDIS_STATUS Status; // // Loopback (for both unicast & multicast) happens in IPv6SendLL. // We never want the link layer to loopback. // Packet->Private.Flags = NDIS_FLAGS_DONT_LOOPBACK; // // Obtain a pointer to space for the link-level header. // BufAddr = AdjustPacketBuffer(Packet, Offset, Interface->ai_hdrsize); switch (Interface->ai_media) { case NdisMedium802_3: { EtherHeader *Ether; // This is an Ethernet. Ether = (EtherHeader *)BufAddr; RtlCopyMemory(Ether->eh_daddr, LinkAddress, IEEE_802_ADDR_LENGTH); RtlCopyMemory(Ether->eh_saddr, Interface->ai_addr, IEEE_802_ADDR_LENGTH); Ether->eh_type = net_short(ETYPE_IPv6); #if 0 // // See if we're using SNAP here. // if (Interface->ai_hdrsize != sizeof(EtherHeader)) { ... } #endif break; } case NdisMediumFddi: { FDDIHeader *FDDI; SNAPHeader *SNAP; // This is a FDDI link. FDDI = (FDDIHeader *)BufAddr; FDDI->fh_pri = FDDI_PRI; // Default frame code. RtlCopyMemory(FDDI->fh_daddr, LinkAddress, IEEE_802_ADDR_LENGTH); RtlCopyMemory(FDDI->fh_saddr, Interface->ai_addr, IEEE_802_ADDR_LENGTH); // FDDI always uses SNAP. SNAP = (SNAPHeader *)(FDDI + 1); SNAP->sh_dsap = SNAP_SAP; SNAP->sh_ssap = SNAP_SAP; SNAP->sh_ctl = SNAP_UI; SNAP->sh_protid[0] = 0; SNAP->sh_protid[1] = 0; SNAP->sh_protid[2] = 0; SNAP->sh_etype = net_short(ETYPE_IPv6); break; } case NdisMediumTunnel: { // // There is no header to construct! // break; } default: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "LanTransmit: Unknown media type\n")); break; } // // Send the packet down to NDIS. // (Interface->ai_outpcount[AI_UCAST_INDEX])++; Interface->ai_qlen++; if (!Interface->ai_resetting) { NdisSend(&Status, Interface->ai_handle, Packet); } else Status = NDIS_STATUS_NOT_ACCEPTED; if (Status != NDIS_STATUS_PENDING) { // // The send finished synchronously. // Call LanTransmitComplete, unifying our treatment // of the synchronous and asynchronous cases. // LanTransmitComplete((NDIS_HANDLE)Interface, Packet, Status); } } //* LanOpenAdapterComplete - LanOpen completion handler. // // This routine is called by the NDIS driver when an open adapter // call completes. Wakeup anyone who is waiting for this event. // void NDIS_API LanOpenAdapterComplete( NDIS_HANDLE Handle, // Binding handle (really our LanInterface). NDIS_STATUS Status, // Final status of command. NDIS_STATUS ErrorStatus) // Final error status. { LanInterface *ai = (LanInterface *)Handle; UNREFERENCED_PARAMETER(ErrorStatus); // // Signal whoever is waiting and pass the final status. // ai->ai_status = Status; KeSetEvent(&ai->ai_event, 0, FALSE); } //* LanCloseAdapterComplete - Lan close adapter complete handler. // // This routine is called by the NDIS driver when a close adapter // call completes. // // At this point, NDIS guarantees that it has no other outstanding // calls to us. // void NDIS_API LanCloseAdapterComplete( NDIS_HANDLE Handle, // Binding handle (really our LanInterface). NDIS_STATUS Status) // Final status of command. { LanInterface *ai = (LanInterface *)Handle; // // Signal whoever is waiting and pass the final status. // ai->ai_status = Status; KeSetEvent(&ai->ai_event, 0, FALSE); } //* LanTDComplete - Lan transfer data complete handler. // // This routine is called by the NDIS driver when a transfer data // call completes. Hopefully we now have a complete packet we can // pass up to IP. Recycle our TD packet descriptor in any event. // void NDIS_API LanTDComplete( NDIS_HANDLE Handle, // Binding handle (really our LanInterface). PNDIS_PACKET Packet, // The packet used for the Transfer Data (TD). NDIS_STATUS Status, // Final status of command. uint BytesCopied) // Number of bytes copied. { LanInterface *Interface = (LanInterface *)Handle; UNREFERENCED_PARAMETER(BytesCopied); // // If things went well, pass TD packet up to IP. // if (Status == NDIS_STATUS_SUCCESS) { PNDIS_BUFFER Buffer; IPv6Packet IPPacket; RtlZeroMemory(&IPPacket, sizeof IPPacket); NdisGetFirstBufferFromPacket(Packet, &Buffer, &IPPacket.FlatData, &IPPacket.ContigSize, &IPPacket.TotalSize); ASSERT(IPPacket.ContigSize == IPPacket.TotalSize); IPPacket.Data = IPPacket.FlatData; if (PC(Packet)->pc_nucast) IPPacket.Flags |= PACKET_NOT_LINK_UNICAST; IPPacket.NTEorIF = Interface->ai_context; (void) IPv6Receive(&IPPacket); } // // In any case, put the packet back on the list. // KeAcquireSpinLockAtDpcLevel(&Interface->ai_lock); PC(Packet)->pc_link = Interface->ai_tdpacket; Interface->ai_tdpacket = Packet; KeReleaseSpinLockFromDpcLevel(&Interface->ai_lock); } //* LanResetComplete - Lan reset complete handler. // // This routine is called by the NDIS driver when a reset completes. // void NDIS_API LanResetComplete( NDIS_HANDLE Handle, // Binding handle (really LanInterface which reset) NDIS_STATUS Status) // Final status of command. { UNREFERENCED_PARAMETER(Handle); UNREFERENCED_PARAMETER(Status); // REVIEW: Do anything here? Axe this routine? } //* LanReceive - Lan receive data handler. // // This routine is called when data arrives from the NDIS driver. // Note that newer NDIS drivers are likely to call LanReceivePacket to // indicate data arrival instead of this routine. // NDIS_STATUS // Indication of whether or not we took the packet. NDIS_API LanReceive( NDIS_HANDLE Handle, // The binding handle we gave NDIS earlier. NDIS_HANDLE Context, // NDIS Context for TransferData operations. void *Header, // Pointer to packet link-level header. uint HeaderSize, // Size of above header (in bytes). void *Data, // Pointer to look-ahead received data buffer. uint Size, // Size of above data (in bytes). uint TotalSize) // Total received data size (in bytes). { LanInterface *Interface = Handle; // Interface for this driver. ushort Type; // Protocol type. uint ProtOffset; // Offset in Data to non-media info. uint NUCast; // TRUE if the frame is not unicast. IPv6Packet IPPacket; if (Interface->ai_state != INTERFACE_UP) { // // Interface is marked as down. // KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE, "IPv6 LanReceive: Interface down\n")); return NDIS_STATUS_NOT_RECOGNIZED; } Interface->ai_inoctets += TotalSize; switch (Interface->ai_media) { case NdisMedium802_3: { EtherHeader UNALIGNED *Ether = (EtherHeader UNALIGNED *)Header; if (HeaderSize < sizeof(*Ether)) { // // Header region too small to contain Ethernet header. // KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "IPv6 LanReceive: Bogus header size (%d bytes)\n", HeaderSize)); return NDIS_STATUS_NOT_RECOGNIZED; } if ((Type = net_short(Ether->eh_type)) >= ETYPE_MIN) { // // Classic Ethernet, no SNAP header. // ProtOffset = 0; break; } // // 802.3 Ethernet w/ SNAP header. Protocol type is in // different spot. This is handled the same as FDDI, so // just fall into that code... // } case NdisMediumFddi: { SNAPHeader UNALIGNED *SNAP = (SNAPHeader UNALIGNED *)Data; // // If we have a SNAP header that's all we need to look at. // if (Size >= sizeof(SNAPHeader) && SNAP->sh_dsap == SNAP_SAP && SNAP->sh_ssap == SNAP_SAP && SNAP->sh_ctl == SNAP_UI) { Type = net_short(SNAP->sh_etype); ProtOffset = sizeof(SNAPHeader); } else { // handle XID/TEST here. Interface->ai_uknprotos++; return NDIS_STATUS_NOT_RECOGNIZED; } break; } case NdisMediumTunnel: { // // We accept everything over the tunnel. // Type = ETYPE_IPv6; ProtOffset = 0; break; } default: // Should never happen. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "IPv6 LanReceive: Got a packet from an unknown media!?!\n")); return NDIS_STATUS_NOT_RECOGNIZED; } // // See if the packet is for a protocol we handle. // if (Type != ETYPE_IPv6) { Interface->ai_uknprotos++; return NDIS_STATUS_NOT_RECOGNIZED; } // // Notice if this packet wasn't received in a unicast frame. // REVIEW: Is this really a media independent solution? Do we care? // NUCast = ((*((uchar UNALIGNED *)Header + Interface->ai_bcastoff) & Interface->ai_bcastmask) == Interface->ai_bcastval) ? AI_NONUCAST_INDEX : AI_UCAST_INDEX; (Interface->ai_inpcount[NUCast])++; // // Check to see if we have the entire packet. // if (Size < TotalSize) { uint Transferred; NDIS_STATUS Status; PNDIS_PACKET TdPacket; // Packet used by NdisTransferData. // // We need to issue a Transfer Data request to get the // portion of the packet we're missing, so we might as well // get the whole packet this way and have it be contiguous. // // // Pull a packet to use for the Transfer Data off the queue. // KeAcquireSpinLockAtDpcLevel(&Interface->ai_lock); TdPacket = Interface->ai_tdpacket; if (TdPacket == (PNDIS_PACKET)NULL) { // Don't have a packet to put it in. // Have to drop it, but let NDIS know we recognized it. KeReleaseSpinLockFromDpcLevel(&Interface->ai_lock); return NDIS_STATUS_SUCCESS; } Interface->ai_tdpacket = PC(TdPacket)->pc_link; KeReleaseSpinLockFromDpcLevel(&Interface->ai_lock); // // Remember NUCast in a handy field in the packet context. // PC(TdPacket)->pc_nucast = NUCast; // // Issue the TD. Start transfer at the IP header. // NdisTransferData(&Status, Interface->ai_handle, Context, ProtOffset, TotalSize - ProtOffset, TdPacket, &Transferred); if (Status != NDIS_STATUS_PENDING) { // // TD completed synchronously, // so call the completion function directly. // LanTDComplete(Handle, TdPacket, Status, Transferred); } return NDIS_STATUS_SUCCESS; } // // We were given all the data directly. Just need to skip // over any link level headers. // (uchar *)Data += ProtOffset; ASSERT(Size == TotalSize); TotalSize -= ProtOffset; // // Pass incoming data up to IPv6. // RtlZeroMemory(&IPPacket, sizeof IPPacket); IPPacket.FlatData = Data; IPPacket.Data = Data; IPPacket.ContigSize = TotalSize; IPPacket.TotalSize = TotalSize; if (NUCast) IPPacket.Flags |= PACKET_NOT_LINK_UNICAST; IPPacket.NTEorIF = Interface->ai_context; (void) IPv6Receive(&IPPacket); return NDIS_STATUS_SUCCESS; } //* LanReceiveComplete - Lan receive complete handler. // // This routine is called by the NDIS driver after some number of // receives. In some sense, it indicates 'idle time'. // void NDIS_API LanReceiveComplete( NDIS_HANDLE Handle) // Binding handle (really our LanInterface). { UNREFERENCED_PARAMETER(Handle); IPv6ReceiveComplete(); } //* LanReceivePacket - Lan receive data handler. // // This routine is called when data arrives from the NDIS driver. // Note that older NDIS drivers are likely to call LanReceive to // indicate data arrival instead of this routine. // int // Returns: number of references we hold to Packet upon return. LanReceivePacket( NDIS_HANDLE Handle, // The binding handle we gave NDIS earlier. PNDIS_PACKET Packet) // Packet descriptor for incoming packet. { LanInterface *Interface = Handle; // Interface for this driver. PNDIS_BUFFER Buffer; // Buffer in packet chain. void *Address; // Address of above Buffer. uint Length, TotalLength; // Length of Buffer, Packet. EtherHeader UNALIGNED *Ether; // Header for Ethernet media. ushort Type; // Protocol type. uint Position; // Offset to non-media info. uint NUCast; // TRUE if the frame is not unicast. IPv6Packet IPPacket; if (Interface->ai_state != INTERFACE_UP) { // Interface is marked as down. return 0; } // // Find out about the packet we've been handed. // NdisGetFirstBufferFromPacket(Packet, &Buffer, &Address, &Length, &TotalLength); Interface->ai_inoctets += TotalLength; // Take statistic. // // Check for obviously bogus packets. // if (TotalLength < (uint)Interface->ai_hdrsize) { // // Packet too small to hold media header, drop it. // return 0; } if (Length < (uint)Interface->ai_hdrsize) { // // First buffer in chain too small to hold header. // This shouldn't happen because of LanLookahead. // return 0; } // // Figure out what protocol type this packet is by looking in the // media-specific header field for this type of media. // switch (Interface->ai_media) { case NdisMedium802_3: { Ether = (EtherHeader UNALIGNED *)Address; if ((Type = net_short(Ether->eh_type)) >= ETYPE_MIN) { // // Classic Ethernet, no SNAP header. // Position = sizeof(EtherHeader); } else { // // 802.3 Ethernet w/ SNAP header. Protocol type is in // different spot and we have to remember to skip over it. // The great thing about standards is that there are so // many to choose from. // SNAPHeader UNALIGNED *SNAP = (SNAPHeader UNALIGNED *) ((char *)Address + sizeof(EtherHeader)); if (Length >= (sizeof(EtherHeader) + sizeof(SNAPHeader)) && SNAP->sh_dsap == SNAP_SAP && SNAP->sh_ssap == SNAP_SAP && SNAP->sh_ctl == SNAP_UI) { Type = net_short(SNAP->sh_etype); Position = sizeof(EtherHeader) + sizeof(SNAPHeader); } else { // handle XID/TEST here. Interface->ai_uknprotos++; return 0; } } break; } case NdisMediumFddi: { SNAPHeader UNALIGNED *SNAP = (SNAPHeader UNALIGNED *) ((char *)Address + sizeof(FDDIHeader)); if (Length >= (sizeof(FDDIHeader) + sizeof(SNAPHeader)) && SNAP->sh_dsap == SNAP_SAP && SNAP->sh_ssap == SNAP_SAP && SNAP->sh_ctl == SNAP_UI) { Type = net_short(SNAP->sh_etype); Position = sizeof(FDDIHeader) + sizeof(SNAPHeader); } else { // handle XID/TEST here. Interface->ai_uknprotos++; return 0; } break; } case NdisMediumTunnel: { // // We accept everything over the tunnel. // Type = ETYPE_IPv6; Position = 0; break; } default: // Should never happen. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "IPv6: Got a packet from an unknown media!?!\n")); return 0; } // // Notice if this packet wasn't received in a unicast frame. // REVIEW: Is this really a media independent solution? // NUCast = ((*((uchar UNALIGNED *)Address + Interface->ai_bcastoff) & Interface->ai_bcastmask) == Interface->ai_bcastval) ? AI_NONUCAST_INDEX : AI_UCAST_INDEX; // // See if the packet is for a protocol we handle. // if (Type == ETYPE_IPv6) { (Interface->ai_inpcount[NUCast])++; // // Skip over any link level headers. // (uchar *)Address += Position; Length -= Position; TotalLength -= Position; // // Pass incoming data up to IPv6. // RtlZeroMemory(&IPPacket, sizeof IPPacket); IPPacket.Position = Position; IPPacket.Data = Address; IPPacket.ContigSize = Length; IPPacket.TotalSize = TotalLength; IPPacket.NdisPacket = Packet; if (NUCast) IPPacket.Flags |= PACKET_NOT_LINK_UNICAST; IPPacket.NTEorIF = Interface->ai_context; return IPv6Receive(&IPPacket); } else { // // Not a protocol we handle. // Interface->ai_uknprotos++; return 0; } } //* LanSetInterfaceLinkStatus // // Helper function for processing media connectivity indications. // void LanSetInterfaceLinkStatus( LanInterface *Interface, int MediaConnected) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanSetInterfaceLinkStatus(%p/%p) - %u %u\n", Interface, Interface->ai_context, Interface->ai_media_check, MediaConnected)); KeAcquireSpinLockAtDpcLevel(&Interface->ai_lock); if (Interface->ai_media_check == MEDIA_CHECK_IDLE) SetInterfaceLinkStatus(Interface->ai_context, MediaConnected); else Interface->ai_media_check = MEDIA_CHECK_CONFLICT; KeReleaseSpinLockFromDpcLevel(&Interface->ai_lock); } //* LanStatus - Lan status handler. // // Called by the NDIS driver when some sort of status change occurs. // We take action depending on the type of status. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // GStatus - General type of status that caused the call. // Status - Pointer to a buffer of status specific information. // StatusSize - Size of the status buffer. // // Exit: Nothing. // void NDIS_API LanStatus( NDIS_HANDLE Handle, // Binding handle (really our LanInterface). NDIS_STATUS GStatus, // General status type which caused the call. void *Status, // Pointer to buffer of status specific info. uint StatusSize) // Size of the above status buffer. { LanInterface *Interface = Handle; // Interface for this driver. uint Index; UNREFERENCED_PARAMETER(Status); switch (GStatus) { case NDIS_STATUS_RESET_START: // // While the interface is resetting, we must avoid calling // NdisSendPackets, NdisSend, and NdisRequest. // KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanStatus(%p) - start reset\n", Interface)); Interface->ai_resetting = TRUE; break; case NDIS_STATUS_RESET_END: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanStatus(%p) - end reset\n", Interface)); Interface->ai_resetting = FALSE; break; case NDIS_STATUS_MEDIA_CONNECT: LanSetInterfaceLinkStatus(Interface, TRUE); break; case NDIS_STATUS_MEDIA_DISCONNECT: LanSetInterfaceLinkStatus(Interface, FALSE); break; default: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "IPv6: LanStatus(%p) - status %x\n", Interface, GStatus)); for (Index = 0; Index < StatusSize/4; Index++) KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, " status %08x\n", ((uint *)Status)[Index])); break; } } //* LanStatusComplete - Lan status complete handler. // // A routine called by the NDIS driver so that we can do postprocessing // after a status event. // void NDIS_API LanStatusComplete( NDIS_HANDLE Handle) // Binding handle (really our LanInterface). { UNREFERENCED_PARAMETER(Handle); // REVIEW: Do anything here? } extern void NDIS_API LanBindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE BindContext, PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2); extern void NDIS_API LanUnbindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE ProtBindContext, NDIS_HANDLE UnbindContext); extern NDIS_STATUS NDIS_API LanPnPEvent(NDIS_HANDLE ProtocolBindingContext, PNET_PNP_EVENT NetPnPEvent); // // Structure passed to NDIS to tell it how to call Lan Interfaces. // // This is carefully arranged so that it can build // with either the NT 4 or NT 5 DDK, and then in either case // run on NT 4 (registering with NDIS 4) and // run on NT 5 (registering with NDIS 5). // NDIS50_PROTOCOL_CHARACTERISTICS LanCharacteristics = { 0, // NdisMajorVersion 0, // NdisMinorVersion // This field was added in NT 5. (Previously it was just a hole.) #ifdef NDIS_FLAGS_DONT_LOOPBACK 0, // Filler #endif 0, // Flags LanOpenAdapterComplete, LanCloseAdapterComplete, LanTransmitComplete, LanTDComplete, LanResetComplete, LanRequestComplete, LanReceive, LanReceiveComplete, LanStatus, LanStatusComplete, { 0, 0, 0 }, // Name LanReceivePacket, LanBindAdapter, LanUnbindAdapter, // The type of this field changed between NT 4 and NT 5. #ifdef NDIS_FLAGS_DONT_LOOPBACK LanPnPEvent, #else (TRANSLATE_HANDLER) LanPnPEvent, #endif NULL, NULL, NULL, NULL, NULL, NULL }; #pragma BEGIN_INIT //* LanInit // // This functions intializes the Lan module. // In particular, it registers with NDIS. // // Returns FALSE to indicate failure to initialize. // int LanInit(void) { NDIS_STATUS Status; RtlInitUnicodeString(&LanCharacteristics.Name, LanName); // // We try to register with NDIS major version = 5. If this fails we try // again for NDIS major version = 4. If this also fails we exit without // any further attempts to register with NDIS. // LanCharacteristics.MajorNdisVersion = 5; NdisRegisterProtocol(&Status, &LanHandle, (NDIS_PROTOCOL_CHARACTERISTICS *) &LanCharacteristics, sizeof(NDIS50_PROTOCOL_CHARACTERISTICS)); if (Status != NDIS_STATUS_SUCCESS) { LanCharacteristics.MajorNdisVersion = 4; // // NDIS 4 has a different semantics - it has TranslateHandler // instead of PnPEventHandler. So do not supply that handler. // #ifdef NDIS_FLAGS_DONT_LOOPBACK LanCharacteristics.PnPEventHandler = NULL; #else LanCharacteristics.TranslateHandler = NULL; #endif NdisRegisterProtocol(&Status, &LanHandle, (NDIS_PROTOCOL_CHARACTERISTICS *) &LanCharacteristics, sizeof(NDIS40_PROTOCOL_CHARACTERISTICS)); if (Status != NDIS_STATUS_SUCCESS) { // // Can't register at all. Just bail out... // KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "LanInit: could not register -> %x\n", Status)); return FALSE; } } // // We've registered OK using NDIS. // NdisVersion = LanCharacteristics.MajorNdisVersion; KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanInit: registered with NDIS %u.\n", NdisVersion)); return TRUE; } #pragma END_INIT //* LanUnload // // Called when then IPv6 stack is unloading. // We need to disconnect from NDIS. // void LanUnload(void) { NDIS_STATUS Status; // // At this point, the adapters should already all be closed // because IPUnload is called first and does that. // NdisDeregisterProtocol(&Status, LanHandle); } //* LanFreeInterface - Free a Lan interface // // Called in the event of some sort of initialization failure. We free all // the memory associated with an Lan interface. // void LanFreeInterface( LanInterface *Interface) // Interface structure to be freed. { NDIS_STATUS Status; // // If we're bound to the adapter, close it now. // if (Interface->ai_handle != NULL) { KeInitializeEvent(&Interface->ai_event, SynchronizationEvent, FALSE); NdisCloseAdapter(&Status, Interface->ai_handle); if (Status == NDIS_STATUS_PENDING) { (void) KeWaitForSingleObject(&Interface->ai_event, UserRequest, KernelMode, FALSE, NULL); Status = Interface->ai_status; } } // // Free the Transfer Data Packet, if any, for this interface. // if (Interface->ai_tdpacket != NULL) IPv6FreePacket(Interface->ai_tdpacket); // // Free the interface structure itself. // ExFreePool(Interface); } //* LanAllocateTDPacket // // Allocate a packet for NdisTransferData. // We always allocate contiguous space for a full MTU of data. // PNDIS_PACKET LanAllocateTDPacket( LanInterface *Interface) // Interface for which to allocate TD packet. { PNDIS_PACKET Packet; void *Mem; NDIS_STATUS Status; Status = IPv6AllocatePacket(Interface->ai_mtu, &Packet, &Mem); if (Status != NDIS_STATUS_SUCCESS) return NULL; return Packet; } extern uint UseEtherSNAP(PNDIS_STRING Name); //* LanRegister - Register a protocol with the Lan module. // // We register an adapter for Lan processing and create a LanInterface // structure to represent it. We also open the NDIS adapter here. // // REVIEW: Should we set the packet filter to NOT accept broadcast packets? // REVIEW: Broadcast isn't used in IPv6. Junk bcast* stuff as well? Switch // REVIEW: this to keeping track of multicasts? // int LanRegister( PNDIS_STRING Adapter, // Name of the adapter to bind to. struct LanInterface **Interface) // Where to return new interace. { LanInterface *ai; // Pointer to interface struct for this interface. NDIS_STATUS Status, OpenStatus; // Status values. uint i = 0; // Medium index. NDIS_MEDIUM MediaArray[2]; uint instance; uint mss; uint speed; uchar bcastmask, bcastval, bcastoff, addrlen, hdrsize; NDIS_OID OID; uint PF; // // Allocate memory to hold new interface. // ai = (LanInterface *) ExAllocatePool(NonPagedPool, sizeof(LanInterface)); if (ai == NULL) return FALSE; // Couldn't allocate memory for this one. RtlZeroMemory(ai, sizeof(LanInterface)); // // In actual practice, we've only tested Ethernet and FDDI. // So disallow other media for now. // MediaArray[0] = NdisMedium802_3; MediaArray[1] = NdisMediumFddi; #if 0 MediaArray[2] = NdisMedium802_5; #endif // Initialize this adapter interface structure. ai->ai_state = INTERFACE_INIT; ai->ai_media_check = MEDIA_CHECK_QUERY; // Initialize the locks. KeInitializeSpinLock(&ai->ai_lock); KeInitializeEvent(&ai->ai_event, SynchronizationEvent, FALSE); // Open the NDIS adapter. NdisOpenAdapter(&Status, &OpenStatus, &ai->ai_handle, &i, MediaArray, 2, LanHandle, ai, Adapter, 0, NULL); // Block for open to complete. if (Status == NDIS_STATUS_PENDING) { (void) KeWaitForSingleObject(&ai->ai_event, UserRequest, KernelMode, FALSE, NULL); Status = ai->ai_status; } ai->ai_media = MediaArray[i]; // Fill in media type. // // Open adapter completed. If it succeeded, we'll finish our // intialization. If it failed, bail out now. // if (Status != NDIS_STATUS_SUCCESS) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "LanRegister: Adapter failed to initialize." " Status = 0x%x\n", Status)); ai->ai_handle = NULL; goto ErrorReturn; } // // Read the maximum frame size. // Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_MAXIMUM_FRAME_SIZE, &mss, sizeof(mss), NULL); if (Status != NDIS_STATUS_SUCCESS) { // // Failed to get maximum frame size. Bail. // KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "LanRegister: Failed to get maximum frame size. " "Status = 0x%x\n", Status)); goto ErrorReturn; } // // Read the local link-level address from the adapter. // switch (ai->ai_media) { case NdisMedium802_3: addrlen = IEEE_802_ADDR_LENGTH; bcastmask = ETHER_BCAST_MASK; bcastval = ETHER_BCAST_VAL; bcastoff = ETHER_BCAST_OFF; OID = OID_802_3_CURRENT_ADDRESS; hdrsize = sizeof(EtherHeader); if (UseEtherSNAP(Adapter)) { hdrsize += sizeof(SNAPHeader); } PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST; break; case NdisMedium802_5: addrlen = IEEE_802_ADDR_LENGTH; bcastmask = TR_BCAST_MASK; bcastval = TR_BCAST_VAL; bcastoff = TR_BCAST_OFF; OID = OID_802_5_CURRENT_ADDRESS; hdrsize = sizeof(TRHeader) + sizeof(SNAPHeader); PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED; // // Figure out the RC len stuff now. // mss -= (sizeof(RC) + (MAX_RD * sizeof(ushort))); break; case NdisMediumFddi: addrlen = IEEE_802_ADDR_LENGTH; bcastmask = FDDI_BCAST_MASK; bcastval = FDDI_BCAST_VAL; bcastoff = FDDI_BCAST_OFF; OID = OID_FDDI_LONG_CURRENT_ADDR; hdrsize = sizeof(FDDIHeader) + sizeof(SNAPHeader); PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST; mss = MIN(mss, FDDI_MSS); break; default: ASSERT(!"bad medium from Ndis"); goto ErrorReturn; } // // NDIS exposes the tunnel interface as 802_3, but ensures that it's the // only interface for which OID_CUSTOM_TUNMP_INSTANCE_ID returns success. // if (DoNDISRequest(ai, NdisRequestQueryInformation, OID_CUSTOM_TUNMP_INSTANCE_ID, &instance, sizeof(instance), NULL) == NDIS_STATUS_SUCCESS) { ai->ai_media = NdisMediumTunnel; // // These values are chosen so NUCast returns FALSE. // bcastmask = 0; bcastval = 1; bcastoff = 0; hdrsize = 0; // // Since we do not construct an ethernet header on transmission, or // expect one on receive, we need to ensure that NDIS does not attempt // to parse frames on this interface. On transmission this is achieved // by setting the NDIS_FLAGS_DONT_LOOPBACK flag. Receives are made // NDIS-Safe by setting the interface in promiscuous mode. // PF |= NDIS_PACKET_TYPE_PROMISCUOUS; // // This is what NDIS should have provided us. // mss = IPv6_MINIMUM_MTU; } ai->ai_bcastmask = bcastmask; ai->ai_bcastval = bcastval; ai->ai_bcastoff = bcastoff; ai->ai_addrlen = addrlen; ai->ai_hdrsize = hdrsize; ai->ai_pfilter = PF; ai->ai_mtu = (ushort)mss; Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID, ai->ai_addr, addrlen, NULL); if (Status != NDIS_STATUS_SUCCESS) { // // Failed to get link-level address. Bail. // KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "LanRegister: Failed to get link-level address. " "Status = 0x%x\n", Status)); goto ErrorReturn; } // // Read the speed for local purposes. // If we can't read the speed that's OK. // Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_LINK_SPEED, &speed, sizeof(speed), NULL); if (Status == NDIS_STATUS_SUCCESS) { ai->ai_speed = speed * 100L; } // // Set the lookahead. This is the minimum amount of packet data // that we wish to see contiguously for every packet received. // Status = DoNDISRequest(ai, NdisRequestSetInformation, OID_GEN_CURRENT_LOOKAHEAD, &LanLookahead, sizeof LanLookahead, NULL); if (Status != NDIS_STATUS_SUCCESS) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "LanRegister: Failed to set lookahead. " "Status = 0x%x\n", Status)); goto ErrorReturn; } // // Allocate a Tranfer Data packet for this interface. // ai->ai_tdpacket = LanAllocateTDPacket(ai); *Interface = ai; return TRUE; ErrorReturn: LanFreeInterface(ai); return FALSE; } //* LanCreateToken // // Given a link-layer address, creates a 64-bit "interface identifier" // in the low eight bytes of an IPv6 address. // Does not modify the other bytes in the IPv6 address. // void LanCreateToken( void *Context, // Interface from which to take interface identifier. IPv6Addr *Address) // IPv6 address to place token into. { LanInterface *Interface = (LanInterface *)Context; uchar *IEEEAddress = Interface->ai_addr; // // This is formed the same way for Ethernet, FDDI and Tunnel. // Address->s6_bytes[8] = IEEEAddress[0] ^ 0x2; Address->s6_bytes[9] = IEEEAddress[1]; Address->s6_bytes[10] = IEEEAddress[2]; Address->s6_bytes[11] = 0xff; Address->s6_bytes[12] = 0xfe; Address->s6_bytes[13] = IEEEAddress[3]; Address->s6_bytes[14] = IEEEAddress[4]; Address->s6_bytes[15] = IEEEAddress[5]; } //* LanTunnelCreateToken // // Given a link-layer address, creates a 64-bit "interface identifier" // in the low eight bytes of an IPv6 address. // Does not modify the other bytes in the IPv6 address. // void LanTunnelCreateToken( void *Context, // Interface from which to take interface identifier. IPv6Addr *Address) // IPv6 address to place token into. { LanInterface *Interface = (LanInterface *)Context; // // Only called for tunnels configured with the default MAC address. // ASSERT((Interface->ai_media == NdisMediumTunnel) && RtlEqualMemory( TUN_CARD_ADDRESS, Interface->ai_addr, Interface->ai_addrlen)); // // Non link-local addresses still use the regular interface identifier. // if (!IsLinkLocal(Address)) { LanCreateToken(Context, Address); return; } // // Create a random interface identifier for the link-local address. // do { uint Identifier[2]; Identifier[0] = Random(); Identifier[1] = Random(); RtlCopyMemory(&Address->s6_bytes[8], (uchar *)Identifier, 8); // // Clear the universal/local bit to indicate local significance. // Address->s6_bytes[8] &= ~0x2; } while (IsKnownAnycast(Address)); } //* LanReadLinkLayerAddressOption - Parse a ND link-layer address option. // // Parses a Neighbor Discovery link-layer address option // and if valid, returns a pointer to the link-layer address. // const void * LanReadLinkLayerAddressOption( void *Context, // Interface for which ND option applies. const uchar *OptionData) // Option data to parse. { LanInterface *Interface = (LanInterface *)Context; // // Check that the option length is correct, // allowing for the option type/length bytes // and rounding up to 8-byte units. // if (((Interface->ai_addrlen + 2 + 7)/8) != OptionData[1]) return NULL; // // Skip over the option type and length bytes, // and return a pointer to the option data. // return OptionData + 2; } //* LanWriteLinkLayerAddressOption - Create a ND link-layer address option. // // Creates a Neighbor Discovery link-layer address option. // Our caller takes care of the option type & length fields. // We handle the padding/alignment/placement of the link address // into the option data. // // (Our caller allocates space for the option by adding 2 to the // link address length and rounding up to a multiple of 8.) // void LanWriteLinkLayerAddressOption( void *Context, // Interface to create option regarding. uchar *OptionData, // Where the option data resides. const void *LinkAddress) // Link-level address. { LanInterface *Interface = (LanInterface *)Context; // // Place the address after the option type/length bytes. // RtlCopyMemory(OptionData + 2, LinkAddress, Interface->ai_addrlen); } //* LanTunnelConvertAddress // // LanTunnel does not use Neighbor Discovery or link-layer addresses. // ushort LanTunnelConvertAddress( void *Context, // Unused (nominally, our LanInterface). const IPv6Addr *Address, // IPv6 multicast address. void *LinkAddress) // Where link-level address to be filled resides. { LanInterface *Interface = (LanInterface *)Context; ASSERT(Interface->ai_media == NdisMediumTunnel); UNREFERENCED_PARAMETER(Address); RtlCopyMemory(LinkAddress, Interface->ai_addr, Interface->ai_addrlen); // // Make the neighbor link layer address different from our own. This // ensures that IPv6SendLL does not loop back packets destined for them. // In fact, a link layer address on the tunnel interface is faked only // because IPv6SendLL does not handle zero length link layer addresses! // ASSERT(Interface->ai_addrlen != 0); ((PUCHAR) LinkAddress)[Interface->ai_addrlen - 1] = ~((PUCHAR) LinkAddress)[Interface->ai_addrlen - 1]; return ND_STATE_PERMANENT; } //* LanConvertAddress // // Converts an IPv6 multicast address to a link-layer address. // Generally this requires hashing the IPv6 address into a set // of link-layer addresses, in a link-layer-specific way. // ushort LanConvertAddress( void *Context, // Unused (nominally, our LanInterface). const IPv6Addr *Address, // IPv6 multicast address. void *LinkAddress) // Where link-level address to be filled resides. { UNREFERENCED_PARAMETER(Context); if (IsMulticast(Address)) { uchar *IEEEAddress = (uchar *)LinkAddress; // // This is formed the same way for Ethernet and FDDI. // IEEEAddress[0] = 0x33; IEEEAddress[1] = 0x33; IEEEAddress[2] = Address->s6_bytes[12]; IEEEAddress[3] = Address->s6_bytes[13]; IEEEAddress[4] = Address->s6_bytes[14]; IEEEAddress[5] = Address->s6_bytes[15]; return ND_STATE_PERMANENT; } else { // // We can't guess at the correct link-layer address. // return ND_STATE_INCOMPLETE; } } //* LanSetMulticastAddressList // // Takes an array of link-layer multicast addresses // (from LanConvertMulticastAddress) from which we should // receive packets. Passes them to NDIS. // NDIS_STATUS LanSetMulticastAddressList( void *Context, const void *LinkAddresses, uint NumKeep, uint NumAdd, uint NumDel) { LanInterface *Interface = (LanInterface *)Context; NDIS_STATUS Status; NDIS_OID OID; UNREFERENCED_PARAMETER(NumDel); // // Set the multicast address list to the current list. // The OID to do this depends upon the media type. // switch (Interface->ai_media) { case NdisMedium802_3: OID = OID_802_3_MULTICAST_LIST; break; case NdisMediumFddi: OID = OID_FDDI_LONG_MULTICAST_LIST; break; default: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "LanSetMulticastAddressList: Unknown media type\n")); return NDIS_STATUS_FAILURE; } Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID, (char *)LinkAddresses, (NumKeep + NumAdd) * Interface->ai_addrlen, NULL); // // If the above request was successful, then turn off the all-multicast // or all-packets filter if we had previously set one of them. // if (Status == NDIS_STATUS_SUCCESS) { if (Interface->ai_pfilter & NDIS_PACKET_TYPE_ALL_MULTICAST || Interface->ai_pfilter & NDIS_PACKET_TYPE_PROMISCUOUS) { Interface->ai_pfilter &= ~(NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_PROMISCUOUS); DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), NULL); } return Status; } // // We get here only if the NDIS request to set the multicast list fails. // First we try to set the packet filter for all multicast packets, and if // this fails, we try to set the packet filter for all packets. // // This code was swiped from the V4 stack: arp.c Interface->ai_pfilter |= NDIS_PACKET_TYPE_ALL_MULTICAST; Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), NULL); if (Status != NDIS_STATUS_SUCCESS) { // All multicast failed, try all packets. Interface->ai_pfilter &= ~(NDIS_PACKET_TYPE_ALL_MULTICAST); Interface->ai_pfilter |= NDIS_PACKET_TYPE_PROMISCUOUS; Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), NULL); } return Status; } //* LanCloseAdapter // // The IPv6 layer calls this function to close a connection to an adapter. // void LanCloseAdapter(void *Context) { LanInterface *Interface = (LanInterface *)Context; // // Mark adapter down. // Interface->ai_state = INTERFACE_DOWN; // // Shut adapter up, so we don't get any more frames. // Interface->ai_pfilter = 0; DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), NULL); // // Release our reference for the interface. // ReleaseInterface(Interface->ai_context); } //* LanCleanupAdapter // // Perform final cleanup of the adapter. // void LanCleanupAdapter(void *Context) { LanInterface *Interface = (LanInterface *)Context; NDIS_STATUS Status; KeInitializeEvent(&Interface->ai_event, SynchronizationEvent, FALSE); // // Close the connection to NDIS. // NdisCloseAdapter(&Status, Interface->ai_handle); // // Block for close to complete. // if (Status == NDIS_STATUS_PENDING) { (void) KeWaitForSingleObject(&Interface->ai_event, UserRequest, KernelMode, FALSE, NULL); Status = Interface->ai_status; } if (Status != NDIS_STATUS_SUCCESS) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "LanCleanupAdapter(%p) - NdisCloseAdapter -> %x\n", Interface, Status)); } // // Tell NDIS that we are done. // NOTE: IOCTL_IPV6_DELETE_INTERFACE does not set ai_unbind, this // ensures that NdisCompleteUnbindAdapter is not invoked along its path. // if (Interface->ai_unbind != NULL) NdisCompleteUnbindAdapter(Interface->ai_unbind, NDIS_STATUS_SUCCESS); // // Free adapter memory. // IPv6FreePacket(Interface->ai_tdpacket); ExFreePool(Interface); } //* LanBindAdapter - Bind and initialize an adapter. // // Called in a PNP environment to initialize and bind an adapter. We open // the adapter and get it running, and then we call up to IP to tell him // about it. IP will initialize, and if all goes well call us back to start // receiving. // void NDIS_API LanBindAdapter( PNDIS_STATUS RetStatus, // Where to return status of this call. NDIS_HANDLE BindContext, // Handle for calling BindingAdapterComplete. PNDIS_STRING AdapterName, // Name of adapter. PVOID SS1, // System specific parameter 1. PVOID SS2) // System specific parameter 2. { LanInterface *Interface; // Newly created interface. LLIPv6BindInfo BindInfo; // Binding information for IP. GUID Guid; UNICODE_STRING GuidName; uint BindPrefixLength; uint MediaStatus; NDIS_STATUS Status; KIRQL OldIrql; UNREFERENCED_PARAMETER(BindContext); UNREFERENCED_PARAMETER(SS1); UNREFERENCED_PARAMETER(SS2); // // Convert the NDIS AdapterName to a Guid. // BindPrefixLength = sizeof(IPV6_BIND_STRING_PREFIX) - sizeof(WCHAR); GuidName.Buffer = (PVOID)((char *)AdapterName->Buffer + BindPrefixLength); GuidName.Length = AdapterName->Length - BindPrefixLength; GuidName.MaximumLength = AdapterName->MaximumLength - BindPrefixLength; if (((int)GuidName.Length < 0) || ! NT_SUCCESS(RtlGUIDFromString(&GuidName, &Guid))) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "LanBindAdapter(%.*ls) - bad guid\n", AdapterName->Length / sizeof(WCHAR), AdapterName->Buffer)); *RetStatus = NDIS_STATUS_FAILURE; return; } // // Now open the adapter and get the info. // if (!LanRegister(AdapterName, &Interface)) { *RetStatus = NDIS_STATUS_FAILURE; return; } // // OK, we've opened the adapter. Notify IP about it. // BindInfo.lip_context = Interface; BindInfo.lip_transmit = LanTransmit; BindInfo.lip_token = LanCreateToken; BindInfo.lip_close = LanCloseAdapter; BindInfo.lip_cleanup = LanCleanupAdapter; BindInfo.lip_defmtu = BindInfo.lip_maxmtu = Interface->ai_mtu; BindInfo.lip_hdrsize = Interface->ai_hdrsize; BindInfo.lip_addrlen = Interface->ai_addrlen; BindInfo.lip_addr = Interface->ai_addr; BindInfo.lip_setrtrlladdr = NULL; switch (Interface->ai_media) { case NdisMediumTunnel: BindInfo.lip_type = IF_TYPE_TUNNEL_TEREDO; BindInfo.lip_rdllopt = NULL; BindInfo.lip_wrllopt = NULL; BindInfo.lip_cvaddr = LanTunnelConvertAddress; BindInfo.lip_mclist = NULL; BindInfo.lip_flags = IF_FLAG_ROUTER_DISCOVERS; BindInfo.lip_dadxmit = 0; BindInfo.lip_pref = LAN_TUNNEL_DEFAULT_PREFERENCE; ASSERT(sizeof(TUN_CARD_ADDRESS) >= Interface->ai_addrlen); if (RtlEqualMemory(TUN_CARD_ADDRESS, Interface->ai_addr, Interface->ai_addrlen)) { // // Create random interface identifiers for link-local addresses to // secure them against attackers. Only required when the adapter // is configured with the default MAC address. // BindInfo.lip_token = LanTunnelCreateToken; } break; case NdisMedium802_3: BindInfo.lip_type = IF_TYPE_ETHERNET; goto Default; case NdisMediumFddi: BindInfo.lip_type = IF_TYPE_FDDI; goto Default; default: ASSERT(! "unrecognized ai_media type"); BindInfo.lip_type = 0; Default: BindInfo.lip_rdllopt = LanReadLinkLayerAddressOption; BindInfo.lip_wrllopt = LanWriteLinkLayerAddressOption; BindInfo.lip_cvaddr = LanConvertAddress; BindInfo.lip_mclist = LanSetMulticastAddressList; BindInfo.lip_flags = IF_FLAG_NEIGHBOR_DISCOVERS | IF_FLAG_ROUTER_DISCOVERS | IF_FLAG_MULTICAST; BindInfo.lip_dadxmit = 1; // Per RFC 2462. BindInfo.lip_pref = 0; break; } // // Should we create the interface in the disconnected state? // Status = DoNDISRequest(Interface, NdisRequestQueryInformation, OID_GEN_MEDIA_CONNECT_STATUS, &MediaStatus, sizeof MediaStatus, NULL); if (Status == NDIS_STATUS_SUCCESS) { if (MediaStatus == NdisMediaStateDisconnected) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanBindAdapter(%p) - media disconnect\n", Interface)); BindInfo.lip_flags |= IF_FLAG_MEDIA_DISCONNECTED; } } if (CreateInterface(&Guid, &BindInfo, &Interface->ai_context) != NDIS_STATUS_SUCCESS) { // // Attempt to create IP interface failed. Need to close the binding. // LanFreeInterface will do that, as well as freeing resources. // LanFreeInterface(Interface); *RetStatus = NDIS_STATUS_FAILURE; return; } // // It's quite possible that during our initialization sequence, // LanStatus was called to indicate a media connectivity change. // Some adapters are bound in the non-connected state and then // shortly after indicate that they are connected. // The above connectivity query may have returned the wrong result. // // An intervening LanStatus indication will change ai_media_check // from MEDIA_CHECK_QUERY to MEDIA_CHECK_CONFLICT. // Status = NDIS_STATUS_FAILURE; KeAcquireSpinLock(&Interface->ai_lock, &OldIrql); while (Interface->ai_media_check == MEDIA_CHECK_CONFLICT) { Interface->ai_media_check = MEDIA_CHECK_QUERY; KeReleaseSpinLock(&Interface->ai_lock, OldIrql); Status = DoNDISRequest(Interface, NdisRequestQueryInformation, OID_GEN_MEDIA_CONNECT_STATUS, &MediaStatus, sizeof MediaStatus, NULL); KeAcquireSpinLock(&Interface->ai_lock, &OldIrql); } Interface->ai_media_check = MEDIA_CHECK_IDLE; if (Status == NDIS_STATUS_SUCCESS) SetInterfaceLinkStatus(Interface->ai_context, MediaStatus != NdisMediaStateDisconnected); KeReleaseSpinLock(&Interface->ai_lock, OldIrql); Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof Interface->ai_pfilter, NULL); if (Status == NDIS_STATUS_SUCCESS) Interface->ai_state = INTERFACE_UP; else Interface->ai_state = INTERFACE_DOWN; *RetStatus = NDIS_STATUS_SUCCESS; } //* LanUnbindAdapter - Unbind from an adapter. // // Called when we need to unbind from an adapter. // We'll notify IP, then free our memory and return. // void NDIS_API // Returns: Nothing. LanUnbindAdapter( PNDIS_STATUS RetStatus, // Where to return status from this call. NDIS_HANDLE ProtBindContext, // Context we gave NDIS earlier. NDIS_HANDLE UnbindContext) // Context for completing this request. { LanInterface *Interface = (LanInterface *)ProtBindContext; Interface->ai_unbind = UnbindContext; // // Call IP to destroy the interface. // IP will call LanCloseAdapter then LanCleanupAdapter. // DestroyInterface(Interface->ai_context); // // We will call NdisCompleteUnbindAdapter later, // when NdisCloseAdapter completes. // *RetStatus = NDIS_STATUS_PENDING; } //* LanPnPEvent // // Gets called for plug'n'play and power-management events. // NDIS_STATUS NDIS_API LanPnPEvent( NDIS_HANDLE ProtocolBindingContext, PNET_PNP_EVENT NetPnPEvent) { LanInterface *Interface = (LanInterface *) ProtocolBindingContext; switch (NetPnPEvent->NetEvent) { case NetEventSetPower: { NET_DEVICE_POWER_STATE PowerState; // // Get the power state of the interface. // ASSERT(NetPnPEvent->BufferLength >= sizeof PowerState); PowerState = * (NET_DEVICE_POWER_STATE *) NetPnPEvent->Buffer; KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanPnPEvent(%p) - set power %u\n", Interface, PowerState)); // // We ignore the events that tell us about power going away. // But when power comes back, we query for connect status. // NDIS does not report connect/disconnect events that occur // while there is no power. // // Note that we may be redundantly setting the link status. // For example saying that it is disconnected when the // IPv6 interface status is already disconnected, // or vice-versa. The IPv6 code must deal with this. // if (PowerState == NetDeviceStateD0) { uint MediaStatus; NDIS_STATUS Status; KIRQL OldIrql; KeAcquireSpinLock(&Interface->ai_lock, &OldIrql); if (Interface->ai_media_check == MEDIA_CHECK_IDLE) { do { Interface->ai_media_check = MEDIA_CHECK_QUERY; KeReleaseSpinLock(&Interface->ai_lock, OldIrql); Status = DoNDISRequest(Interface, NdisRequestQueryInformation, OID_GEN_MEDIA_CONNECT_STATUS, &MediaStatus, sizeof MediaStatus, NULL); KeAcquireSpinLock(&Interface->ai_lock, &OldIrql); } while (Interface->ai_media_check == MEDIA_CHECK_CONFLICT); Interface->ai_media_check = MEDIA_CHECK_IDLE; if (Status == NDIS_STATUS_SUCCESS) SetInterfaceLinkStatus(Interface->ai_context, MediaStatus != NdisMediaStateDisconnected); } else Interface->ai_media_check = MEDIA_CHECK_CONFLICT; KeReleaseSpinLock(&Interface->ai_lock, OldIrql); } break; } case NetEventBindsComplete: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanPnPEvent(%p) - binds complete\n", Interface)); IPv6ProviderReady(); break; case NetEventQueryPower: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanPnPEvent(%p) - query power\n", Interface)); break; case NetEventQueryRemoveDevice: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanPnPEvent(%p) - query remove device\n", Interface)); break; case NetEventCancelRemoveDevice: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanPnPEvent(%p) - cancel remove device\n", Interface)); break; case NetEventReconfigure: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanPnPEvent(%p) - reconfigure\n", Interface)); break; case NetEventBindList: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanPnPEvent(%p) - bind list\n", Interface)); break; case NetEventPnPCapabilities: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanPnPEvent(%p) - pnp capabilities\n", Interface)); break; default: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "LanPnPEvent(%p) - unknown code %u length %u\n", Interface, NetPnPEvent->NetEvent, NetPnPEvent->BufferLength)); break; } return NDIS_STATUS_SUCCESS; }