softether-vpn/src/Neo/NDIS5.c

1731 lines
38 KiB
C

// SoftEther VPN Source Code - Stable Edition Repository
// Kernel Device Driver
//
// SoftEther VPN Server, Client and Bridge are free software under the Apache License, Version 2.0.
//
// Copyright (c) Daiyuu Nobori.
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) SoftEther Corporation.
// Copyright (c) all contributors on SoftEther VPN project in GitHub.
//
// All Rights Reserved.
//
// http://www.softether.org/
//
// This stable branch is officially managed by Daiyuu Nobori, the owner of SoftEther VPN Project.
// Pull requests should be sent to the Developer Edition Master Repository on https://github.com/SoftEtherVPN/SoftEtherVPN
//
// License: The Apache License, Version 2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// DISCLAIMER
// ==========
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN, UNDER
// JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY, MERGE, PUBLISH,
// DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS SOFTWARE, THAT ANY
// JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS SOFTWARE OR ITS CONTENTS,
// AGAINST US (SOFTETHER PROJECT, SOFTETHER CORPORATION, DAIYUU NOBORI OR OTHER
// SUPPLIERS), OR ANY JURIDICAL DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND
// OF USING, COPYING, MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING,
// AND/OR SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO EXCLUSIVE
// JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO, JAPAN. YOU MUST WAIVE
// ALL DEFENSES OF LACK OF PERSONAL JURISDICTION AND FORUM NON CONVENIENS.
// PROCESS MAY BE SERVED ON EITHER PARTY IN THE MANNER AUTHORIZED BY APPLICABLE
// LAW OR COURT RULE.
//
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS YOU HAVE
// A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY CRIMINAL LAWS OR CIVIL
// RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS SOFTWARE IN OTHER COUNTRIES IS
// COMPLETELY AT YOUR OWN RISK. THE SOFTETHER VPN PROJECT HAS DEVELOPED AND
// DISTRIBUTED THIS SOFTWARE TO COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING
// CIVIL RIGHTS INCLUDING PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER
// COUNTRIES' LAWS OR CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES.
// WE HAVE NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+ COUNTRIES
// AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE WORLD, WITH
// DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY COUNTRIES' LAWS, REGULATIONS
// AND CIVIL RIGHTS TO MAKE THE SOFTWARE COMPLY WITH ALL COUNTRIES' LAWS BY THE
// PROJECT. EVEN IF YOU WILL BE SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A
// PUBLIC SERVANT IN YOUR COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE
// LIABLE TO RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT JUST A
// STATEMENT FOR WARNING AND DISCLAIMER.
//
// READ AND UNDERSTAND THE 'WARNING.TXT' FILE BEFORE USING THIS SOFTWARE.
// SOME SOFTWARE PROGRAMS FROM THIRD PARTIES ARE INCLUDED ON THIS SOFTWARE WITH
// LICENSE CONDITIONS WHICH ARE DESCRIBED ON THE 'THIRD_PARTY.TXT' FILE.
//
//
// SOURCE CODE CONTRIBUTION
// ------------------------
//
// Your contribution to SoftEther VPN Project is much appreciated.
// Please send patches to us through GitHub.
// Read the SoftEther VPN Patch Acceptance Policy in advance:
// http://www.softether.org/5-download/src/9.patch
//
//
// DEAR SECURITY EXPERTS
// ---------------------
//
// If you find a bug or a security vulnerability please kindly inform us
// about the problem immediately so that we can fix the security problem
// to protect a lot of users around the world as soon as possible.
//
// Our e-mail address for security reports is:
// softether-vpn-security [at] softether.org
//
// Please note that the above e-mail address is not a technical support
// inquiry address. If you need technical assistance, please visit
// http://www.softether.org/ and ask your question on the users forum.
//
// Thank you for your cooperation.
//
//
// NO MEMORY OR RESOURCE LEAKS
// ---------------------------
//
// The memory-leaks and resource-leaks verification under the stress
// test has been passed before release this source code.
// NDIS5.c
// Description: Windows NDIS 5.0 Routine
#include <GlobalConst.h>
#define NEO_DEVICE_DRIVER
#include "Neo.h"
static UINT max_speed = NEO_MAX_SPEED_DEFAULT;
static bool keep_link = false;
BOOLEAN
PsGetVersion(
PULONG MajorVersion OPTIONAL,
PULONG MinorVersion OPTIONAL,
PULONG BuildNumber OPTIONAL,
PUNICODE_STRING CSDVersion OPTIONAL
);
// Memory related
static NDIS_PHYSICAL_ADDRESS HighestAcceptableMax = NDIS_PHYSICAL_ADDRESS_CONST(-1, -1);
NDIS_HANDLE ndis_wrapper_handle = NULL;
// Whether Windows 8
bool g_is_win8 = false;
// Win32 driver entry point
NDIS_STATUS DriverEntry(DRIVER_OBJECT *DriverObject, UNICODE_STRING *RegistryPath)
{
NDIS_MINIPORT_CHARACTERISTICS miniport;
ULONG os_major_ver = 0, os_minor_ver = 0;
// Initialize the Neo library
if (NeoInit() == FALSE)
{
// Initialization Failed
return STATUS_UNSUCCESSFUL;
}
g_is_win8 = false;
#ifndef NDIS30_MINIPORT
// Get the OS version
PsGetVersion(&os_major_ver, &os_minor_ver, NULL, NULL);
if (os_major_ver >= 7 || (os_major_ver == 6 && os_minor_ver >= 2))
{
// Windows 8
g_is_win8 = true;
}
#endif // NDIS30_MINIPORT
// Initialize the NDIS wrapper
NdisMInitializeWrapper(&ctx->NdisWrapper, DriverObject, RegistryPath, NULL);
ndis_wrapper_handle = ctx->NdisWrapper;
// Register a NDIS miniport driver
NeoZero(&miniport, sizeof(NDIS_MINIPORT_CHARACTERISTICS));
miniport.MajorNdisVersion = NEO_NDIS_MAJOR_VERSION;
miniport.MinorNdisVersion = NEO_NDIS_MINOR_VERSION;
// Register the handler
miniport.InitializeHandler = NeoNdisInit;
miniport.HaltHandler = NeoNdisHalt;
miniport.QueryInformationHandler = NeoNdisQuery;
miniport.ResetHandler = NeoNdisReset;
miniport.SetInformationHandler = NeoNdisSet;
#ifndef NDIS30_MINIPORT
miniport.SendPacketsHandler = NeoNdisSendPackets;
#else // NDIS30_MINIPORT
miniport.SendHandler = NULL;
#endif // NDIS30_MINIPORT
if (NG(NdisMRegisterMiniport(ctx->NdisWrapper, &miniport, sizeof(NDIS_MINIPORT_CHARACTERISTICS))))
{
// Registration failure
return STATUS_UNSUCCESSFUL;
}
// Initialization success
return STATUS_SUCCESS;
}
// Initialization handler of adapter
NDIS_STATUS NeoNdisInit(NDIS_STATUS *OpenErrorStatus,
UINT *SelectedMediumIndex,
NDIS_MEDIUM *MediumArray,
UINT MediumArraySize,
NDIS_HANDLE MiniportAdapterHandle,
NDIS_HANDLE WrapperConfigurationContext)
{
BOOL media_check;
UINT i;
if (ctx == NULL)
{
return NDIS_STATUS_FAILURE;
}
if (ctx->NdisWrapper == NULL)
{
ctx->NdisWrapper = ndis_wrapper_handle;
}
// Prevention of multiple start
if (ctx->Initing != FALSE)
{
// Multiple started
return NDIS_STATUS_FAILURE;
}
ctx->Initing = TRUE;
// Examine whether it has already been initialized
if (ctx->Inited != FALSE)
{
// Driver is started on another instance already.
// PacketiX VPN driver can start only one instance per one service.
// User can start multiple drivers with different instance ID
return NDIS_STATUS_FAILURE;
}
// Current value of the packet filter
ctx->CurrentPacketFilter = NDIS_PACKET_TYPE_ALL_LOCAL | NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_FUNCTIONAL;
// Examine whether the Ethernet is available
media_check = FALSE;
for (i = 0;i < MediumArraySize;i++)
{
if (MediumArray[i] == NEO_MEDIA)
{
media_check = TRUE;
break;
}
}
if (media_check == FALSE)
{
// Ethernet is unavailable
ctx->Initing = FALSE;
return NDIS_STATUS_FAILURE;
}
// Media number to use
*SelectedMediumIndex = i;
// Initialize the adapter information
ctx->NdisMiniport = MiniportAdapterHandle;
ctx->NdisConfig = WrapperConfigurationContext;
ctx->NdisContext = ctx;
ctx->HardwareStatus = NdisHardwareStatusReady;
ctx->Halting = FALSE;
ctx->Connected = ctx->ConnectedOld = FALSE;
if (keep_link == false)
{
ctx->ConnectedForce = TRUE;
}
// Read the information from the registry
if (NeoLoadRegistory() == FALSE)
{
// Failure
ctx->Initing = FALSE;
return NDIS_STATUS_FAILURE;
}
// Register the device attributes
if (g_is_win8 == false)
{
NdisMSetAttributes(ctx->NdisMiniport, ctx->NdisContext, FALSE, NdisInterfaceInternal);
}
else
{
NdisMSetAttributesEx(ctx->NdisMiniport, ctx->NdisContext, 16,
NDIS_ATTRIBUTE_DESERIALIZE | NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT | NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
NdisInterfaceInternal);
}
// Initialize the received packet array
NeoInitPacketArray();
// Initialize the control device
NeoInitControlDevice();
// Start the adapter
NeoStartAdapter();
// Flag setting
ctx->Initing = FALSE;
ctx->Inited = TRUE;
// Notify the connection state
NeoSetConnectState(FALSE);
return NDIS_STATUS_SUCCESS;
}
// Open the device
BOOL NeoNdisOnOpen(IRP *irp, IO_STACK_LOCATION *stack)
{
char name[MAX_SIZE];
if (ctx == NULL)
{
return FALSE;
}
if (ctx->Opened != FALSE)
{
// Another client is connected already
return FALSE;
}
ctx->Opened = TRUE;
// Initialize the event name
sprintf(name, NDIS_NEO_EVENT_NAME, ctx->HardwareID);
// Register a Event
#ifndef WIN9X
ctx->Event = NeoNewEvent(name);
if (ctx->Event == NULL)
{
ctx->Opened = FALSE;
return FALSE;
}
#endif // WIN9X
// Set the connection state
NeoSetConnectState(TRUE);
return TRUE;
}
// Close the device
BOOL NeoNdisOnClose(IRP *irp, IO_STACK_LOCATION *stack)
{
if (ctx == NULL)
{
return FALSE;
}
if (ctx->Opened == FALSE)
{
// Client is not connected
return FALSE;
}
ctx->Opened = FALSE;
// Release the event
NeoFreeEvent(ctx->Event);
ctx->Event = NULL;
// Release all packets
NeoClearPacketQueue();
NeoSetConnectState(FALSE);
return TRUE;
}
// Crash
void NeoNdisCrash()
{
NEO_QUEUE *q;
q = (NEO_QUEUE *)0xACACACAC;
q->Size = 128;
NeoCopy(q->Buf, "ABCDEFG", 8);
}
// Dispatch table for control
NTSTATUS NeoNdisDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp)
{
NTSTATUS status;
IO_STACK_LOCATION *stack;
void *buf;
BOOL ok;
status = STATUS_SUCCESS;
if (ctx == NULL)
{
return NDIS_STATUS_FAILURE;
}
// Get the IRP stack
stack = IoGetCurrentIrpStackLocation(Irp);
// Initialize the number of bytes
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
buf = Irp->UserBuffer;
if (ctx->Halting != FALSE)
{
// Device driver is terminating
Irp->IoStatus.Information = STATUS_UNSUCCESSFUL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
// Branch to each operation
switch (stack->MajorFunction)
{
case IRP_MJ_CREATE:
// Device is opened
if (NeoNdisOnOpen(Irp, stack) == FALSE)
{
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
status = STATUS_UNSUCCESSFUL;
}
break;
case IRP_MJ_CLOSE:
// Device is closed
if (NeoNdisOnClose(Irp, stack) == FALSE)
{
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
status = STATUS_UNSUCCESSFUL;
}
break;
case IRP_MJ_READ:
#ifndef WIN9X
// Read (Reading of the received packet)
ok = false;
if (buf != NULL)
{
if (ctx->Opened && ctx->Inited)
{
if (stack->Parameters.Read.Length == NEO_EXCHANGE_BUFFER_SIZE)
{
// Address check
bool check_ok = true;
__try
{
ProbeForWrite(buf, NEO_EXCHANGE_BUFFER_SIZE, 1);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
check_ok = false;
}
if (check_ok)
{
MDL *mdl = IoAllocateMdl(buf, NEO_EXCHANGE_BUFFER_SIZE, false, false, NULL);
if (mdl != NULL)
{
MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess);
}
// Read
NeoRead(buf);
Irp->IoStatus.Information = NEO_EXCHANGE_BUFFER_SIZE;
ok = true;
if (mdl != NULL)
{
MmUnlockPages(mdl);
IoFreeMdl(mdl);
}
}
}
}
}
if (ok == FALSE)
{
// An error occurred
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
status = STATUS_UNSUCCESSFUL;
}
#endif // WIN9X
break;
case IRP_MJ_WRITE:
#ifndef WIN9X
// Write (Writing of a transmission packet)
ok = false;
if (buf != NULL)
{
if (ctx->Opened && ctx->Inited)
{
if (stack->Parameters.Write.Length == NEO_EXCHANGE_BUFFER_SIZE)
{
// Address check
bool check_ok = true;
__try
{
ProbeForRead(buf, NEO_EXCHANGE_BUFFER_SIZE, 1);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
check_ok = false;
}
if (check_ok)
{
MDL *mdl = IoAllocateMdl(buf, NEO_EXCHANGE_BUFFER_SIZE, false, false, NULL);
if (mdl != NULL)
{
MmProbeAndLockPages(mdl, KernelMode, IoReadAccess);
}
ProbeForRead(buf, NEO_EXCHANGE_BUFFER_SIZE, 1);
// Write
NeoWrite(buf);
Irp->IoStatus.Information = stack->Parameters.Write.Length;
ok = true;
if (mdl != NULL)
{
MmUnlockPages(mdl);
IoFreeMdl(mdl);
}
}
}
}
}
if (ok == FALSE)
{
// An error occurred
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
status = STATUS_UNSUCCESSFUL;
}
break;
#endif // WIN9X
case IRP_MJ_DEVICE_CONTROL:
#ifdef WIN9X
// IO Control
switch (stack->Parameters.DeviceIoControl.IoControlCode)
{
case NEO_IOCTL_SET_EVENT:
// Specify a event
if (Irp->AssociatedIrp.SystemBuffer == NULL ||
stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(DWORD))
{
// An error occurred
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
}
else
{
DWORD value = *((DWORD *)Irp->AssociatedIrp.SystemBuffer);
ctx->Event = NeoCreateWin9xEvent(value);
Irp->IoStatus.Information = sizeof(DWORD);
}
break;
case NEO_IOCTL_PUT_PACKET:
// Write a packet
ok = false;
buf = Irp->AssociatedIrp.SystemBuffer;
if (buf != NULL)
{
if (stack->Parameters.DeviceIoControl.InputBufferLength == NEO_EXCHANGE_BUFFER_SIZE)
{
// Write
NeoWrite(buf);
Irp->IoStatus.Information = NEO_EXCHANGE_BUFFER_SIZE;
ok = true;
}
}
if (ok == false)
{
// An error occurred
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
status = STATUS_UNSUCCESSFUL;
}
break;
case NEO_IOCTL_GET_PACKET:
// Get the packet
ok = false;
buf = Irp->AssociatedIrp.SystemBuffer;
if (buf != NULL)
{
if (stack->Parameters.DeviceIoControl.OutputBufferLength == NEO_EXCHANGE_BUFFER_SIZE)
{
// Read
NeoRead(buf);
Irp->IoStatus.Information = NEO_EXCHANGE_BUFFER_SIZE;
ok = true;
}
}
if (ok == false)
{
// An error occurred
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
status = STATUS_UNSUCCESSFUL;
}
break;
}
#endif // WIN9X
break;
}
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// Initialize the control device
void NeoInitControlDevice()
{
char name_kernel[MAX_SIZE];
char name_win32[MAX_SIZE];
UNICODE *unicode_kernel, *unicode_win32;
DEVICE_OBJECT *control_device_object;
NDIS_HANDLE ndis_control_handle;
if (ctx == NULL)
{
return;
}
// Initialize the dispatch table
NeoZero(ctx->DispatchTable, sizeof(PDRIVER_DISPATCH) * IRP_MJ_MAXIMUM_FUNCTION);
// Register the handler
ctx->DispatchTable[IRP_MJ_CREATE] =
ctx->DispatchTable[IRP_MJ_CLOSE] =
ctx->DispatchTable[IRP_MJ_READ] =
ctx->DispatchTable[IRP_MJ_WRITE] =
ctx->DispatchTable[IRP_MJ_DEVICE_CONTROL] = NeoNdisDispatch;
ctx->Opened = FALSE;
// Generate the device name
sprintf(name_kernel, NDIS_NEO_DEVICE_NAME, ctx->HardwareID);
unicode_kernel = NewUnicode(name_kernel);
sprintf(name_win32, NDIS_NEO_DEVICE_NAME_WIN32, ctx->HardwareID);
unicode_win32 = NewUnicode(name_win32);
// Register the Device
NdisMRegisterDevice(ctx->NdisWrapper, GetUnicode(unicode_kernel),
GetUnicode(unicode_win32), ctx->DispatchTable,
&control_device_object,
&ndis_control_handle);
ctx->NdisControlDevice = control_device_object;
ctx->NdisControl = ndis_control_handle;
// Initialize the display name
if (strlen(ctx->HardwareID) > 11)
{
sprintf(ctx->HardwarePrintableID, NDIS_NEO_HARDWARE_ID, ctx->HardwareID_Raw + 11);
}
else
{
sprintf(ctx->HardwarePrintableID, NDIS_NEO_HARDWARE_ID, ctx->HardwareID_Raw);
}
}
// Release the control device
void NeoFreeControlDevice()
{
if (ctx == NULL)
{
return;
}
if (ctx->Opened != FALSE)
{
// Delete the event
NeoSet(ctx->Event);
NeoFreeEvent(ctx->Event);
ctx->Event = NULL;
ctx->Opened = FALSE;
}
// Delet the device
NdisMDeregisterDevice(ctx->NdisControl);
}
// Read the information from the registry
BOOL NeoLoadRegistory()
{
void *buf;
NDIS_STATUS ret;
UINT size;
NDIS_HANDLE config;
NDIS_CONFIGURATION_PARAMETER *param;
UNICODE *name;
ANSI_STRING ansi;
UNICODE_STRING *unicode;
UINT speed;
BOOL keep;
// Get the config handle
NdisOpenConfiguration(&ret, &config, ctx->NdisConfig);
if (NG(ret))
{
// Failure
return FALSE;
}
// Read the MAC address
NdisReadNetworkAddress(&ret, &buf, &size, config);
if (NG(ret))
{
// Failure
NdisCloseConfiguration(config);
return FALSE;
}
// Copy the MAC address
if (size != NEO_MAC_ADDRESS_SIZE)
{
// Invalid size
NdisCloseConfiguration(config);
return FALSE;
}
NeoCopy(ctx->MacAddress, buf, NEO_MAC_ADDRESS_SIZE);
if (ctx->MacAddress[0] == 0x00 &&
ctx->MacAddress[1] == 0x00 &&
ctx->MacAddress[2] == 0x01 &&
ctx->MacAddress[3] == 0x00 &&
ctx->MacAddress[4] == 0x00 &&
ctx->MacAddress[5] == 0x01)
{
// Special MAC address
UINT ptr32 = (UINT)((UINT64)ctx);
ctx->MacAddress[0] = 0x00;
ctx->MacAddress[1] = 0xAD;
ctx->MacAddress[2] = ((UCHAR *)(&ptr32))[0];
ctx->MacAddress[3] = ((UCHAR *)(&ptr32))[1];
ctx->MacAddress[4] = ((UCHAR *)(&ptr32))[2];
ctx->MacAddress[5] = ((UCHAR *)(&ptr32))[3];
}
// Initialize the key name of the device name
name = NewUnicode("MatchingDeviceId");
// Read the hardware ID
NdisReadConfiguration(&ret, &param, config, GetUnicode(name), NdisParameterString);
FreeUnicode(name);
if (NG(ret))
{
// Failure
NdisCloseConfiguration(config);
return FALSE;
}
// Type checking
if (param->ParameterType != NdisParameterString)
{
// Failure
NdisCloseConfiguration(config);
return FALSE;
}
unicode = &param->ParameterData.StringData;
// Prepare a buffer for ANSI string
NeoZero(&ansi, sizeof(ANSI_STRING));
ansi.MaximumLength = MAX_SIZE - 1;
ansi.Buffer = NeoZeroMalloc(MAX_SIZE);
// Convert to ANSI string
NdisUnicodeStringToAnsiString(&ansi, unicode);
// Copy
strcpy(ctx->HardwareID, ansi.Buffer);
strcpy(ctx->HardwareID_Raw, ctx->HardwareID);
// Convert to upper case
_strupr(ctx->HardwareID);
// Release the memory
NeoFree(ansi.Buffer);
// Read the bit rate
name = NewUnicode("MaxSpeed");
NdisReadConfiguration(&ret, &param, config, GetUnicode(name), NdisParameterInteger);
FreeUnicode(name);
if (NG(ret) || param->ParameterType != NdisParameterInteger)
{
speed = NEO_MAX_SPEED_DEFAULT;
}
else
{
speed = param->ParameterData.IntegerData * 10000;
}
max_speed = speed;
// Read the link keeping flag
name = NewUnicode("KeepLink");
NdisReadConfiguration(&ret, &param, config, GetUnicode(name), NdisParameterInteger);
FreeUnicode(name);
if (NG(ret) || param->ParameterType != NdisParameterInteger)
{
keep = false;
}
else
{
keep = (param->ParameterData.IntegerData == 0 ? false : true);
}
keep_link = keep;
// Close the Config handle
NdisCloseConfiguration(config);
return TRUE;
}
// Stop handler of adapter
NDIS_STATUS NeoNdisHalt(NDIS_HANDLE MiniportAdapterContext)
{
if (ctx == NULL)
{
return NDIS_STATUS_FAILURE;
}
if (ctx->Halting != FALSE)
{
// That has already been stopped
return NDIS_STATUS_SUCCESS;
}
ctx->Halting = TRUE;
// Stop the adapter
NeoStopAdapter();
// Release the packet array
NeoFreePacketArray();
// Delete the control device
NeoFreeControlDevice();
// Complete to stop
ctx->Initing = ctx->Inited = FALSE;
ctx->Connected = ctx->ConnectedForce = ctx->ConnectedOld = FALSE;
ctx->Halting = FALSE;
// Shutdown of Neo
NeoShutdown();
return NDIS_STATUS_SUCCESS;
}
// Reset handler of adapter
NDIS_STATUS NeoNdisReset(BOOLEAN *AddressingReset, NDIS_HANDLE MiniportAdapterContext)
{
NdisMResetComplete(ctx->NdisMiniport, NDIS_STATUS_SUCCESS, FALSE);
return NDIS_STATUS_SUCCESS;
}
// Information acquisition handler of adapter
NDIS_STATUS NeoNdisQuery(NDIS_HANDLE MiniportAdapterContext,
NDIS_OID Oid,
void *InformationBuffer,
ULONG InformationBufferLength,
ULONG *BytesWritten,
ULONG *BytesNeeded)
{
NDIS_MEDIUM media;
void *buf;
UINT value32;
USHORT value16;
UINT size;
if (ctx == NULL)
{
return NDIS_STATUS_FAILURE;
}
// Initialization
size = sizeof(UINT);
value32 = value16 = 0;
buf = &value32;
// Branch processing
switch (Oid)
{
case OID_GEN_SUPPORTED_LIST:
// Return a list of supported OID
buf = SupportedOids;
size = sizeof(SupportedOids);
break;
case OID_GEN_MAC_OPTIONS:
// Ethernet option
value32 = NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | NDIS_MAC_OPTION_RECEIVE_SERIALIZED |
NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | NDIS_MAC_OPTION_NO_LOOPBACK;
break;
case OID_GEN_HARDWARE_STATUS:
// Hardware state
buf = &ctx->HardwareStatus;
size = sizeof(NDIS_HARDWARE_STATUS);
break;
case OID_GEN_MEDIA_SUPPORTED:
case OID_GEN_MEDIA_IN_USE:
// Type of media
media = NdisMedium802_3;
buf = &media;
size = sizeof(NDIS_MEDIUM);
break;
case OID_GEN_CURRENT_LOOKAHEAD:
case OID_GEN_MAXIMUM_LOOKAHEAD:
// Available look-ahead size
value32 = NEO_MAX_PACKET_SIZE_ANNOUNCE - NEO_MIN_PACKET_SIZE;
break;
case OID_GEN_MAXIMUM_FRAME_SIZE:
// Maximum frame size
value32 = NEO_MAX_PACKET_SIZE_ANNOUNCE - NEO_MIN_PACKET_SIZE;
break;
case OID_GEN_MAXIMUM_TOTAL_SIZE:
case OID_GEN_TRANSMIT_BLOCK_SIZE:
case OID_GEN_RECEIVE_BLOCK_SIZE:
// Maximum packet size
value32 = NEO_MAX_PACKET_SIZE_ANNOUNCE;
break;
case OID_GEN_TRANSMIT_BUFFER_SPACE:
case OID_GEN_RECEIVE_BUFFER_SPACE:
// Buffer size
value32 = NEO_MAX_PACKET_SIZE_ANNOUNCE * NEO_MAX_PACKET_EXCHANGE;
break;
case OID_GEN_LINK_SPEED:
// Communication speed
value32 = max_speed;
break;
case OID_GEN_VENDOR_ID:
// Vendor ID
NeoCopy(&value32, ctx->MacAddress, 3);
value32 &= 0xFFFFFF00;
value32 |= 0x01;
break;
case OID_GEN_VENDOR_DESCRIPTION:
// Hardware ID
buf = ctx->HardwarePrintableID;
size = (UINT)strlen(ctx->HardwarePrintableID) + 1;
break;
case OID_GEN_DRIVER_VERSION:
// Driver version
value16 = ((USHORT)NEO_NDIS_MAJOR_VERSION << 8) | NEO_NDIS_MINOR_VERSION;
buf = &value16;
size = sizeof(USHORT);
break;
case OID_GEN_VENDOR_DRIVER_VERSION:
// Vendor driver version
value16 = ((USHORT)NEO_NDIS_MAJOR_VERSION << 8) | NEO_NDIS_MINOR_VERSION;
buf = &value16;
size = sizeof(USHORT);
break;
case OID_802_3_PERMANENT_ADDRESS:
case OID_802_3_CURRENT_ADDRESS:
// MAC address
buf = ctx->MacAddress;
size = NEO_MAC_ADDRESS_SIZE;
break;
case OID_802_3_MAXIMUM_LIST_SIZE:
// Number of multicast
value32 = NEO_MAX_MULTICASE;
break;
case OID_GEN_MAXIMUM_SEND_PACKETS:
// Number of packets that can be sent at a time
value32 = NEO_MAX_PACKET_EXCHANGE;
break;
case OID_GEN_XMIT_OK:
// Number of packets sent
value32 = ctx->Status.NumPacketSend;
break;
case OID_GEN_RCV_OK:
// Number of received packets
value32 = ctx->Status.NumPacketRecv;
break;
case OID_GEN_XMIT_ERROR:
// Number of transmission error packets
value32 = ctx->Status.NumPacketSendError;
break;
case OID_GEN_RCV_ERROR:
// Number of error packets received
value32 = ctx->Status.NumPacketRecvError;
break;
case OID_GEN_RCV_NO_BUFFER:
// Number of reception buffer shortage occurrences
value32 = ctx->Status.NumPacketRecvNoBuffer;
break;
case OID_802_3_RCV_ERROR_ALIGNMENT:
// Number of errors
value32 = 0;
break;
case OID_GEN_MEDIA_CONNECT_STATUS:
// Cable connection state
NeoCheckConnectState();
if (keep_link == false)
{
value32 = ctx->Connected ? NdisMediaStateConnected : NdisMediaStateDisconnected;
}
else
{
value32 = NdisMediaStateConnected;
}
break;
case OID_802_3_XMIT_ONE_COLLISION:
case OID_802_3_XMIT_MORE_COLLISIONS:
// Number of collisions
value32 = 0;
break;
case OID_GEN_CURRENT_PACKET_FILTER:
// Current settings of the packet filter
value32 = ctx->CurrentPacketFilter;
break;
/* case OID_GEN_PROTOCOL_OPTIONS:
// Current value of the protocol option
value32 = ctx->CurrentProtocolOptions;
break;*/
default:
// Unknown OID
*BytesWritten = 0;
return NDIS_STATUS_INVALID_OID;
}
if (size > InformationBufferLength)
{
// Undersize
*BytesNeeded = size;
*BytesWritten = 0;
return NDIS_STATUS_INVALID_LENGTH;
}
// Data copy
NeoCopy(InformationBuffer, buf, size);
*BytesWritten = size;
return NDIS_STATUS_SUCCESS;
}
// Set the cable connection state
void NeoSetConnectState(BOOL connected)
{
if (ctx == NULL)
{
return;
}
ctx->Connected = connected;
NeoCheckConnectState();
}
// Check the cable connection state
void NeoCheckConnectState()
{
if (ctx == NULL || ctx->NdisMiniport == NULL)
{
return;
}
if (keep_link == false)
{
if (ctx->ConnectedOld != ctx->Connected || ctx->ConnectedForce)
{
ctx->ConnectedForce = FALSE;
ctx->ConnectedOld = ctx->Connected;
if (ctx->Halting == FALSE)
{
NdisMIndicateStatus(ctx->NdisMiniport,
ctx->Connected ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT,
0, 0);
NdisMIndicateStatusComplete(ctx->NdisMiniport);
}
}
}
else
{
if (ctx->ConnectedForce)
{
ctx->ConnectedForce = false;
if (ctx->Halting == FALSE)
{
NdisMIndicateStatus(ctx->NdisMiniport,
NDIS_STATUS_MEDIA_CONNECT,
0, 0);
NdisMIndicateStatusComplete(ctx->NdisMiniport);
}
}
}
}
// Information setting handler of adapter
NDIS_STATUS NeoNdisSet(
NDIS_HANDLE MiniportAdapterContext,
NDIS_OID Oid,
void *InformationBuffer,
ULONG InformationBufferLength,
ULONG *BytesRead,
ULONG *BytesNeeded)
{
if (ctx == NULL)
{
return STATUS_UNSUCCESSFUL;
}
// Initialization
*BytesRead = 0;
*BytesNeeded = 0;
// Branch processing
switch (Oid)
{
case OID_GEN_CURRENT_PACKET_FILTER:
/* Packet filter */
if (InformationBufferLength != 4)
{
*BytesNeeded = 4;
return NDIS_STATUS_INVALID_LENGTH;
}
*BytesRead = 4;
ctx->CurrentPacketFilter = *((UINT *)InformationBuffer);
return NDIS_STATUS_SUCCESS;
// case OID_GEN_PROTOCOL_OPTIONS:
/* Current protocol option value */
/* if (InformationBufferLength != 4)
{
*BytesNeeded = 4;
return NDIS_STATUS_INVALID_LENGTH;
}
*BytesRead = 4;
ctx->CurrentProtocolOptions = *((UINT *)InformationBuffer);
return NDIS_STATUS_SUCCESS;*/
case OID_GEN_CURRENT_LOOKAHEAD:
/* Look ahead */
if (InformationBufferLength != 4)
{
*BytesNeeded = 4;
return NDIS_STATUS_INVALID_LENGTH;
}
*BytesRead = 4;
return NDIS_STATUS_SUCCESS;
case OID_802_3_MULTICAST_LIST:
// Multicast list
*BytesRead = InformationBufferLength;
return NDIS_STATUS_SUCCESS;
}
return NDIS_STATUS_INVALID_OID;
}
// NDIS 3.0 packet send handler
NDIS_STATUS NeoNdisSend(NDIS_HANDLE MiniportAdapterContext,
NDIS_PACKET *Packet, UINT Flags)
{
NDIS_PACKET *PacketArray[1];
PacketArray[0] = Packet;
NeoNdisSendPackets(MiniportAdapterContext, PacketArray, 1);
return NDIS_STATUS_SUCCESS;
}
// Packet send handler
void NeoNdisSendPackets(NDIS_HANDLE MiniportAdapterContext,
NDIS_PACKET **PacketArray,
UINT NumberOfPackets)
{
UCHAR *Buf,*BufCopy;
PNDIS_BUFFER Buffer;
UCHAR *Tmp;
UINT PacketLength;
UINT CurrentLength;
UINT i;
if (ctx == NULL)
{
return;
}
// Update the connection state
NeoCheckConnectState();
if (NumberOfPackets == 0)
{
// The number of packets is 0
return;
}
if (NeoNdisSendPacketsHaltCheck(PacketArray, NumberOfPackets) == FALSE)
{
// Device is stopped
return;
}
// Operation of the packet queue
NeoLockPacketQueue();
{
if (NeoNdisSendPacketsHaltCheck(PacketArray, NumberOfPackets) == FALSE)
{
// Device is stopped
NeoUnlockPacketQueue();
return;
}
// Place the packet in the queue in order
for (i = 0;i < NumberOfPackets;i++)
{
// Get a packet
NdisQueryPacket(PacketArray[i], NULL, NULL, &Buffer, &PacketLength);
// Extract the packet.
// Memory allocated here is used for the queue and is released at the time of releasing the queue.
Buf = NeoMalloc(PacketLength);
BufCopy = Buf;
while (Buffer)
{
NdisQueryBuffer(Buffer, &Tmp, &CurrentLength);
if (CurrentLength == 0)
{
// Complete
break;
}
NeoCopy(BufCopy, Tmp, CurrentLength);
BufCopy += CurrentLength;
NdisGetNextBuffer(Buffer, &Buffer);
}
// Process this packet
if (PacketLength > NEO_MIN_PACKET_SIZE)
{
if (PacketLength > NEO_MAX_PACKET_SIZE)
{
// Packet is too large
NDIS_SET_PACKET_STATUS(PacketArray[i], NDIS_STATUS_FAILURE);
if (g_is_win8)
{
NdisMSendComplete(ctx->NdisMiniport, PacketArray[i], NDIS_STATUS_FAILURE);
}
ctx->Status.NumPacketSendError++;
NeoFree(Buf);
}
else
{
// Insert the packet into the queue
NeoInsertQueue(Buf, PacketLength);
NDIS_SET_PACKET_STATUS(PacketArray[i], NDIS_STATUS_SUCCESS);
if (g_is_win8)
{
NdisMSendComplete(ctx->NdisMiniport, PacketArray[i], NDIS_STATUS_SUCCESS);
}
ctx->Status.NumPacketSend++;
}
}
else
{
// Release if the packet doesn't contain data
NDIS_SET_PACKET_STATUS(PacketArray[i], NDIS_STATUS_SUCCESS);
if (g_is_win8)
{
NdisMSendComplete(ctx->NdisMiniport, PacketArray[i], NDIS_STATUS_SUCCESS);
}
NeoFree(Buf);
}
}
}
NeoUnlockPacketQueue();
// Reception event
NeoSet(ctx->Event);
}
// Stop check of packet transmission
BOOL NeoNdisSendPacketsHaltCheck(NDIS_PACKET **PacketArray, UINT NumberOfPackets)
{
UINT i;
if (ctx == NULL)
{
return FALSE;
}
if (ctx->Halting != FALSE || ctx->Opened == FALSE)
{
// Finishing
for (i = 0;i < NumberOfPackets;i++)
{
NDIS_SET_PACKET_STATUS(PacketArray[i], NDIS_STATUS_FAILURE);
if (g_is_win8)
{
NdisMSendComplete(ctx->NdisMiniport, PacketArray[i], NDIS_STATUS_SUCCESS);
}
ctx->Status.NumPacketSendError++;
}
return FALSE;
}
return TRUE;
}
// Initialize the packet array
void NeoInitPacketArray()
{
UINT i;
// Create a packet buffer
for (i = 0;i < NEO_MAX_PACKET_EXCHANGE;i++)
{
ctx->PacketBuffer[i] = NeoNewPacketBuffer();
// Store in the array
ctx->PacketBufferArray[i] = ctx->PacketBuffer[i]->NdisPacket;
}
}
// Release the packet array
void NeoFreePacketArray()
{
UINT i;
for (i = 0;i < NEO_MAX_PACKET_EXCHANGE;i++)
{
NeoFreePacketBuffer(ctx->PacketBuffer[i]);
ctx->PacketBuffer[i] = NULL;
ctx->PacketBufferArray[i] = NULL;
}
}
// Release the packet buffer
void NeoFreePacketBuffer(PACKET_BUFFER *p)
{
// Validate arguments
if (p == NULL)
{
return;
}
// Detach the buffer from the packet
NdisUnchainBufferAtFront(p->NdisPacket, &p->NdisBuffer);
// Release the packet
NdisFreePacket(p->NdisPacket);
// Release the packet pool
NdisFreePacketPool(p->PacketPool);
// Release the buffer
NdisFreeBuffer(p->NdisBuffer);
// Release the memory
NeoFree(p->Buf);
// Release the buffer pool
NdisFreeBufferPool(p->BufferPool);
// Release the memory
NeoFree(p);
}
// Create a packet buffer
PACKET_BUFFER *NeoNewPacketBuffer()
{
PACKET_BUFFER *p;
NDIS_STATUS ret;
// Memory allocation
p = NeoZeroMalloc(sizeof(PACKET_BUFFER));
// Memory allocation for packet
p->Buf = NeoMalloc(NEO_MAX_PACKET_SIZE);
// Allocate the buffer pool
NdisAllocateBufferPool(&ret, &p->BufferPool, 1);
// Allocate the buffer
NdisAllocateBuffer(&ret, &p->NdisBuffer, p->BufferPool, p->Buf, NEO_MAX_PACKET_SIZE);
// Secure the packet pool
NdisAllocatePacketPool(&ret, &p->PacketPool, 1, PROTOCOL_RESERVED_SIZE_IN_PACKET);
// Secure the packet
NdisAllocatePacket(&ret, &p->NdisPacket, p->PacketPool);
NDIS_SET_PACKET_HEADER_SIZE(p->NdisPacket, NEO_PACKET_HEADER_SIZE);
// Attach the buffer to the packet
NdisChainBufferAtFront(p->NdisPacket, p->NdisBuffer);
return p;
}
// Reset the event
void NeoReset(NEO_EVENT *event)
{
// Validate arguments
if (event == NULL)
{
return;
}
#ifndef WIN9X
KeResetEvent(event->event);
#else // WIN9X
if (event->win32_event != 0)
{
DWORD h = event->win32_event;
_asm mov eax, h;
VxDCall(_VWIN32_ResetWin32Event);
}
#endif // WIN9X
}
// Set the event
void NeoSet(NEO_EVENT *event)
{
// Validate arguments
if (event == NULL)
{
return;
}
#ifndef WIN9X
KeSetEvent(event->event, 0, FALSE);
#else // WIN9X
if (event->win32_event != 0)
{
DWORD h = event->win32_event;
_asm mov eax, h;
VxDCall(_VWIN32_SetWin32Event);
}
#endif // WIN9X
}
// Release the event
void NeoFreeEvent(NEO_EVENT *event)
{
// Validate arguments
if (event == NULL)
{
return;
}
#ifdef WIN9X
if (0)
{
if (event->win32_event != 0)
{
DWORD h = event->win32_event;
_asm mov eax, h;
VxDCall(_VWIN32_CloseVxDHandle);
}
}
#endif WIN9X
ZwClose(event->event_handle);
// Release the memory
NeoFree(event);
}
// Create a new event
#ifndef WIN9X
NEO_EVENT *NeoNewEvent(char *name)
{
UNICODE *unicode_name;
NEO_EVENT *event;
// Validate arguments
if (name == NULL)
{
return NULL;
}
// Convert the name to Unicode
unicode_name = NewUnicode(name);
if (unicode_name == NULL)
{
return NULL;
}
// Memory allocation
event = NeoZeroMalloc(sizeof(NEO_EVENT));
if (event == NULL)
{
FreeUnicode(unicode_name);
return NULL;
}
// Create an Event
event->event = IoCreateNotificationEvent(GetUnicode(unicode_name), &event->event_handle);
if (event->event == NULL)
{
NeoFree(event);
FreeUnicode(unicode_name);
return NULL;
}
// Initialize the event
KeInitializeEvent(event->event, NotificationEvent, FALSE);
KeClearEvent(event->event);
// Release a string
FreeUnicode(unicode_name);
return event;
}
#else // WIN9X
NEO_EVENT *NeoCreateWin9xEvent(DWORD h)
{
NEO_EVENT *event;
// Validate arguments
if (h == NULL)
{
return NULL;
}
// Memory allocation
event = NeoZeroMalloc(sizeof(NEO_EVENT));
if (event == NULL)
{
return NULL;
}
event->win32_event = h;
return event;
}
#endif // WIN9X
// Get the Unicode string
NDIS_STRING *GetUnicode(UNICODE *u)
{
// Validate arguments
if (u == NULL)
{
return NULL;
}
return &u->String;
}
// Release the Unicode strings
void FreeUnicode(UNICODE *u)
{
// Validate arguments
if (u == NULL)
{
return;
}
// Release a string
NdisFreeString(u->String);
// Release the memory
NeoFree(u);
}
// Create a new Unicode string
UNICODE *NewUnicode(char *str)
{
UNICODE *u;
// Validate arguments
if (str == NULL)
{
return NULL;
}
// Memory allocation
u = NeoZeroMalloc(sizeof(UNICODE));
if (u == NULL)
{
return NULL;
}
// String initialization
_NdisInitializeString(&u->String, str);
return u;
}
// Release the lock
void NeoFreeLock(NEO_LOCK *lock)
{
NDIS_SPIN_LOCK *spin_lock;
// Validate arguments
if (lock == NULL)
{
return;
}
spin_lock = &lock->spin_lock;
NdisFreeSpinLock(spin_lock);
// Release the memory
NeoFree(lock);
}
// Unlock
void NeoUnlock(NEO_LOCK *lock)
{
NDIS_SPIN_LOCK *spin_lock;
// Validate arguments
if (lock == NULL)
{
return;
}
spin_lock = &lock->spin_lock;
NdisReleaseSpinLock(spin_lock);
}
// Lock
void NeoLock(NEO_LOCK *lock)
{
NDIS_SPIN_LOCK *spin_lock;
// Validate arguments
if (lock == NULL)
{
return;
}
spin_lock = &lock->spin_lock;
NdisAcquireSpinLock(spin_lock);
}
// Create a new lock
NEO_LOCK *NeoNewLock()
{
NDIS_SPIN_LOCK *spin_lock;
// Memory allocation
NEO_LOCK *lock = NeoZeroMalloc(sizeof(NEO_LOCK));
if (lock == NULL)
{
return NULL;
}
// Initialize spin lock
spin_lock = &lock->spin_lock;
NdisAllocateSpinLock(spin_lock);
return lock;
}
// Memory copy
void NeoCopy(void *dst, void *src, UINT size)
{
// Validate arguments
if (dst == NULL || src == NULL || size == 0)
{
return;
}
// Copy
NdisMoveMemory(dst, src, size);
}
// Memory clear
void NeoZero(void *dst, UINT size)
{
// Validate arguments
if (dst == NULL || size == 0)
{
return;
}
// Clear
NdisZeroMemory(dst, size);
}
// Clear to zero by memory allocation
void *NeoZeroMalloc(UINT size)
{
void *p = NeoMalloc(size);
if (p == NULL)
{
// Memory allocation failure
return NULL;
}
// Clear to zero
NeoZero(p, size);
return p;
}
// Memory allocation
void *NeoMalloc(UINT size)
{
NDIS_STATUS r;
void *p;
if (size == 0)
{
size = 1;
}
// Allocate the non-paged memory
r = NdisAllocateMemoryWithTag(&p, size, 'SETH');
if (NG(r))
{
return NULL;
}
return p;
}
// Release the memory
void NeoFree(void *p)
{
// Validate arguments
if (p == NULL)
{
return;
}
// Release the memory
NdisFreeMemory(p, 0, 0);
}