[+] Linux: missing AuNet adapter API

[*] Update README
This commit is contained in:
Reece Wilson 2022-12-17 13:57:00 +00:00
parent 1e4082c02f
commit 6f6be5c545
3 changed files with 614 additions and 136 deletions

274
README.md
View File

@ -1,6 +1,4 @@
## PREALPHA (in-dev, missing polish and APIs are volatile)
## Minimum viable product ETA: September 2022
## PREALPHA
## AuroraRuntime
The Aurora Runtime is a low level platform abstraction layer for modern cross-platform C++
@ -14,26 +12,30 @@ pipeline to get started.
## Features
- Reduced C++ standard template library dependence (^1)
- High performance threading and synchronization primitives (os userland sched optimized)
- Async even driven subsystem with high perf sync primitives
- Abstract kernel file/net transaction, IPC, timer, semaphore, et al abstraction in the form of LoopQueues (eg, MacOS RunLoops)
- Asynchronous and synchronous IO (network, character, file, buffered, process, and io watcher)
- Optional event driven async programming paradigm
- Consoles; graphical and standard, file archives
- Logging; UTF-8 logger, common sink backends
- Debug and Telementry; asserts, exception logging, fio, nio backends
- Crypto ECC/[25519, P-384, P-256], [AES, RSA, X509], [common digests]
- Reduced C++ standard template library dependence despite requiring a modern-ish driver (^1) (portable when?)
- Logging; UTF-8 logger, common sink backends, formating interface
- Debug and Telementry; asserts, panics, exception logging, demangling of symbols, more
- Crypto ECC/[25519, P-384, P-256], [AES, RSA, X509], CBC[AES, Stinky3DES], HMAC, HashCash, BCrypt, [common digests]
- Basic cmdline parsing from any module
- Exit and fatal save condition callbacks
- IPC
- Network [WIP]
- Random; secure and fast
- Hardware Info; memory and cpu info (including features, topology, e-core, and cache info)
- Software Stack Info (kernel, version, brand, build string, etc)
- FIO settings registry
- Compression
- Random; secure, user-seeded, and fast
- Hardware Info; memory and cpu info (including cpu feature bits, core topology, e-core awareness, and basic cache size)
- Software stack information for retrieving kernel, version, brand, family, build string, etc
- Compression (deflate, gzip, zstd, LZ4, bzip2, TODO: lzma, brotli)
- Locale and encoding
- High performance threading and synchronization primitives (os userland sched optimized)
- Async subsystem backed by high performance sync primitives (cv loop) and hybrid switching into IO polling (think userland cv-backed promises + waitmultipleobjects)
- IO subsystem for standard cross-platform IO loop queues, IPC (mutex with auto-unlock, semaphores, full-duplex single-connection pipes, and shared memory), file (direct uncached access), and network
- Abstract kernel IO transactions, IPC objects, timers, semaphores, and others in the form of ILoopSources
- Common IO transaction interface for network, file, and handle async access (with workarounds for platform querks)
- IO processor for common network, pipe processing, and general work on any given thread (think of it as an io context)
- IO pipe processor for the processing of data when invoked by io transactions and other signalable interfaces
- Protocol stack concept, for implementing low-overhead IO stream processors, where data is streamed through layers of interceptors
- Builtin support for TLS and compression in the form of protocol stack interceptors
- Non-locking file system watchers with IO subsystem interoperability
- Process spawning with stream redirection backed by the IO subsystem
- Process memory management with IPC and file mapping
- FIO settings registry
- C++ utility templates and macros
- Follows all strings are UTF-8 convention
@ -55,36 +57,28 @@ Discord: [Invite](https://discord.gg/XYjCGWWa4J)
| Platform | Support |
| ----------- | ------- |
| NT/Win32-like |✅ |
| NT/Win7 | ⚠️ |
| NT/Win8.1+ | ✅ |
| NT/UWP | 🕖 |
| NT/GameOS|❌ |
| Linux |🕖 |
| Linux | |
| FreeBSD 9 | ❌ |
| FreeBSD 11 | ❌ |
| OpenBSD | ❌ |
| XNU/NS-like | ❌ |
Win7: some apis are inherently limited before Win8.1.
For client applications, win7 should not be crippled.
## Performance
Performance of each system should ideally be that of the best implementation on the platform,
and no worse than the STL. Due to heavyweight requirements and spiral model defined objectives,
a handful of portable C libraries have been brought into achieve compression, crypto, alloc, and
str formatting objectives using known good industry standard libraries. Footprint is expected to
be on the heavier side for optimal performance (incl toll for C++ tradeoffs) and flexibility.
a handful of unknown good industry standard libraries have been brought into achieve compression,
crypto, alloc, and formatting objectives. Footprint is expected to be on the heavier side for optimal
performance (incl toll for C++ tradeoffs), usability, and flexibility.
Runtime as of 2022-02-01 *without the wxWidgets toolkit, with all compression libraries* on Windows LTSC 2019:
DLL Disk: 4.2MB \
Size Of Image: 0x50B000 (5MB) \
Real Commit Charge of a Console App: 9.7MB \
...the task manager lie: 3,168K (3.1MB -> less than our DLL) \
...HWInfo reports
[13:07:38] [Info] | RamInfo Private Allocation: 10215424/71987290112 (^1) \
[13:07:38] [Info] | RamInfo Address Space: 11669504/71987290112
^1 ...on LTSC 2019. Modern Windows 10 and 11 will return the exact task manager value of 3,168K (3.1MB).
Defer to benchmarks
@ -201,9 +195,8 @@ resources.
## IO
The Aurora Runtime implements a multiple io wait loop sub-subsystem, file io, network io,
various adapters and connectors, io processors, io/character, io/buffered, and other such
concepts to aid with writing low-level cross-platform IO.
The Aurora Runtime implements loop, file io, network io, and other sub-subsystems with
various adapters and connectors.
An important note about texting encoding. Stdin, file encoding, text decoders, and other IO
resources work with codepage UTF-8 as the internal encoding scheme. String overloads and
@ -212,16 +205,22 @@ to read a BOM to translate any other arbitrary user generated text input to UTF-
### Loop
The Aurora Runtime implements a kernel-scheduler optimized IO subsystem for managing GUIs,
Network AIO, File AIO, IPC AIO, and thread synchronization objects through loop the loop subsystem.
The Aurora Runtime implements a kernel-scheduler optimized IO loop subsystem for managing GUIs;
network, file, ipc AIO; and thread synchronization objects for when these waitables converse.
ILoopSource is an interface defined by the loop subsystem for IO objects with a signalable state.
Attached to an ILoopQueue, the ILoopQueue will provide wait-on and similar functionality; and
subscription notifications of signal state change. ILoopQueue's are thread-safe allowing for
cross-thread or mid-wait work scheduling. Subscription notifications allow for optimized loop
source removal or no-action/non-removal replies from subscription implementer. In addition to
the synchronization provided by the ILoopQueue, the ILoopSource interface permits arbitrary
is-signaled-and-latch (TryLock) queries and timed-wait (WaitOn) calls on a per IO object basis.
Attached to an ILoopQueue, the ILoopQueue will provide wait-any/wait-all/is-signaled polling with
optional subscription functionality. Furthermore, ILoopQueues are thread-safe allowing for cross-thread
or mid-wait work scheduling (as in, the addition of new subscribers during sleep or callback).
It is possible to run loop queues like a poll object or with an arbitrary amount of optional
subscribers per loop source.
Subscription notifications allow for optimized loop source removal or no-action/non-removal replies
from subscription implementer. If you and all other subscribers want to evict the ILoopSource,
the source will be automatically removed from the ILoopQueue. If just you vote to evict, you will
no longer receive updates for the object, but the ILoopSource and other subscribers will remain.
### IPC
@ -233,16 +232,28 @@ are used to implement IPC within the applications namespace/sandbox.
### FIO
A simple file stream interface is provided by an Open function which accepts an Aurora path and
an advisory lock level. However, all such functions are blocking in face of platform specific
asynchronous alternatives. An alternative `IAsyncFileStream` is provided to supply the user with
an supplier of `IAsyncTransaction`'s - an overlapped IO style interface for starting a read/write
transaction, registering an APC-like callback, requesting a loop subsystem waitable object, and
clearing the request. AIO is backed by `io_submit` under Linux, POSIX AIO under BSD, and Overlapped
IO under NT. A glibc approach of spamming threads akin to libuv and skipping the synchronization
on completion step isn't our style. Instead, you are reliant on the native async capabilities of the
underlying operating system. Special consideration must be made for alignment, cached/uncached access,
and supported file systems.
A simple blocking file stream is provided by an open function given an Aurora path string and a
file advisory lock level. This object can be used with AuProcess to map regions of the file into
the address map. However, everything about this object is blocking.
An alternative asynchronous IAsyncFileStream interface is available which supplies IO transaction
objects for scheduling direct disk reads. One should be careful to note each platform has file AIO
querks. For instance...
- Linux will block on most file systems if metadata has to be poked
- FS support is limited (NT/NTFS > Linux/XFS > NT/xxxx (w/ caching) > Linux/EXT4 > unsupported)
- Read/Writes musts be made with respect to sector alignment
- Removing caching on Linux will mitigate blocking behaviour (~O_DIRECT blocking io_submit)
- ...but caching on Win32 is sometimes desirable
- Linux reads might be limited by max_sectors_kb or max_segments
For large block reads: \
bDirectIO = true; read directly from fs (recommend) \
offsets -> must align
When data is small enough for file caches to be useful: \
bDirectIO = !AuBuild::kIsNtDerived (recommend) \
offsets -> align for the highest denominator
Additional utility functions exist outside of the two file interfaces for: stat, directory iteration,
UTF-8 string reading and writing, blocking binary read/writes, and more.
@ -265,10 +276,16 @@ All string paths are simply expanded, similar to MSCRT's `fullpath` or UNIX's `r
| `\` | Agnostic Directory Splitter |
| `.` [SPLITTER] | Nothing |
### TLS
TLS client and partial server support is provided by protocol stack interceptors meaning that our implementation is no-socket.
It's possible to write into a buffered protocol stack using the provided stream writer, simulating data coming through a socket channel;
and to fetch the response/translated message using an end protocol piece to be supplied with the data, or using the provided stream reader to read the end
to read the end interceptors buffer once the protocol stack has been ticked.
### Resources
The Aurora Runtime provides reports system, application, and user specific paths under the `Aurora::IO::FS` subsystem.
The Aurora Runtime provides system, application, and user specific paths under the `Aurora::IO::FS` subsystem.
These include the users home directory, a per vendor sandboxed application user directory, a per vendor sandboxed application
all users directory, the user-installable program directory, the user's real home directory, and other such relevant paths.
@ -285,44 +302,47 @@ The Aurora Runtime provides child process monitoring, asynchronous child stdin/o
## Locale
Encoding and decoding of UTF-8, UTF-16, UTF-32, GBK, GB-2312, and SJIS is supported through platform provided decoders.
System localization information, including system codepage, country, and system language, is provided by the envrionment variables which
are available, OS specific interfaces, or the user overload mechanism.
Encoding and decoding of UTF-8, UTF-16, UTF-32, GBK, GB-2312, and SJIS is supported through OS provided decoders.
System localization information, including system codepage, country, and system language, is provided by the available
envrionment variables, OS specific interfaces, or the overload mechanism.
## Memory
### Allocator
Objects are allocated across API/Module boundaries. So long as the high level API design isn't horribly inefficient to an extent
that cache invalidation and indirect lookups are minimalized, object-heavy code can optimized. On modern hardware, legitmate indirect
branching versus short jumps aren't so expensive between modules in real world usage; and in combination with a fast enough allocator,
there is little reason why couldn't achieve reasonable OOP performance through a C-with-classes-like API.
Objects are allocated across API/module boundaries using an ABI defined by Aurora Runtime.
As for allocation, we generally expect a dependence on Microsoft's mimalloc. Linking against the Aurora Runtime in the Aurora
Ecosystem will automatically replace global allocators with `Aurora::Memory`, which in turn, proxies any other suitable allocator
interface with extended zero, array, and alignment respecting APIs. A suitably fast allocator, such as mimalloc, should reduce the
cost of the OOP design.
Such OOP design in contrast to header-everything is contingent on well-defined interface boundaries which make sense from a
perspective of optimizaton (primarily allocation and indirect branching) and usability.
Allocations are optimized by using a known good third party library, mimalloc. In the future, it should be possible to replace the
standard allocator; however, for now all allocations are routed through Aurora::Memory which is backed by mi_xxxx calls.
Mimalloc and/or other such slab and zone based allocators can and must be explored as an optimization for C++'s tendency to
arbitrary allocate memory. This should improve the performance of reasonbly written modules with OOP in mind.
Indirect branches are optimized by simply trying to design APIs that arent complete ass for humans and systems to work with.
### Memory Heap
Aurora provides a heap allocator for dividing up a large preallocated region of memory
Aurora provides a heap allocator for dividing up a large preallocated region of memory.
Currently, we use a modified version of O(1) heap that provides a constant worst case allocation and deallocation
of any given request. Memory is provided by AuProcess for the requested allocation, but AuMemory might be unfortunately
hit during the initialization of a heap.
### Shared Pointers
Memory objects, including shared pointers, and the object allocation model is defined by AuROXTL. The `AuSPtr` class template
is backed by the standard `std::shared_ptr`, extended by `#include <auROXTL/auMemoryModel.hpp>,` in the default configuration.
AuROXTL memory primitives, and most STL containers, are source compatible with the base STL classes, such that any Aurora
specific behaviour is lost during type reduction.
There are benefits of using the Aurora extended classes, include redefining null dereference on shared ptrs to throw
an AU_THROW_STRING. Without support for native behaviour within the C++ driver, such features are rather expensive using the performace
hacks we have available (outside of ripping the compiler apart to emit special debug info for hacky trap handlers). Arguably, it's an
experiment worth trying now that modern hardware can make up for software and microcode flaws; and architecture translation. Most users
probably wont even notice the performance loss, until it saves them from a hard crash and they realize dereferences are bloated.
This is default behaviour, and can be easily disabled or configured from within your ecosystem's AuroraConfiguration.h to globally
modify the behaviour and subsequent ABI of the AuSPtr's.
The reason we wrap shared pointers is two folds
1) in code that doesn't need to be fast, we can simply hack in is valid checks on assignment or use an interface with panic branches
to handle null access conditions.
2) in the future, it may be desirable to use established AuEnableSharedOnThis, AuSharedFromThis, etc idoms to migrate existing code
to an alternative non-std::shared_ptr backed system.
AuSPtr and friends are reducable and constructible from and from their std::shared_ptr/unique counterparts.
```
Types:
@ -342,34 +362,11 @@ Macros:
AuFunction<...> AuBindThis(This_t *::?, ...)
```
### Note
Aurora provides a bring your own container and shared pointer model overloadable in your configuration header.<br>
User-overloadable type declerations and generic access utilities are defined under [auROXTL](https://git.reece.sx/AuroraSupport/auROXTL)
Defer to [auROXTL](https://git.reece.sx/AuroraSupport/auROXTL) for more information
## Debug
### Error Markers
SysPushError[EFailureCategory shorthand] can be used to include additional side-channel telemetry
information about the execution of a program. SysPushError_(...) takes a string format sequence and
a variadic sequence of substitute values - or no arguments whatsoever.
#### Example:
```cpp
IBufferedCharacterConsumer *BufferConsumerFromProviderNew(const AuSPtr<ICharacterProvider> &provider)
{
if (!provider)
{
SysPushErrorArg("Missing ICharacterProvider");
return {};
}
return _new BufferedCharacterConsumer(provider);
}
```
### Asserts
[TODO]
@ -385,38 +382,46 @@ Debug and Release (debug and optimized ship-with-debug) assertions:
SysAssertDbg(AuFunction{}, "unexpected default function")
```
### Something went wrong
### Error stack
You should ensure AuDebug::CheckErrors() or a SysPushError-like function is called to ensure enchanced TLS-state telemetry is
captured. AuDebug::PrintErrors() will print the the errors gathered by the debug subsystem for telemetry purposes. These may
include the crts errno, the last reported posix return value, the last Win32 error code, and/or last reported microkernel error.
#### Example
Try/Catch:
```
try
```cpp
TEST(ErrorStack, A)
{
AuErrorStack errors;
SysPushErrorIO("Something something IO error");
SysPushErrorIO("Something something IO error 1");
SysPushErrorIO("Something something IO error 2 {}", "hello worlds");
ASSERT_TRUE(errors.HasCaptured());
ASSERT_TRUE(errors.HasMultipleOccurred());
ASSERT_TRUE(errors.FirstMessage()->pNextThreadMesage);
ASSERT_TRUE(errors.FirstMessage()->pNextThreadMesage->pNextThreadMesage);
AuLogDbg("{}, {}, {}",
*errors.ToString(),
errors.FirstMessage()->pNextThreadMesage->ToString(),
errors.FirstMessage()->pNextThreadMesage->pNextThreadMesage->ToString());
}
catch (...)
TEST(ErrorStack, B)
{
SysPushErrorCatch(); // THIS IS NOT REQUIRED FOR EXCEPTION TELEMETRY
AuErrorStack errors;
try
{
AU_THROW_FORMATTED("hello people {}", 23423423);
//throw "hello modern platforms";
}
catch (...)
{
}
ASSERT_TRUE(errors.HasCaptured());
AuLogDbg("{}", *errors.ToString());
}
```
[More](https://gitea.reece.sx/AuroraSupport/HelloAurora/src/branch/master/Tests/Public/19.%20Hello%20Debug/Main.cpp)
Windows System Error Messages: \
CRT:
```cpp
```
POSIX:
```cpp
```
## Binding
Aurora Runtime provides C++ APIs; however, it should be noted that two libraries are used to extend interfaces and enums
@ -448,9 +453,12 @@ can lead to some memory leaks.
## Aurora Async
The Aurora Runtime offers an optional asynchronous task driven model under the AuAsync namespace. Featuring promises,
thread group pooling, functional-to-task wrapping, and task-completion callback-task-dispatch idioms built around 3
concepts.
[TODO]
- Promises
- Thread Pool
- Optional shutdown on work exhaustion
- Wait for task[s] completion builtin to work objects
- IO subsystem interop
Example:
@ -534,4 +542,4 @@ Utility (third party)
->> Writing a [D]TLS/allocator stack would take too much time<br>
->> Linking against external allocators, small cross-platform utilities, and <br>
so on is probably fine <br>
-> Shim libcurl instead of inventing yet another http stack <br>
-> Shim libcurl instead of inventing yet another http stack <br>

View File

@ -9,6 +9,11 @@
#include "AuNetAdapter.hpp"
#include "AuNetEndpoint.hpp"
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
namespace Aurora::IO::Net
{
AuString NetAdapter::GetHostname()
@ -22,13 +27,478 @@ namespace Aurora::IO::Net
return name;
}
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
struct NetlinkDevice
{
NetlinkDevice();
~NetlinkDevice();
bool Connect();
AuSInt Send(const void *pBuffer, size_t uLength);
AuSInt Recv(void *pBuffer, size_t uLength);
AuList<NetAdapter> GetAdaptersForFamily(sa_family_t family);
AuList<AuPair<int, NetEndpoint>> ReadRoutes(int index, int family, AuString &name);
void UpdateAdapterInfo(int family, NetAdapter &adapter);
int fd { -1 };
};
static NetlinkDevice gNetlinkDevice;
NetlinkDevice::NetlinkDevice()
{
}
NetlinkDevice::~NetlinkDevice()
{
if (this->fd != -1)
{
::close(this->fd);
}
}
bool NetlinkDevice::Connect()
{
sockaddr_nl nladdr {};
this->fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (this->fd < 0)
{
return false;
}
int flags = ::fcntl(this->fd, F_GETFL, 0);
if (flags != -1)
{
::fcntl(this->fd, F_SETFL, flags | FD_CLOEXEC);
}
nladdr.nl_family = AF_NETLINK;
if (::bind(this->fd, (struct sockaddr *) &nladdr, sizeof(nladdr)) != 0)
{
return false;
}
return true;
}
AuSInt NetlinkDevice::Send(const void *pBuffer, size_t uLength)
{
sockaddr_nl nladdr {};
msghdr msg {};
iovec vec {};
nladdr.nl_family = AF_NETLINK;
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
vec.iov_base = (char *)pBuffer;
vec.iov_len = uLength;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
return ::sendmsg(fd, &msg, 0);
}
AuSInt NetlinkDevice::Recv(void *pBuffer, size_t uLength)
{
sockaddr_nl nladdr {};
msghdr msg {};
iovec vec;
nladdr.nl_family = AF_NETLINK;
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
vec.iov_base = pBuffer;
vec.iov_len = uLength;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
auto ret = ::recvmsg(fd, &msg, 0);
if (msg.msg_flags & MSG_TRUNC)
{
return -1;
}
return ret;
}
struct addr_t
{
sa_family_t family;
union
{
in_addr in_addr;
in6_addr in6_addr;
} u;
};
AuList<NetAdapter> NetlinkDevice::GetAdaptersForFamily(sa_family_t family)
{
static const auto kRouteBufferSize = 12 * 1024;
struct nlmsghdr *hdr;
struct rtmsg *rt;
ssize_t len;
size_t reqlen;
reqlen = NLMSG_SPACE(sizeof(*rt));
auto pReq = AuMakeSharedArray<AuUInt8>(kRouteBufferSize);
hdr = (struct nlmsghdr *)pReq.get();
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*rt));
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
hdr->nlmsg_type = RTM_GETROUTE;
rt = (struct rtmsg *)NLMSG_DATA(hdr);
rt->rtm_family = family;
rt->rtm_table = RT_TABLE_MAIN | RT_TABLE_LOCAL;
if (this->Send(pReq.get(), reqlen) < 0)
{
SysPushErrorIO("netlink error");
return {};
}
auto pResp = AuMakeSharedArray<AuUInt8>(kRouteBufferSize);
if (!pResp)
{
SysPushErrorMemory();
return {};
}
AuList<NetAdapter> ret;
while (1)
{
len = this->Recv(pResp.get(), kRouteBufferSize);
if (len < 0)
{
SysPushErrorIO("netlink error");
return {};
}
for (hdr = (struct nlmsghdr *)pResp.get();
NLMSG_OK(hdr, len);
hdr = NLMSG_NEXT(hdr, len))
{
NetAdapter adapter;
if (hdr->nlmsg_type == NLMSG_DONE)
{
return ret;
}
if (hdr->nlmsg_type == NLMSG_ERROR)
{
return {};
}
rt = (struct rtmsg *)NLMSG_DATA(hdr);
auto attr = RTM_RTA(rt);
auto attrlen = RTM_PAYLOAD(hdr);
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen))
{
const size_t expectedAddressLength = (family == AF_INET) ? 4 : 16;
size_t dstlen = RTA_PAYLOAD(attr);
switch (attr->rta_type)
{
case RTA_GATEWAY:
case RTA_DST:
case RTA_SRC:
{
if (dstlen != expectedAddressLength)
{
continue;
}
NetEndpoint ep;
addr_t *address2 = (addr_t*)ep.hint;
address2->family = family;
AuMemcpy(&address2->u.in_addr, RTA_DATA(attr), dstlen);
DeoptimizeEndpoint(ep);
if (attr->rta_type == RTA_GATEWAY)
{
adapter.gateway = ep.ip;
}
if (attr->rta_type == RTA_DST)
{
adapter.address = ep.ip;
}
break;
}
case RTA_OIF:
{
adapter.index = *(int*)RTA_DATA(attr);
break;
}
default:
{
break;
}
}
}
ret.push_back(adapter);
}
}
return ret;
}
AuList<AuPair<int, NetEndpoint>> NetlinkDevice::ReadRoutes(int index, int family, AuString &name)
{
static const auto kAddrBufferSize = 12 * 1024;
AuList<AuPair<int, NetEndpoint>> ret;
nlmsghdr *hdr {};
ifaddrmsg *ifa {};
ssize_t len;
size_t reqlen;
/* Allocate space for the request. */
reqlen = NLMSG_SPACE(sizeof(*ifa));
auto pReq = AuMakeSharedArray<AuUInt8>(kAddrBufferSize);
hdr = (struct nlmsghdr *)pReq.get();
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa));
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
hdr->nlmsg_type = RTM_GETADDR;
ifa = (struct ifaddrmsg *)NLMSG_DATA(hdr);
ifa->ifa_family = family;
ifa->ifa_index = index;
if (this->Send(pReq.get(), reqlen) < 0)
{
SysPushErrorIO("netlink error");
return {};
}
auto pResp = AuMakeSharedArray<AuUInt8>(kAddrBufferSize);
if (!pResp)
{
SysPushErrorMemory();
return {};
}
while (1)
{
len = this->Recv(pResp.get(), kAddrBufferSize);
if (len < 0)
{
SysPushErrorIO("netlink error");
return {};
}
for (hdr = (struct nlmsghdr *)pResp.get(); NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len))
{
if (hdr->nlmsg_type == NLMSG_DONE)
{
return ret;
}
if (hdr->nlmsg_type == NLMSG_ERROR)
{
return {};
}
ifa = (struct ifaddrmsg *)NLMSG_DATA(hdr);
if ((ifa->ifa_family != family) ||
(ifa->ifa_index != index))
{
continue;
}
if (ifa->ifa_scope != RT_SCOPE_UNIVERSE)
{
continue;
}
auto attr = IFA_RTA(ifa);
auto attrlen = RTM_PAYLOAD(hdr);
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen))
{
size_t dstlen = RTA_PAYLOAD(attr);
const size_t addrlen = (family == AF_INET) ? sizeof(struct in_addr) : sizeof(struct in6_addr);
if (attr->rta_type == IFA_LABEL)
{
name = (const char *)RTA_DATA(attr);
}
else if (addrlen == dstlen)
{
NetEndpoint ep;
addr_t *address2 = (addr_t*)ep.hint;
address2->family = family;
AuMemcpy(&address2->u.in_addr, RTA_DATA(attr), addrlen);
DeoptimizeEndpoint(ep);
ret.push_back(AuMakePair(attr->rta_type, ep));
}
}
}
}
return ret;
}
void NetlinkDevice::UpdateAdapterInfo(int family, NetAdapter &adapter)
{
static const auto kAddrBufferSize = 12 * 1024;
nlmsghdr *hdr {};
ifinfomsg *ifa {};
auto reqlen = NLMSG_SPACE(sizeof(*ifa));
auto pReq = AuMakeSharedArray<AuUInt8>(kAddrBufferSize);
hdr = (struct nlmsghdr *)pReq.get();
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa));
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
hdr->nlmsg_type = RTM_GETLINK;
ifa = (struct ifinfomsg *)NLMSG_DATA(hdr);
ifa->ifi_family = family;
ifa->ifi_index = adapter.index;
if (this->Send(pReq.get(), reqlen) < 0)
{
SysPushErrorIO("netlink error");
return;
}
auto pResp = AuMakeSharedArray<AuUInt8>(kAddrBufferSize);
if (!pResp)
{
SysPushErrorMemory();
return ;
}
while (1)
{
auto len = this->Recv(pResp.get(), kAddrBufferSize);
if (len < 0)
{
SysPushErrorIO("netlink error");
return;
}
for (hdr = (struct nlmsghdr *)pResp.get(); NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len))
{
if (hdr->nlmsg_type == NLMSG_DONE)
{
return;
}
if (hdr->nlmsg_type == NLMSG_ERROR)
{
return;
}
ifa = (struct ifinfomsg *)NLMSG_DATA(hdr);
if ((ifa->ifi_index != adapter.index))
{
continue;
}
auto attr = IFLA_RTA(ifa);
auto attrlen = hdr->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) {
size_t dstlen = RTA_PAYLOAD(attr);
const size_t addrlen = (family == AF_INET) ? sizeof(struct in_addr) : sizeof(struct in6_addr);
if (attr->rta_type == IFLA_IFNAME)
{
adapter.device = (const char *)RTA_DATA(attr);
}
// TODO: we can pull MTU and other stats from here.
}
}
}
}
static AuList<AuSPtr<INetAdapter>> GetForFamily(int family)
{
AuList<AuSPtr<INetAdapter>> ret;
if (gNetlinkDevice.fd == -1)
{
if (!gNetlinkDevice.Connect())
{
return {};
}
}
AuList<IPAddress> dups;
auto adapters = gNetlinkDevice.GetAdaptersForFamily(family);
for (auto &adapter : adapters)
{
gNetlinkDevice.UpdateAdapterInfo(family, adapter);
auto pAdapter = AuMakeShared<NetAdapter>(adapter);
if (!pAdapter)
{
SysPushErrorMemory();
return {};
}
bool bBreak = false;
auto ips = gNetlinkDevice.ReadRoutes(adapter.index, family, pAdapter->name);
for (const auto &[type, endpoint] : ips)
{
if (type == IFA_ANYCAST)
{
pAdapter->anycast = endpoint.ip;
}
if (type == IFA_ADDRESS)
{
bBreak |= AuExists(dups, endpoint.ip);
dups.push_back(endpoint.ip);
pAdapter->address = endpoint.ip;
}
if (type == IFA_BROADCAST)
{
pAdapter->broadcast = endpoint.ip;
}
}
if (bBreak)
{
continue;
}
ret.push_back(pAdapter);
}
return ret;
}
AuList<AuSPtr<INetAdapter>> NetAdapter::GetIPv4s()
{
return {};
return GetForFamily(AF_INET);
}
AuList<AuSPtr<INetAdapter>> NetAdapter::GetIPv6s()
{
return {};
return GetForFamily(AF_INET6);
}
}

View File

@ -56,7 +56,7 @@ namespace Aurora::IO::Net
return true;
#endif
#if defined(AURORA_PLATFORM_LINUX)
//return true;
return true;
#endif
return false;
}