621 lines
20 KiB
C
621 lines
20 KiB
C
// ethernet.c - tiny pre-lwIP Ethernet/ARP/IPv4/ICMP shim.
|
|
//
|
|
// Goals:
|
|
// * prove NIC RX/TX works before lwIP is integrated;
|
|
// * reply to ARP for a configured static IPv4 address;
|
|
// * reply to ICMP echo requests for that address;
|
|
// * provide a tiny NetPingIpv4() smoke-test path;
|
|
// * auto-configure QEMU's default user-mode network only when the MAC looks
|
|
// like the QEMU OUI (52:54:00), so real hardware is not assigned a bogus IP.
|
|
//
|
|
// This is not intended to grow into a full stack. When lwIP arrives, keep the
|
|
// NET_DEVICE send/receive boundary and replace this protocol code with lwIP's
|
|
// netif input path.
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include <ke_debug.h>
|
|
#include <ke_spinlock.h>
|
|
#include <ethernet.h>
|
|
|
|
#define NET_MAX_ETH_STATES 8
|
|
#define NET_ARP_CACHE_SIZE 8
|
|
|
|
#define ETH_ADDR_LEN NET_ETHERNET_ADDRESS_LENGTH
|
|
#define ETH_HDR_LEN NET_ETHERNET_HEADER_LENGTH
|
|
#define ETH_TYPE_IPV4 0x0800u
|
|
#define ETH_TYPE_ARP 0x0806u
|
|
|
|
#define ARP_HTYPE_ETHERNET 0x0001u
|
|
#define ARP_PTYPE_IPV4 0x0800u
|
|
#define ARP_OPER_REQUEST 0x0001u
|
|
#define ARP_OPER_REPLY 0x0002u
|
|
#define ARP_PACKET_LEN 28u
|
|
|
|
#define IPV4_PROTO_ICMP 1u
|
|
#define ICMP_ECHO_REPLY 0u
|
|
#define ICMP_ECHO_REQUEST 8u
|
|
|
|
#define NET_ETH_MAX_FRAME NET_ETHERNET_MAX_FRAME
|
|
#define NET_LOG_LIMIT 16u
|
|
|
|
static const uint8_t gBroadcastMac[ETH_ADDR_LEN] = {
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
|
};
|
|
|
|
typedef struct NET_ARP_ENTRY {
|
|
bool Valid;
|
|
uint32_t Ip;
|
|
uint8_t HardwareAddress[ETH_ADDR_LEN];
|
|
} NET_ARP_ENTRY;
|
|
|
|
typedef struct NET_ETH_STATE {
|
|
bool Present;
|
|
bool Ipv4Configured;
|
|
bool GatewayResolved;
|
|
uint32_t Address;
|
|
uint32_t Netmask;
|
|
uint32_t Gateway;
|
|
uint8_t GatewayHardwareAddress[ETH_ADDR_LEN];
|
|
NET_ARP_ENTRY ArpCache[NET_ARP_CACHE_SIZE];
|
|
uint32_t ArpNextReplace;
|
|
bool PendingPing;
|
|
uint32_t PendingPingTarget;
|
|
uint32_t PendingPingNextHop;
|
|
uint32_t RxFrames;
|
|
uint32_t TxFrames;
|
|
uint16_t IcmpEchoSequence;
|
|
uint32_t ArpFrames;
|
|
uint32_t Ipv4Frames;
|
|
uint32_t IcmpFrames;
|
|
} NET_ETH_STATE;
|
|
|
|
static NET_ETH_STATE gEthStates[NET_MAX_ETH_STATES];
|
|
static SPINLOCK gEthLock = SPINLOCK_INIT;
|
|
|
|
static uint16_t NetLoadBe16(const uint8_t* p) {
|
|
return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
|
|
}
|
|
|
|
static uint32_t NetLoadBe32(const uint8_t* p) {
|
|
return ((uint32_t)p[0] << 24) |
|
|
((uint32_t)p[1] << 16) |
|
|
((uint32_t)p[2] << 8) |
|
|
(uint32_t)p[3];
|
|
}
|
|
|
|
static void NetStoreBe16(uint8_t* p, uint16_t v) {
|
|
p[0] = (uint8_t)(v >> 8);
|
|
p[1] = (uint8_t)v;
|
|
}
|
|
|
|
static void NetStoreBe32(uint8_t* p, uint32_t v) {
|
|
p[0] = (uint8_t)(v >> 24);
|
|
p[1] = (uint8_t)(v >> 16);
|
|
p[2] = (uint8_t)(v >> 8);
|
|
p[3] = (uint8_t)v;
|
|
}
|
|
|
|
static void NetPrintIpv4(uint32_t ip) {
|
|
DbgPrint("%u.%u.%u.%u",
|
|
(unsigned)((ip >> 24) & 0xFFu),
|
|
(unsigned)((ip >> 16) & 0xFFu),
|
|
(unsigned)((ip >> 8) & 0xFFu),
|
|
(unsigned)(ip & 0xFFu));
|
|
}
|
|
|
|
static void NetPrintMac(const uint8_t* mac) {
|
|
DbgPrint("%02x:%02x:%02x:%02x:%02x:%02x",
|
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
}
|
|
|
|
static bool NetHasEthernetAddress(const NET_DEVICE* device) {
|
|
return device && device->HardwareAddressLength == ETH_ADDR_LEN;
|
|
}
|
|
|
|
static bool NetIsQemuMac(const NET_DEVICE* device) {
|
|
return NetHasEthernetAddress(device) &&
|
|
device->HardwareAddress[0] == 0x52 &&
|
|
device->HardwareAddress[1] == 0x54 &&
|
|
device->HardwareAddress[2] == 0x00;
|
|
}
|
|
|
|
static bool NetIsBroadcastMac(const uint8_t* mac) {
|
|
return mac && memcmp(mac, gBroadcastMac, ETH_ADDR_LEN) == 0;
|
|
}
|
|
|
|
static bool NetIsMulticastMac(const uint8_t* mac) {
|
|
return mac && (mac[0] & 0x01u) != 0;
|
|
}
|
|
|
|
static bool NetEthernetFrameIsForDevice(const NET_DEVICE* device, const uint8_t* frame) {
|
|
if (!NetHasEthernetAddress(device) || !frame)
|
|
return false;
|
|
return memcmp(frame, device->HardwareAddress, ETH_ADDR_LEN) == 0 ||
|
|
NetIsBroadcastMac(frame) ||
|
|
NetIsMulticastMac(frame);
|
|
}
|
|
|
|
static bool NetIpv4SameSubnet(uint32_t a, uint32_t b, uint32_t mask) {
|
|
return ((a ^ b) & mask) == 0;
|
|
}
|
|
|
|
static NET_ETH_STATE* NetGetEthState(NET_DEVICE* device) {
|
|
if (!device || device->Index >= NET_MAX_ETH_STATES)
|
|
return (NET_ETH_STATE*)0;
|
|
return &gEthStates[device->Index];
|
|
}
|
|
|
|
static uint16_t NetChecksum(const uint8_t* data, size_t length) {
|
|
uint32_t sum = 0;
|
|
|
|
while (length >= 2) {
|
|
sum += NetLoadBe16(data);
|
|
data += 2;
|
|
length -= 2;
|
|
}
|
|
|
|
if (length)
|
|
sum += (uint16_t)((uint16_t)data[0] << 8);
|
|
|
|
while (sum >> 16)
|
|
sum = (sum & 0xFFFFu) + (sum >> 16);
|
|
|
|
return (uint16_t)~sum;
|
|
}
|
|
|
|
static void NetBuildEthernetHeader(uint8_t* frame,
|
|
const uint8_t* dst,
|
|
const uint8_t* src,
|
|
uint16_t etherType) {
|
|
memcpy(frame, dst, ETH_ADDR_LEN);
|
|
memcpy(frame + ETH_ADDR_LEN, src, ETH_ADDR_LEN);
|
|
NetStoreBe16(frame + 12, etherType);
|
|
}
|
|
|
|
static bool NetArpLookup(NET_ETH_STATE* state, uint32_t ip, uint8_t* macOut) {
|
|
if (!state)
|
|
return false;
|
|
|
|
for (unsigned i = 0; i < NET_ARP_CACHE_SIZE; ++i) {
|
|
if (state->ArpCache[i].Valid && state->ArpCache[i].Ip == ip) {
|
|
if (macOut)
|
|
memcpy(macOut, state->ArpCache[i].HardwareAddress, ETH_ADDR_LEN);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void NetArpRemember(NET_ETH_STATE* state, uint32_t ip, const uint8_t* mac) {
|
|
if (!state || !mac || ip == 0)
|
|
return;
|
|
|
|
for (unsigned i = 0; i < NET_ARP_CACHE_SIZE; ++i) {
|
|
if (state->ArpCache[i].Valid && state->ArpCache[i].Ip == ip) {
|
|
memcpy(state->ArpCache[i].HardwareAddress, mac, ETH_ADDR_LEN);
|
|
return;
|
|
}
|
|
}
|
|
|
|
unsigned slot = state->ArpNextReplace % NET_ARP_CACHE_SIZE;
|
|
state->ArpNextReplace = (state->ArpNextReplace + 1) % NET_ARP_CACHE_SIZE;
|
|
state->ArpCache[slot].Valid = true;
|
|
state->ArpCache[slot].Ip = ip;
|
|
memcpy(state->ArpCache[slot].HardwareAddress, mac, ETH_ADDR_LEN);
|
|
}
|
|
|
|
static KSTATUS NetSendArp(NET_DEVICE* device,
|
|
uint16_t operation,
|
|
const uint8_t* targetMac,
|
|
uint32_t targetIp) {
|
|
NET_ETH_STATE* state = NetGetEthState(device);
|
|
if (!state || !state->Ipv4Configured)
|
|
return STATUS_NOT_FOUND;
|
|
if (!NetHasEthernetAddress(device))
|
|
return STATUS_INVALID_PARAMETER;
|
|
if (operation == ARP_OPER_REPLY && !targetMac)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
uint8_t frame[ETH_HDR_LEN + ARP_PACKET_LEN];
|
|
const uint8_t* ethDst = (operation == ARP_OPER_REQUEST) ? gBroadcastMac : targetMac;
|
|
NetBuildEthernetHeader(frame, ethDst, device->HardwareAddress, ETH_TYPE_ARP);
|
|
|
|
uint8_t* arp = frame + ETH_HDR_LEN;
|
|
NetStoreBe16(arp + 0, ARP_HTYPE_ETHERNET);
|
|
NetStoreBe16(arp + 2, ARP_PTYPE_IPV4);
|
|
arp[4] = ETH_ADDR_LEN;
|
|
arp[5] = 4;
|
|
NetStoreBe16(arp + 6, operation);
|
|
memcpy(arp + 8, device->HardwareAddress, ETH_ADDR_LEN);
|
|
NetStoreBe32(arp + 14, state->Address);
|
|
|
|
if (operation == ARP_OPER_REQUEST)
|
|
memset(arp + 18, 0, ETH_ADDR_LEN);
|
|
else
|
|
memcpy(arp + 18, targetMac, ETH_ADDR_LEN);
|
|
NetStoreBe32(arp + 24, targetIp);
|
|
|
|
KSTATUS st = NetSendFrame(device->Index, frame, sizeof(frame));
|
|
if (KSUCCESS(st))
|
|
state->TxFrames++;
|
|
return st;
|
|
}
|
|
|
|
static void NetSendArpRequest(NET_DEVICE* device, uint32_t targetIp) {
|
|
DbgPrint("NET: %s tx ARP who-has ", device->Name);
|
|
NetPrintIpv4(targetIp);
|
|
DbgPrint("\n");
|
|
|
|
KSTATUS st = NetSendArp(device, ARP_OPER_REQUEST, gBroadcastMac, targetIp);
|
|
if (!KSUCCESS(st))
|
|
DbgPrint("NET: %s ARP probe failed: 0x%x\n", device->Name, st);
|
|
}
|
|
|
|
static KSTATUS NetSendIcmpEchoRequestResolved(NET_DEVICE* device,
|
|
NET_ETH_STATE* state,
|
|
uint32_t targetIp,
|
|
const uint8_t* nextHopMac) {
|
|
if (!device || !state || !state->Ipv4Configured || !nextHopMac)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
enum {
|
|
IPV4_HEADER_LEN = 20,
|
|
ICMP_HEADER_LEN = 8,
|
|
ICMP_PAYLOAD_LEN = 32,
|
|
ICMP_TOTAL_LEN = ICMP_HEADER_LEN + ICMP_PAYLOAD_LEN,
|
|
IPV4_TOTAL_LEN = IPV4_HEADER_LEN + ICMP_TOTAL_LEN,
|
|
FRAME_LEN = ETH_HDR_LEN + IPV4_TOTAL_LEN
|
|
};
|
|
|
|
uint8_t frame[FRAME_LEN];
|
|
NetBuildEthernetHeader(frame,
|
|
nextHopMac,
|
|
device->HardwareAddress,
|
|
ETH_TYPE_IPV4);
|
|
|
|
uint8_t* ip = frame + ETH_HDR_LEN;
|
|
memset(ip, 0, IPV4_TOTAL_LEN);
|
|
ip[0] = 0x45; // IPv4, 20-byte header.
|
|
ip[1] = 0; // DSCP/ECN.
|
|
NetStoreBe16(ip + 2, IPV4_TOTAL_LEN);
|
|
NetStoreBe16(ip + 4, state->IcmpEchoSequence);
|
|
NetStoreBe16(ip + 6, 0x4000); // Don't fragment.
|
|
ip[8] = 64; // TTL.
|
|
ip[9] = IPV4_PROTO_ICMP;
|
|
NetStoreBe32(ip + 12, state->Address);
|
|
NetStoreBe32(ip + 16, targetIp);
|
|
NetStoreBe16(ip + 10, NetChecksum(ip, IPV4_HEADER_LEN));
|
|
|
|
uint8_t* icmp = ip + IPV4_HEADER_LEN;
|
|
icmp[0] = ICMP_ECHO_REQUEST;
|
|
icmp[1] = 0;
|
|
NetStoreBe16(icmp + 4, 0x4b4e); // temporary pre-lwIP ping ID: 'KN'.
|
|
NetStoreBe16(icmp + 6, state->IcmpEchoSequence);
|
|
for (unsigned i = 0; i < ICMP_PAYLOAD_LEN; ++i)
|
|
icmp[ICMP_HEADER_LEN + i] = (uint8_t)('A' + (i % 26));
|
|
NetStoreBe16(icmp + 2, NetChecksum(icmp, ICMP_TOTAL_LEN));
|
|
|
|
DbgPrint("NET: %s tx ICMP echo request to ", device->Name);
|
|
NetPrintIpv4(targetIp);
|
|
DbgPrint(" seq=%u\n", (unsigned)state->IcmpEchoSequence);
|
|
|
|
KSTATUS st = NetSendFrame(device->Index, frame, FRAME_LEN);
|
|
if (KSUCCESS(st)) {
|
|
state->TxFrames++;
|
|
state->IcmpEchoSequence++;
|
|
} else {
|
|
DbgPrint("NET: %s ICMP echo request failed: 0x%x\n", device->Name, st);
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
static KSTATUS NetPingIpv4OnDevice(NET_DEVICE* device,
|
|
NET_ETH_STATE* state,
|
|
uint32_t targetIp) {
|
|
if (!device || !state || !state->Ipv4Configured || targetIp == 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t nextHop = targetIp;
|
|
if (!NetIpv4SameSubnet(state->Address, targetIp, state->Netmask)) {
|
|
if (state->Gateway == 0)
|
|
return STATUS_NOT_FOUND;
|
|
nextHop = state->Gateway;
|
|
}
|
|
|
|
uint8_t nextHopMac[ETH_ADDR_LEN];
|
|
if (NetArpLookup(state, nextHop, nextHopMac))
|
|
return NetSendIcmpEchoRequestResolved(device, state, targetIp, nextHopMac);
|
|
|
|
state->PendingPing = true;
|
|
state->PendingPingTarget = targetIp;
|
|
state->PendingPingNextHop = nextHop;
|
|
|
|
DbgPrint("NET: %s ping ", device->Name);
|
|
NetPrintIpv4(targetIp);
|
|
DbgPrint(" waiting for ARP ");
|
|
NetPrintIpv4(nextHop);
|
|
DbgPrint("\n");
|
|
|
|
NetSendArpRequest(device, nextHop);
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
static void NetHandleArp(NET_DEVICE* device, NET_ETH_STATE* state,
|
|
const uint8_t* frame, size_t length) {
|
|
if (length < ETH_HDR_LEN + ARP_PACKET_LEN)
|
|
return;
|
|
|
|
const uint8_t* arp = frame + ETH_HDR_LEN;
|
|
if (NetLoadBe16(arp + 0) != ARP_HTYPE_ETHERNET ||
|
|
NetLoadBe16(arp + 2) != ARP_PTYPE_IPV4 ||
|
|
arp[4] != ETH_ADDR_LEN || arp[5] != 4)
|
|
return;
|
|
|
|
uint16_t op = NetLoadBe16(arp + 6);
|
|
const uint8_t* senderMac = arp + 8;
|
|
uint32_t senderIp = NetLoadBe32(arp + 14);
|
|
uint32_t targetIp = NetLoadBe32(arp + 24);
|
|
state->ArpFrames++;
|
|
|
|
if (state->ArpFrames <= NET_LOG_LIMIT) {
|
|
DbgPrint("NET: %s rx ARP %s ",
|
|
device->Name,
|
|
op == ARP_OPER_REQUEST ? "request" :
|
|
op == ARP_OPER_REPLY ? "reply" : "op");
|
|
NetPrintIpv4(senderIp);
|
|
DbgPrint(" ");
|
|
NetPrintMac(senderMac);
|
|
DbgPrint(" -> ");
|
|
NetPrintIpv4(targetIp);
|
|
DbgPrint("\n");
|
|
}
|
|
|
|
if ((op == ARP_OPER_REQUEST || op == ARP_OPER_REPLY) && senderIp != 0)
|
|
NetArpRemember(state, senderIp, senderMac);
|
|
|
|
if (op == ARP_OPER_REPLY && state->Ipv4Configured && senderIp == state->Gateway) {
|
|
memcpy(state->GatewayHardwareAddress, senderMac, ETH_ADDR_LEN);
|
|
state->GatewayResolved = true;
|
|
DbgPrint("NET: %s gateway resolved ", device->Name);
|
|
NetPrintIpv4(senderIp);
|
|
DbgPrint(" = ");
|
|
NetPrintMac(senderMac);
|
|
DbgPrint("\n");
|
|
}
|
|
|
|
if (op == ARP_OPER_REPLY && state->PendingPing && senderIp == state->PendingPingNextHop) {
|
|
uint32_t pingTarget = state->PendingPingTarget;
|
|
state->PendingPing = false;
|
|
(void)NetSendIcmpEchoRequestResolved(device, state, pingTarget, senderMac);
|
|
}
|
|
|
|
if (op == ARP_OPER_REQUEST && state->Ipv4Configured && targetIp == state->Address) {
|
|
KSTATUS st = NetSendArp(device, ARP_OPER_REPLY, senderMac, senderIp);
|
|
if (KSUCCESS(st)) {
|
|
DbgPrint("NET: %s tx ARP reply for ", device->Name);
|
|
NetPrintIpv4(state->Address);
|
|
DbgPrint("\n");
|
|
} else {
|
|
DbgPrint("NET: %s ARP reply failed: 0x%x\n", device->Name, st);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void NetHandleIcmpEcho(NET_DEVICE* device, NET_ETH_STATE* state,
|
|
const uint8_t* frame, size_t length,
|
|
const uint8_t* ip, size_t ipHeaderLength,
|
|
size_t totalLength) {
|
|
(void)length;
|
|
|
|
if (totalLength < ipHeaderLength + 8)
|
|
return;
|
|
|
|
const uint8_t* icmp = ip + ipHeaderLength;
|
|
size_t icmpLength = totalLength - ipHeaderLength;
|
|
if (NetChecksum(icmp, icmpLength) != 0)
|
|
return;
|
|
if (icmp[0] != ICMP_ECHO_REQUEST || icmp[1] != 0)
|
|
return;
|
|
|
|
if (ETH_HDR_LEN + totalLength > NET_ETH_MAX_FRAME)
|
|
return;
|
|
|
|
uint8_t out[NET_ETH_MAX_FRAME];
|
|
NetBuildEthernetHeader(out, frame + 6, device->HardwareAddress, ETH_TYPE_IPV4);
|
|
memcpy(out + ETH_HDR_LEN, ip, totalLength);
|
|
|
|
uint8_t* outIp = out + ETH_HDR_LEN;
|
|
uint8_t* outIcmp = outIp + ipHeaderLength;
|
|
|
|
// Swap IPv4 endpoints and rebuild the IPv4 header checksum.
|
|
memcpy(outIp + 12, ip + 16, 4);
|
|
memcpy(outIp + 16, ip + 12, 4);
|
|
outIp[8] = 64;
|
|
outIp[10] = 0;
|
|
outIp[11] = 0;
|
|
NetStoreBe16(outIp + 10, NetChecksum(outIp, ipHeaderLength));
|
|
|
|
// Convert echo request to echo reply and rebuild the ICMP checksum.
|
|
outIcmp[0] = ICMP_ECHO_REPLY;
|
|
outIcmp[2] = 0;
|
|
outIcmp[3] = 0;
|
|
NetStoreBe16(outIcmp + 2, NetChecksum(outIcmp, icmpLength));
|
|
|
|
KSTATUS st = NetSendFrame(device->Index, out, ETH_HDR_LEN + totalLength);
|
|
if (KSUCCESS(st)) {
|
|
state->TxFrames++;
|
|
if (state->IcmpFrames <= NET_LOG_LIMIT) {
|
|
DbgPrint("NET: %s tx ICMP echo reply to ", device->Name);
|
|
NetPrintIpv4(NetLoadBe32(ip + 12));
|
|
DbgPrint("\n");
|
|
}
|
|
} else {
|
|
DbgPrint("NET: %s ICMP reply failed: 0x%x\n", device->Name, st);
|
|
}
|
|
}
|
|
|
|
static void NetHandleIpv4(NET_DEVICE* device, NET_ETH_STATE* state,
|
|
const uint8_t* frame, size_t length) {
|
|
if (!state->Ipv4Configured || length < ETH_HDR_LEN + 20)
|
|
return;
|
|
|
|
const uint8_t* ip = frame + ETH_HDR_LEN;
|
|
uint8_t version = (uint8_t)(ip[0] >> 4);
|
|
size_t ipHeaderLength = (size_t)(ip[0] & 0x0Fu) * 4u;
|
|
if (version != 4 || ipHeaderLength < 20 || length < ETH_HDR_LEN + ipHeaderLength)
|
|
return;
|
|
|
|
size_t totalLength = NetLoadBe16(ip + 2);
|
|
if (totalLength < ipHeaderLength || length < ETH_HDR_LEN + totalLength)
|
|
return;
|
|
|
|
if (NetChecksum(ip, ipHeaderLength) != 0)
|
|
return;
|
|
|
|
uint16_t fragment = NetLoadBe16(ip + 6);
|
|
if (fragment & 0x3FFFu)
|
|
return;
|
|
|
|
uint32_t dstIp = NetLoadBe32(ip + 16);
|
|
if (dstIp != state->Address)
|
|
return;
|
|
|
|
state->Ipv4Frames++;
|
|
if (ip[9] == IPV4_PROTO_ICMP) {
|
|
if (totalLength < ipHeaderLength + 8)
|
|
return;
|
|
|
|
const uint8_t* icmp = ip + ipHeaderLength;
|
|
size_t icmpLength = totalLength - ipHeaderLength;
|
|
if (NetChecksum(icmp, icmpLength) != 0)
|
|
return;
|
|
|
|
state->IcmpFrames++;
|
|
|
|
if (icmp[0] == ICMP_ECHO_REPLY && icmp[1] == 0) {
|
|
DbgPrint("NET: %s rx ICMP echo reply from ", device->Name);
|
|
NetPrintIpv4(NetLoadBe32(ip + 12));
|
|
DbgPrint(" seq=%u\n", (unsigned)NetLoadBe16(icmp + 6));
|
|
return;
|
|
}
|
|
|
|
if (state->IcmpFrames <= NET_LOG_LIMIT) {
|
|
DbgPrint("NET: %s rx ICMP type=%u code=%u from ",
|
|
device->Name,
|
|
(unsigned)icmp[0],
|
|
(unsigned)icmp[1]);
|
|
NetPrintIpv4(NetLoadBe32(ip + 12));
|
|
DbgPrint("\n");
|
|
}
|
|
|
|
NetHandleIcmpEcho(device, state, frame, length, ip, ipHeaderLength, totalLength);
|
|
}
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS NetConfigureIpv4Static(uint32_t deviceIndex,
|
|
uint32_t address,
|
|
uint32_t netmask,
|
|
uint32_t gateway) {
|
|
if (deviceIndex >= NET_MAX_ETH_STATES || address == 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
NET_DEVICE* device = NetGetDevice(deviceIndex);
|
|
if (!device)
|
|
return STATUS_NOT_FOUND;
|
|
if (!NetHasEthernetAddress(device))
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gEthLock);
|
|
NET_ETH_STATE* state = &gEthStates[deviceIndex];
|
|
state->Ipv4Configured = true;
|
|
state->GatewayResolved = false;
|
|
state->PendingPing = false;
|
|
state->IcmpEchoSequence = 1;
|
|
state->Address = address;
|
|
state->Netmask = netmask;
|
|
state->Gateway = gateway;
|
|
state->ArpNextReplace = 0;
|
|
memset(state->GatewayHardwareAddress, 0, sizeof(state->GatewayHardwareAddress));
|
|
memset(state->ArpCache, 0, sizeof(state->ArpCache));
|
|
KeReleaseSpinlock(&gEthLock, flags);
|
|
|
|
DbgPrint("NET: eth%u IPv4 ", deviceIndex);
|
|
NetPrintIpv4(address);
|
|
DbgPrint(" mask ");
|
|
NetPrintIpv4(netmask);
|
|
DbgPrint(" gw ");
|
|
NetPrintIpv4(gateway);
|
|
DbgPrint("\n");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS NetPingIpv4(uint32_t deviceIndex, uint32_t targetIp) {
|
|
NET_DEVICE* device = NetGetDevice(deviceIndex);
|
|
if (!device || deviceIndex >= NET_MAX_ETH_STATES)
|
|
return STATUS_NOT_FOUND;
|
|
|
|
NET_ETH_STATE* state = &gEthStates[deviceIndex];
|
|
if (!state->Present || !state->Ipv4Configured)
|
|
return STATUS_NOT_FOUND;
|
|
|
|
return NetPingIpv4OnDevice(device, state, targetIp);
|
|
}
|
|
|
|
void NetEthernetAttachDevice(NET_DEVICE* device) {
|
|
if (!device || device->Index >= NET_MAX_ETH_STATES)
|
|
return;
|
|
if (!NetHasEthernetAddress(device)) {
|
|
DbgPrint("NET: %s has unsupported hardware address length %u; Ethernet shim disabled\n",
|
|
device->Name, (unsigned)device->HardwareAddressLength);
|
|
return;
|
|
}
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gEthLock);
|
|
NET_ETH_STATE* state = &gEthStates[device->Index];
|
|
memset(state, 0, sizeof(*state));
|
|
state->Present = true;
|
|
KeReleaseSpinlock(&gEthLock, flags);
|
|
|
|
if (NetIsQemuMac(device)) {
|
|
NetConfigureIpv4Static(device->Index,
|
|
NET_IPV4_ADDR(10, 0, 2, 15),
|
|
NET_IPV4_ADDR(255, 255, 255, 0),
|
|
NET_IPV4_ADDR(10, 0, 2, 2));
|
|
state = &gEthStates[device->Index];
|
|
(void)NetPingIpv4OnDevice(device, state, state->Gateway);
|
|
} else {
|
|
DbgPrint("NET: %s has no IPv4 address yet; ARP/ICMP replies disabled\n",
|
|
device->Name);
|
|
}
|
|
}
|
|
|
|
void NetEthernetReceive(NET_DEVICE* device, const void* frame, size_t length) {
|
|
NET_ETH_STATE* state = NetGetEthState(device);
|
|
if (!state || !state->Present || !frame || length < ETH_HDR_LEN)
|
|
return;
|
|
|
|
const uint8_t* b = (const uint8_t*)frame;
|
|
if (!NetEthernetFrameIsForDevice(device, b))
|
|
return;
|
|
|
|
uint16_t etherType = NetLoadBe16(b + 12);
|
|
state->RxFrames++;
|
|
|
|
switch (etherType) {
|
|
case ETH_TYPE_ARP:
|
|
NetHandleArp(device, state, b, length);
|
|
break;
|
|
case ETH_TYPE_IPV4:
|
|
NetHandleIpv4(device, state, b, length);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|