181 lines
6.1 KiB
C
181 lines
6.1 KiB
C
// Network device registry. This file owns net-device registration and RX handoff to the active stack provider.
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <ntlite/ddi/net_device.h>
|
|
#include <ntlite/ddi/net_stack.h>
|
|
#include <kernel/ke_debug.h>
|
|
#include <kernel/ke_spinlock.h>
|
|
|
|
#define NET_MAX_DEVICES 8u
|
|
|
|
static NET_DEVICE* gNetDevices[NET_MAX_DEVICES];
|
|
static uint32_t gNetDeviceCount = 0;
|
|
static SPINLOCK gNetLock = SPINLOCK_INIT;
|
|
|
|
// Implements the net Make Default Name operation.
|
|
static void NetMakeDefaultName(NET_DEVICE* device, uint32_t index) {
|
|
if (!device || device->Name[0])
|
|
return;
|
|
|
|
device->Name[0] = 'e';
|
|
device->Name[1] = 't';
|
|
device->Name[2] = 'h';
|
|
device->Name[3] = (char)('0' + (index % 10u));
|
|
device->Name[4] = '\0';
|
|
}
|
|
|
|
// Implements the net Is Device Registered Locked operation.
|
|
static bool NetIsDeviceRegisteredLocked(const NET_DEVICE* device) {
|
|
for (uint32_t i = 0; i < gNetDeviceCount; ++i) {
|
|
if (gNetDevices[i] == device)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Implements the net Device Has Ethernet Address operation.
|
|
static bool NetDeviceHasEthernetAddress(const NET_DEVICE* device) {
|
|
return device && device->HardwareAddressLength == NET_ETHERNET_ADDRESS_LENGTH;
|
|
}
|
|
|
|
// Implements the net Log Hardware Address operation.
|
|
static void NetLogHardwareAddress(const NET_DEVICE* device) {
|
|
if (NetDeviceHasEthernetAddress(device)) {
|
|
DbgPrint("%02x:%02x:%02x:%02x:%02x:%02x",
|
|
device->HardwareAddress[0], device->HardwareAddress[1],
|
|
device->HardwareAddress[2], device->HardwareAddress[3],
|
|
device->HardwareAddress[4], device->HardwareAddress[5]);
|
|
} else {
|
|
DbgPrint("hwlen=%u", (unsigned)device->HardwareAddressLength);
|
|
}
|
|
}
|
|
|
|
// Implements the net Register Device operation.
|
|
__declspec(dllexport) KSTATUS NetRegisterDevice(NET_DEVICE* device) {
|
|
if (!device || !device->SendFrame)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (device->HardwareAddressLength > NET_HWADDR_MAX)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
|
if (NetIsDeviceRegisteredLocked(device)) {
|
|
KeReleaseSpinlock(&gNetLock, flags);
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
if (gNetDeviceCount >= NET_MAX_DEVICES) {
|
|
KeReleaseSpinlock(&gNetLock, flags);
|
|
return NTLSTATUS_QUEUE_FULL;
|
|
}
|
|
|
|
uint32_t index = gNetDeviceCount;
|
|
device->Index = index;
|
|
if (device->Mtu == 0)
|
|
device->Mtu = NET_MTU_ETHERNET;
|
|
memset(&device->Stats, 0, sizeof(device->Stats));
|
|
NetMakeDefaultName(device, index);
|
|
gNetDevices[index] = device;
|
|
gNetDeviceCount++;
|
|
KeReleaseSpinlock(&gNetLock, flags);
|
|
|
|
DbgPrint("NET: registered %s index=%u mac=", device->Name, device->Index);
|
|
NetLogHardwareAddress(device);
|
|
DbgPrint(" mtu=%u\n", device->Mtu);
|
|
|
|
const NET_STACK_PROVIDER* provider = NetGetStackProvider();
|
|
if (provider && provider->AttachDevice) {
|
|
KSTATUS attach = provider->AttachDevice(device);
|
|
if (!KSUCCESS(attach)) {
|
|
DbgPrint("NET: provider %s attach for %s failed: 0x%x\n",
|
|
provider->Name, device->Name, attach);
|
|
}
|
|
}
|
|
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the net Get Device Count operation.
|
|
__declspec(dllexport) uint32_t NetGetDeviceCount(void) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
|
uint32_t count = gNetDeviceCount;
|
|
KeReleaseSpinlock(&gNetLock, flags);
|
|
return count;
|
|
}
|
|
|
|
// Implements the net Get Device operation.
|
|
__declspec(dllexport) NET_DEVICE* NetGetDevice(uint32_t index) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
|
NET_DEVICE* device = (index < gNetDeviceCount) ? gNetDevices[index] : (NET_DEVICE*)0;
|
|
KeReleaseSpinlock(&gNetLock, flags);
|
|
return device;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS NetGetDeviceStats(uint32_t index,
|
|
NET_DEVICE_STATS* stats) {
|
|
if (!stats)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
NET_DEVICE* device = NetGetDevice(index);
|
|
if (!device)
|
|
return NTLSTATUS_NOT_FOUND;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
|
memcpy(stats, &device->Stats, sizeof(*stats));
|
|
KeReleaseSpinlock(&gNetLock, flags);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS NetSendFrame(uint32_t index,
|
|
const void* frame,
|
|
size_t length) {
|
|
NET_DEVICE* device = NetGetDevice(index);
|
|
if (!device || !device->SendFrame)
|
|
return NTLSTATUS_NOT_FOUND;
|
|
if (!frame || length < NET_ETHERNET_HEADER_LENGTH)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (length > (size_t)device->Mtu + NET_ETHERNET_HEADER_LENGTH)
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
|
|
KSTATUS st = device->SendFrame(device, frame, length);
|
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
|
if (KSUCCESS(st)) {
|
|
device->Stats.TxPackets++;
|
|
device->Stats.TxBytes += length;
|
|
} else {
|
|
device->Stats.TxErrors++;
|
|
}
|
|
KeReleaseSpinlock(&gNetLock, flags);
|
|
return st;
|
|
}
|
|
|
|
__declspec(dllexport) void NetReceiveFrame(NET_DEVICE* device,
|
|
const void* frame,
|
|
size_t length) {
|
|
if (!device || !frame || length < NET_ETHERNET_HEADER_LENGTH) {
|
|
if (device) {
|
|
uintptr_t dropFlags = KeAcquireSpinlock(&gNetLock);
|
|
device->Stats.RxDrops++;
|
|
KeReleaseSpinlock(&gNetLock, dropFlags);
|
|
}
|
|
return;
|
|
}
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
|
device->Stats.RxPackets++;
|
|
device->Stats.RxBytes += length;
|
|
KeReleaseSpinlock(&gNetLock, flags);
|
|
|
|
const NET_STACK_PROVIDER* provider = NetGetStackProvider();
|
|
KSTATUS rxStatus = NTLSTATUS_NOT_SUPPORTED;
|
|
if (provider && provider->ReceiveFrame)
|
|
rxStatus = provider->ReceiveFrame(device, frame, length);
|
|
|
|
if (!KSUCCESS(rxStatus)) {
|
|
uintptr_t dropFlags = KeAcquireSpinlock(&gNetLock);
|
|
device->Stats.RxDrops++;
|
|
KeReleaseSpinlock(&gNetLock, dropFlags);
|
|
}
|
|
}
|