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