You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2002 lines
63 KiB
2002 lines
63 KiB
// -*- 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;
|
|
}
|