essence-os/kernel/networking.cpp

2172 lines
71 KiB
C++

// This file is part of the Essence operating system.
// It is released under the terms of the MIT license -- see LICENSE.md.
// Written by: nakst.
// TODO Event-based API for userland.
// TODO Locking: In NetTCPReceive, lock on getting task so it can't be destroyed by NetConnectionDestroy.
// TODO Limiting the size of the ARP table; LRU.
// TODO Sending ARP requests not working in VBox.
// TODO Domain name resolve button doesn't work in the test program.
// TODO Resolved domain name cache.
// TODO UDP and TCP: checking packets are received from the correct NetInterface, MAC and IP.
// TODO UDP and TCP: checking source port matches expected value on received packets.
// TODO UDP and TCP (and possibly others): lock in the NetTask callback when processing a received packet,
// to allow for a NetInterface to have multiple dispatcher threads.
// TODO TCP: merging ACK responses.
// TODO TCP: high performance extensions.
// TODO TCP: reducing duplication of non-reply code.
// TODO Cancelling tasks after losing connection; retrying tasks; timeout tasks.
// TODO Retrying the NetAddressSetupTask if it completes with error.
// TODO Handling more ICMP messages.
// TODO Sending ICMP messages on certain bad packets.
// TODO IPv6.
// TODO Check receive buffer is treated as empty when read pointer == write pointer.
// TODO Make most log messages LOG_VERBOSE.
#ifndef IMPLEMENTATION
struct EthernetHeader {
KMACAddress destinationMAC;
KMACAddress sourceMAC;
uint16_t type;
} ES_STRUCT_PACKED;
#define ETHERNET_HEADER(ethernet, _type, _destinationMAC) \
if (ethernet) { \
ethernet->destinationMAC = _destinationMAC; \
ethernet->sourceMAC = interface->macAddress; \
ethernet->type = SwapBigEndian16(_type); \
}
#define ETHERNET_TYPE_IPV4 (0x0800)
#define ETHERNET_TYPE_ARP (0x0806)
struct IPHeader {
uint8_t versionAndLength;
uint8_t serviceType;
uint16_t totalLength;
uint16_t identification;
uint16_t flagsAndFragmentOffset;
uint8_t timeToLive;
uint8_t protocol;
uint16_t headerChecksum;
KIPAddress sourceAddress;
KIPAddress destinationAddress;
uint16_t CalculateHeaderChecksum() const {
const uint8_t *in = (const uint8_t *) this;
uint32_t sum = 0;
for (uintptr_t i = 0; i < 20; i += 2) {
if (i == 10) continue;
sum += ((uint16_t) in[i] << 8) + (uint16_t) in[i + 1];
}
while (sum > 0xFFFF) {
sum = (sum >> 16) + (sum & 0xFFFF);
}
return SwapBigEndian16(~sum);
}
} ES_STRUCT_PACKED;
#define IP_HEADER(ip, _destinationAddress, _protocol) \
if (ip) { \
ip->versionAndLength = (4 << 4) | (5 << 0); /* We're using IPv4 with a 5 DWORD header. */ \
ip->identification = ByteSwap16(++interface->ipIdentification); \
ip->timeToLive = 64; /* Live for at most 64 seconds. */ \
ip->protocol = _protocol; \
ip->sourceAddress = interface->ipAddress; \
ip->destinationAddress = _destinationAddress; \
}
#define IP_PROTOCOL_ICMP (1)
#define IP_PROTOCOL_TCP (6)
#define IP_PROTOCOL_UDP (17)
struct UDPHeader {
uint16_t sourcePort;
uint16_t destinationPort;
uint16_t length;
uint16_t checksum;
uint16_t CalculateChecksum() const {
const IPHeader *ipHeader = (const IPHeader *) this - 1;
struct {
KIPAddress sourceAddress;
KIPAddress destinationAddress;
uint8_t zero;
uint8_t protocol;
uint16_t udpLength;
} pseudoHeader = {
.sourceAddress = ipHeader->sourceAddress,
.destinationAddress = ipHeader->destinationAddress,
.zero = 0,
.protocol = ipHeader->protocol,
.udpLength = length,
};
const uint8_t *in = (const uint8_t *) this;
const uint8_t *in2 = (const uint8_t *) &pseudoHeader;
const uint8_t *data = (const uint8_t *) (this + 1);
uint32_t sum = 0;
for (uintptr_t i = 0; i < 8; i += 2) {
if (i == 6) continue;
sum += ((uint16_t) in[i] << 8) + (uint16_t) in[i + 1];
}
for (uintptr_t i = 0; i < 12; i += 2) {
sum += ((uint16_t) in2[i] << 8) + (uint16_t) in2[i + 1];
}
uintptr_t dataBytes = ByteSwap16(length) - 8;
for (uintptr_t i = 0; i < dataBytes; i += 2) {
if (i + 1 == dataBytes) {
sum += (uint16_t) data[i] << 8;
} else {
sum += ((uint16_t) data[i] << 8) + (uint16_t) data[i + 1];
}
}
while (sum > 0xFFFF) {
sum = (sum >> 16) + (sum & 0xFFFF);
}
return (sum == 0xFFFF) ? 0xFFFF : SwapBigEndian16(~sum);
}
} ES_STRUCT_PACKED;
#define UDP_HEADER(udp, _sourcePort, _destinationPort) \
if (udp) { \
udp->sourcePort = SwapBigEndian16(_sourcePort); \
udp->destinationPort = SwapBigEndian16(_destinationPort); \
}
#define UDP_PORT_BASE (49152)
struct TCPHeader {
uint16_t sourcePort;
uint16_t destinationPort;
uint32_t sequenceNumber;
uint32_t ackNumber;
uint16_t flags;
uint16_t window;
uint16_t checksum;
uint16_t urgentPointer;
uint16_t CalculateChecksum(uint16_t length) const {
const IPHeader *ipHeader = (const IPHeader *) this - 1;
struct {
KIPAddress sourceAddress;
KIPAddress destinationAddress;
uint8_t zero;
uint8_t protocol;
uint16_t tcpLength;
} pseudoHeader = {
.sourceAddress = ipHeader->sourceAddress,
.destinationAddress = ipHeader->destinationAddress,
.zero = 0,
.protocol = ipHeader->protocol,
.tcpLength = ByteSwap16(length),
};
const uint8_t *in = (const uint8_t *) this;
const uint8_t *in2 = (const uint8_t *) &pseudoHeader;
uint32_t sum = 0;
for (uintptr_t i = 0; i < length; i += 2) {
if (i == 16) {
// Checksum field; skip.
} else if (i + 1 == length) {
sum += (uint16_t) in[i] << 8;
} else {
sum += ((uint16_t) in[i] << 8) + (uint16_t) in[i + 1];
}
}
for (uintptr_t i = 0; i < 12; i += 2) {
sum += ((uint16_t) in2[i] << 8) + (uint16_t) in2[i + 1];
}
while (sum > 0xFFFF) {
sum = (sum >> 16) + (sum & 0xFFFF);
}
return SwapBigEndian16(~sum);
}
} ES_STRUCT_PACKED;
struct TCPReceivedData {
uint16_t flags;
uint16_t segmentLength;
uint32_t ackNumber;
uint32_t sequenceNumber;
const EthernetHeader *ethernet;
const IPHeader *ip;
const TCPHeader *tcp;
const void *segment;
};
// NOTE Keep these in order!
// We've sent a packet asking to start a connection. We're expecting the server to ACK it with a matching request.
#define TCP_STEP_SYN_SENT (1)
// We've received a packet asking to start a connection. We've sent a matching request back. We're waiting for that to be ACK'd.
#define TCP_STEP_SYN_RECEIVED (2)
// We're in normal data communication.
#define TCP_STEP_ESTABLISHED (3)
// Closing steps:
#define TCP_STEP_FIN_WAIT_1 (4)
#define TCP_STEP_FIN_WAIT_2 (5)
#define TCP_STEP_CLOSE_WAIT (6)
#define TCP_STEP_CLOSING (7)
#define TCP_STEP_LAST_ACK (8)
#define TCP_FIN (1 << 0)
#define TCP_SYN (1 << 1)
#define TCP_RST (1 << 2)
#define TCP_PSH (1 << 3)
#define TCP_ACK (1 << 4)
#define TCP_PORT_BASE (49152)
#define TCP_PREPARE_REPLY(_data1, _data1Bytes, _data2, _data2Bytes) \
EthernetHeader *ethernetReply = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader)); \
ETHERNET_HEADER(ethernetReply, ETHERNET_TYPE_IPV4, data->ethernet->sourceMAC); \
IPHeader *ipReply = (IPHeader *) buffer.Write(nullptr, sizeof(IPHeader)); \
IP_HEADER(ipReply, data->ip->sourceAddress, IP_PROTOCOL_TCP); \
TCPHeader *tcpReply = (TCPHeader *) buffer.Write(nullptr, sizeof(TCPHeader)); \
buffer.Write(_data1, _data1Bytes); \
buffer.Write(_data2, _data2Bytes); \
\
if (buffer.error) { \
KernelPanic("TCP_PREPARE_REPLY - Network interface buffer size too small.\n"); \
} \
\
ipReply->totalLength = ByteSwap16(buffer.position - sizeof(*ethernetReply)); \
ipReply->flagsAndFragmentOffset = SwapBigEndian16(1 << 14 /* do not fragment */); \
ipReply->headerChecksum = ipReply->CalculateHeaderChecksum(); \
\
tcpReply->sourcePort = data->tcp->destinationPort; \
tcpReply->destinationPort = data->tcp->sourcePort;
#define TCP_PREPARE_REPLY_2(_data1, _data1Bytes, _data2, _data2Bytes, _flags, _headerDWORDs) \
TCP_PREPARE_REPLY(_data1, _data1Bytes, _data2, _data2Bytes); \
tcpReply->flags = SwapBigEndian16((_flags) | ((_headerDWORDs) << 12 /* header is 5 DWORDs */)); \
tcpReply->sequenceNumber = SwapBigEndian32(task->sendNext); \
tcpReply->ackNumber = SwapBigEndian32(task->receiveNext); \
tcpReply->window = SwapBigEndian16(task->receiveWindow); \
#define TCP_MAKE_STANDARD_REPLY(_flags) \
{ \
EsBuffer buffer = NetTransmitBufferGet(); \
if (buffer.error) { NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES); return; } \
TCP_PREPARE_REPLY_2(nullptr, 0, nullptr, 0, _flags, 5); \
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES); \
}
struct DHCPHeader {
uint8_t opCode;
uint8_t hardwareAddressType;
uint8_t hardwareAddressLength;
uint8_t hops;
uint32_t transactionID;
uint16_t seconds;
uint16_t flags;
KIPAddress clientIPAddress;
KIPAddress yourIPAddress;
KIPAddress nextServerIPAddress;
KIPAddress relayAgentIPAddress;
uint8_t clientHardwareAddress[16];
uint8_t serverHostName[64];
uint8_t bootFileName[128];
uint8_t optionsMagic[4];
} ES_STRUCT_PACKED;
#define DHCP_HEADER(dhcp, _opCode) \
if (dhcp) { \
dhcp->opCode = _opCode; \
dhcp->hardwareAddressType = 1; /* Ethernet. */ \
dhcp->hardwareAddressLength = 6; \
dhcp->transactionID = (uint32_t) EsRandomU64(); \
EsMemoryCopy(&dhcp->clientHardwareAddress, &interface->macAddress, sizeof(KMACAddress)); \
dhcp->optionsMagic[0] = 99; /* Magic cookie. */ \
dhcp->optionsMagic[1] = 130; \
dhcp->optionsMagic[2] = 83; \
dhcp->optionsMagic[3] = 99; \
}
#define DHCP_START() \
EthernetHeader *ethernet = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader)); \
ETHERNET_HEADER(ethernet, ETHERNET_TYPE_IPV4, broadcastMAC); \
IPHeader *ip = (IPHeader *) buffer.Write(nullptr, sizeof(IPHeader)); \
IP_HEADER(ip, broadcastIP, IP_PROTOCOL_UDP); \
UDPHeader *udp = (UDPHeader *) buffer.Write(nullptr, sizeof(UDPHeader)); \
UDP_HEADER(udp, 68, 67); \
DHCPHeader *dhcp = (DHCPHeader *) buffer.Write(nullptr, sizeof(DHCPHeader)); \
DHCP_HEADER(dhcp, DHCP_BOOTREQUEST);
#define DHCP_END() \
if (!buffer.error) { \
ip->totalLength = ByteSwap16(buffer.position - sizeof(*ethernet)); \
udp->length = ByteSwap16(buffer.position - sizeof(*ethernet) - sizeof(*ip)); \
}
#define DHCP_OPTION_MESSAGE_TYPE(x) 53, 1, (x)
#define DHCP_OPTION_REQUESTED_IP(x) 50, 4, (x).d[0], (x).d[1], (x).d[2], (x).d[3]
#define DHCP_OPTION_SERVER_IDENTIFIER(x) 54, 4, (x).d[0], (x).d[1], (x).d[2], (x).d[3]
#define DHCPDISCOVER (1)
#define DHCPOFFER (2)
#define DHCPREQUEST (3)
#define DHCPACK (5)
#define DHCPNAK (6)
#define DHCPRELEASE (7)
#define DHCP_BOOTREQUEST (1)
#define DHCP_BOOTREPLY (2)
struct ARPHeader {
uint16_t hardwareAddressSpace;
uint16_t protocolAddressSpace;
uint8_t hardwareAddressLength;
uint8_t protocolAddressLength;
uint16_t opCode;
} ES_STRUCT_PACKED;
#define ARP_ETHERNET (0x0001)
#define ARP_IPV4 (0x0800)
#define ARP_REQUEST (1)
#define ARP_REPLY (2)
struct ARPEntry {
KIPAddress ip;
KMACAddress mac;
};
struct ARPRequest {
KIPAddress ip;
NetTask *task;
};
struct ICMPHeader {
uint8_t type;
uint8_t code;
uint16_t checksum;
uint16_t CalculateChecksum(size_t dataBytes) const {
const uint8_t *in = (const uint8_t *) this;
uint32_t sum = 0;
for (uintptr_t i = 0; i < 4 + dataBytes; i += 2) {
if (i == 2) continue;
sum += ((uint16_t) in[i] << 8) + (uint16_t) in[i + 1];
}
while (sum > 0xFFFF) {
sum = (sum >> 16) + (sum & 0xFFFF);
}
return SwapBigEndian16(~sum);
}
} ES_STRUCT_PACKED;
struct DNSHeader {
uint16_t identifier;
uint16_t flags;
uint16_t questionCount;
uint16_t answerCount;
uint16_t authorityCount;
uint16_t additionalCount;
} ES_STRUCT_PACKED;
struct Networking {
KMutex interfacesListMutex;
SimpleList interfaces;
#define MAX_UDP_TASKS (1024)
NetTask *udpTasks[MAX_UDP_TASKS];
KMutex udpTaskBitsetMutex;
Bitset udpTaskBitset;
#define MAX_TCP_TASKS (1024)
uintptr_t tcpTasks[MAX_TCP_TASKS]; // If (1 << 0) set, task is in use.
uint16_t tcpTaskLRU, tcpTaskMRU;
KMutex tcpTaskListMutex;
KMutex echoRequestTaskMutex;
NetTask *echoRequestTask;
KMutex transmitBufferPoolMutex;
Arena transmitBufferPool;
};
struct NetDomainNameResolveTask : NetTask {
const char *name;
size_t nameBytes;
EsAddress *address;
uint16_t identifier;
KEvent *event;
};
struct NetEchoRequestTask : NetTask {
uint8_t *data;
EsAddress *address;
KEvent *event;
};
struct NetTCPConnectionTask : NetTask {
uint32_t sendUnacknowledged; // Points at the end of the data the server has acknowledged receiving from us.
uint32_t sendNext; // Points at the end of data we've sent.
uint32_t sendWindow; // The maximum distance sendNext can be past sendUnacknowledged.
uint32_t receiveNext; // Points at the end of data we've acknowledged receiving from the server.
uint16_t receiveWindow; // The maximum distance the server can sent data past receiveNext.
uint32_t initialSend, initialReceive;
uint32_t finSequence;
uint32_t sendWL1;
uint32_t sendWL2;
KMACAddress destinationMAC;
};
struct NetConnection {
NetTCPConnectionTask task;
MMSharedRegion *bufferRegion;
uint8_t *sendBuffer;
uint8_t *receiveBuffer;
size_t sendBufferBytes;
size_t receiveBufferBytes;
uintptr_t sendReadPointer; // The end of the data that we've sent to the server (possibly unacknolwedged).
uintptr_t sendWritePointer; // The end of the data that the application has written for us to send.
uintptr_t receiveWritePointer; // The end of the data that we've received from the server with no missing segments.
uintptr_t receiveReadPointer; // The end of the data that the user has processed from the receive buffer.
RangeSet receivedData;
EsAddress address;
KMutex mutex;
volatile uintptr_t handles;
};
void NetDomainNameResolve(NetTask *_task, void *data);
void NetEchoRequest(NetTask *_task, void *data);
void NetTCPConnection(NetTask *_task, void *data);
void NetAddressSetup(NetTask *_task, void *data);
NetConnection *NetConnectionOpen(EsAddress *address, size_t sendBufferBytes, size_t receiveBufferBytes, uint32_t flags);
void NetConnectionClose(NetConnection *connection);
void NetConnectionNotify(NetConnection *connection, uintptr_t sendWritePointer, uintptr_t receiveReadPointer);
void NetConnectionDestroy(NetConnection *connection);
extern Networking networking;
#else
Networking networking;
const KIPAddress broadcastIP = { 255, 255, 255, 255 };
const KMACAddress broadcastMAC = { 255, 255, 255, 255, 255, 255 };
void NetPrintPacket(const char *cName, const void *packet, size_t bytes) {
EsPrint("%z packet: ", cName);
for (uintptr_t i = 0; i < bytes; i++) {
EsPrint("%X ", ((uint8_t *) packet)[i]);
}
EsPrint("\n");
}
EsBuffer NetTransmitBufferGet() {
KMutexAcquire(&networking.transmitBufferPoolMutex);
EsBuffer buffer = {};
buffer.out = (uint8_t *) ArenaAllocate(&networking.transmitBufferPool, false);
buffer.bytes = networking.transmitBufferPool.slotSize;
if (!buffer.out) {
buffer.error = true, buffer.bytes = 0;
KernelLog(LOG_ERROR, "Networking", "out of memory", "Could not allocate a transmit buffer.\n");
}
KMutexRelease(&networking.transmitBufferPoolMutex);
return buffer;
}
void NetTransmitBufferReturn(void *data) {
KMutexAcquire(&networking.transmitBufferPoolMutex);
ArenaFree(&networking.transmitBufferPool, data);
KMutexRelease(&networking.transmitBufferPoolMutex);
}
bool NetTransmit(NetInterface *interface, EsBuffer *buffer, NetPacketType packetType) {
if (buffer->error) {
KernelPanic("NetTransmit - Trying to transmit a write buffer with errors.\n");
}
if (packetType == NET_PACKET_ETHERNET) {
if (buffer->position < 64) {
buffer->Write(nullptr, 64 - buffer->position);
}
EthernetHeader *ethernet = (EthernetHeader *) buffer->out;
if (ethernet->type == SwapBigEndian16(ETHERNET_TYPE_IPV4)) {
IPHeader *ip = (IPHeader *) (ethernet + 1);
if (ip->protocol == IP_PROTOCOL_UDP) {
UDPHeader *udp = (UDPHeader *) (ip + 1);
udp->checksum = udp->CalculateChecksum();
} else if (ip->protocol == IP_PROTOCOL_TCP) {
TCPHeader *tcp = (TCPHeader *) (ip + 1);
tcp->checksum = tcp->CalculateChecksum(ByteSwap16(ip->totalLength) - sizeof(*ip));
} else if (ip->protocol == IP_PROTOCOL_ICMP) {
ICMPHeader *icmp = (ICMPHeader *) (ip + 1);
icmp->checksum = icmp->CalculateChecksum(ByteSwap16(ip->totalLength) - sizeof(*ip));
}
ip->headerChecksum = ip->CalculateHeaderChecksum();
}
}
uintptr_t address = (uintptr_t) buffer->out;
uintptr_t physical = (address & (K_PAGE_SIZE - 1)) + MMArchTranslateAddress(kernelMMSpace, address);
if (!interface->transmit(interface, buffer->out, physical, buffer->position)) {
NetTransmitBufferReturn(buffer->out);
return false;
}
return true;
}
bool NetARPLookup(NetTask *task, KIPAddress targetIP, KMACAddress *targetMAC) {
NetInterface *interface = task->interface;
KWriterLockTake(&interface->arpTableLock, K_LOCK_SHARED);
for (uintptr_t i = 0; i < interface->arpTable.Length(); i++) {
if (0 == EsMemoryCompare(&interface->arpTable[i].ip, &targetIP, sizeof(KIPAddress))) {
*targetMAC = interface->arpTable[i].mac;
KWriterLockReturn(&interface->arpTableLock, K_LOCK_SHARED);
return true;
}
}
KWriterLockReturn(&interface->arpTableLock, K_LOCK_SHARED);
KernelLog(LOG_INFO, "Networking", "send ARP", "Sending ARP to find MAC address of IP %d.%d.%d.%d.\n",
targetIP.d[0], targetIP.d[1], targetIP.d[2], targetIP.d[3]);
// TODO Prevent sending multiple requests for a given MAC address at the same time.
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return false;
}
EthernetHeader *ethernet = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader));
ETHERNET_HEADER(ethernet, ETHERNET_TYPE_ARP, broadcastMAC);
ARPHeader *arp = (ARPHeader *) buffer.Write(nullptr, sizeof(ARPHeader));
if (arp) {
arp->hardwareAddressSpace = SwapBigEndian16(ARP_ETHERNET);
arp->protocolAddressSpace = SwapBigEndian16(ARP_IPV4);
arp->hardwareAddressLength = 6;
arp->protocolAddressLength = 4;
arp->opCode = SwapBigEndian16(ARP_REQUEST);
}
buffer.Write(&interface->macAddress, sizeof(KMACAddress));
buffer.Write(&interface->ipAddress, sizeof(KIPAddress));
buffer.Write(nullptr, sizeof(KMACAddress));
buffer.Write(&targetIP, sizeof(KIPAddress));
if (buffer.error) {
KernelPanic("NetARPLookup - Network interface buffer size too small.\n");
}
KWriterLockTake(&interface->arpTableLock, K_LOCK_EXCLUSIVE);
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
} else {
ARPRequest request = { targetIP, task };
interface->arpRequests.Add(request);
}
KWriterLockReturn(&interface->arpTableLock, K_LOCK_EXCLUSIVE);
return false;
}
void NetARPReceive(NetInterface *interface, EsBuffer *buffer) {
const ARPHeader *arp = (const ARPHeader *) buffer->Read(sizeof(ARPHeader));
if (!arp) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing ARP header.\n");
return;
}
if (SwapBigEndian16(arp->hardwareAddressSpace) != ARP_ETHERNET
|| SwapBigEndian16(arp->protocolAddressSpace) != ARP_IPV4) {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "ARP packet has unrecognised address space(s).\n");
return;
}
const KMACAddress *senderMAC = (const KMACAddress *) buffer->Read(sizeof(KMACAddress));
const KIPAddress *senderIP = (const KIPAddress *) buffer->Read(sizeof(KIPAddress));
const KMACAddress *targetMAC = (const KMACAddress *) buffer->Read(sizeof(KMACAddress));
const KIPAddress *targetIP = (const KIPAddress *) buffer->Read(sizeof(KIPAddress));
if (!senderMAC || !senderIP || !targetMAC || !targetIP) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "ARP packet too short.\n");
return;
}
KernelLog(LOG_INFO, "Networking", "received ARP packet",
"Received ARP packet. Sender: %d.%d.%d.%d (%X:%X:%X:%X:%X:%X). Destination: %d.%d.%d.%d (%X:%X:%X:%X:%X:%X). Op code %d.\n",
senderIP->d[0], senderIP->d[1], senderIP->d[2], senderIP->d[3],
senderMAC->d[0], senderMAC->d[1], senderMAC->d[2], senderMAC->d[3], senderMAC->d[4], senderMAC->d[5],
targetIP->d[0], targetIP->d[1], targetIP->d[2], targetIP->d[3],
targetMAC->d[0], targetMAC->d[1], targetMAC->d[2], targetMAC->d[3], targetMAC->d[4], targetMAC->d[5],
SwapBigEndian16(arp->opCode));
bool merged = false;
Array<NetTask *, K_FIXED> completedRequests = {};
KWriterLockTake(&interface->arpTableLock, K_LOCK_EXCLUSIVE);
for (uintptr_t i = 0; i < interface->arpTable.Length(); i++) {
if (0 == EsMemoryCompare(&interface->arpTable[i].ip, senderIP, sizeof(KIPAddress))) {
interface->arpTable[i].mac = *senderMAC;
merged = true;
}
}
if (interface->hasIP && 0 == EsMemoryCompare(targetIP, &interface->ipAddress, sizeof(KIPAddress))) {
if (!merged) {
ARPEntry entry = {};
entry.ip = *senderIP;
entry.mac = *senderMAC;
if (!interface->arpTable.Add(entry)) {
KernelLog(LOG_ERROR, "Networking", "allocation error", "Could not add entry to ARP table.\n");
}
for (uintptr_t i = 0; i < interface->arpRequests.Length(); i++) {
if (0 == EsMemoryCompare(&interface->arpRequests[i].ip, &entry.ip, sizeof(KIPAddress))) {
completedRequests.Add(interface->arpRequests[i].task);
interface->arpRequests.DeleteSwap(i);
i--;
}
}
}
if (SwapBigEndian16(arp->opCode) == ARP_REQUEST) {
// Reply with our MAC address.
EsBuffer buffer = NetTransmitBufferGet();
if (!buffer.error) {
EthernetHeader *ethernet = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader));
ETHERNET_HEADER(ethernet, ETHERNET_TYPE_ARP, broadcastMAC);
ARPHeader *arpReply = (ARPHeader *) buffer.Write(arp, sizeof(ARPHeader));
buffer.Write(&interface->macAddress, sizeof(KMACAddress));
buffer.Write(targetIP, sizeof(KIPAddress));
buffer.Write(senderMAC, sizeof(KMACAddress));
buffer.Write(senderIP, sizeof(KIPAddress));
if (buffer.error) {
KernelPanic("NetARPReceive - Network interface buffer size too small.\n");
} else {
arpReply->opCode = ARP_REPLY;
NetTransmit(interface, &buffer, NET_PACKET_ETHERNET); // Don't care about errors.
}
}
} else if (SwapBigEndian16(arp->opCode) == ARP_REPLY) {
// Don't need to do anything.
} else {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Unrecognised ARP op code %d.\n", SwapBigEndian16(arp->opCode));
}
} else {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "ARP packet not destined for us.\n");
}
KWriterLockReturn(&interface->arpTableLock, K_LOCK_EXCLUSIVE);
for (uintptr_t i = 0; i < completedRequests.Length(); i++) {
completedRequests[i]->callback(completedRequests[i], nullptr);
}
completedRequests.Free();
}
void NetICMPReceive(NetInterface *interface, EsBuffer *buffer, const IPHeader *ip, const EthernetHeader *ethernet) {
const ICMPHeader *icmp = (const ICMPHeader *) buffer->Read(sizeof(ICMPHeader));
if (!icmp) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing ICMP header.\n");
return;
}
if (icmp->checksum != icmp->CalculateChecksum(buffer->bytes - buffer->position)) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Incorrect ICMP checksum.\n");
return;
}
if (icmp->type == 8 /* echo request */) {
// Send echo reply.
// TODO Test this.
EsBuffer bufferReply = NetTransmitBufferGet();
if (bufferReply.error) return;
EthernetHeader *ethernetReply = (EthernetHeader *) bufferReply.Write(nullptr, sizeof(EthernetHeader));
ETHERNET_HEADER(ethernetReply, ETHERNET_TYPE_IPV4, ethernet->sourceMAC);
IPHeader *ipReply = (IPHeader *) bufferReply.Write(nullptr, sizeof(IPHeader));
IP_HEADER(ipReply, ip->sourceAddress, IP_PROTOCOL_ICMP);
ICMPHeader *icmpReply = (ICMPHeader *) bufferReply.Write(icmp, sizeof(ICMPHeader));
bufferReply.Write(icmp + 1, buffer->bytes - buffer->position);
if (bufferReply.error) {
KernelPanic("NetICMPReceive - Network interface buffer size too small.\n");
} else {
icmpReply->type = 0; // Echo reply.
ipReply->totalLength = ByteSwap16(bufferReply.position - sizeof(*ethernetReply));
NetTransmit(interface, &bufferReply, NET_PACKET_ETHERNET); // Don't care about errors.
}
} else if (icmp->type == 0 /* echo reply */) {
if (networking.echoRequestTask) {
networking.echoRequestTask->callback(networking.echoRequestTask, buffer);
} else {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Unexpected ICMP echo reply.\n");
}
}
}
void NetTCPReceive(NetInterface *interface, EsBuffer *buffer, const IPHeader *ip, const EthernetHeader *ethernet) {
// Validate the TCP header.
size_t tcpPosition = buffer->position;
const TCPHeader *tcp = (const TCPHeader *) buffer->Read(sizeof(TCPHeader));
if (!tcp) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing TCP header.\n");
return;
}
uint16_t flags = SwapBigEndian16(tcp->flags);
uint32_t headerDWORDs = flags >> 12;
if (headerDWORDs < 5) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "TCP header DWORDs was less than 5.\n");
return;
}
if (tcp->CalculateChecksum(buffer->bytes - tcpPosition) != tcp->checksum) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "TCP header has incorrect checksum.\n");
return;
}
if (!buffer->Read((headerDWORDs - 5) * sizeof(uint32_t))) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "TCP header is shorter than expected.\n");
return;
}
uint32_t segmentLength = buffer->bytes - buffer->position;
// If a task exists at the destination port, send it the segment.
// TODO Locking on getting the task.
NetTCPConnectionTask *task = nullptr;
uint16_t destinationPort = SwapBigEndian16(tcp->destinationPort);
if (destinationPort >= TCP_PORT_BASE
&& destinationPort < TCP_PORT_BASE + MAX_TCP_TASKS
&& (networking.tcpTasks[destinationPort - TCP_PORT_BASE] & 1)) {
task = (NetTCPConnectionTask *) (networking.tcpTasks[destinationPort - TCP_PORT_BASE] & ~1);
OpenHandleToObject(task, KERNEL_OBJECT_CONNECTION);
}
TCPReceivedData _data = {};
_data.ethernet = ethernet;
_data.ip = ip;
_data.tcp = tcp;
_data.flags = flags;
_data.segmentLength = segmentLength;
_data.segment = buffer->Read(segmentLength);
_data.sequenceNumber = SwapBigEndian32(tcp->sequenceNumber);
_data.ackNumber = SwapBigEndian32(tcp->ackNumber);
TCPReceivedData *data = &_data;
if (task) {
task->callback(task, data);
CloseHandleToObject(task, KERNEL_OBJECT_CONNECTION);
return;
}
// The destination port is closed.
// If the segment does not have the RST flag, then we should send a RST segment in response.
if (~flags & (1 << 2 /* RST */)) {
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) {
return; // Ignore.
}
TCP_PREPARE_REPLY(nullptr, 0, nullptr, 0);
tcpReply->sourcePort = tcp->destinationPort;
tcpReply->destinationPort = tcp->sourcePort;
tcpReply->flags = SwapBigEndian16(TCP_RST | ~(flags & TCP_ACK) | (5 << 12 /* header is 5 DWORDs */));
tcpReply->sequenceNumber = (flags & TCP_ACK) ? tcp->ackNumber : 0;
tcpReply->ackNumber = (flags & TCP_ACK) ? 0 : SwapBigEndian32(SwapBigEndian32(tcp->sequenceNumber) + segmentLength);
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
}
}
}
void NetUDPReceive(NetInterface *interface, EsBuffer *buffer) {
const UDPHeader *udp = (const UDPHeader *) buffer->Read(sizeof(UDPHeader));
if (!udp) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing UDP header.\n");
return;
}
if (!interface->hasIP && udp->destinationPort != SwapBigEndian16(68)) {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Only expecting DHCP packets until IP address accepted.\n");
return;
}
if (SwapBigEndian16(udp->length) > buffer->bytes - buffer->position + sizeof(UDPHeader)) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "UDP length was longer than remaining buffer size.\n");
return;
}
if (udp->CalculateChecksum() != udp->checksum) { // NOTE Don't compute the checksum until the length field has been validated!
KernelLog(LOG_ERROR, "Networking", "bad packet", "Incorrect checksum in UDP header.\n");
return;
}
uint16_t destinationPort = SwapBigEndian16(udp->destinationPort);
if (destinationPort == 68 /* DHCP */) {
interface->addressSetupTask.callback(&interface->addressSetupTask, buffer);
} else if (destinationPort >= UDP_PORT_BASE
&& destinationPort < UDP_PORT_BASE + MAX_UDP_TASKS
&& !networking.udpTaskBitset.Read(destinationPort - UDP_PORT_BASE)) {
NetTask *task = networking.udpTasks[destinationPort - UDP_PORT_BASE];
task->callback(task, buffer);
} else {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Unknown destination UDP port %d.\n", udp->destinationPort);
}
}
void NetIPReceive(NetInterface *interface, EsBuffer *buffer, const EthernetHeader *ethernet) {
const IPHeader *ip = (const IPHeader *) buffer->Read(sizeof(IPHeader));
if (!ip) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing IP header.\n");
return;
}
if ((ip->versionAndLength >> 4) != 4) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Unsupported IP version %d.\n", ip->versionAndLength >> 4);
return;
}
size_t headerLength = (ip->versionAndLength & 0xF) * 4;
if (headerLength < 20) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "IP header too short.\n");
return;
}
uint16_t totalLength = SwapBigEndian16(ip->totalLength);
if (totalLength > buffer->bytes - buffer->position + sizeof(IPHeader)) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "IP header total length was longer than remaining buffer size.\n");
return;
}
buffer->bytes = buffer->position + totalLength - sizeof(IPHeader);
if (totalLength < headerLength) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "IP header total length was less than header length.\n");
return;
}
if (interface->hasIP && EsMemoryCompare(&interface->ipAddress, &ip->destinationAddress, 4)
&& EsMemoryCompare(&broadcastIP, &ip->destinationAddress, 4)) {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Destination IP address mismatch (%d.%d.%d.%d).\n",
ip->destinationAddress.d[0], ip->destinationAddress.d[1], ip->destinationAddress.d[2], ip->destinationAddress.d[3]);
return;
}
if (ip->CalculateHeaderChecksum() != ip->headerChecksum) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Incorrect checksum in IP header.\n");
return;
}
buffer->Read(headerLength - 20);
if (!interface->hasIP && ip->protocol != IP_PROTOCOL_UDP) {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Only expecting DHCP packets over UDP until IP address accepted.\n");
return;
}
if (ip->protocol == IP_PROTOCOL_ICMP) {
NetICMPReceive(interface, buffer, ip, ethernet);
} else if (ip->protocol == IP_PROTOCOL_TCP) {
NetTCPReceive(interface, buffer, ip, ethernet);
} else if (ip->protocol == IP_PROTOCOL_UDP) {
NetUDPReceive(interface, buffer);
} else {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Unrecognised IP protocol type %d.\n", ip->protocol);
}
}
void NetEthernetReceive(NetInterface *interface, EsBuffer *buffer) {
const EthernetHeader *ethernet = (const EthernetHeader *) buffer->Read(sizeof(EthernetHeader));
if (!ethernet) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing ethernet header.\n");
return;
}
if (EsMemoryCompare(&ethernet->destinationMAC, &interface->macAddress, sizeof(KMACAddress))
&& EsMemoryCompare(&ethernet->destinationMAC, &broadcastMAC, sizeof(KMACAddress))) {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Destination MAC address mismatch.\n");
return;
}
if (SwapBigEndian16(ethernet->type) == ETHERNET_TYPE_IPV4) {
NetIPReceive(interface, buffer, ethernet);
} else if (SwapBigEndian16(ethernet->type) == ETHERNET_TYPE_ARP) {
NetARPReceive(interface, buffer);
} else {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Unrecognised ethernet packet type %x.\n", SwapBigEndian16(ethernet->type));
}
}
void NetInterfaceReceive(NetInterface *interface, const uint8_t *data, size_t dataBytes, NetPacketType packetType) {
KWriterLockTake(&interface->connectionLock, K_LOCK_SHARED);
EsBuffer buffer = { .in = data, .bytes = dataBytes };
if (!interface->connected) {
KernelLog(LOG_ERROR, "Networking", "packet while disconnected", "Interface %x is disconnected.\n", interface);
} else if (packetType == NET_PACKET_ETHERNET) {
NetEthernetReceive(interface, &buffer);
} else {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Unsupported packet type %d.\n", packetType);
}
KWriterLockReturn(&interface->connectionLock, K_LOCK_SHARED);
if (interface->addressSetupTask.changedState) {
interface->addressSetupTask.changedState = false;
KWriterLockTake(&interface->connectionLock, K_LOCK_EXCLUSIVE);
interface->hasIP = interface->addressSetupTask.error == ES_SUCCESS;
if (!interface->hasIP) {
// TODO Report connection lost and cancel in progress tasks.
// TODO Retry the addressSetupTask.
KernelLog(LOG_INFO, "Networking", "interface disconnected", "NetInterfaceReceive - Interface %x has lost IP address.\n", interface);
}
KWriterLockReturn(&interface->connectionLock, K_LOCK_EXCLUSIVE);
}
}
void NetInterfaceShutdown(NetInterface *interface) {
if (!interface->hasIP) {
return;
}
// Release our IP address.
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) return;
DHCP_START();
uint8_t dhcpOptions[] = {
DHCP_OPTION_MESSAGE_TYPE(DHCPRELEASE),
DHCP_OPTION_SERVER_IDENTIFIER(interface->serverIdentifier),
255, // End of options.
};
buffer.Write(dhcpOptions, sizeof(dhcpOptions));
DHCP_END();
if (buffer.error) {
KernelPanic("NetInterface::ReceiveDHCPReply - Network interface buffer size too small.\n");
}
NetTransmit(interface, &buffer, NET_PACKET_ETHERNET); // Don't care about errors.
}
void NetInterfaceSetConnected(NetInterface *interface, bool connected) {
if (interface->connected == connected) {
return;
}
KWriterLockTake(&interface->connectionLock, K_LOCK_EXCLUSIVE);
interface->connected = connected;
if (!connected) {
// TODO Report connection lost and cancel in progress tasks.
KernelLog(LOG_INFO, "Networking", "interface disconnected", "NetInterface::SetConnected - Interface %x has disconnected.\n", interface);
interface->hasIP = false;
interface->arpTable.SetLength(0);
interface->arpRequests.SetLength(0);
} else {
KernelLog(LOG_INFO, "Networking", "interface connected", "NetInterface::SetConnected - Interface %x has connected. Requesting an IP address...\n", interface);
}
KWriterLockReturn(&interface->connectionLock, K_LOCK_EXCLUSIVE);
if (connected) {
NetTaskBegin(&interface->addressSetupTask);
}
}
void NetDomainNameResolve(NetTask *_task, void *_buffer) {
EsBuffer *buffer = (EsBuffer *) _buffer;
NetDomainNameResolveTask *task = (NetDomainNameResolveTask *) _task;
NetInterface *interface = task->interface;
if (task->completed) {
if (task->index != 0xFFFF) {
KMutexAcquire(&networking.udpTaskBitsetMutex);
networking.udpTaskBitset.Put(task->index);
KMutexRelease(&networking.udpTaskBitsetMutex);
}
if (task->event) {
// This must be the last thing we do, otherwise the NetTask might be freed.
KEventSet(task->event);
}
} else if (task->step == 0) {
KMACAddress dnsServerMAC;
if (!NetARPLookup(task, interface->dnsServerIP, &dnsServerMAC)) {
return;
}
KMutexAcquire(&networking.udpTaskBitsetMutex);
ptrdiff_t taskIndex = networking.udpTaskBitset.Get();
if (taskIndex == -1) {
KMutexRelease(&networking.udpTaskBitsetMutex);
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return;
}
networking.udpTasks[taskIndex] = task;
task->index = taskIndex;
KMutexRelease(&networking.udpTaskBitsetMutex);
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return;
}
EthernetHeader *ethernet = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader));
ETHERNET_HEADER(ethernet, ETHERNET_TYPE_IPV4, dnsServerMAC);
IPHeader *ip = (IPHeader *) buffer.Write(nullptr, sizeof(IPHeader));
IP_HEADER(ip, interface->dnsServerIP, IP_PROTOCOL_UDP);
UDPHeader *udp = (UDPHeader *) buffer.Write(nullptr, sizeof(UDPHeader));
UDP_HEADER(udp, taskIndex + UDP_PORT_BASE, 53 /* DNS server */);
DNSHeader *dns = (DNSHeader *) buffer.Write(nullptr, sizeof(DNSHeader));
dns->identifier = task->identifier = EsRandomU64();
dns->flags = SwapBigEndian16(1 << 8 /* recursion desired */);
dns->questionCount = SwapBigEndian16(1);
for (uintptr_t i = 0, j = 0; i <= task->nameBytes; i++) {
if (i == task->nameBytes || task->name[i] == '.') {
uint8_t bytes = i - j;
buffer.Write(&bytes, 1);
buffer.Write(task->name + j, bytes);
j = i + 1;
}
}
{
uint8_t zero = 0;
buffer.Write(&zero, 1);
uint16_t queryType = SwapBigEndian16(1 /* A - IPv4 address */);
buffer.Write(&queryType, 2);
uint16_t queryClass = SwapBigEndian16(1 /* IN - the internet */);
buffer.Write(&queryClass, 2);
}
if (buffer.error) {
KernelPanic("NetInterface::DomainNameResolve - Network interface buffer size too small.\n");
}
ip->totalLength = ByteSwap16(buffer.position - sizeof(*ethernet));
udp->length = ByteSwap16(buffer.position - sizeof(*ethernet) - sizeof(*ip));
task->step++;
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return;
}
} else if (task->step == 1) {
const DNSHeader *header = (const DNSHeader *) buffer->Read(sizeof(DNSHeader));
if (!header) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing DNS header.\n");
return;
}
if (header->identifier != task->identifier) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Received DNS packet with wrong identifier.\n");
return;
}
uint16_t flags = SwapBigEndian16(header->flags);
if (~flags & (1 << 15)) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Received DNS request (expecting reponse).\n");
return;
}
EsError error = ES_ERROR_UNKNOWN;
if ((flags & 15) == 3) {
error = ES_ERROR_NO_ADDRESS_FOR_DOMAIN_NAME;
} else if ((flags & 15) == 0) {
error = ES_SUCCESS;
}
for (uintptr_t i = 0; i < SwapBigEndian16(header->questionCount); i++) {
while (true) {
const uint8_t *length = (const uint8_t *) buffer->Read(1);
if (!length) break;
if ((*length & 0xC0) == 0xC0) {
buffer->Read(1);
break;
} else if (*length == 0) {
break;
}
buffer->Read(*length);
}
buffer->Read(4);
}
bool foundAddress = false;
for (uintptr_t i = 0; i < SwapBigEndian16(header->answerCount); i++) {
while (true) {
const uint8_t *length = (const uint8_t *) buffer->Read(1);
if (!length) break;
if ((*length & 0xC0) == 0xC0) {
buffer->Read(1);
break;
} else if (*length == 0) {
break;
}
buffer->Read(*length);
}
const uint16_t *type = (const uint16_t *) buffer->Read(2);
const uint16_t *classType = (const uint16_t *) buffer->Read(2);
const uint32_t *timeToLive = (const uint32_t *) buffer->Read(4);
const uint16_t *dataLength = (const uint16_t *) buffer->Read(2);
if (!type || !classType || !timeToLive || !dataLength) {
break;
}
const void *data = (const void *) buffer->Read(SwapBigEndian16(*dataLength));
if (!data) {
break;
}
if (SwapBigEndian16(*type) == 1 /* A - IPv4 address */
&& SwapBigEndian16(*classType) == 1 /* IN - the internet */) {
if (SwapBigEndian16(*dataLength) != 4) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "IPv4 address was not 4 bytes.\n");
return;
}
EsMemoryCopy(&task->address->ipv4, data, 4);
foundAddress = true;
break;
}
}
if (buffer->error) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing data after DNS header.\n");
return;
}
if (!foundAddress) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Could not find IP address in DNS packet.\n");
error = ES_ERROR_UNKNOWN;
}
NetTaskComplete(task, error);
} else {
KernelPanic("NetDomainNameResolve - Invalid step.\n");
}
}
void NetEchoRequest(NetTask *_task, void *_buffer) {
EsBuffer *buffer = (EsBuffer *) _buffer;
NetEchoRequestTask *task = (NetEchoRequestTask *) _task;
NetInterface *interface = task->interface;
if (task->completed) {
KMutexAcquire(&networking.echoRequestTaskMutex);
if (networking.echoRequestTask == task) {
networking.echoRequestTask = nullptr;
}
KMutexRelease(&networking.echoRequestTaskMutex);
if (task->event) {
// This must be the last thing we do, otherwise the NetTask might be freed.
KEventSet(task->event);
}
} else if (task->step == 0) {
KMACAddress routerMAC;
if (!NetARPLookup(task, interface->routerIP, &routerMAC)) {
return;
}
KMutexAcquire(&networking.echoRequestTaskMutex);
if (networking.echoRequestTask) {
KMutexRelease(&networking.echoRequestTaskMutex);
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return;
}
networking.echoRequestTask = task;
KMutexRelease(&networking.echoRequestTaskMutex);
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return;
}
EthernetHeader *ethernet = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader));
ETHERNET_HEADER(ethernet, ETHERNET_TYPE_IPV4, routerMAC);
IPHeader *ip = (IPHeader *) buffer.Write(nullptr, sizeof(IPHeader));
KIPAddress destinationIP;
EsMemoryCopy(&destinationIP, &task->address->ipv4, sizeof(KIPAddress));
IP_HEADER(ip, destinationIP, IP_PROTOCOL_ICMP);
ICMPHeader *icmp = (ICMPHeader *) buffer.Write(nullptr, sizeof(ICMPHeader));
buffer.Write(task->data, ES_ECHO_REQUEST_MAX_LENGTH);
if (buffer.error) {
KernelPanic("NetInterface::DomainNameResolve - Network interface buffer size too small.\n");
}
icmp->type = 8; // Echo request.
ip->totalLength = ByteSwap16(buffer.position - sizeof(*ethernet));
task->step++;
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return;
}
} else if (task->step == 1) {
size_t dataBytes = buffer->bytes - buffer->position;
const uint8_t *data = (const uint8_t *) buffer->Read(dataBytes);
if (dataBytes > ES_ECHO_REQUEST_MAX_LENGTH) {
dataBytes = ES_ECHO_REQUEST_MAX_LENGTH;
}
EsMemoryZero(task->data, ES_ECHO_REQUEST_MAX_LENGTH);
EsMemoryCopy(task->data, data, dataBytes);
NetTaskComplete(task, ES_SUCCESS);
} else {
KernelPanic("NetEchoRequest - Invalid step.\n");
}
}
bool NetTCPIsBetween(uint32_t low, uint32_t value, uint32_t high) {
if (low <= high) {
return low <= value && value <= high;
} else {
return high <= value && value <= low;
}
}
bool NetTCPIsLessThan(uint32_t left, uint32_t right) {
return left - right > 0x80000000;
}
bool NetTCPIsLessThanOrEqual(uint32_t left, uint32_t right) {
return left - right - 1 > 0x80000000;
}
void NetTCPFreeTaskIndex(uint16_t index, bool initialFree = false) {
if (index != 0xFFFF) {
// Free the port index.
KMutexAcquire(&networking.tcpTaskListMutex);
if ((~networking.tcpTasks[index] & 1) && !initialFree) {
KernelPanic("NetTCPFreeTaskIndex - TCP task list double-free.\n");
} else if (networking.tcpTaskLRU == 0xFFFF && networking.tcpTaskMRU == 0xFFFF) {
networking.tcpTaskLRU = index;
networking.tcpTaskMRU = index;
networking.tcpTasks[index] = 0xFFFF << 1;
} else if (networking.tcpTaskLRU == 0xFFFF || networking.tcpTaskMRU == 0xFFFF
|| networking.tcpTasks[networking.tcpTaskMRU] != (0xFFFF << 1)) {
KernelPanic("NetTCPFreeTaskIndex - Broken TCP task list.\n");
} else {
networking.tcpTasks[networking.tcpTaskMRU] = index << 1;
networking.tcpTaskMRU = index;
networking.tcpTasks[index] = 0xFFFF << 1;
}
KMutexRelease(&networking.tcpTaskListMutex);
}
}
bool NetTCPAllocateTaskIndex(NetTask *task) {
KMutexAcquire(&networking.tcpTaskListMutex);
uint16_t taskIndex = networking.tcpTaskLRU;
if (taskIndex == 0xFFFF) {
KMutexRelease(&networking.tcpTaskListMutex);
return false;
}
networking.tcpTaskLRU = networking.tcpTasks[taskIndex] >> 1;
if (networking.tcpTaskLRU == 0xFFFF) networking.tcpTaskMRU = 0xFFFF;
networking.tcpTasks[taskIndex] = (uintptr_t) task | 1;
task->index = taskIndex;
KMutexRelease(&networking.tcpTaskListMutex);
return true;
}
bool NetConnectionTransmitData(NetConnection *connection) {
// TODO Send segments as a NetTask, so that they can be retransmitted.
NetTCPConnectionTask *task = &connection->task;
NetInterface *interface = task->interface;
KWriterLockAssertShared(&interface->connectionLock);
bool sent = false;
while (connection->sendReadPointer != connection->sendWritePointer) {
uintptr_t bytesAvailable = connection->sendWritePointer - connection->sendReadPointer;
if (connection->sendReadPointer > connection->sendWritePointer) {
bytesAvailable += connection->sendBufferBytes;
}
uintptr_t maximumBytesPerSegment = 1000; // TODO Parse the maximum segment size option.
uintptr_t bytesToSendInSegment = MinimumInteger(maximumBytesPerSegment, bytesAvailable);
const void *data1 = connection->sendBuffer + connection->sendReadPointer;
const void *data2 = connection->sendBuffer;
size_t dataBytes1 = bytesToSendInSegment;
size_t dataBytes2 = 0;
if (connection->sendReadPointer + bytesToSendInSegment > connection->sendBufferBytes) {
dataBytes1 = connection->sendBufferBytes - connection->sendReadPointer;
dataBytes2 = bytesToSendInSegment - dataBytes1;
}
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return true;
}
EthernetHeader *ethernet = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader));
ETHERNET_HEADER(ethernet, ETHERNET_TYPE_IPV4, task->destinationMAC);
IPHeader *ip = (IPHeader *) buffer.Write(nullptr, sizeof(IPHeader));
IP_HEADER(ip, *(KIPAddress *) &connection->address.ipv4, IP_PROTOCOL_TCP);
TCPHeader *tcp = (TCPHeader *) buffer.Write(nullptr, sizeof(TCPHeader));
buffer.Write(data1, dataBytes1);
buffer.Write(data2, dataBytes2);
if (buffer.error) {
KernelPanic("NetConnectionTransmitData - Network interface buffer size too small.\n");
}
ip->totalLength = ByteSwap16(buffer.position - sizeof(*ethernet));
ip->flagsAndFragmentOffset = SwapBigEndian16(1 << 14 /* do not fragment */);
ip->headerChecksum = ip->CalculateHeaderChecksum();
tcp->sourcePort = SwapBigEndian16(task->index + TCP_PORT_BASE);
tcp->destinationPort = SwapBigEndian16(connection->address.port);
tcp->flags = SwapBigEndian16(TCP_ACK | (5 << 12 /* header is 5 DWORDs */));
tcp->sequenceNumber = SwapBigEndian32(task->sendNext);
tcp->ackNumber = SwapBigEndian32(task->receiveNext);
tcp->window = SwapBigEndian16(task->receiveWindow);
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return true;
}
sent = true;
connection->sendReadPointer = (connection->sendReadPointer + bytesToSendInSegment) % connection->sendBufferBytes;
task->sendNext += bytesToSendInSegment;
}
return sent;
}
bool NetConnectionUpdateReceiveWindow(NetConnection *connection) {
NetTCPConnectionTask *task = &connection->task;
uint16_t oldReceiveWindow = task->receiveWindow;
if (connection->receiveReadPointer == connection->receiveWritePointer) {
task->receiveWindow = MinimumInteger(0xF000, connection->receiveBufferBytes - 1);
} else if (connection->receiveReadPointer < connection->receiveWritePointer) {
task->receiveWindow = MinimumInteger(0xF000, connection->receiveReadPointer - connection->receiveWritePointer + connection->receiveBufferBytes);
} else {
task->receiveWindow = MinimumInteger(0xF000, connection->receiveReadPointer - connection->receiveWritePointer);
}
EsPrint("ORW: %d; RW: %d; RRP: %d; RWP: %d; RBB: %d\n", oldReceiveWindow, task->receiveWindow,
connection->receiveReadPointer, connection->receiveWritePointer, connection->receiveBufferBytes);
return oldReceiveWindow != task->receiveWindow;
}
void NetConnectionNotify(NetConnection *connection, uintptr_t sendWritePointer, uintptr_t receiveReadPointer) {
NetTCPConnectionTask *task = &connection->task;
NetInterface *interface = task->interface;
KWriterLockTake(&interface->connectionLock, K_LOCK_SHARED);
KMutexAcquire(&connection->mutex);
connection->sendWritePointer = sendWritePointer % connection->sendBufferBytes;
connection->receiveReadPointer = receiveReadPointer % connection->receiveBufferBytes;
bool receiveWindowModified = NetConnectionUpdateReceiveWindow(connection);
if (task->step == TCP_STEP_ESTABLISHED
&& !NetConnectionTransmitData(connection)
&& receiveWindowModified) {
// ACK the new window size.
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
} else {
EthernetHeader *ethernet = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader));
ETHERNET_HEADER(ethernet, ETHERNET_TYPE_IPV4, task->destinationMAC);
IPHeader *ip = (IPHeader *) buffer.Write(nullptr, sizeof(IPHeader));
IP_HEADER(ip, *(KIPAddress *) &connection->address.ipv4, IP_PROTOCOL_TCP);
TCPHeader *tcp = (TCPHeader *) buffer.Write(nullptr, sizeof(TCPHeader));
if (buffer.error) {
KernelPanic("NetConnectionNotify - Network interface buffer size too small.\n");
}
ip->totalLength = ByteSwap16(buffer.position - sizeof(*ethernet));
ip->flagsAndFragmentOffset = SwapBigEndian16(1 << 14 /* do not fragment */);
ip->headerChecksum = ip->CalculateHeaderChecksum();
tcp->sourcePort = SwapBigEndian16(task->index + TCP_PORT_BASE);
tcp->destinationPort = SwapBigEndian16(connection->address.port);
tcp->flags = SwapBigEndian16(TCP_ACK | (5 << 12 /* header is 5 DWORDs */));
tcp->sequenceNumber = SwapBigEndian32(task->sendNext);
tcp->ackNumber = SwapBigEndian32(task->receiveNext);
tcp->window = SwapBigEndian16(task->receiveWindow);
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
}
}
}
KMutexRelease(&connection->mutex);
KWriterLockReturn(&interface->connectionLock, K_LOCK_SHARED);
}
void NetTCPConnection(NetTask *_task, void *_data) {
TCPReceivedData *data = (TCPReceivedData *) _data;
NetTCPConnectionTask *task = (NetTCPConnectionTask *) _task;
NetInterface *interface = task->interface;
NetConnection *connection = EsContainerOf(NetConnection, task, task);
if (task->completed) {
NetTCPFreeTaskIndex(task->index);
CloseHandleToObject(connection, KERNEL_OBJECT_CONNECTION);
return;
}
KMutexAcquire(&connection->mutex);
EsDefer(KMutexRelease(&connection->mutex));
if (task->step == 0) {
if (!NetARPLookup(task, interface->routerIP, &task->destinationMAC)) {
return;
}
if (!NetTCPAllocateTaskIndex(task)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return;
}
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return;
}
task->initialSend = (uint32_t) EsRandomU64() & 0x0FFFFFFF;
task->sendUnacknowledged = task->initialSend;
task->sendNext = task->initialSend + 1;
task->step = TCP_STEP_SYN_SENT;
EthernetHeader *ethernet = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader));
ETHERNET_HEADER(ethernet, ETHERNET_TYPE_IPV4, task->destinationMAC);
IPHeader *ip = (IPHeader *) buffer.Write(nullptr, sizeof(IPHeader));
KIPAddress destinationIP = {};
EsMemoryCopy(&destinationIP, &connection->address.ipv4, sizeof(KIPAddress));
IP_HEADER(ip, destinationIP, IP_PROTOCOL_TCP);
TCPHeader *tcp = (TCPHeader *) buffer.Write(nullptr, sizeof(TCPHeader));
if (buffer.error) {
KernelPanic("NetTCPConnection - Network interface buffer size too small.\n");
}
ip->totalLength = ByteSwap16(buffer.position - sizeof(*ethernet));
ip->flagsAndFragmentOffset = SwapBigEndian16(1 << 14 /* do not fragment */);
tcp->window = SwapBigEndian16(task->receiveWindow);
tcp->flags = SwapBigEndian16(TCP_SYN | (5 << 12 /* header is 5 DWORDs */));
tcp->sourcePort = SwapBigEndian16(task->index + TCP_PORT_BASE);
tcp->destinationPort = SwapBigEndian16(connection->address.port);
tcp->sequenceNumber = SwapBigEndian32(task->initialSend);
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
}
} else if (task->step == TCP_STEP_SYN_SENT) {
if ((data->flags & TCP_ACK) && !NetTCPIsBetween(task->sendUnacknowledged, data->ackNumber, task->sendNext)) {
if (data->flags & TCP_RST) return;
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) return;
TCP_PREPARE_REPLY(nullptr, 0, nullptr, 0);
tcpReply->flags = SwapBigEndian16(TCP_RST | (5 << 12 /* header is 5 DWORDs */));
tcpReply->sequenceNumber = data->tcp->ackNumber;
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
} else if (data->flags & TCP_RST) {
NetTaskComplete(task, ES_ERROR_CONNECTION_RESET);
} else if (data->flags & TCP_SYN) {
task->initialReceive = data->sequenceNumber;
task->receiveNext = data->sequenceNumber + 1;
if (data->flags & TCP_ACK) {
task->sendUnacknowledged = data->ackNumber;
task->step = TCP_STEP_ESTABLISHED;
TCP_MAKE_STANDARD_REPLY(TCP_ACK);
NetConnectionTransmitData(connection);
} else {
task->step = TCP_STEP_SYN_RECEIVED;
TCP_MAKE_STANDARD_REPLY(TCP_SYN | TCP_ACK);
}
}
} else {
bool acceptable = NetTCPIsBetween(task->receiveNext, data->sequenceNumber, task->receiveNext + task->receiveWindow - 1);
if (!data->segmentLength && !task->receiveWindow) {
acceptable = data->sequenceNumber == task->receiveNext;
} else if (!data->segmentLength && task->receiveWindow) {
acceptable = NetTCPIsBetween(task->receiveNext, data->sequenceNumber, task->receiveNext + task->receiveWindow - 1);
} else if (data->segmentLength && task->receiveWindow) {
acceptable = NetTCPIsBetween(task->receiveNext, data->sequenceNumber, task->receiveNext + task->receiveWindow - 1)
|| NetTCPIsBetween(task->receiveNext, data->sequenceNumber + data->segmentLength - 1, task->receiveNext + task->receiveWindow - 1);
}
if (!acceptable) {
// ACK the unacceptable segment (if the RST flag wasn't set).
if (~data->flags & TCP_RST) TCP_MAKE_STANDARD_REPLY(TCP_ACK);
return;
}
{
// Truncate segments that are partially outside the window.
// TODO Test this!
if (NetTCPIsLessThan(data->sequenceNumber, task->receiveNext)) {
data->segment = (uint8_t *) data->segment + task->receiveNext - data->sequenceNumber;
data->segmentLength -= task->receiveNext - data->sequenceNumber;
data->sequenceNumber = task->receiveNext;
}
if (NetTCPIsLessThan(task->receiveNext + task->receiveWindow, data->sequenceNumber + data->segmentLength)) {
data->segmentLength = (task->receiveNext + task->receiveWindow) - data->sequenceNumber;
}
}
if (data->flags & TCP_RST) {
if (task->step == TCP_STEP_SYN_RECEIVED) {
NetTaskComplete(task, ES_ERROR_CONNECTION_REFUSED);
} else if (task->step >= TCP_STEP_ESTABLISHED && task->step <= TCP_STEP_CLOSE_WAIT) {
NetTaskComplete(task, ES_ERROR_CONNECTION_RESET);
} else {
NetTaskComplete(task, ES_SUCCESS);
}
} else if (data->flags & TCP_SYN) {
TCP_MAKE_STANDARD_REPLY(TCP_RST);
NetTaskComplete(task, ES_ERROR_CONNECTION_RESET);
} else if (data->flags & TCP_ACK) {
if (task->step == TCP_STEP_SYN_RECEIVED) {
if (NetTCPIsBetween(task->sendUnacknowledged, data->ackNumber, task->sendNext)) {
task->step = TCP_STEP_ESTABLISHED;
task->sendUnacknowledged = data->ackNumber;
task->sendWindow = SwapBigEndian16(data->tcp->window);
TCP_MAKE_STANDARD_REPLY(TCP_ACK);
NetConnectionTransmitData(connection);
} else {
task->sendNext = data->ackNumber;
task->receiveNext = 0;
TCP_MAKE_STANDARD_REPLY(TCP_RST);
return;
}
} else if (task->step == TCP_STEP_LAST_ACK) {
if (NetTCPIsLessThanOrEqual(task->finSequence, task->sendUnacknowledged)) {
NetTaskComplete(task, ES_SUCCESS);
}
} else {
if (NetTCPIsBetween(task->sendUnacknowledged + 1, data->ackNumber, task->sendNext)) {
task->sendUnacknowledged = data->ackNumber;
// Don't update the window using old packets.
if (NetTCPIsLessThan(task->sendWL1, data->sequenceNumber)
|| (task->sendWL1 == data->sequenceNumber && NetTCPIsLessThanOrEqual(task->sendWL2, data->ackNumber))) {
task->sendWindow = SwapBigEndian16(data->tcp->window);
task->sendWL1 = data->sequenceNumber;
task->sendWL2 = data->ackNumber;
}
} else if (NetTCPIsLessThanOrEqual(data->ackNumber, task->sendUnacknowledged)) {
TCP_MAKE_STANDARD_REPLY(TCP_ACK);
return;
}
if (NetTCPIsLessThanOrEqual(task->finSequence, task->sendUnacknowledged)) {
if (task->step == TCP_STEP_FIN_WAIT_1) {
task->step = TCP_STEP_FIN_WAIT_2;
} else if (task->step == TCP_STEP_CLOSING) {
NetTaskComplete(task, ES_SUCCESS);
return;
}
}
}
}
if (task->step >= TCP_STEP_ESTABLISHED && task->step <= TCP_STEP_FIN_WAIT_2 && data->segmentLength) {
uint32_t start = data->sequenceNumber - task->receiveNext;
uint32_t end = data->sequenceNumber + data->segmentLength - task->receiveNext;
if (start >= task->receiveWindow || end > task->receiveWindow || (end - start) >= connection->receiveBufferBytes) {
KernelPanic("NetTCPConnection - Segment %x incorrectly truncated to fit inside receive window of task %x.\n", data, task);
}
uintptr_t startWrite = (start + connection->receiveWritePointer) % connection->receiveBufferBytes;
uintptr_t endWrite = (end + connection->receiveWritePointer) % connection->receiveBufferBytes;
if (startWrite < endWrite) {
EsMemoryCopy(connection->receiveBuffer + startWrite, data->segment, endWrite - startWrite);
#if 0
EsPrint("Writing segment RSN %d to %d->%d: %s\n",
data->sequenceNumber - task->initialReceive, startWrite, endWrite,
data->segmentLength, data->segment);
#endif
} else {
uintptr_t firstCopy = connection->receiveBufferBytes - startWrite;
EsMemoryCopy(connection->receiveBuffer + startWrite, data->segment, firstCopy);
EsMemoryCopy(connection->receiveBuffer, (uint8_t *) data->segment + firstCopy, endWrite);
#if 0
EsPrint("Writing segment RSN %d to %d->%d and %d->%d: %s\n",
data->sequenceNumber - task->initialReceive,
startWrite, firstCopy, 0, endWrite,
data->segmentLength, data->segment);
#endif
}
if (start) {
if (!connection->receivedData.Set(start, end, nullptr, true)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
return;
}
} else {
uintptr_t advanceBy = end;
if (connection->receivedData.contiguous) {
KernelPanic("NetTCPConnection - Received data range set in %x was unexpectedly contiguous.\n", connection);
}
if (connection->receivedData.ranges.Length()) {
if (connection->receivedData.ranges.Length()
&& connection->receivedData.ranges[0].from <= advanceBy) {
advanceBy = connection->receivedData.ranges[0].to;
connection->receivedData.ranges.Delete(0);
}
for (uintptr_t i = 0; i < connection->receivedData.ranges.Length(); i++) {
connection->receivedData.ranges[0].from -= advanceBy;
connection->receivedData.ranges[0].to -= advanceBy;
}
connection->receivedData.Validate();
}
task->receiveNext += advanceBy;
connection->receiveWritePointer = (connection->receiveWritePointer + advanceBy) % connection->receiveBufferBytes;
NetConnectionUpdateReceiveWindow(connection);
TCP_MAKE_STANDARD_REPLY(TCP_ACK);
}
}
if (data->flags & TCP_FIN) {
task->receiveNext = data->sequenceNumber + 1;
TCP_MAKE_STANDARD_REPLY(TCP_ACK);
if (task->step == TCP_STEP_SYN_RECEIVED || task->step == TCP_STEP_ESTABLISHED) {
task->step = TCP_STEP_CLOSE_WAIT;
} else if (task->step == TCP_STEP_FIN_WAIT_1) {
if (NetTCPIsLessThanOrEqual(task->finSequence, task->sendUnacknowledged)) {
NetTaskComplete(task, ES_SUCCESS);
} else {
task->step = TCP_STEP_CLOSING;
}
} else if (task->step == TCP_STEP_FIN_WAIT_2) {
NetTaskComplete(task, ES_SUCCESS);
}
}
}
}
void NetAddressSetup(NetTask *_task, void *_buffer) {
EsBuffer *buffer = (EsBuffer *) _buffer;
NetAddressSetupTask *task = (NetAddressSetupTask *) _task;
NetInterface *interface = task->interface;
const DHCPHeader *dhcp;
if (task->completed) {
return;
} else if (task->step == 0) {
// Broadcast a DHCPDISCOVER request to get an IP address.
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) {
return;
}
DHCP_START();
uint8_t dhcpOptions[] = {
DHCP_OPTION_MESSAGE_TYPE(DHCPDISCOVER),
55, 2, 3 /* get the router's IP */, 6 /* get the DNS server's IP */,
255, // End of options.
};
buffer.Write(dhcpOptions, sizeof(dhcpOptions));
DHCP_END();
if (buffer.error) {
KernelPanic("NetAddressSetup - Network interface buffer size too small.\n");
}
task->dhcpTransactionID = dhcp->transactionID;
task->step++;
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
}
return;
}
dhcp = (const DHCPHeader *) buffer->Read(sizeof(DHCPHeader));
if (dhcp->opCode != DHCP_BOOTREPLY /* boot reply */
|| dhcp->hardwareAddressType != 0x01 /* ethernet */
|| dhcp->hardwareAddressLength != 6
|| dhcp->transactionID != task->dhcpTransactionID
|| dhcp->optionsMagic[0] != 99
|| dhcp->optionsMagic[1] != 130
|| dhcp->optionsMagic[2] != 83
|| dhcp->optionsMagic[3] != 99) {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Unsupported DHCP packet.\n");
return;
}
task->dhcpTransactionID = 0;
const KIPAddress *dnsServerOption = nullptr;
const KIPAddress *serverIdentifierOption = nullptr;
const KIPAddress *routerIPOption = nullptr;
const uint8_t *messageTypeOption = nullptr;
while (true) {
const uint8_t *optionType = (const uint8_t *) buffer->Read(sizeof(uint8_t));
if (!optionType) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing DHCP option type.\n");
return;
}
if (*optionType == 0) {
continue;
} else if (*optionType == 255) {
break;
} else {
const uint8_t *optionLength = (const uint8_t *) buffer->Read(sizeof(uint8_t));
if (!optionLength) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing DHCP option length.\n");
return;
}
const uint8_t *optionValue = (const uint8_t *) buffer->Read(*optionLength);
if (!optionValue) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing DHCP option value.\n");
return;
}
if (*optionType == 6 /* DNS server */) {
if (*optionLength < 4) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "DNS server IP in DHCP option too short.\n");
return;
}
dnsServerOption = (const KIPAddress *) optionValue;
} else if (*optionType == 3 /* Router IP */) {
if (*optionLength < 4) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Router IP in DHCP option too short.\n");
return;
}
routerIPOption = (const KIPAddress *) optionValue;
} else if (*optionType == 54 /* server identifier */) {
if (*optionLength < 4) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Server identifier in DHCP option too short.\n");
return;
}
serverIdentifierOption = (const KIPAddress *) optionValue;
} else if (*optionType == 53 /* message type */) {
if (*optionLength < 1) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Message type in DHCP option too short.\n");
return;
}
messageTypeOption = (const uint8_t *) optionValue;
}
}
}
if (!messageTypeOption) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Message type option in DHCP missing.\n");
return;
}
uint8_t messageType = *messageTypeOption;
if (messageType == DHCPOFFER) {
if (task->step != 1) {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Received DHCPOFFER, but task step is %d.\n", task->step);
return;
}
if (!serverIdentifierOption) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Server identifier option missing in DHCPOFFER.\n");
return;
}
KIPAddress offer = dhcp->yourIPAddress;
KernelLog(LOG_INFO, "Networking", "received IP", "Network interface %x has been offered IP address %d.%d.%d.%d.\n",
interface, offer.d[0], offer.d[1], offer.d[2], offer.d[3]);
// Broadcast a DHCPREQUEST message to accept this IP address.
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) return;
DHCP_START();
uint8_t dhcpOptions[] = {
DHCP_OPTION_MESSAGE_TYPE(DHCPREQUEST),
DHCP_OPTION_REQUESTED_IP(offer),
DHCP_OPTION_SERVER_IDENTIFIER(*serverIdentifierOption),
55, 2, 3 /* get the router's IP */, 6 /* get the DNS server's IP */,
255, // End of options.
};
buffer.Write(dhcpOptions, sizeof(dhcpOptions));
DHCP_END();
if (buffer.error) {
KernelPanic("KNetworkInterface::ReceiveDHCPReply - Network interface buffer size too small.\n");
}
task->dhcpTransactionID = dhcp->transactionID;
task->step++;
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
}
} else if (messageType == DHCPACK) {
if (task->step != 2) {
KernelLog(LOG_ERROR, "Networking", "ignored packet", "Received DHCPACK, but task step is %d.\n", task->step);
return;
}
if (!dnsServerOption || !serverIdentifierOption || !routerIPOption) {
KernelLog(LOG_ERROR, "Networking", "bad packet", "Missing options in DHCPACK.\n");
return;
}
interface->ipAddress = dhcp->yourIPAddress;
interface->routerIP = *routerIPOption;
interface->dnsServerIP = *dnsServerOption;
interface->serverIdentifier = *serverIdentifierOption;
KernelLog(LOG_INFO, "Networking", "accepted IP",
"Network interface %x has accepted IP address %d.%d.%d.%d with DNS server %d.%d.%d.%d and router %d.%d.%d.%d.\n",
interface, dhcp->yourIPAddress.d[0], dhcp->yourIPAddress.d[1], dhcp->yourIPAddress.d[2], dhcp->yourIPAddress.d[3],
dnsServerOption->d[0], dnsServerOption->d[1], dnsServerOption->d[2], dnsServerOption->d[3],
routerIPOption->d[0], routerIPOption->d[1], routerIPOption->d[2], routerIPOption->d[3]);
task->changedState = true;
NetTaskComplete(task, ES_SUCCESS);
} else if (messageType == DHCPNAK) {
KernelLog(LOG_INFO, "Networking", "lost IP", "Network interface %x has lost IP address %d.%d.%d.%d.\n",
interface, interface->ipAddress.d[0], interface->ipAddress.d[1], interface->ipAddress.d[2], interface->ipAddress.d[3]);
task->changedState = true;
NetTaskComplete(task, ES_ERROR_LOST_IP_ADDRESS);
}
}
void NetTaskBegin(NetTask *task) {
if (!task->interface) {
KMutexAcquire(&networking.interfacesListMutex);
SimpleList *item = networking.interfaces.first;
while (item && item != &networking.interfaces) {
NetInterface *interface = EsContainerOf(NetInterface, item, item);
KWriterLockTake(&interface->connectionLock, K_LOCK_SHARED);
if (interface->connected && interface->hasIP) {
task->interface = interface;
break;
}
KWriterLockReturn(&interface->connectionLock, K_LOCK_SHARED);
item = item->next;
}
KMutexRelease(&networking.interfacesListMutex);
} else {
KWriterLockTake(&task->interface->connectionLock, K_LOCK_SHARED);
}
if (!task->interface) {
NetTaskComplete(task, ES_ERROR_NO_CONNECTED_NETWORK_INTERFACES);
} else {
task->index = 0xFFFF;
task->callback(task, nullptr);
KWriterLockReturn(&task->interface->connectionLock, K_LOCK_SHARED);
}
}
void NetTaskComplete(NetTask *task, EsError error) {
KWriterLockAssertShared(&task->interface->connectionLock);
if (task->completed) {
KernelPanic("NetTaskComplete - Task already completed.\n");
}
task->error = error;
task->completed = true;
task->callback(task, nullptr);
}
void NetConnectionDestroy(NetConnection *connection) {
MMFree(kernelMMSpace, connection->sendBuffer, connection->sendBufferBytes + connection->receiveBufferBytes);
connection->receivedData.ranges.Free();
CloseHandleToObject(connection->bufferRegion, KERNEL_OBJECT_SHMEM);
EsHeapFree(connection, sizeof(NetConnection), K_FIXED);
}
NetConnection *NetConnectionOpen(EsAddress *address, size_t sendBufferBytes, size_t receiveBufferBytes, uint32_t flags) {
(void) flags;
NetConnection *connection = (NetConnection *) EsHeapAllocate(sizeof(NetConnection), true, K_FIXED);
if (!connection) {
return nullptr;
}
connection->sendBufferBytes = sendBufferBytes;
connection->receiveBufferBytes = receiveBufferBytes;
connection->address = *address;
connection->handles = 2;
connection->bufferRegion = MMSharedCreateRegion(sendBufferBytes + receiveBufferBytes, true);
if (!connection->bufferRegion) {
EsHeapFree(connection, sizeof(NetConnection), K_FIXED);
return nullptr;
}
connection->sendBuffer = (uint8_t *) MMMapShared(kernelMMSpace, connection->bufferRegion, 0, sendBufferBytes + receiveBufferBytes);
connection->receiveBuffer = connection->sendBuffer + sendBufferBytes;
connection->task.callback = NetTCPConnection;
connection->task.receiveWindow = MinimumInteger(receiveBufferBytes - 1, 0xF000);
NetTaskBegin(&connection->task);
return connection;
}
void NetConnectionClose(NetConnection *connection) {
bool destroy = false;
NetTCPConnectionTask *task = &connection->task;
NetInterface *interface = task->interface;
KWriterLockTake(&interface->connectionLock, K_LOCK_SHARED);
KMutexAcquire(&connection->mutex);
connection->handles++; // Prevent NetTaskComplete from destroying the connection.
if (task->completed) {
destroy = true;
} else if (task->step == TCP_STEP_SYN_RECEIVED || task->step == TCP_STEP_ESTABLISHED || task->step == TCP_STEP_CLOSE_WAIT) {
// Send a FIN packet.
EsBuffer buffer = NetTransmitBufferGet();
if (buffer.error) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
} else {
EthernetHeader *ethernet = (EthernetHeader *) buffer.Write(nullptr, sizeof(EthernetHeader));
ETHERNET_HEADER(ethernet, ETHERNET_TYPE_IPV4, task->destinationMAC);
IPHeader *ip = (IPHeader *) buffer.Write(nullptr, sizeof(IPHeader));
IP_HEADER(ip, *(KIPAddress *) &connection->address.ipv4, IP_PROTOCOL_TCP);
TCPHeader *tcp = (TCPHeader *) buffer.Write(nullptr, sizeof(TCPHeader));
if (buffer.error) {
KernelPanic("NetConnectionClose - Network interface buffer size too small.\n");
}
ip->totalLength = ByteSwap16(buffer.position - sizeof(*ethernet));
ip->flagsAndFragmentOffset = SwapBigEndian16(1 << 14 /* do not fragment */);
ip->headerChecksum = ip->CalculateHeaderChecksum();
tcp->sourcePort = SwapBigEndian16(task->index + TCP_PORT_BASE);
tcp->destinationPort = SwapBigEndian16(connection->address.port);
tcp->flags = SwapBigEndian16(TCP_FIN | TCP_ACK | (5 << 12 /* header is 5 DWORDs */));
tcp->sequenceNumber = SwapBigEndian32(task->sendNext);
tcp->ackNumber = SwapBigEndian32(task->receiveNext);
tcp->window = SwapBigEndian16(task->receiveWindow);
task->finSequence = task->sendNext;
task->sendNext++;
task->step = TCP_STEP_FIN_WAIT_1;
if (!NetTransmit(interface, &buffer, NET_PACKET_ETHERNET)) {
NetTaskComplete(task, ES_ERROR_INSUFFICIENT_RESOURCES);
}
}
}
connection->handles--;
KMutexRelease(&connection->mutex);
KWriterLockReturn(&interface->connectionLock, K_LOCK_SHARED);
if (destroy) {
NetConnectionDestroy(connection);
}
}
void KRegisterNetInterface(NetInterface *interface) {
KernelLog(LOG_INFO, "Networking", "register interface", "Registered interface with MAC address %X:%X:%X:%X:%X:%X and name '%z'.\n",
interface->macAddress.d[0], interface->macAddress.d[1], interface->macAddress.d[2],
interface->macAddress.d[3], interface->macAddress.d[4], interface->macAddress.d[5],
interface->cDebugName);
KMutexAcquire(&networking.interfacesListMutex);
networking.interfaces.Insert(&interface->item, false /* end */);
KMutexRelease(&networking.interfacesListMutex);
interface->addressSetupTask.interface = interface;
interface->addressSetupTask.callback = NetAddressSetup;
}
void NetInitialise(KDevice *) {
networking.udpTaskBitset.Initialise(MAX_UDP_TASKS);
networking.udpTaskBitset.PutAll();
ArenaInitialise(&networking.transmitBufferPool, 1048576, 2048);
networking.tcpTaskLRU = networking.tcpTaskMRU = 0xFFFF;
for (uintptr_t i = 0; i < MAX_TCP_TASKS; i++) {
NetTCPFreeTaskIndex(i, true);
}
}
KDriver driverNetworking = {
.attach = NetInitialise,
};
#endif