net: AF_NETLINK, so getifaddrs() works for everything and not just one program
fastfetch reported "getifaddrs(&ifAddrStruct) failed", and the right answer to
that is not a patch to fastfetch. Asking what interfaces a machine has is
something every networked program does, and on Linux there is exactly one way:
a NETLINK_ROUTE socket. musl implements getifaddrs() over it, so a libc call
that looks like a syscall is really a small protocol conversation -- and without
this side of it every caller fails the same way.
kernel/net/netlink.c is a responder rather than a general socket family. No
event stream, no multicast groups, nothing subscribes to link state: a request
arrives, the whole answer is generated into the socket's buffer, and reads drain
it. That shape is chosen, not a shortcut -- getifaddrs sends a dump and reads
until NLMSG_DONE with MSG_DONTWAIT, so generating at send time means a read
never blocks and the non-blocking flag is honoured for free.
RTM_GETLINK returns one RTM_NEWLINK per interface with IFLA_IFNAME and
IFLA_ADDRESS; RTM_GETADDR returns one RTM_NEWADDR per address with IFA_ADDRESS,
IFA_LOCAL and IFA_LABEL. Anything else gets a bare NLMSG_DONE, because the
caller's next move is a read either way and an unexpected error message is worse
than nothing to read.
The data is not netlink's. Interfaces come from the netdev class and addresses
from lwIP's netifs, exactly as /proc/netif reads them; nothing in the file knows
what an e1000 is. Interface indices matter more than they look -- getifaddrs
matches address records to link records by index, so the two dumps have to
agree. Loopback is 1, netdevs follow from 2.
Verified by reading it back through musl rather than by inspecting the bytes:
lo flags=0x49 fam=-1
eth0 flags=0x1043 fam=17 (AF_PACKET, the MAC)
lo flags=0x49 fam=2 addr=127.0.0.1 mask=255.0.0.0
eth0 flags=0x1043 fam=2 addr=10.0.2.15 mask=255.255.255.0
Correct flags, addresses and netmasks, and the link records pair with the
address records.
One thing this does not close: fastfetch's Local IP module defaults to
default-route-only, which calls ffNetifGetDefaultRouteIfName() and reads
/proc/net/route. SchOS has no routing table and no such file, so the module
still shows nothing -- now for a reason that is about routing rather than about
enumeration. That is the next file to provide.
socktest still passes; the AF_UNIX and AF_INET paths are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
This commit is contained in:
parent
25ae0cfad9
commit
f395c39704
9 changed files with 462 additions and 6 deletions
|
|
@ -231,6 +231,44 @@ There is no per-socket echo-id filtering. Linux rewrites the id and matches on
|
|||
it; here a reply goes to every open ICMP socket, which is invisible with one
|
||||
ping running and means a second one may see a sequence number it never sent.
|
||||
|
||||
### AF_NETLINK, and why it exists
|
||||
|
||||
`getifaddrs(3)` is how every program asks *what interfaces does this machine
|
||||
have, and what addresses do they carry* — and on Linux there is exactly one
|
||||
answer: a `NETLINK_ROUTE` socket. musl implements the libc call over it, so what
|
||||
looks like a syscall is really a small protocol conversation, and without this
|
||||
side of it every caller fails identically.
|
||||
|
||||
`kernel/net/netlink.c` is a **responder**, not a general socket family. There is
|
||||
no kernel-to-userspace event stream, no multicast groups, nothing subscribes to
|
||||
link state. A request arrives, the whole answer is generated into the socket's
|
||||
buffer, and reads drain it.
|
||||
|
||||
That shape is deliberate. `getifaddrs` sends a dump request and reads until
|
||||
`NLMSG_DONE` with `MSG_DONTWAIT`, so generating the answer at send time means a
|
||||
read never blocks and the non-blocking flag is honoured for free.
|
||||
|
||||
Two message types are served — `RTM_GETLINK` returns one `RTM_NEWLINK` per
|
||||
interface (name, flags, MAC), `RTM_GETADDR` returns one `RTM_NEWADDR` per
|
||||
address (address, prefix length, label). Anything else gets a bare `NLMSG_DONE`.
|
||||
|
||||
**The data is not netlink's.** Interfaces come from the netdev class and
|
||||
addresses from lwIP's netifs, exactly as `/proc/netif` reads them. Nothing in
|
||||
the file knows what an e1000 is.
|
||||
|
||||
Interface indices matter more than they look: `getifaddrs` matches address
|
||||
records to link records *by index*, so the two dumps have to agree. Loopback is
|
||||
1 and the netdevs follow from 2.
|
||||
|
||||
What it produces, read back through musl:
|
||||
|
||||
```
|
||||
lo flags=0x49 fam=-1
|
||||
eth0 flags=0x1043 fam=17 (AF_PACKET, the MAC)
|
||||
lo flags=0x49 fam=2 addr=127.0.0.1 mask=255.0.0.0
|
||||
eth0 flags=0x1043 fam=2 addr=10.0.2.15 mask=255.255.255.0
|
||||
```
|
||||
|
||||
### Loopback
|
||||
|
||||
`127.0.0.1` is a real destination: `LWIP_HAVE_LOOPIF` creates the interface and
|
||||
|
|
@ -259,9 +297,15 @@ be fixed before `recvfrom` could report a source address at all.
|
|||
- **No resolver reachable from userland.** lwIP's DNS client is compiled but
|
||||
nothing exposes it, so `ping` takes a dotted quad. `localhost` is special-cased
|
||||
in the program rather than resolved.
|
||||
- **No routing table.** With two interfaces up, the first is the default route
|
||||
and the second is reachable only by an address on its own subnet. That is a
|
||||
rule rather than an accident, and it is not routing.
|
||||
- **No routing table**, and no `/proc/net/route` to report one. With two
|
||||
interfaces up, the first is the default route and the second is reachable only
|
||||
by an address on its own subnet. That is a rule rather than an accident, and it
|
||||
is not routing. The missing *file* is the more immediately visible half: it is
|
||||
how a program asks which interface carries the default route, and without it
|
||||
anything that filters on "the default one" sees nothing.
|
||||
- **Netlink serves two message types.** No `RTM_GETROUTE`, no `RTM_NEWROUTE`, no
|
||||
event stream, no multicast groups. A program that expects to *subscribe* to
|
||||
link changes gets a socket that answers dumps and never speaks again.
|
||||
- **No `getsockname`/`getpeername`**, and `accept()` does not report the peer's
|
||||
address for an AF_INET socket. A server that wants to log who connected cannot.
|
||||
- **`setsockopt` covers six options**, and refuses the rest with `ENOPROTOOPT`
|
||||
|
|
|
|||
Loading…
Reference in a new issue