mirror of https://gitlab.com/nakst/essence
2172 lines
71 KiB
C++
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(ðernet->destinationMAC, &interface->macAddress, sizeof(KMACAddress))
|
|
&& EsMemoryCompare(ðernet->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
|