211 lines
7.3 KiB
C++
211 lines
7.3 KiB
C++
/*
|
|
* Net.hpp
|
|
* Networking syscalls: SYS_PING, SYS_SOCKET, SYS_CONNECT, SYS_BIND,
|
|
* SYS_LISTEN, SYS_ACCEPT, SYS_SEND, SYS_RECV, SYS_CLOSESOCK,
|
|
* SYS_SENDTO, SYS_RECVFROM, SYS_GETNETCFG, SYS_SETNETCFG, SYS_RESOLVE
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <Sched/Scheduler.hpp>
|
|
#include <Timekeeping/ApicTimer.hpp>
|
|
#include <Net/Icmp.hpp>
|
|
#include <Net/Dns.hpp>
|
|
#include <Net/Ipv4.hpp>
|
|
#include <Net/Socket.hpp>
|
|
#include <Net/NetConfig.hpp>
|
|
#include <Net/NetIf.hpp>
|
|
#include <Drivers/Net/E1000.hpp>
|
|
#include <Drivers/Net/E1000E.hpp>
|
|
#include <Drivers/Net/Wifi/Wifi.hpp>
|
|
|
|
#include "Syscall.hpp"
|
|
|
|
namespace montauk::abi {
|
|
|
|
static uint16_t g_pingSeq = 0;
|
|
static constexpr uint16_t PING_ID = 0x2E01; // "ZE"
|
|
|
|
static int32_t Sys_Ping(uint32_t ipAddr, uint32_t timeoutMs) {
|
|
uint16_t seq = g_pingSeq++;
|
|
|
|
Net::Icmp::ResetReply();
|
|
Net::Icmp::SendEchoRequest(ipAddr, PING_ID, seq);
|
|
|
|
uint64_t start = Timekeeping::GetMilliseconds();
|
|
while (!Net::Icmp::HasReply(PING_ID, seq)) {
|
|
if (Timekeeping::GetMilliseconds() - start >= timeoutMs) {
|
|
return -1;
|
|
}
|
|
Sched::Schedule();
|
|
}
|
|
|
|
return (int32_t)(Timekeeping::GetMilliseconds() - start);
|
|
}
|
|
|
|
// ---- Socket syscalls ----
|
|
|
|
static int Sys_Socket(int type) {
|
|
return Net::Socket::Create(type, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static int Sys_Connect(int fd, uint32_t ip, uint16_t port) {
|
|
return Net::Socket::Connect(fd, ip, port, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static int Sys_Bind(int fd, uint16_t port) {
|
|
return Net::Socket::Bind(fd, port, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static int Sys_Listen(int fd) {
|
|
return Net::Socket::Listen(fd, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static int Sys_Accept(int fd) {
|
|
return Net::Socket::Accept(fd, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static int Sys_Send(int fd, const uint8_t* data, uint32_t len) {
|
|
return Net::Socket::Send(fd, data, len, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static int Sys_Recv(int fd, uint8_t* buf, uint32_t maxLen) {
|
|
return Net::Socket::Recv(fd, buf, maxLen, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static void Sys_CloseSock(int fd) {
|
|
Net::Socket::Close(fd, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static int Sys_SendTo(int fd, const uint8_t* data, uint32_t len,
|
|
uint32_t destIp, uint16_t destPort) {
|
|
return Net::Socket::SendTo(fd, data, len, destIp, destPort, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static int Sys_RecvFrom(int fd, uint8_t* buf, uint32_t maxLen,
|
|
uint32_t* srcIp, uint16_t* srcPort) {
|
|
// A preceding UDP send may be queued while ARP resolves the next hop.
|
|
// Drive the pending IPv4 queue on each non-blocking receive so a lost
|
|
// initial ARP request is retried without unrelated network traffic.
|
|
Net::Ipv4::FlushPending();
|
|
return Net::Socket::RecvFrom(fd, buf, maxLen, srcIp, srcPort, Sched::GetCurrentPid());
|
|
}
|
|
|
|
static void Sys_GetNetCfg(NetCfg* out) {
|
|
if (out == nullptr) return;
|
|
out->ipAddress = Net::GetIpAddress();
|
|
out->subnetMask = Net::GetSubnetMask();
|
|
out->gateway = Net::GetGateway();
|
|
|
|
// Whichever interface is carrying traffic; on a Wi-Fi link this is the
|
|
// adapter's MAC, not an idle Ethernet port's.
|
|
const uint8_t* mac = ::Net::NetIf::ActiveMac();
|
|
if (mac) {
|
|
for (int i = 0; i < 6; i++) out->macAddress[i] = mac[i];
|
|
} else {
|
|
for (int i = 0; i < 6; i++) out->macAddress[i] = 0;
|
|
}
|
|
out->_pad[0] = 0;
|
|
out->_pad[1] = 0;
|
|
out->dnsServer = Net::GetDnsServer();
|
|
}
|
|
|
|
static void CopyNetStatusDriver(char* dst, const char* src) {
|
|
static constexpr uint64_t DriverNameBytes = 32;
|
|
if (!dst) return;
|
|
uint64_t i = 0;
|
|
for (; i < DriverNameBytes - 1 && src && src[i]; i++) {
|
|
dst[i] = src[i];
|
|
}
|
|
dst[i] = '\0';
|
|
}
|
|
|
|
// Reports whichever interface is currently carrying traffic, so a machine
|
|
// that joined a Wi-Fi network sees the wireless counters here rather than
|
|
// an idle Ethernet port.
|
|
static void Sys_NetStatus(NetStatus* out) {
|
|
if (out == nullptr) return;
|
|
|
|
out->initialized = 0;
|
|
out->linkUp = 0;
|
|
out->polling = 0;
|
|
out->_pad0 = 0;
|
|
out->rxPackets = 0;
|
|
out->txPackets = 0;
|
|
CopyNetStatusDriver(out->driver, "No adapter");
|
|
|
|
const auto* iface = ::Net::NetIf::Active();
|
|
if (iface == nullptr) return;
|
|
|
|
out->initialized = 1;
|
|
out->linkUp = iface->IsLinkUp() ? 1 : 0;
|
|
|
|
if (iface->Type == ::Net::NetIf::Kind::Wireless) {
|
|
WifiInfo wi;
|
|
if (Drivers::Net::Wifi::GetInfo(&wi) == 0) {
|
|
out->rxPackets = wi.rxPackets;
|
|
out->txPackets = wi.txPackets;
|
|
}
|
|
CopyNetStatusDriver(out->driver, "Intel Wi-Fi (wlan0)");
|
|
} else if (Drivers::Net::E1000::IsInitialized()) {
|
|
out->rxPackets = Drivers::Net::E1000::GetRxPacketCount();
|
|
out->txPackets = Drivers::Net::E1000::GetTxPacketCount();
|
|
CopyNetStatusDriver(out->driver, "Intel 82540EM");
|
|
} else if (Drivers::Net::E1000E::IsInitialized()) {
|
|
out->polling = Drivers::Net::E1000E::RequiresPolling() ? 1 : 0;
|
|
out->rxPackets = Drivers::Net::E1000E::GetRxPacketCount();
|
|
out->txPackets = Drivers::Net::E1000E::GetTxPacketCount();
|
|
CopyNetStatusDriver(out->driver, "Intel e1000e");
|
|
}
|
|
}
|
|
|
|
// List the registered link-layer interfaces. The panel needs this to keep
|
|
// wired and wireless status apart: the IP configuration is global, so the
|
|
// only way to say which interface owns it is `active`.
|
|
static int Sys_NetIfs(NetIfInfo* out, int maxCount) {
|
|
if (out == nullptr || maxCount <= 0) return -1;
|
|
|
|
const auto* active = ::Net::NetIf::Active();
|
|
int count = ::Net::NetIf::Count();
|
|
int n = 0;
|
|
|
|
for (int i = 0; i < count && n < maxCount; i++) {
|
|
const auto* iface = ::Net::NetIf::At(i);
|
|
if (iface == nullptr) continue;
|
|
|
|
NetIfInfo& info = out[n];
|
|
for (uint64_t k = 0; k < sizeof(info.name); k++) info.name[k] = '\0';
|
|
for (uint64_t k = 0; k + 1 < sizeof(info.name) && iface->Name && iface->Name[k]; k++)
|
|
info.name[k] = iface->Name[k];
|
|
|
|
const uint8_t* mac = iface->GetMac ? iface->GetMac() : nullptr;
|
|
for (int k = 0; k < 6; k++) info.mac[k] = mac ? mac[k] : 0;
|
|
|
|
info.kind = iface->Type == ::Net::NetIf::Kind::Wireless
|
|
? NETIF_KIND_WIRELESS : NETIF_KIND_ETHERNET;
|
|
info.linkUp = (iface->IsLinkUp && iface->IsLinkUp()) ? 1 : 0;
|
|
info.active = iface == active ? 1 : 0;
|
|
info._pad[0] = info._pad[1] = info._pad[2] = 0;
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static int Sys_SetNetCfg(const NetCfg* in) {
|
|
if (in == nullptr) return -1;
|
|
Net::SetIpAddress(in->ipAddress);
|
|
Net::SetSubnetMask(in->subnetMask);
|
|
Net::SetGateway(in->gateway);
|
|
Net::SetDnsServer(in->dnsServer);
|
|
return 0;
|
|
}
|
|
|
|
// ---- DNS resolve ----
|
|
|
|
static int64_t Sys_Resolve(const char* hostname) {
|
|
uint32_t ip = Net::Dns::Resolve(hostname);
|
|
return (int64_t)ip;
|
|
}
|
|
};
|